mirror of
https://github.com/flarum/framework.git
synced 2024-11-22 12:48:28 +08:00
Add typechecks, typescript coverage GH action, fix many type errors (#3136)
This commit is contained in:
parent
563d40d7da
commit
bac0e594ee
50
.github/workflows/js.yml
vendored
50
.github/workflows/js.yml
vendored
|
@ -29,10 +29,56 @@ jobs:
|
|||
run: yarn run format-check
|
||||
working-directory: ./js
|
||||
|
||||
typecheck:
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: "yarn"
|
||||
cache-dependency-path: js/yarn.lock
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
working-directory: ./js
|
||||
|
||||
- name: Typecheck
|
||||
run: yarn run check-typings || true # REMOVE THIS ONCE TYPE SAFETY REACHED
|
||||
working-directory: ./js
|
||||
|
||||
type-coverage:
|
||||
name: Type Coverage
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: "yarn"
|
||||
cache-dependency-path: js/yarn.lock
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
working-directory: ./js
|
||||
|
||||
- name: Check type coverage
|
||||
run: yarn run check-typings-coverage
|
||||
working-directory: ./js
|
||||
|
||||
build-prod:
|
||||
name: Build and commit
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prettier]
|
||||
needs: [prettier, typecheck, type-coverage]
|
||||
|
||||
# Only commit JS on push to master branch
|
||||
# Remember to change in `build-test` job too
|
||||
|
@ -62,7 +108,7 @@ jobs:
|
|||
build-test:
|
||||
name: Test build
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prettier]
|
||||
needs: [prettier, typecheck, type-coverage]
|
||||
|
||||
# Inverse check of `build-prod`
|
||||
# Remember to change in `build-prod` job too
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ tests/.phpunit.result.cache
|
|||
.vagrant
|
||||
.idea/*
|
||||
.vscode
|
||||
js/coverage-ts
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"flarum-webpack-config": "^2.0.0",
|
||||
"prettier": "^2.4.1",
|
||||
"typescript": "^4.4.4",
|
||||
"typescript-coverage-report": "^0.6.1",
|
||||
"webpack": "^5.61.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-merge": "^5.8.0"
|
||||
|
@ -41,7 +42,9 @@
|
|||
"format": "prettier --write src",
|
||||
"format-check": "prettier --check src",
|
||||
"clean-typings": "npx rimraf dist-typings && mkdir dist-typings",
|
||||
"build-typings": "npm run clean-typings && cp -r src/@types dist-typings/@types && tsc"
|
||||
"build-typings": "npm run clean-typings && cp -r src/@types dist-typings/@types && tsc",
|
||||
"check-typings": "tsc --noEmit --emitDeclarationOnly false",
|
||||
"check-typings-coverage": "typescript-coverage-report"
|
||||
},
|
||||
"packageManager": "yarn@3.1.0"
|
||||
}
|
||||
|
|
7
js/src/@types/global.d.ts
vendored
7
js/src/@types/global.d.ts
vendored
|
@ -98,3 +98,10 @@ interface JSX {
|
|||
attrs: Record<string, unknown>;
|
||||
};
|
||||
}
|
||||
|
||||
interface Event {
|
||||
/**
|
||||
* Whether this event should trigger a Mithril redraw.
|
||||
*/
|
||||
redraw: boolean;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,32 @@ import Navigation from '../common/components/Navigation';
|
|||
import AdminNav from './components/AdminNav';
|
||||
import ExtensionData from './utils/ExtensionData';
|
||||
|
||||
export type Extension = {
|
||||
id: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
icon?: {
|
||||
name: string;
|
||||
};
|
||||
links: {
|
||||
authors?: {
|
||||
name?: string;
|
||||
link?: string;
|
||||
}[];
|
||||
discuss?: string;
|
||||
documentation?: string;
|
||||
support?: string;
|
||||
website?: string;
|
||||
donate?: string;
|
||||
source?: string;
|
||||
};
|
||||
extra: {
|
||||
'flarum-extension': {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export default class AdminApplication extends Application {
|
||||
extensionData = new ExtensionData();
|
||||
|
||||
|
@ -24,6 +50,20 @@ export default class AdminApplication extends Application {
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings are serialized to the admin dashboard as strings.
|
||||
* Additional encoding/decoding is possible, but must take
|
||||
* place on the client side.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
||||
data!: Application['data'] & {
|
||||
extensions: Record<string, Extension>;
|
||||
settings: Record<string, string>;
|
||||
modelStatistics: Record<string, { total: number }>;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
@ -41,20 +81,20 @@ export default class AdminApplication extends Application {
|
|||
m.route.prefix = '#';
|
||||
super.mount();
|
||||
|
||||
m.mount(document.getElementById('app-navigation'), {
|
||||
m.mount(document.getElementById('app-navigation')!, {
|
||||
view: () =>
|
||||
Navigation.component({
|
||||
className: 'App-backControl',
|
||||
drawer: true,
|
||||
}),
|
||||
});
|
||||
m.mount(document.getElementById('header-navigation'), Navigation);
|
||||
m.mount(document.getElementById('header-primary'), HeaderPrimary);
|
||||
m.mount(document.getElementById('header-secondary'), HeaderSecondary);
|
||||
m.mount(document.getElementById('admin-navigation'), AdminNav);
|
||||
m.mount(document.getElementById('header-navigation')!, Navigation);
|
||||
m.mount(document.getElementById('header-primary')!, HeaderPrimary);
|
||||
m.mount(document.getElementById('header-secondary')!, HeaderSecondary);
|
||||
m.mount(document.getElementById('admin-navigation')!, AdminNav);
|
||||
}
|
||||
|
||||
getRequiredPermissions(permission) {
|
||||
getRequiredPermissions(permission: string) {
|
||||
const required = [];
|
||||
|
||||
if (permission === 'startDiscussion' || permission.indexOf('discussion.') === 0) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import Admin from './AdminApplication';
|
|||
|
||||
const app = new Admin();
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-expect-error We need to do this for backwards compatibility purposes.
|
||||
window.app = app;
|
||||
|
||||
export default app;
|
||||
|
|
|
@ -12,26 +12,39 @@ import ExtensionPermissionGrid from './ExtensionPermissionGrid';
|
|||
import isExtensionEnabled from '../utils/isExtensionEnabled';
|
||||
import AdminPage from './AdminPage';
|
||||
import ReadmeModal from './ReadmeModal';
|
||||
import RequestError from '../../common/utils/RequestError';
|
||||
import { Extension } from '../AdminApplication';
|
||||
import { IPageAttrs } from '../../common/components/Page';
|
||||
import type Mithril from 'mithril';
|
||||
|
||||
export default class ExtensionPage extends AdminPage {
|
||||
oninit(vnode) {
|
||||
export interface ExtensionPageAttrs extends IPageAttrs {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> extends AdminPage<Attrs> {
|
||||
extension!: Extension;
|
||||
|
||||
changingState = false;
|
||||
|
||||
infoFields = {
|
||||
discuss: 'fas fa-comment-alt',
|
||||
documentation: 'fas fa-book',
|
||||
support: 'fas fa-life-ring',
|
||||
website: 'fas fa-link',
|
||||
donate: 'fas fa-donate',
|
||||
source: 'fas fa-code',
|
||||
};
|
||||
|
||||
oninit(vnode: Mithril.Vnode<Attrs, this>) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.extension = app.data.extensions[this.attrs.id];
|
||||
this.changingState = false;
|
||||
const extension = app.data.extensions[this.attrs.id];
|
||||
|
||||
this.infoFields = {
|
||||
discuss: 'fas fa-comment-alt',
|
||||
documentation: 'fas fa-book',
|
||||
support: 'fas fa-life-ring',
|
||||
website: 'fas fa-link',
|
||||
donate: 'fas fa-donate',
|
||||
source: 'fas fa-code',
|
||||
};
|
||||
|
||||
if (!this.extension) {
|
||||
if (!extension) {
|
||||
return m.route.set(app.route('dashboard'));
|
||||
}
|
||||
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
className() {
|
||||
|
@ -40,7 +53,7 @@ export default class ExtensionPage extends AdminPage {
|
|||
return this.extension.id + '-Page';
|
||||
}
|
||||
|
||||
view() {
|
||||
view(vnode: Mithril.VnodeDOM<Attrs, this>) {
|
||||
if (!this.extension) return null;
|
||||
|
||||
return (
|
||||
|
@ -51,7 +64,7 @@ export default class ExtensionPage extends AdminPage {
|
|||
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.enable_to_see')}</h3>
|
||||
</div>
|
||||
) : (
|
||||
<div className="ExtensionPage-body">{this.sections().toArray()}</div>
|
||||
<div className="ExtensionPage-body">{this.sections(vnode).toArray()}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -92,10 +105,10 @@ export default class ExtensionPage extends AdminPage {
|
|||
];
|
||||
}
|
||||
|
||||
sections() {
|
||||
sections(vnode: Mithril.VnodeDOM<Attrs, this>) {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('content', this.content());
|
||||
items.add('content', this.content(vnode));
|
||||
|
||||
items.add('permissions', [
|
||||
<div className="ExtensionPage-permissions">
|
||||
|
@ -117,7 +130,7 @@ export default class ExtensionPage extends AdminPage {
|
|||
return items;
|
||||
}
|
||||
|
||||
content() {
|
||||
content(vnode: Mithril.VnodeDOM<Attrs, this>) {
|
||||
const settings = app.extensionData.getSettings(this.extension.id);
|
||||
|
||||
return (
|
||||
|
@ -126,7 +139,7 @@ export default class ExtensionPage extends AdminPage {
|
|||
{settings ? (
|
||||
<div className="Form">
|
||||
{settings.map(this.buildSettingComponent.bind(this))}
|
||||
<div className="Form-group">{this.submitButton()}</div>
|
||||
<div className="Form-group">{this.submitButton(vnode)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.no_settings')}</h3>
|
||||
|
@ -172,21 +185,17 @@ export default class ExtensionPage extends AdminPage {
|
|||
|
||||
const links = this.extension.links;
|
||||
|
||||
if (links.authors.length) {
|
||||
let authors = [];
|
||||
|
||||
links.authors.map((author) => {
|
||||
authors.push(
|
||||
<Link href={author.link} external={true} target="_blank">
|
||||
{author.name}
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
if (links.authors?.length) {
|
||||
const authors = links.authors.map((author) => (
|
||||
<Link href={author.link} external={true} target="_blank">
|
||||
{author.name}
|
||||
</Link>
|
||||
));
|
||||
|
||||
items.add('authors', [icon('fas fa-user'), <span>{punctuateSeries(authors)}</span>]);
|
||||
}
|
||||
|
||||
Object.keys(this.infoFields).map((field) => {
|
||||
(Object.keys(this.infoFields) as (keyof ExtensionPage['infoFields'])[]).map((field) => {
|
||||
if (links[field]) {
|
||||
items.add(
|
||||
field,
|
||||
|
@ -240,7 +249,7 @@ export default class ExtensionPage extends AdminPage {
|
|||
return isExtensionEnabled(this.extension.id);
|
||||
}
|
||||
|
||||
onerror(e) {
|
||||
onerror(e: RequestError) {
|
||||
// We need to give the modal animation time to start; if we close the modal too early,
|
||||
// it breaks the bootstrap modal library.
|
||||
// TODO: This workaround should be removed when we move away from bootstrap JS for modals.
|
||||
|
@ -254,14 +263,16 @@ export default class ExtensionPage extends AdminPage {
|
|||
throw e;
|
||||
}
|
||||
|
||||
const error = e.response.errors[0];
|
||||
const error = e.response?.errors?.[0];
|
||||
|
||||
app.alerts.show(
|
||||
{ type: 'error' },
|
||||
app.translator.trans(`core.lib.error.${error.code}_message`, {
|
||||
extension: error.extension,
|
||||
extensions: error.extensions.join(', '),
|
||||
})
|
||||
);
|
||||
if (error) {
|
||||
app.alerts.show(
|
||||
{ type: 'error' },
|
||||
app.translator.trans(`core.lib.error.${error.code}_message`, {
|
||||
extension: error.extension,
|
||||
extensions: (error.extensions as string[]).join(', '),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import app from '../../admin/app';
|
||||
import Modal from '../../common/components/Modal';
|
||||
|
||||
export default class LoadingModal extends Modal {
|
||||
export default class LoadingModal<ModalAttrs = {}> extends Modal<ModalAttrs> {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
static isDismissible = false;
|
||||
static readonly isDismissible: boolean = false;
|
||||
|
||||
className() {
|
||||
return 'LoadingModal Modal--small';
|
||||
|
@ -18,4 +18,8 @@ export default class LoadingModal extends Modal {
|
|||
content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
onsubmit(e: Event): void {
|
||||
throw new Error('LoadingModal should not throw errors.');
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ export { app };
|
|||
import compatObj from './compat';
|
||||
import proxifyCompat from '../common/utils/proxifyCompat';
|
||||
|
||||
// @ts-expect-error The `app` instance needs to be available on compat.
|
||||
compatObj.app = app;
|
||||
|
||||
export const compat = proxifyCompat(compatObj, 'admin');
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import app from '../../admin/app';
|
||||
import DefaultResolver from '../../common/resolvers/DefaultResolver';
|
||||
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
|
||||
|
||||
/**
|
||||
* A custom route resolver for ExtensionPage that generates handles routes
|
||||
* to default extension pages or a page provided by an extension.
|
||||
*/
|
||||
export default class ExtensionPageResolver extends DefaultResolver {
|
||||
export default class ExtensionPageResolver<
|
||||
Attrs extends ExtensionPageAttrs = ExtensionPageAttrs,
|
||||
RouteArgs extends Record<string, unknown> = {}
|
||||
> extends DefaultResolver<Attrs, ExtensionPage<Attrs>, RouteArgs> {
|
||||
static extension: string | null = null;
|
||||
|
||||
onmatch(args, requestedPath, route) {
|
||||
onmatch(args: Attrs & RouteArgs, requestedPath: string, route: string) {
|
||||
const extensionPage = app.extensionData.getPage(args.id);
|
||||
|
||||
if (extensionPage) {
|
||||
|
|
|
@ -11,9 +11,10 @@ import Session from './Session';
|
|||
import extract from './utils/extract';
|
||||
import Drawer from './utils/Drawer';
|
||||
import mapRoutes from './utils/mapRoutes';
|
||||
import RequestError from './utils/RequestError';
|
||||
import RequestError, { InternalFlarumRequestOptions } from './utils/RequestError';
|
||||
import ScrollListener from './utils/ScrollListener';
|
||||
import liveHumanTimes from './utils/liveHumanTimes';
|
||||
// @ts-expect-error We need to explicitly use the prefix to distinguish between the extend folder.
|
||||
import { extend } from './extend.ts';
|
||||
|
||||
import Forum from './models/Forum';
|
||||
|
@ -40,7 +41,7 @@ export type FlarumGenericRoute = RouteItem<
|
|||
>;
|
||||
|
||||
export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.RequestOptions<ResponseType>, 'extract'> {
|
||||
errorHandler: (errorMessage: string) => void;
|
||||
errorHandler?: (error: RequestError) => void;
|
||||
url: string;
|
||||
// TODO: [Flarum 2.0] Remove deprecated option
|
||||
/**
|
||||
|
@ -48,13 +49,13 @@ export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.Request
|
|||
*
|
||||
* @deprecated Please use `modifyText` instead.
|
||||
*/
|
||||
extract: (responseText: string) => string;
|
||||
extract?: (responseText: string) => string;
|
||||
/**
|
||||
* Manipulate the response text before it is parsed into JSON.
|
||||
*
|
||||
* This overrides any `extract` method provided.
|
||||
*/
|
||||
modifyText: (responseText: string) => string;
|
||||
modifyText?: (responseText: string) => string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,12 +249,12 @@ export default class Application {
|
|||
|
||||
initialRoute!: string;
|
||||
|
||||
load(payload: Application['data']) {
|
||||
public load(payload: Application['data']) {
|
||||
this.data = payload;
|
||||
this.translator.setLocale(payload.locale);
|
||||
}
|
||||
|
||||
boot() {
|
||||
public boot() {
|
||||
this.initializers.toArray().forEach((initializer) => initializer(this));
|
||||
|
||||
this.store.pushPayload({ data: this.data.resources });
|
||||
|
@ -268,7 +269,7 @@ export default class Application {
|
|||
}
|
||||
|
||||
// TODO: This entire system needs a do-over for v2
|
||||
bootExtensions(extensions: Record<string, { extend?: unknown[] }>) {
|
||||
public bootExtensions(extensions: Record<string, { extend?: unknown[] }>) {
|
||||
Object.keys(extensions).forEach((name) => {
|
||||
const extension = extensions[name];
|
||||
|
||||
|
@ -278,12 +279,13 @@ export default class Application {
|
|||
const extenders = extension.extend.flat(Infinity);
|
||||
|
||||
for (const extender of extenders) {
|
||||
// @ts-expect-error This is beyond saving atm.
|
||||
extender.extend(this, { name, exports: extension });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mount(basePath: string = '') {
|
||||
protected mount(basePath: string = '') {
|
||||
// An object with a callable view property is used in order to pass arguments to the component; see https://mithril.js.org/mount.html
|
||||
m.mount(document.getElementById('modal')!, { view: () => ModalManager.component({ state: this.modal }) });
|
||||
m.mount(document.getElementById('alerts')!, { view: () => AlertManager.component({ state: this.alerts }) });
|
||||
|
@ -367,22 +369,36 @@ export default class Application {
|
|||
document.title = count + pageTitleWithSeparator + title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an AJAX request, handling any low-level errors that may occur.
|
||||
*
|
||||
* @see https://mithril.js.org/request.html
|
||||
*
|
||||
* @param options
|
||||
* @return {Promise}
|
||||
*/
|
||||
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType | string> {
|
||||
const options = { ...originalOptions };
|
||||
protected transformRequestOptions<ResponseType>(flarumOptions: FlarumRequestOptions<ResponseType>): InternalFlarumRequestOptions<ResponseType> {
|
||||
const { background, deserialize, errorHandler, extract, modifyText, ...tmpOptions } = { ...flarumOptions };
|
||||
|
||||
// Set some default options if they haven't been overridden. We want to
|
||||
// authenticate all requests with the session token. We also want all
|
||||
// requests to run asynchronously in the background, so that they don't
|
||||
// prevent redraws from occurring.
|
||||
options.background ||= true;
|
||||
// Unless specified otherwise, requests should run asynchronously in the
|
||||
// background, so that they don't prevent redraws from occurring.
|
||||
const defaultBackground = true;
|
||||
|
||||
// When we deserialize JSON data, if for some reason the server has provided
|
||||
// a dud response, we don't want the application to crash. We'll show an
|
||||
// error message to the user instead.
|
||||
|
||||
// @ts-expect-error Typescript doesn't know we return promisified `ReturnType` OR `string`,
|
||||
// so it errors due to Mithril's typings
|
||||
const defaultDeserialize = (response: string) => response as ResponseType;
|
||||
|
||||
const defaultErrorHandler = (error: RequestError) => {
|
||||
throw error;
|
||||
};
|
||||
|
||||
// When extracting the data from the response, we can check the server
|
||||
// response code and show an error message to the user if something's gone
|
||||
// awry.
|
||||
const originalExtract = modifyText || extract;
|
||||
|
||||
const options: InternalFlarumRequestOptions<ResponseType> = {
|
||||
background: background ?? defaultBackground,
|
||||
deserialize: deserialize ?? defaultDeserialize,
|
||||
errorHandler: errorHandler ?? defaultErrorHandler,
|
||||
...tmpOptions,
|
||||
};
|
||||
|
||||
extend(options, 'config', (_: undefined, xhr: XMLHttpRequest) => {
|
||||
xhr.setRequestHeader('X-CSRF-Token', this.session.csrfToken!);
|
||||
|
@ -401,37 +417,19 @@ export default class Application {
|
|||
options.method = 'POST';
|
||||
}
|
||||
|
||||
// When we deserialize JSON data, if for some reason the server has provided
|
||||
// a dud response, we don't want the application to crash. We'll show an
|
||||
// error message to the user instead.
|
||||
|
||||
// @ts-expect-error Typescript doesn't know we return promisified `ReturnType` OR `string`,
|
||||
// so it errors due to Mithril's typings
|
||||
options.deserialize ||= (responseText: string) => responseText;
|
||||
|
||||
options.errorHandler ||= (error) => {
|
||||
throw error;
|
||||
};
|
||||
|
||||
// When extracting the data from the response, we can check the server
|
||||
// response code and show an error message to the user if something's gone
|
||||
// awry.
|
||||
const original = options.modifyText || options.extract;
|
||||
|
||||
// @ts-expect-error
|
||||
options.extract = (xhr: XMLHttpRequest) => {
|
||||
let responseText;
|
||||
|
||||
if (original) {
|
||||
responseText = original(xhr.responseText);
|
||||
if (originalExtract) {
|
||||
responseText = originalExtract(xhr.responseText);
|
||||
} else {
|
||||
responseText = xhr.responseText || null;
|
||||
responseText = xhr.responseText;
|
||||
}
|
||||
|
||||
const status = xhr.status;
|
||||
|
||||
if (status < 200 || status > 299) {
|
||||
throw new RequestError(`${status}`, `${responseText}`, options, xhr);
|
||||
throw new RequestError<ResponseType>(status, `${responseText}`, options, xhr);
|
||||
}
|
||||
|
||||
if (xhr.getResponseHeader) {
|
||||
|
@ -440,25 +438,38 @@ export default class Application {
|
|||
}
|
||||
|
||||
try {
|
||||
// @ts-expect-error
|
||||
return JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
throw new RequestError('500', `${responseText}`, options, xhr);
|
||||
throw new RequestError<ResponseType>(500, `${responseText}`, options, xhr);
|
||||
}
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an AJAX request, handling any low-level errors that may occur.
|
||||
*
|
||||
* @see https://mithril.js.org/request.html
|
||||
*
|
||||
* @param options
|
||||
* @return {Promise}
|
||||
*/
|
||||
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType | string> {
|
||||
const options = this.transformRequestOptions(originalOptions);
|
||||
|
||||
if (this.requestErrorAlert) this.alerts.dismiss(this.requestErrorAlert);
|
||||
|
||||
// Now make the request. If it's a failure, inspect the error that was
|
||||
// returned and show an alert containing its contents.
|
||||
return m.request(options).then(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
(error: RequestError) => {
|
||||
let content;
|
||||
|
||||
switch (error.status) {
|
||||
case 422:
|
||||
content = (error.response.errors as Record<string, unknown>[])
|
||||
content = ((error.response?.errors ?? {}) as Record<string, unknown>[])
|
||||
.map((error) => [error.detail, <br />])
|
||||
.flat()
|
||||
.slice(0, -1);
|
||||
|
@ -490,7 +501,7 @@ export default class Application {
|
|||
// contains a formatted errors if possible, response must be an JSON API array of errors
|
||||
// the details property is decoded to transform escaped characters such as '\n'
|
||||
const errors = error.response && error.response.errors;
|
||||
const formattedError = Array.isArray(errors) && errors[0] && errors[0].detail && errors.map((e) => decodeURI(e.detail));
|
||||
const formattedError = (Array.isArray(errors) && errors?.[0]?.detail && errors.map((e) => decodeURI(e.detail ?? ''))) || undefined;
|
||||
|
||||
error.alert = {
|
||||
type: 'error',
|
||||
|
@ -505,18 +516,22 @@ export default class Application {
|
|||
try {
|
||||
options.errorHandler(error);
|
||||
} catch (error) {
|
||||
if (isDebug && error.xhr) {
|
||||
const { method, url } = error.options;
|
||||
const { status = '' } = error.xhr;
|
||||
if (error instanceof RequestError) {
|
||||
if (isDebug && error.xhr) {
|
||||
const { method, url } = error.options;
|
||||
const { status = '' } = error.xhr;
|
||||
|
||||
console.group(`${method} ${url} ${status}`);
|
||||
console.group(`${method} ${url} ${status}`);
|
||||
|
||||
console.error(...(formattedError || [error]));
|
||||
console.error(...(formattedError || [error]));
|
||||
|
||||
console.groupEnd();
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
this.requestErrorAlert = this.alerts.show(error.alert, error.alert.content);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.requestErrorAlert = this.alerts.show(error.alert, error.alert.content);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
|
|
@ -121,7 +121,7 @@ export default abstract class Component<Attrs extends ComponentAttrs = Component
|
|||
*
|
||||
* @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children
|
||||
*/
|
||||
static component(attrs: Attrs = {}, children: Mithril.Children = null): Mithril.Vnode {
|
||||
static component<SAttrs extends ComponentAttrs = ComponentAttrs>(attrs: SAttrs = {} as SAttrs, children: Mithril.Children = null): Mithril.Vnode {
|
||||
const componentAttrs = { ...attrs };
|
||||
|
||||
return m(this as any, componentAttrs, children);
|
||||
|
|
|
@ -34,8 +34,8 @@ export default abstract class Fragment {
|
|||
* @returns {jQuery} the jQuery object for the DOM node
|
||||
* @final
|
||||
*/
|
||||
public $(selector) {
|
||||
const $element = $(this.element);
|
||||
public $(selector?: string): JQuery {
|
||||
const $element = $(this.element) as JQuery<HTMLElement>;
|
||||
|
||||
return selector ? $element.find(selector) : $element;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { RichMessageFormatter, mithrilRichHandler } from '@askvortsov/rich-icu-message-formatter';
|
||||
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
|
||||
import username from './helpers/username';
|
||||
import User from './models/User';
|
||||
import extract from './utils/extract';
|
||||
|
||||
type Translations = Record<string, string>;
|
||||
|
@ -50,7 +51,7 @@ export default class Translator {
|
|||
// translation key is used.
|
||||
|
||||
if ('user' in parameters) {
|
||||
const user = extract(parameters, 'user');
|
||||
const user = extract(parameters, 'user') as User;
|
||||
|
||||
if (!parameters.username) parameters.username = username(user);
|
||||
}
|
||||
|
|
|
@ -20,21 +20,21 @@ export interface AlertAttrs extends ComponentAttrs {
|
|||
* some controls, and may be dismissible.
|
||||
*/
|
||||
export default class Alert<T extends AlertAttrs = AlertAttrs> extends Component<T> {
|
||||
view(vnode: Mithril.Vnode) {
|
||||
view(vnode: Mithril.VnodeDOM<T, this>) {
|
||||
const attrs = Object.assign({}, this.attrs);
|
||||
|
||||
const type = extract(attrs, 'type');
|
||||
attrs.className = 'Alert Alert--' + type + ' ' + (attrs.className || '');
|
||||
|
||||
const content = extract(attrs, 'content') || vnode.children;
|
||||
const controls = (extract(attrs, 'controls') || []) as Mithril.ChildArray;
|
||||
const controls = (extract(attrs, 'controls') || []) as Mithril.Vnode[];
|
||||
|
||||
// If the alert is meant to be dismissible (which is the case by default),
|
||||
// then we will create a dismiss button to append as the final control in
|
||||
// the alert.
|
||||
const dismissible = extract(attrs, 'dismissible');
|
||||
const ondismiss = extract(attrs, 'ondismiss');
|
||||
const dismissControl = [];
|
||||
const dismissControl: Mithril.Vnode[] = [];
|
||||
|
||||
if (dismissible || dismissible === undefined) {
|
||||
dismissControl.push(<Button icon="fas fa-times" className="Button Button--link Button--icon Alert-dismiss" onclick={ondismiss} />);
|
||||
|
|
|
@ -67,7 +67,7 @@ export interface IButtonAttrs extends ComponentAttrs {
|
|||
* styles can be applied by providing `className="Button"` to the Button component.
|
||||
*/
|
||||
export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> extends Component<CustomAttrs> {
|
||||
view(vnode: Mithril.Vnode<IButtonAttrs, never>) {
|
||||
view(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
|
||||
let { type, title, 'aria-label': ariaLabel, icon: iconName, disabled, loading, className, class: _class, ...attrs } = this.attrs;
|
||||
|
||||
// If no `type` attr provided, set to "button"
|
||||
|
@ -102,7 +102,7 @@ export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> ext
|
|||
return <button {...buttonAttrs}>{this.getButtonContent(vnode.children)}</button>;
|
||||
}
|
||||
|
||||
oncreate(vnode: Mithril.VnodeDOM<IButtonAttrs, this>) {
|
||||
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
|
||||
super.oncreate(vnode);
|
||||
|
||||
const { 'aria-label': ariaLabel } = this.attrs;
|
||||
|
|
|
@ -22,7 +22,7 @@ export default abstract class Modal<ModalAttrs = {}> extends Component<ModalAttr
|
|||
/**
|
||||
* Determine whether or not the modal should be dismissible via an 'x' button.
|
||||
*/
|
||||
static readonly isDismissible = true;
|
||||
static readonly isDismissible: boolean = true;
|
||||
|
||||
protected loading: boolean = false;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import type Mithril from 'mithril';
|
||||
import app from '../app';
|
||||
import Component from '../Component';
|
||||
import PageState from '../states/PageState';
|
||||
|
@ -13,7 +14,22 @@ export interface IPageAttrs {
|
|||
* @abstract
|
||||
*/
|
||||
export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs> extends Component<CustomAttrs> {
|
||||
oninit(vnode) {
|
||||
/**
|
||||
* A class name to apply to the body while the route is active.
|
||||
*/
|
||||
protected bodyClass = '';
|
||||
|
||||
/**
|
||||
* Whether we should scroll to the top of the page when its rendered.
|
||||
*/
|
||||
protected scrollTopOnCreate = true;
|
||||
|
||||
/**
|
||||
* Whether the browser should restore scroll state on refreshes.
|
||||
*/
|
||||
protected useBrowserScrollRestoration = true;
|
||||
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||
super.oninit(vnode);
|
||||
|
||||
app.previous = app.current;
|
||||
|
@ -21,30 +37,9 @@ export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs>
|
|||
|
||||
app.drawer.hide();
|
||||
app.modal.close();
|
||||
|
||||
/**
|
||||
* A class name to apply to the body while the route is active.
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
this.bodyClass = '';
|
||||
|
||||
/**
|
||||
* Whether we should scroll to the top of the page when its rendered.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.scrollTopOnCreate = true;
|
||||
|
||||
/**
|
||||
* Whether the browser should restore scroll state on refreshes.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.useBrowserScrollRestoration = true;
|
||||
}
|
||||
|
||||
oncreate(vnode) {
|
||||
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
|
||||
super.oncreate(vnode);
|
||||
|
||||
if (this.bodyClass) {
|
||||
|
@ -60,7 +55,7 @@ export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs>
|
|||
}
|
||||
}
|
||||
|
||||
onremove(vnode) {
|
||||
onremove(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
|
||||
super.onremove(vnode);
|
||||
|
||||
if (this.bodyClass) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Component from '../Component';
|
||||
import type Mithril from 'mithril';
|
||||
import classList from '../utils/classList';
|
||||
import { TooltipCreationOptions } from '../../../@types/tooltips';
|
||||
import extractText from '../utils/extractText';
|
||||
|
||||
export interface TooltipAttrs extends Mithril.CommonAttributes<TooltipAttrs, Tooltip> {
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import type Mithril from 'mithril';
|
||||
import type { ComponentAttrs } from '../Component';
|
||||
import User from '../models/User';
|
||||
|
||||
export interface AvatarAttrs extends ComponentAttrs {}
|
||||
|
||||
/**
|
||||
* The `avatar` helper displays a user's avatar.
|
||||
*
|
||||
* @param user
|
||||
* @param attrs Attributes to apply to the avatar element
|
||||
*/
|
||||
export default function avatar(user: User, attrs: Object = {}): Mithril.Vnode {
|
||||
export default function avatar(user: User, attrs: ComponentAttrs = {}): Mithril.Vnode {
|
||||
attrs.className = 'Avatar ' + (attrs.className || '');
|
||||
let content: string = '';
|
||||
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
import type Mithril from 'mithril';
|
||||
import Component, { ComponentAttrs } from '../Component';
|
||||
import Separator from '../components/Separator';
|
||||
import classList from '../utils/classList';
|
||||
import type * as Component from '../Component';
|
||||
|
||||
function isSeparator(item: Mithril.Children): boolean {
|
||||
export interface ModdedVnodeAttrs {
|
||||
itemClassName?: string;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export type ModdedVnode<Attrs> = Mithril.Vnode<ModdedVnodeAttrs, Component<Attrs> | {}> & {
|
||||
itemName?: string;
|
||||
itemClassName?: string;
|
||||
tag: Mithril.Vnode['tag'] & {
|
||||
isListItem?: boolean;
|
||||
isActive?: (attrs: ComponentAttrs) => boolean;
|
||||
};
|
||||
};
|
||||
|
||||
function isSeparator<Attrs>(item: ModdedVnode<Attrs>): boolean {
|
||||
return item.tag === Separator;
|
||||
}
|
||||
|
||||
function withoutUnnecessarySeparators(items: Mithril.Children): Mithril.Children {
|
||||
const newItems: Mithril.Children = [];
|
||||
let prevItem: Mithril.Child;
|
||||
function withoutUnnecessarySeparators<Attrs>(items: ModdedVnode<Attrs>[]): ModdedVnode<Attrs>[] {
|
||||
const newItems: ModdedVnode<Attrs>[] = [];
|
||||
let prevItem: ModdedVnode<Attrs>;
|
||||
|
||||
items.filter(Boolean).forEach((item: Mithril.Vnode, i: number) => {
|
||||
if (!isSeparator(item) || (prevItem && !isSeparator(prevItem) && i !== items.length - 1)) {
|
||||
|
@ -21,16 +35,6 @@ function withoutUnnecessarySeparators(items: Mithril.Children): Mithril.Children
|
|||
return newItems;
|
||||
}
|
||||
|
||||
export interface ModdedVnodeAttrs {
|
||||
itemClassName?: string;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export type ModdedVnode<Attrs> = Mithril.Vnode<ModdedVnodeAttrs, Component.default<Attrs> | {}> & {
|
||||
itemName?: string;
|
||||
itemClassName?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The `listItems` helper wraps an array of components in the provided tag,
|
||||
* stripping out any unnecessary `Separator` components.
|
||||
|
@ -39,12 +43,11 @@ export type ModdedVnode<Attrs> = Mithril.Vnode<ModdedVnodeAttrs, Component.defau
|
|||
* second function parameter, `customTag`.
|
||||
*/
|
||||
export default function listItems<Attrs extends Record<string, unknown>>(
|
||||
items: ModdedVnode<Attrs> | ModdedVnode<Attrs>[],
|
||||
customTag: string | Component.default<Attrs> = 'li',
|
||||
attributes: Attrs = {}
|
||||
rawItems: ModdedVnode<Attrs> | ModdedVnode<Attrs>[],
|
||||
customTag: string | Component<Attrs> = 'li',
|
||||
attributes: Attrs = {} as Attrs
|
||||
): Mithril.Vnode[] {
|
||||
if (!(items instanceof Array)) items = [items];
|
||||
|
||||
const items = rawItems instanceof Array ? rawItems : [rawItems];
|
||||
const Tag = customTag;
|
||||
|
||||
return withoutUnnecessarySeparators(items).map((item: ModdedVnode<Attrs>) => {
|
||||
|
|
|
@ -5,8 +5,10 @@ import icon from './icon';
|
|||
/**
|
||||
* The `useronline` helper displays a green circle if the user is online
|
||||
*/
|
||||
export default function userOnline(user: User): Mithril.Vnode {
|
||||
export default function userOnline(user: User): Mithril.Vnode<{}, {}> | null {
|
||||
if (user.lastSeenAt() && user.isOnline()) {
|
||||
return <span className="UserOnline">{icon('fas fa-circle')}</span>;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export default class ModalManagerState {
|
|||
attrs?: Record<string, unknown>;
|
||||
} = null;
|
||||
|
||||
private closeTimeout?: number;
|
||||
private closeTimeout?: NodeJS.Timeout;
|
||||
|
||||
/**
|
||||
* Shows a modal dialog.
|
||||
|
@ -36,7 +36,7 @@ export default class ModalManagerState {
|
|||
throw new Error(invalidModalWarning);
|
||||
}
|
||||
|
||||
clearTimeout(this.closeTimeout);
|
||||
if (this.closeTimeout) clearTimeout(this.closeTimeout);
|
||||
|
||||
this.modal = { componentClass, attrs };
|
||||
|
||||
|
|
|
@ -15,18 +15,22 @@ export interface PaginationLocation {
|
|||
endIndex?: number;
|
||||
}
|
||||
|
||||
export default abstract class PaginatedListState<T extends Model> {
|
||||
export interface PaginatedListParams {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export default abstract class PaginatedListState<T extends Model, P extends PaginatedListParams = PaginatedListParams> {
|
||||
protected location!: PaginationLocation;
|
||||
protected pageSize: number;
|
||||
|
||||
protected pages: Page<T>[] = [];
|
||||
protected params: any = {};
|
||||
protected params: P = {} as P;
|
||||
|
||||
protected initialLoading: boolean = false;
|
||||
protected loadingPrev: boolean = false;
|
||||
protected loadingNext: boolean = false;
|
||||
|
||||
protected constructor(params: any = {}, page: number = 1, pageSize: number = 20) {
|
||||
protected constructor(params: P = {} as P, page: number = 1, pageSize: number = 20) {
|
||||
this.params = params;
|
||||
|
||||
this.location = { page };
|
||||
|
@ -123,12 +127,14 @@ export default abstract class PaginatedListState<T extends Model> {
|
|||
* @param page
|
||||
* @see requestParams
|
||||
*/
|
||||
public refreshParams(newParams, page: number) {
|
||||
public refreshParams(newParams: P, page: number): Promise<void> {
|
||||
if (this.isEmpty() || this.paramsChanged(newParams)) {
|
||||
this.params = newParams;
|
||||
|
||||
return this.refresh(page);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public refresh(page: number = 1) {
|
||||
|
@ -222,7 +228,7 @@ export default abstract class PaginatedListState<T extends Model> {
|
|||
}
|
||||
}
|
||||
|
||||
protected paramsChanged(newParams): boolean {
|
||||
protected paramsChanged(newParams: P): boolean {
|
||||
return Object.keys(newParams).some((key) => this.getParams()[key] !== newParams[key]);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ export default class BasicEditorDriver implements EditorDriverInterface {
|
|||
this.el.placeholder = params.placeholder;
|
||||
this.el.value = params.value;
|
||||
|
||||
const callInputListeners = (e) => {
|
||||
const callInputListeners = (e: Event) => {
|
||||
params.inputListeners.forEach((listener) => {
|
||||
listener();
|
||||
});
|
||||
|
@ -46,7 +46,7 @@ export default class BasicEditorDriver implements EditorDriverInterface {
|
|||
keyHandlers(params: EditorDriverParams): ItemList {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('submit', function (e) {
|
||||
items.add('submit', function (e: KeyboardEvent) {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
||||
params.onsubmit();
|
||||
}
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
export default class RequestError {
|
||||
import type Mithril from 'mithril';
|
||||
|
||||
export type InternalFlarumRequestOptions<ResponseType> = Mithril.RequestOptions<ResponseType> & {
|
||||
errorHandler: (error: RequestError) => void;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export default class RequestError<ResponseType = string> {
|
||||
status: number;
|
||||
options: Record<string, unknown>;
|
||||
options: InternalFlarumRequestOptions<ResponseType>;
|
||||
xhr: XMLHttpRequest;
|
||||
|
||||
responseText: string | null;
|
||||
response: Record<string, unknown> | null;
|
||||
response: {
|
||||
[key: string]: unknown;
|
||||
errors?: {
|
||||
detail?: string;
|
||||
code?: string;
|
||||
[key: string]: unknown;
|
||||
}[];
|
||||
} | null;
|
||||
|
||||
alert: any;
|
||||
|
||||
constructor(status: number, responseText: string | null, options: Record<string, unknown>, xhr: XMLHttpRequest) {
|
||||
constructor(status: number, responseText: string | null, options: InternalFlarumRequestOptions<ResponseType>, xhr: XMLHttpRequest) {
|
||||
this.status = status;
|
||||
this.responseText = responseText;
|
||||
this.options = options;
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
//
|
||||
// Needed to provide support for Safari on iOS < 12
|
||||
|
||||
// ts-ignored because we can afford to encapsulate some messy logic behind the clean typings.
|
||||
|
||||
if (!Array.prototype['flat']) {
|
||||
Array.prototype['flat'] = function flat(this: any[], depth: number = 1): any[] {
|
||||
return depth > 0
|
||||
? Array.prototype.reduce.call(this, (acc, val): any[] => acc.concat(Array.isArray(val) ? flat.call(val, depth - 1) : val), [])
|
||||
Array.prototype['flat'] = function flat<A, D extends number = 1>(this: A, depth?: D | unknown): any[] {
|
||||
// @ts-ignore
|
||||
return (depth ?? 1) > 0
|
||||
? // @ts-ignore
|
||||
Array.prototype.reduce.call(this, (acc, val): any[] => acc.concat(Array.isArray(val) ? flat.call(val, depth - 1) : val), [])
|
||||
: // If no depth is provided, or depth is 0, just return a copy of
|
||||
// the array. Spread is supported in all major browsers (iOS 8+)
|
||||
// @ts-ignore
|
||||
[...this];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import dayjs from 'dayjs';
|
|||
* The `humanTime` utility converts a date to a localized, human-readable time-
|
||||
* ago string.
|
||||
*/
|
||||
export default function humanTime(time: Date): string {
|
||||
export default function humanTime(time: dayjs.ConfigType): string {
|
||||
let d = dayjs(time);
|
||||
const now = dayjs();
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
type RGB = { r: number; g: number; b: number };
|
||||
|
||||
function hsvToRgb(h: number, s: number, v: number): RGB {
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
let r!: number;
|
||||
let g!: number;
|
||||
let b!: number;
|
||||
|
||||
const i = Math.floor(h * 6);
|
||||
const f = h * 6 - i;
|
||||
|
|
|
@ -11,5 +11,5 @@
|
|||
*/
|
||||
export default (key: string, cb: Function) =>
|
||||
function (this: Element) {
|
||||
cb(this.getAttribute(key) || this[key]);
|
||||
cb(this.getAttribute(key) || (this as any)[key]);
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ import isSafariMobile from './utils/isSafariMobile';
|
|||
|
||||
import type Notification from './components/Notification';
|
||||
import type Post from './components/Post';
|
||||
import Discussion from '../common/models/Discussion';
|
||||
|
||||
export default class ForumApplication extends Application {
|
||||
/**
|
||||
|
@ -134,11 +135,8 @@ export default class ForumApplication extends Application {
|
|||
|
||||
/**
|
||||
* Check whether or not the user is currently viewing a discussion.
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @return {Boolean}
|
||||
*/
|
||||
viewingDiscussion(discussion) {
|
||||
public viewingDiscussion(discussion: Discussion): boolean {
|
||||
return this.current.matches(DiscussionPage, { discussion });
|
||||
}
|
||||
|
||||
|
@ -149,13 +147,8 @@ export default class ForumApplication extends Application {
|
|||
* If the payload indicates that the user has been logged in, then the page
|
||||
* will be reloaded. Otherwise, a SignUpModal will be opened, prefilled
|
||||
* with the provided details.
|
||||
*
|
||||
* @param {Object} payload A dictionary of attrs to pass into the sign up
|
||||
* modal. A truthy `loggedIn` attr indicates that the user has logged
|
||||
* in, and thus the page is reloaded.
|
||||
* @public
|
||||
*/
|
||||
authenticationComplete(payload) {
|
||||
public authenticationComplete(payload: Record<string, unknown>): void {
|
||||
if (payload.loggedIn) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,7 @@ import Forum from './ForumApplication';
|
|||
|
||||
const app = new Forum();
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-expect-error We need to do this for backwards compatibility purposes.
|
||||
window.app = app;
|
||||
|
||||
export default app;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import type Mithril from 'mithril';
|
||||
|
||||
import app from '../../forum/app';
|
||||
import Page from '../../common/components/Page';
|
||||
import Page, { IPageAttrs } from '../../common/components/Page';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import DiscussionHero from './DiscussionHero';
|
||||
import DiscussionListPane from './DiscussionListPane';
|
||||
|
@ -10,31 +12,39 @@ import SplitDropdown from '../../common/components/SplitDropdown';
|
|||
import listItems from '../../common/helpers/listItems';
|
||||
import DiscussionControls from '../utils/DiscussionControls';
|
||||
import PostStreamState from '../states/PostStreamState';
|
||||
import Discussion from '../../common/models/Discussion';
|
||||
import Post from '../../common/models/Post';
|
||||
|
||||
export interface IDiscussionPageAttrs extends IPageAttrs {
|
||||
id: string;
|
||||
near?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DiscussionPage` component displays a whole discussion page, including
|
||||
* the discussion list pane, the hero, the posts, and the sidebar.
|
||||
*/
|
||||
export default class DiscussionPage extends Page {
|
||||
oninit(vnode) {
|
||||
export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = IDiscussionPageAttrs> extends Page<CustomAttrs> {
|
||||
/**
|
||||
* The discussion that is being viewed.
|
||||
*/
|
||||
protected discussion: Discussion | null = null;
|
||||
|
||||
/**
|
||||
* A public API for interacting with the post stream.
|
||||
*/
|
||||
protected stream: PostStreamState | null = null;
|
||||
|
||||
/**
|
||||
* The number of the first post that is currently visible in the viewport.
|
||||
*/
|
||||
protected near: number = 0;
|
||||
|
||||
protected useBrowserScrollRestoration = true;
|
||||
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.useBrowserScrollRestoration = false;
|
||||
|
||||
/**
|
||||
* The discussion that is being viewed.
|
||||
*
|
||||
* @type {Discussion}
|
||||
*/
|
||||
this.discussion = null;
|
||||
|
||||
/**
|
||||
* The number of the first post that is currently visible in the viewport.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this.near = m.route.param('near') || 0;
|
||||
|
||||
this.load();
|
||||
|
||||
// If the discussion list has been loaded, then we'll enable the pane (and
|
||||
|
@ -43,25 +53,23 @@ export default class DiscussionPage extends Page {
|
|||
// then the pane would redraw which would be slow and would cause problems with
|
||||
// event handlers.
|
||||
if (app.discussions.hasItems()) {
|
||||
app.pane.enable();
|
||||
app.pane.hide();
|
||||
app.pane?.enable();
|
||||
app.pane?.hide();
|
||||
}
|
||||
|
||||
app.history.push('discussion');
|
||||
|
||||
this.bodyClass = 'App--discussion';
|
||||
}
|
||||
|
||||
onremove(vnode) {
|
||||
onremove(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
|
||||
super.onremove(vnode);
|
||||
|
||||
// If we are indeed navigating away from this discussion, then disable the
|
||||
// discussion list pane. Also, if we're composing a reply to this
|
||||
// discussion, minimize the composer – unless it's empty, in which case
|
||||
// we'll just close it.
|
||||
app.pane.disable();
|
||||
app.pane?.disable();
|
||||
|
||||
if (app.composer.composingReplyTo(this.discussion) && !app.composer.fields.content()) {
|
||||
if (app.composer.composingReplyTo(this.discussion) && !app.composer?.fields?.content()) {
|
||||
app.composer.hide();
|
||||
} else {
|
||||
app.composer.minimize();
|
||||
|
@ -155,7 +163,7 @@ export default class DiscussionPage extends Page {
|
|||
* Load the discussion from the API or use the preloaded one.
|
||||
*/
|
||||
load() {
|
||||
const preloadedDiscussion = app.preloadedApiDocument();
|
||||
const preloadedDiscussion = app.preloadedApiDocument() as Discussion | null;
|
||||
if (preloadedDiscussion) {
|
||||
// We must wrap this in a setTimeout because if we are mounting this
|
||||
// component for the first time on page load, then any calls to m.redraw
|
||||
|
@ -186,10 +194,8 @@ export default class DiscussionPage extends Page {
|
|||
|
||||
/**
|
||||
* Initialize the component to display the given discussion.
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
*/
|
||||
show(discussion) {
|
||||
show(discussion: Discussion) {
|
||||
app.history.push('discussion', discussion.title());
|
||||
app.setTitle(discussion.title());
|
||||
app.setTitleCount(0);
|
||||
|
@ -214,7 +220,7 @@ export default class DiscussionPage extends Page {
|
|||
record.relationships.discussion.data.id === discussionId
|
||||
)
|
||||
.map((record) => app.store.getById('posts', record.id))
|
||||
.sort((a, b) => a.createdAt() - b.createdAt())
|
||||
.sort((a: Post, b: Post) => a.createdAt() - b.createdAt())
|
||||
.slice(0, 20);
|
||||
}
|
||||
|
||||
|
@ -266,13 +272,12 @@ export default class DiscussionPage extends Page {
|
|||
/**
|
||||
* When the posts that are visible in the post stream change (i.e. the user
|
||||
* scrolls up or down), then we update the URL and mark the posts as read.
|
||||
*
|
||||
* @param {Integer} startNumber
|
||||
* @param {Integer} endNumber
|
||||
*/
|
||||
positionChanged(startNumber, endNumber) {
|
||||
positionChanged(startNumber: number, endNumber: number): void {
|
||||
const discussion = this.discussion;
|
||||
|
||||
if (!discussion) return;
|
||||
|
||||
// Construct a URL to this discussion with the updated position, then
|
||||
// replace it into the window's history and our own history stack.
|
||||
const url = app.route.discussion(discussion, (this.near = startNumber));
|
|
@ -4,15 +4,16 @@ import LinkButton from '../../common/components/LinkButton';
|
|||
import Link from '../../common/components/Link';
|
||||
import { SearchSource } from './Search';
|
||||
import type Mithril from 'mithril';
|
||||
import Discussion from '../../common/models/Discussion';
|
||||
|
||||
/**
|
||||
* The `DiscussionsSearchSource` finds and displays discussion search results in
|
||||
* the search dropdown.
|
||||
*/
|
||||
export default class DiscussionsSearchSource implements SearchSource {
|
||||
protected results = new Map<string, unknown[]>();
|
||||
protected results = new Map<string, Discussion[]>();
|
||||
|
||||
search(query: string) {
|
||||
async search(query: string): Promise<void> {
|
||||
query = query.toLowerCase();
|
||||
|
||||
this.results.set(query, []);
|
||||
|
@ -23,13 +24,16 @@ export default class DiscussionsSearchSource implements SearchSource {
|
|||
include: 'mostRelevantPost',
|
||||
};
|
||||
|
||||
return app.store.find('discussions', params).then((results) => this.results.set(query, results));
|
||||
return app.store.find('discussions', params).then((results) => {
|
||||
this.results.set(query, results);
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
view(query: string): Array<Mithril.Vnode> {
|
||||
query = query.toLowerCase();
|
||||
|
||||
const results = (this.results.get(query) || []).map((discussion: unknown) => {
|
||||
const results = (this.results.get(query) || []).map((discussion) => {
|
||||
const mostRelevantPost = discussion.mostRelevantPost();
|
||||
|
||||
return (
|
||||
|
|
|
@ -10,6 +10,7 @@ import SearchState from '../states/SearchState';
|
|||
import DiscussionsSearchSource from './DiscussionsSearchSource';
|
||||
import UsersSearchSource from './UsersSearchSource';
|
||||
import type Mithril from 'mithril';
|
||||
import Model from '../../common/Model';
|
||||
|
||||
/**
|
||||
* The `SearchSource` interface defines a section of search results in the
|
||||
|
@ -24,8 +25,9 @@ import type Mithril from 'mithril';
|
|||
export interface SearchSource {
|
||||
/**
|
||||
* Make a request to get results for the given query.
|
||||
* The results will be updated internally in the search source, not exposed.
|
||||
*/
|
||||
search(query: string);
|
||||
search(query: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get an array of virtual <li>s that list the search results for the given
|
||||
|
@ -57,7 +59,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
*/
|
||||
protected static MIN_SEARCH_LEN = 3;
|
||||
|
||||
protected state!: SearchState;
|
||||
protected searchState!: SearchState;
|
||||
|
||||
/**
|
||||
* Whether or not the search input has focus.
|
||||
|
@ -84,18 +86,18 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
|
||||
protected navigator!: KeyboardNavigatable;
|
||||
|
||||
protected searchTimeout?: number;
|
||||
protected searchTimeout?: NodeJS.Timeout;
|
||||
|
||||
private updateMaxHeightHandler?: () => void;
|
||||
|
||||
oninit(vnode: Mithril.Vnode<T, this>) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.state = this.attrs.state;
|
||||
this.searchState = this.attrs.state;
|
||||
}
|
||||
|
||||
view() {
|
||||
const currentSearch = this.state.getInitialSearch();
|
||||
const currentSearch = this.searchState.getInitialSearch();
|
||||
|
||||
// Initialize search sources in the view rather than the constructor so
|
||||
// that we have access to app.forum.
|
||||
|
@ -107,15 +109,15 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
const searchLabel = extractText(app.translator.trans('core.forum.header.search_placeholder'));
|
||||
|
||||
const isActive = !!currentSearch;
|
||||
const shouldShowResults = !!(!this.loadingSources && this.state.getValue() && this.hasFocus);
|
||||
const shouldShowClearButton = !!(!this.loadingSources && this.state.getValue());
|
||||
const shouldShowResults = !!(!this.loadingSources && this.searchState.getValue() && this.hasFocus);
|
||||
const shouldShowClearButton = !!(!this.loadingSources && this.searchState.getValue());
|
||||
|
||||
return (
|
||||
<div
|
||||
role="search"
|
||||
aria-label={app.translator.trans('core.forum.header.search_role_label')}
|
||||
className={classList('Search', {
|
||||
open: this.state.getValue() && this.hasFocus,
|
||||
open: this.searchState.getValue() && this.hasFocus,
|
||||
focused: this.hasFocus,
|
||||
active: isActive,
|
||||
loading: !!this.loadingSources,
|
||||
|
@ -127,8 +129,8 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
className="FormControl"
|
||||
type="search"
|
||||
placeholder={searchLabel}
|
||||
value={this.state.getValue()}
|
||||
oninput={(e) => this.state.setValue(e.target.value)}
|
||||
value={this.searchState.getValue()}
|
||||
oninput={(e: InputEvent) => this.searchState.setValue((e?.target as HTMLInputElement)?.value)}
|
||||
onfocus={() => (this.hasFocus = true)}
|
||||
onblur={() => (this.hasFocus = false)}
|
||||
/>
|
||||
|
@ -148,7 +150,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
aria-hidden={!shouldShowResults || undefined}
|
||||
aria-live={shouldShowResults ? 'polite' : undefined}
|
||||
>
|
||||
{shouldShowResults && this.sources.map((source) => source.view(this.state.getValue()))}
|
||||
{shouldShowResults && this.sources.map((source) => source.view(this.searchState.getValue()))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
@ -159,11 +161,12 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
// we need to calculate and set the max height dynamically.
|
||||
const resultsElementMargin = 14;
|
||||
const maxHeight =
|
||||
window.innerHeight - this.element.querySelector('.Search-input>.FormControl').getBoundingClientRect().bottom - resultsElementMargin;
|
||||
this.element.querySelector('.Search-results').style['max-height'] = `${maxHeight}px`;
|
||||
window.innerHeight - this.element.querySelector('.Search-input>.FormControl')!.getBoundingClientRect().bottom - resultsElementMargin;
|
||||
|
||||
this.element.querySelector('.Search-results')?.setAttribute('style', `max-height: ${maxHeight}px`);
|
||||
}
|
||||
|
||||
onupdate(vnode) {
|
||||
onupdate(vnode: Mithril.VnodeDOM<T, this>) {
|
||||
super.onupdate(vnode);
|
||||
|
||||
// Highlight the item that is currently selected.
|
||||
|
@ -175,11 +178,11 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
this.updateMaxHeight();
|
||||
}
|
||||
|
||||
oncreate(vnode) {
|
||||
oncreate(vnode: Mithril.VnodeDOM<T, this>) {
|
||||
super.oncreate(vnode);
|
||||
|
||||
const search = this;
|
||||
const state = this.state;
|
||||
const state = this.searchState;
|
||||
|
||||
// Highlight the item that is currently selected.
|
||||
this.setIndex(this.getCurrentNumericIndex());
|
||||
|
@ -210,7 +213,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
|
||||
if (!query) return;
|
||||
|
||||
clearTimeout(search.searchTimeout);
|
||||
if (search.searchTimeout) clearTimeout(search.searchTimeout);
|
||||
search.searchTimeout = setTimeout(() => {
|
||||
if (state.isCached(query)) return;
|
||||
|
||||
|
@ -242,21 +245,25 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
window.addEventListener('resize', this.updateMaxHeightHandler);
|
||||
}
|
||||
|
||||
onremove(vnode) {
|
||||
onremove(vnode: Mithril.VnodeDOM<T, this>) {
|
||||
super.onremove(vnode);
|
||||
|
||||
window.removeEventListener('resize', this.updateMaxHeightHandler);
|
||||
if (this.updateMaxHeightHandler) {
|
||||
window.removeEventListener('resize', this.updateMaxHeightHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the currently selected search result and close the list.
|
||||
*/
|
||||
selectResult() {
|
||||
clearTimeout(this.searchTimeout);
|
||||
if (this.searchTimeout) clearTimeout(this.searchTimeout);
|
||||
|
||||
this.loadingSources = 0;
|
||||
|
||||
if (this.state.getValue()) {
|
||||
m.route.set(this.getItem(this.index).find('a').attr('href'));
|
||||
const selectedUrl = this.getItem(this.index).find('a').attr('href');
|
||||
if (this.searchState.getValue() && selectedUrl) {
|
||||
m.route.set(selectedUrl);
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
|
@ -268,7 +275,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
* Clear the search
|
||||
*/
|
||||
clear() {
|
||||
this.state.clear();
|
||||
this.searchState.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,11 +338,11 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||
this.index = parseInt($item.attr('data-index') as string) || fixedIndex;
|
||||
|
||||
if (scrollToItem) {
|
||||
const dropdownScroll = $dropdown.scrollTop();
|
||||
const dropdownTop = $dropdown.offset().top;
|
||||
const dropdownBottom = dropdownTop + $dropdown.outerHeight();
|
||||
const itemTop = $item.offset().top;
|
||||
const itemBottom = itemTop + $item.outerHeight();
|
||||
const dropdownScroll = $dropdown.scrollTop()!;
|
||||
const dropdownTop = $dropdown.offset()!.top;
|
||||
const dropdownBottom = dropdownTop + $dropdown.outerHeight()!;
|
||||
const itemTop = $item.offset()!.top;
|
||||
const itemBottom = itemTop + $item.outerHeight()!;
|
||||
|
||||
let scrollTop;
|
||||
if (itemTop < dropdownTop) {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import type Mithril from 'mithril';
|
||||
|
||||
import app from '../../forum/app';
|
||||
import highlight from '../../common/helpers/highlight';
|
||||
import avatar from '../../common/helpers/avatar';
|
||||
import username from '../../common/helpers/username';
|
||||
import Link from '../../common/components/Link';
|
||||
import { SearchSource } from './Search';
|
||||
import type Mithril from 'mithril';
|
||||
import User from '../../common/models/User';
|
||||
|
||||
/**
|
||||
* The `UsersSearchSource` finds and displays user search results in the search
|
||||
* dropdown.
|
||||
*/
|
||||
export default class UsersSearchResults implements SearchSource {
|
||||
protected results = new Map<string, unknown[]>();
|
||||
protected results = new Map<string, User[]>();
|
||||
|
||||
search(query: string) {
|
||||
async search(query: string): Promise<void> {
|
||||
return app.store
|
||||
.find('users', {
|
||||
filter: { q: query },
|
||||
|
|
|
@ -10,6 +10,7 @@ export { app };
|
|||
import compatObj from './compat';
|
||||
import proxifyCompat from '../common/utils/proxifyCompat';
|
||||
|
||||
// @ts-ignore
|
||||
compatObj.app = app;
|
||||
|
||||
export const compat = proxifyCompat(compatObj, 'forum');
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import type Mithril from 'mithril';
|
||||
|
||||
import app from '../../forum/app';
|
||||
import DefaultResolver from '../../common/resolvers/DefaultResolver';
|
||||
import DiscussionPage from '../components/DiscussionPage';
|
||||
import DiscussionPage, { IDiscussionPageAttrs } from '../components/DiscussionPage';
|
||||
|
||||
/**
|
||||
* A custom route resolver for DiscussionPage that generates the same key to all posts
|
||||
* on the same discussion. It triggers a scroll when going from one post to another
|
||||
* in the same discussion.
|
||||
*/
|
||||
export default class DiscussionPageResolver extends DefaultResolver {
|
||||
static scrollToPostNumber: string | null = null;
|
||||
export default class DiscussionPageResolver<
|
||||
Attrs extends IDiscussionPageAttrs = IDiscussionPageAttrs,
|
||||
RouteArgs extends Record<string, unknown> = {}
|
||||
> extends DefaultResolver<Attrs, DiscussionPage<Attrs>, RouteArgs> {
|
||||
static scrollToPostNumber: number | null = null;
|
||||
|
||||
/**
|
||||
* Remove optional parts of a discussion's slug to keep the substring
|
||||
|
@ -34,16 +39,16 @@ export default class DiscussionPageResolver extends DefaultResolver {
|
|||
return this.routeName.replace('.near', '') + JSON.stringify(params);
|
||||
}
|
||||
|
||||
onmatch(args, requestedPath, route) {
|
||||
onmatch(args: Attrs & RouteArgs, requestedPath: string, route: string) {
|
||||
if (app.current.matches(DiscussionPage) && this.canonicalizeDiscussionSlug(args.id) === this.canonicalizeDiscussionSlug(m.route.param('id'))) {
|
||||
// By default, the first post number of any discussion is 1
|
||||
DiscussionPageResolver.scrollToPostNumber = args.near || '1';
|
||||
DiscussionPageResolver.scrollToPostNumber = args.near || 1;
|
||||
}
|
||||
|
||||
return super.onmatch(args, requestedPath, route);
|
||||
}
|
||||
|
||||
render(vnode) {
|
||||
render(vnode: Mithril.Vnode<Attrs, DiscussionPage<Attrs>>) {
|
||||
if (DiscussionPageResolver.scrollToPostNumber !== null) {
|
||||
const number = DiscussionPageResolver.scrollToPostNumber;
|
||||
// Scroll after a timeout to avoid clashes with the render.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import app from '../../forum/app';
|
||||
import PaginatedListState, { Page } from '../../common/states/PaginatedListState';
|
||||
import PaginatedListState, { Page, PaginatedListParams } from '../../common/states/PaginatedListState';
|
||||
import Discussion from '../../common/models/Discussion';
|
||||
|
||||
export interface IRequestParams {
|
||||
|
@ -8,10 +8,14 @@ export interface IRequestParams {
|
|||
sort?: string;
|
||||
}
|
||||
|
||||
export default class DiscussionListState extends PaginatedListState<Discussion> {
|
||||
export interface DiscussionListParams extends PaginatedListParams {
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
export default class DiscussionListState<P extends DiscussionListParams = DiscussionListParams> extends PaginatedListState<Discussion, P> {
|
||||
protected extraDiscussions: Discussion[] = [];
|
||||
|
||||
constructor(params: any, page: number = 1) {
|
||||
constructor(params: P, page: number = 1) {
|
||||
super(params, page, 20);
|
||||
}
|
||||
|
||||
|
@ -25,7 +29,7 @@ export default class DiscussionListState extends PaginatedListState<Discussion>
|
|||
filter: this.params.filter || {},
|
||||
};
|
||||
|
||||
params.sort = this.sortMap()[this.params.sort];
|
||||
params.sort = this.sortMap()[this.params.sort ?? ''];
|
||||
|
||||
if (this.params.q) {
|
||||
params.filter.q = this.params.q;
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import setRouteWithForcedRefresh from '../../common/utils/setRouteWithForcedRefresh';
|
||||
|
||||
export interface HistoryEntry {
|
||||
name: string;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `History` class keeps track and manages a stack of routes that the user
|
||||
* has navigated to in their session.
|
||||
|
@ -12,46 +18,34 @@ import setRouteWithForcedRefresh from '../../common/utils/setRouteWithForcedRefr
|
|||
* rather than the previous discussion.
|
||||
*/
|
||||
export default class History {
|
||||
constructor(defaultRoute) {
|
||||
/**
|
||||
* The stack of routes that have been navigated to.
|
||||
*
|
||||
* @type {Array}
|
||||
* @protected
|
||||
*/
|
||||
this.stack = [];
|
||||
}
|
||||
/**
|
||||
* The stack of routes that have been navigated to.
|
||||
*/
|
||||
protected stack: HistoryEntry[] = [];
|
||||
|
||||
/**
|
||||
* Get the item on the top of the stack.
|
||||
*
|
||||
* @return {Object}
|
||||
* @public
|
||||
*/
|
||||
getCurrent() {
|
||||
getCurrent(): HistoryEntry {
|
||||
return this.stack[this.stack.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous item on the stack.
|
||||
*
|
||||
* @return {Object}
|
||||
* @public
|
||||
*/
|
||||
getPrevious() {
|
||||
getPrevious(): HistoryEntry {
|
||||
return this.stack[this.stack.length - 2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an item to the top of the stack.
|
||||
*
|
||||
* @param {String} name The name of the route.
|
||||
* @param {String} title The title of the route.
|
||||
* @param {String} [url] The URL of the route. The current URL will be used if
|
||||
* @param {string} name The name of the route.
|
||||
* @param {string} title The title of the route.
|
||||
* @param {string} [url] The URL of the route. The current URL will be used if
|
||||
* not provided.
|
||||
* @public
|
||||
*/
|
||||
push(name, title, url = m.route.get()) {
|
||||
push(name: string, title: string, url = m.route.get()) {
|
||||
// If we're pushing an item with the same name as second-to-top item in the
|
||||
// stack, we will assume that the user has clicked the 'back' button in
|
||||
// their browser. In this case, we don't want to push a new item, so we will
|
||||
|
@ -74,18 +68,13 @@ export default class History {
|
|||
|
||||
/**
|
||||
* Check whether or not the history stack is able to be popped.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
canGoBack() {
|
||||
canGoBack(): boolean {
|
||||
return this.stack.length > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go back to the previous route in the history stack.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
back() {
|
||||
if (!this.canGoBack()) {
|
||||
|
@ -99,10 +88,8 @@ export default class History {
|
|||
|
||||
/**
|
||||
* Get the URL of the previous page.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
backUrl() {
|
||||
backUrl(): string {
|
||||
const secondTop = this.stack[this.stack.length - 2];
|
||||
|
||||
return secondTop.url;
|
||||
|
@ -110,8 +97,6 @@ export default class History {
|
|||
|
||||
/**
|
||||
* Go to the first route in the history stack.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
home() {
|
||||
this.stack.splice(0);
|
|
@ -91,7 +91,7 @@ export default class KeyboardNavigatable {
|
|||
*/
|
||||
onRemove(callback: KeyboardEventHandler): KeyboardNavigatable {
|
||||
this.callbacks.set(8, (e) => {
|
||||
if (e.target.selectionStart === 0 && e.target.selectionEnd === 0) {
|
||||
if (e instanceof KeyboardEvent && e.target instanceof HTMLInputElement && e.target.selectionStart === 0 && e.target.selectionEnd === 0) {
|
||||
callback(e);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ export default class KeyboardNavigatable {
|
|||
*/
|
||||
bindTo($element: JQuery) {
|
||||
// Handle navigation key events on the navigatable element.
|
||||
$element.on('keydown', this.navigate.bind(this));
|
||||
$element[0].addEventListener('keydown', this.navigate.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
export default function isSafariMobile(): boolean {
|
||||
return (
|
||||
'ontouchstart' in window &&
|
||||
navigator.vendor &&
|
||||
navigator.vendor != null &&
|
||||
navigator.vendor.includes('Apple') &&
|
||||
navigator.userAgent &&
|
||||
navigator.userAgent != null &&
|
||||
!navigator.userAgent.includes('CriOS') &&
|
||||
!navigator.userAgent.includes('FxiOS')
|
||||
);
|
||||
|
|
601
js/yarn.lock
601
js/yarn.lock
|
@ -1321,6 +1321,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.1.2":
|
||||
version: 7.16.3
|
||||
resolution: "@babel/runtime@npm:7.16.3"
|
||||
dependencies:
|
||||
regenerator-runtime: ^0.13.4
|
||||
checksum: ab8ac887096d76185ddbf291d28fb976cd32473696dc497ad4905b784acbd5aa462533ad83a5c5104e10ead28c2e0e119840ee28ed8eff90dcdde9d57f916eda
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.8.4":
|
||||
version: 7.16.0
|
||||
resolution: "@babel/runtime@npm:7.16.0"
|
||||
|
@ -1404,6 +1413,7 @@ __metadata:
|
|||
textarea-caret: ^3.1.0
|
||||
throttle-debounce: ^3.0.1
|
||||
typescript: ^4.4.4
|
||||
typescript-coverage-report: ^0.6.1
|
||||
webpack: ^5.61.0
|
||||
webpack-cli: ^4.9.1
|
||||
webpack-merge: ^5.8.0
|
||||
|
@ -1417,6 +1427,46 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hypnosphi/create-react-context@npm:^0.3.1":
|
||||
version: 0.3.1
|
||||
resolution: "@hypnosphi/create-react-context@npm:0.3.1"
|
||||
dependencies:
|
||||
gud: ^1.0.0
|
||||
warning: ^4.0.3
|
||||
peerDependencies:
|
||||
prop-types: ^15.0.0
|
||||
react: ">=0.14.0"
|
||||
checksum: d2f069a562e138057aa067e1483e28cea3193bbacd33ca9528131f31e656939cfeb552af760b3be437d3a8074315a8854fc6d5d89878e2746618ad930c817122
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nodelib/fs.scandir@npm:2.1.5":
|
||||
version: 2.1.5
|
||||
resolution: "@nodelib/fs.scandir@npm:2.1.5"
|
||||
dependencies:
|
||||
"@nodelib/fs.stat": 2.0.5
|
||||
run-parallel: ^1.1.9
|
||||
checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2":
|
||||
version: 2.0.5
|
||||
resolution: "@nodelib/fs.stat@npm:2.0.5"
|
||||
checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nodelib/fs.walk@npm:^1.2.3":
|
||||
version: 1.2.8
|
||||
resolution: "@nodelib/fs.walk@npm:1.2.8"
|
||||
dependencies:
|
||||
"@nodelib/fs.scandir": 2.1.5
|
||||
fastq: ^1.6.0
|
||||
checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polka/url@npm:^1.0.0-next.20":
|
||||
version: 1.0.0-next.21
|
||||
resolution: "@polka/url@npm:1.0.0-next.21"
|
||||
|
@ -1424,6 +1474,46 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@semantic-ui-react/event-stack@npm:^3.1.0":
|
||||
version: 3.1.2
|
||||
resolution: "@semantic-ui-react/event-stack@npm:3.1.2"
|
||||
dependencies:
|
||||
exenv: ^1.2.2
|
||||
prop-types: ^15.6.2
|
||||
peerDependencies:
|
||||
react: ^16.0.0 || ^17.0.0
|
||||
react-dom: ^16.0.0 || ^17.0.0
|
||||
checksum: 3e3a27294e24478d57f324552008975a8c7b2deec9a974f627a75d7bc9fc1257da6c62907cf4684b7cb9b3b1252f5e9b2f312200d5dafcf1f019a645fd21ad0b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@stardust-ui/react-component-event-listener@npm:~0.38.0":
|
||||
version: 0.38.0
|
||||
resolution: "@stardust-ui/react-component-event-listener@npm:0.38.0"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.1.2
|
||||
prop-types: ^15.7.2
|
||||
peerDependencies:
|
||||
react: ^16.8.0
|
||||
react-dom: ^16.8.0
|
||||
checksum: f9f54c976781f5bed766cded56be1a35b0d02bf1aa244db5db61e508420ae76241254772c626ee99322455793a38ffdffd195c3515e8a628a08457ba711ce1ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@stardust-ui/react-component-ref@npm:~0.38.0":
|
||||
version: 0.38.0
|
||||
resolution: "@stardust-ui/react-component-ref@npm:0.38.0"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.1.2
|
||||
prop-types: ^15.7.2
|
||||
react-is: ^16.6.3
|
||||
peerDependencies:
|
||||
react: ^16.8.0
|
||||
react-dom: ^16.8.0
|
||||
checksum: c5bd6ec22fdcfdaee37b255d68fef5420d84e47b080d3947bcd0d7063548e1a8aa1627285bdf8c6c85eb4516845c763d7c183e57505ca8eca7c3d87e266a83ec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/eslint-scope@npm:^3.7.0":
|
||||
version: 3.7.1
|
||||
resolution: "@types/eslint-scope@npm:3.7.1"
|
||||
|
@ -1903,6 +1993,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"braces@npm:^3.0.1":
|
||||
version: 3.0.2
|
||||
resolution: "braces@npm:3.0.2"
|
||||
dependencies:
|
||||
fill-range: ^7.0.1
|
||||
checksum: e2a8e769a863f3d4ee887b5fe21f63193a891c68b612ddb4b68d82d1b5f3ff9073af066c343e9867a393fe4c2555dcb33e89b937195feb9c1613d259edfcd459
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"browserslist@npm:^4.14.5, browserslist@npm:^4.16.6, browserslist@npm:^4.17.6":
|
||||
version: 4.17.6
|
||||
resolution: "browserslist@npm:4.17.6"
|
||||
|
@ -1952,7 +2051,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"call-bind@npm:^1.0.0":
|
||||
"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "call-bind@npm:1.0.2"
|
||||
dependencies:
|
||||
|
@ -2004,6 +2103,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"classnames@npm:^2.2.6":
|
||||
version: 2.3.1
|
||||
resolution: "classnames@npm:2.3.1"
|
||||
checksum: 14db8889d56c267a591f08b0834989fe542d47fac659af5a539e110cc4266694e8de86e4e3bbd271157dbd831361310a8293e0167141e80b0f03a0f175c80960
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clone-deep@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "clone-deep@npm:4.0.1"
|
||||
|
@ -2068,6 +2174,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"colors@npm:^1.0.3, colors@npm:^1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "colors@npm:1.4.0"
|
||||
checksum: 98aa2c2418ad87dedf25d781be69dc5fc5908e279d9d30c34d8b702e586a0474605b3a189511482b9d5ed0d20c867515d22749537f7bc546256c6014f3ebdcec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^2.20.0":
|
||||
version: 2.20.3
|
||||
resolution: "commander@npm:2.20.3"
|
||||
|
@ -2164,6 +2277,20 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-equal@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "deep-equal@npm:1.1.1"
|
||||
dependencies:
|
||||
is-arguments: ^1.0.4
|
||||
is-date-object: ^1.0.1
|
||||
is-regex: ^1.0.4
|
||||
object-is: ^1.0.1
|
||||
object-keys: ^1.1.1
|
||||
regexp.prototype.flags: ^1.2.0
|
||||
checksum: f92686f2c5bcdf714a75a5fa7a9e47cb374a8ec9307e717b8d1ce61f56a75aaebf5619c2a12b8087a705b5a2f60d0292c35f8b58cb1f72e3268a3a15cab9f78d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"define-properties@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "define-properties@npm:1.1.3"
|
||||
|
@ -2180,6 +2307,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eastasianwidth@npm:^0.1.0":
|
||||
version: 0.1.1
|
||||
resolution: "eastasianwidth@npm:0.1.1"
|
||||
checksum: d387cada4bbb8f50634098f0b204a9046cfb3748add45442232bae57b715692fbf1795154178293550cc28e08eee8ab1218cdcb28b7342c238dfd3bf42fbba10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-to-chromium@npm:^1.3.886":
|
||||
version: 1.3.888
|
||||
resolution: "electron-to-chromium@npm:1.3.888"
|
||||
|
@ -2307,6 +2441,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"exenv@npm:^1.2.2":
|
||||
version: 1.2.2
|
||||
resolution: "exenv@npm:1.2.2"
|
||||
checksum: a894f3b60ab8419e0b6eec99c690a009c8276b4c90655ccaf7d28faba2de3a6b93b3d92210f9dc5efd36058d44f04098f6bbccef99859151104bfd49939904dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"expose-loader@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "expose-loader@npm:3.1.0"
|
||||
|
@ -2323,6 +2464,19 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-glob@npm:3":
|
||||
version: 3.2.7
|
||||
resolution: "fast-glob@npm:3.2.7"
|
||||
dependencies:
|
||||
"@nodelib/fs.stat": ^2.0.2
|
||||
"@nodelib/fs.walk": ^1.2.3
|
||||
glob-parent: ^5.1.2
|
||||
merge2: ^1.3.0
|
||||
micromatch: ^4.0.4
|
||||
checksum: 2f4708ff112d2b451888129fdd9a0938db88b105b0ddfd043c064e3c4d3e20eed8d7c7615f7565fee660db34ddcf08a2db1bf0ab3c00b87608e4719694642d78
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-json-stable-stringify@npm:^2.0.0":
|
||||
version: 2.1.0
|
||||
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
||||
|
@ -2337,6 +2491,24 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fastq@npm:^1.6.0":
|
||||
version: 1.13.0
|
||||
resolution: "fastq@npm:1.13.0"
|
||||
dependencies:
|
||||
reusify: ^1.0.4
|
||||
checksum: 32cf15c29afe622af187d12fc9cd93e160a0cb7c31a3bb6ace86b7dea3b28e7b72acde89c882663f307b2184e14782c6c664fa315973c03626c7d4bff070bb0b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fill-range@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "fill-range@npm:7.0.1"
|
||||
dependencies:
|
||||
to-regex-range: ^5.0.1
|
||||
checksum: cc283f4e65b504259e64fd969bcf4def4eb08d85565e906b7d36516e87819db52029a76b6363d0f02d0d532f0033c9603b9e2d943d56ee3b0d4f7ad3328ff917
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"find-cache-dir@npm:^3.3.1":
|
||||
version: 3.3.2
|
||||
resolution: "find-cache-dir@npm:3.3.2"
|
||||
|
@ -2442,6 +2614,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-parent@npm:^5.1.2":
|
||||
version: 5.1.2
|
||||
resolution: "glob-parent@npm:5.1.2"
|
||||
dependencies:
|
||||
is-glob: ^4.0.1
|
||||
checksum: f4f2bfe2425296e8a47e36864e4f42be38a996db40420fe434565e4480e3322f18eb37589617a98640c5dc8fdec1a387007ee18dbb1f3f5553409c34d17f425e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-to-regexp@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "glob-to-regexp@npm:0.4.1"
|
||||
|
@ -2449,7 +2630,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:^7.1.2":
|
||||
"glob@npm:^7.1.2, glob@npm:^7.1.3":
|
||||
version: 7.2.0
|
||||
resolution: "glob@npm:7.2.0"
|
||||
dependencies:
|
||||
|
@ -2477,6 +2658,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gud@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "gud@npm:1.0.0"
|
||||
checksum: 3e2eb37cf794364077c18f036d6aa259c821c7fd188f2b7935cb00d589d82a41e0ebb1be809e1a93679417f62f1ad0513e745c3cf5329596e489aef8c5e5feae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gzip-size@npm:^5.1.1":
|
||||
version: 5.1.1
|
||||
resolution: "gzip-size@npm:5.1.1"
|
||||
|
@ -2510,13 +2698,22 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-symbols@npm:^1.0.1":
|
||||
"has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "has-symbols@npm:1.0.2"
|
||||
checksum: 2309c426071731be792b5be43b3da6fb4ed7cbe8a9a6bcfca1862587709f01b33d575ce8f5c264c1eaad09fca2f9a8208c0a2be156232629daa2dd0c0740976b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-tostringtag@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "has-tostringtag@npm:1.0.0"
|
||||
dependencies:
|
||||
has-symbols: ^1.0.2
|
||||
checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "has@npm:1.0.3"
|
||||
|
@ -2576,6 +2773,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-arguments@npm:^1.0.4":
|
||||
version: 1.1.1
|
||||
resolution: "is-arguments@npm:1.1.1"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
has-tostringtag: ^1.0.0
|
||||
checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-arrayish@npm:^0.2.1":
|
||||
version: 0.2.1
|
||||
resolution: "is-arrayish@npm:0.2.1"
|
||||
|
@ -2592,6 +2799,38 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-date-object@npm:^1.0.1":
|
||||
version: 1.0.5
|
||||
resolution: "is-date-object@npm:1.0.5"
|
||||
dependencies:
|
||||
has-tostringtag: ^1.0.0
|
||||
checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-extglob@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "is-extglob@npm:2.1.1"
|
||||
checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-glob@npm:^4.0.1":
|
||||
version: 4.0.3
|
||||
resolution: "is-glob@npm:4.0.3"
|
||||
dependencies:
|
||||
is-extglob: ^2.1.1
|
||||
checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-number@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "is-number@npm:7.0.0"
|
||||
checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-plain-object@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "is-plain-object@npm:2.0.4"
|
||||
|
@ -2601,6 +2840,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-regex@npm:^1.0.4":
|
||||
version: 1.1.4
|
||||
resolution: "is-regex@npm:1.1.4"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
has-tostringtag: ^1.0.0
|
||||
checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-stream@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "is-stream@npm:2.0.1"
|
||||
|
@ -2647,7 +2896,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"js-tokens@npm:^4.0.0":
|
||||
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "js-tokens@npm:4.0.0"
|
||||
checksum: 8a95213a5a77deb6cbe94d86340e8d9ace2b93bc367790b260101d2f36a2eaf4e4e22d9fa9cf459b38af3a32fb4190e638024cf82ec95ef708680e405ea7cc78
|
||||
|
@ -2722,6 +2971,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"keyboard-key@npm:^1.0.4":
|
||||
version: 1.1.0
|
||||
resolution: "keyboard-key@npm:1.1.0"
|
||||
checksum: 8bf7c0d796820a0d4802930ef103339e2ffddf369f441d8e19f0a1d3b841da71a0a4da8c7a5270f4814da54300434f5fc5b3489aae4ae3ba47418ac106b71a64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"kind-of@npm:^6.0.2":
|
||||
version: 6.0.3
|
||||
resolution: "kind-of@npm:6.0.3"
|
||||
|
@ -2777,13 +3033,24 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash@npm:^4.17.20":
|
||||
"lodash@npm:^4.17.15, lodash@npm:^4.17.20":
|
||||
version: 4.17.21
|
||||
resolution: "lodash@npm:4.17.21"
|
||||
checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "loose-envify@npm:1.4.0"
|
||||
dependencies:
|
||||
js-tokens: ^3.0.0 || ^4.0.0
|
||||
bin:
|
||||
loose-envify: cli.js
|
||||
checksum: 6517e24e0cad87ec9888f500c5b5947032cdfe6ef65e1c1936a0c48a524b81e65542c9c3edc91c97d5bddc806ee2a985dbc79be89215d613b1de5db6d1cfe6f4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-dir@npm:^3.0.2, make-dir@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "make-dir@npm:3.1.0"
|
||||
|
@ -2800,6 +3067,23 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge2@npm:^1.3.0":
|
||||
version: 1.4.1
|
||||
resolution: "merge2@npm:1.4.1"
|
||||
checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromatch@npm:^4.0.4":
|
||||
version: 4.0.4
|
||||
resolution: "micromatch@npm:4.0.4"
|
||||
dependencies:
|
||||
braces: ^3.0.1
|
||||
picomatch: ^2.2.3
|
||||
checksum: ef3d1c88e79e0a68b0e94a03137676f3324ac18a908c245a9e5936f838079fcc108ac7170a5fadc265a9c2596963462e402841406bda1a4bb7b68805601d631c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-db@npm:1.50.0":
|
||||
version: 1.50.0
|
||||
resolution: "mime-db@npm:1.50.0"
|
||||
|
@ -2832,7 +3116,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^3.0.4":
|
||||
"minimatch@npm:3, minimatch@npm:^3.0.4":
|
||||
version: 3.0.4
|
||||
resolution: "minimatch@npm:3.0.4"
|
||||
dependencies:
|
||||
|
@ -2873,6 +3157,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ncp@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "ncp@npm:2.0.0"
|
||||
bin:
|
||||
ncp: ./bin/ncp
|
||||
checksum: ea9b19221da1d1c5529bdb9f8e85c9d191d156bcaae408cce5e415b7fbfd8744c288e792bd7faf1fe3b70fd44c74e22f0d43c39b209bc7ac1fb8016f70793a16
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"neo-async@npm:^2.6.2":
|
||||
version: 2.6.2
|
||||
resolution: "neo-async@npm:2.6.2"
|
||||
|
@ -2899,6 +3192,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"normalize-path@npm:3":
|
||||
version: 3.0.0
|
||||
resolution: "normalize-path@npm:3.0.0"
|
||||
checksum: 88eeb4da891e10b1318c4b2476b6e2ecbeb5ff97d946815ffea7794c31a89017c70d7f34b3c2ebf23ef4e9fc9fb99f7dffe36da22011b5b5c6ffa34f4873ec20
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npm-run-path@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "npm-run-path@npm:4.0.1"
|
||||
|
@ -2908,6 +3208,23 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-assign@npm:^4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "object-assign@npm:4.1.1"
|
||||
checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-is@npm:^1.0.1":
|
||||
version: 1.1.5
|
||||
resolution: "object-is@npm:1.1.5"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
define-properties: ^1.1.3
|
||||
checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "object-keys@npm:1.1.1"
|
||||
|
@ -3035,6 +3352,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"picomatch@npm:^2.2.3":
|
||||
version: 2.3.0
|
||||
resolution: "picomatch@npm:2.3.0"
|
||||
checksum: 16818720ea7c5872b6af110760dee856c8e4cd79aed1c7a006d076b1cc09eff3ae41ca5019966694c33fbd2e1cc6ea617ab10e4adac6df06556168f13be3fca2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pify@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "pify@npm:4.0.1"
|
||||
|
@ -3051,6 +3375,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"popper.js@npm:^1.14.4":
|
||||
version: 1.16.1
|
||||
resolution: "popper.js@npm:1.16.1"
|
||||
checksum: c56ae5001ec50a77ee297a8061a0221d99d25c7348d2e6bcd3e45a0d0f32a1fd81bca29d46cb0d4bdf13efb77685bd6a0ce93f9eb3c608311a461f945fffedbe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:^2.4.1":
|
||||
version: 2.4.1
|
||||
resolution: "prettier@npm:2.4.1"
|
||||
|
@ -3060,6 +3391,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2":
|
||||
version: 15.7.2
|
||||
resolution: "prop-types@npm:15.7.2"
|
||||
dependencies:
|
||||
loose-envify: ^1.4.0
|
||||
object-assign: ^4.1.1
|
||||
react-is: ^16.8.1
|
||||
checksum: 5eef82fdda64252c7e75aa5c8cc28a24bbdece0f540adb60ce67c205cf978a5bd56b83e4f269f91c6e4dcfd80b36f2a2dec24d362e278913db2086ca9c6f9430
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"punycode@npm:^2.1.0, punycode@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "punycode@npm:2.1.1"
|
||||
|
@ -3067,6 +3409,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"queue-microtask@npm:^1.2.2":
|
||||
version: 1.2.3
|
||||
resolution: "queue-microtask@npm:1.2.3"
|
||||
checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"randombytes@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "randombytes@npm:2.1.0"
|
||||
|
@ -3076,6 +3425,55 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-dom@npm:^16.13.1":
|
||||
version: 16.14.0
|
||||
resolution: "react-dom@npm:16.14.0"
|
||||
dependencies:
|
||||
loose-envify: ^1.1.0
|
||||
object-assign: ^4.1.1
|
||||
prop-types: ^15.6.2
|
||||
scheduler: ^0.19.1
|
||||
peerDependencies:
|
||||
react: ^16.14.0
|
||||
checksum: 5a5c49da0f106b2655a69f96c622c347febcd10532db391c262b26aec225b235357d9da1834103457683482ab1b229af7a50f6927a6b70e53150275e31785544
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-is@npm:^16.6.3, react-is@npm:^16.8.1, react-is@npm:^16.8.6":
|
||||
version: 16.13.1
|
||||
resolution: "react-is@npm:16.13.1"
|
||||
checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-popper@npm:^1.3.4":
|
||||
version: 1.3.11
|
||||
resolution: "react-popper@npm:1.3.11"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.1.2
|
||||
"@hypnosphi/create-react-context": ^0.3.1
|
||||
deep-equal: ^1.1.1
|
||||
popper.js: ^1.14.4
|
||||
prop-types: ^15.6.1
|
||||
typed-styles: ^0.0.7
|
||||
warning: ^4.0.2
|
||||
peerDependencies:
|
||||
react: 0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
checksum: a0f5994f5799f1c7364498f74df123dd2561fff4ae834b10fdcca74d9a8e159b523ed1f0708db33bad606933ab4f0d5ce9c90e48cbb671bf30016c890f3c7ea4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react@npm:^16.13.1":
|
||||
version: 16.14.0
|
||||
resolution: "react@npm:16.14.0"
|
||||
dependencies:
|
||||
loose-envify: ^1.1.0
|
||||
object-assign: ^4.1.1
|
||||
prop-types: ^15.6.2
|
||||
checksum: 8484f3ecb13414526f2a7412190575fc134da785c02695eb92bb6028c930bfe1c238d7be2a125088fec663cc7cda0a3623373c46807cf2c281f49c34b79881ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"read-pkg-up@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "read-pkg-up@npm:7.0.1"
|
||||
|
@ -3140,6 +3538,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"regexp.prototype.flags@npm:^1.2.0":
|
||||
version: 1.3.1
|
||||
resolution: "regexp.prototype.flags@npm:1.3.1"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
define-properties: ^1.1.3
|
||||
checksum: 343595db5a6bbbb3bfbda881f9c74832cfa9fc0039e64a43843f6bb9158b78b921055266510800ed69d4997638890b17a46d55fd9f32961f53ae56ac3ec4dd05
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"regexpu-core@npm:^4.7.1":
|
||||
version: 4.8.0
|
||||
resolution: "regexpu-core@npm:4.8.0"
|
||||
|
@ -3208,6 +3616,33 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"reusify@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "reusify@npm:1.0.4"
|
||||
checksum: c3076ebcc22a6bc252cb0b9c77561795256c22b757f40c0d8110b1300723f15ec0fc8685e8d4ea6d7666f36c79ccc793b1939c748bf36f18f542744a4e379fcc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rimraf@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "rimraf@npm:3.0.2"
|
||||
dependencies:
|
||||
glob: ^7.1.3
|
||||
bin:
|
||||
rimraf: bin.js
|
||||
checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"run-parallel@npm:^1.1.9":
|
||||
version: 1.2.0
|
||||
resolution: "run-parallel@npm:1.2.0"
|
||||
dependencies:
|
||||
queue-microtask: ^1.2.2
|
||||
checksum: cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-buffer@npm:^5.1.0":
|
||||
version: 5.2.1
|
||||
resolution: "safe-buffer@npm:5.2.1"
|
||||
|
@ -3222,6 +3657,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"scheduler@npm:^0.19.1":
|
||||
version: 0.19.1
|
||||
resolution: "scheduler@npm:0.19.1"
|
||||
dependencies:
|
||||
loose-envify: ^1.1.0
|
||||
object-assign: ^4.1.1
|
||||
checksum: 73e185a59e2ff5aa3609f5b9cb97ddd376f89e1610579d29939d952411ca6eb7a24907a4ea4556569dacb931467a1a4a56d94fe809ef713aa76748642cd96a6c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"schema-utils@npm:^2.6.5":
|
||||
version: 2.7.1
|
||||
resolution: "schema-utils@npm:2.7.1"
|
||||
|
@ -3244,6 +3689,28 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semantic-ui-react@npm:^0.88.2":
|
||||
version: 0.88.2
|
||||
resolution: "semantic-ui-react@npm:0.88.2"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.1.2
|
||||
"@semantic-ui-react/event-stack": ^3.1.0
|
||||
"@stardust-ui/react-component-event-listener": ~0.38.0
|
||||
"@stardust-ui/react-component-ref": ~0.38.0
|
||||
classnames: ^2.2.6
|
||||
keyboard-key: ^1.0.4
|
||||
lodash: ^4.17.15
|
||||
prop-types: ^15.7.2
|
||||
react-is: ^16.8.6
|
||||
react-popper: ^1.3.4
|
||||
shallowequal: ^1.1.0
|
||||
peerDependencies:
|
||||
react: ^16.8.0
|
||||
react-dom: ^16.8.0
|
||||
checksum: b7d6d46ec2f8a08c50c5831977b2df8d7849cd5660d99d18be93e191853bb5259a0912e66c4d882e463ade4eacec62462f817b3c6000a65a6c63fbe17d6bd262
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:2 || 3 || 4 || 5":
|
||||
version: 5.7.1
|
||||
resolution: "semver@npm:5.7.1"
|
||||
|
@ -3289,6 +3756,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"shallowequal@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "shallowequal@npm:1.1.0"
|
||||
checksum: f4c1de0837f106d2dbbfd5d0720a5d059d1c66b42b580965c8f06bb1db684be8783538b684092648c981294bf817869f743a066538771dbecb293df78f765e00
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"shebang-command@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "shebang-command@npm:2.0.0"
|
||||
|
@ -3429,6 +3903,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"terminal-table@npm:^0.0.12":
|
||||
version: 0.0.12
|
||||
resolution: "terminal-table@npm:0.0.12"
|
||||
dependencies:
|
||||
colors: ^1.0.3
|
||||
eastasianwidth: ^0.1.0
|
||||
checksum: ea678bf685170b228e5f36b83ff2a4e979b4664245431583a327f0c87ca64292e52f582fd5c869cf593249b5e5df7fc7113444c91b47da20ac1a6c914a6cba59
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"terser-webpack-plugin@npm:^5.1.3":
|
||||
version: 5.2.4
|
||||
resolution: "terser-webpack-plugin@npm:5.2.4"
|
||||
|
@ -3486,6 +3970,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-regex-range@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "to-regex-range@npm:5.0.1"
|
||||
dependencies:
|
||||
is-number: ^7.0.0
|
||||
checksum: f76fa01b3d5be85db6a2a143e24df9f60dd047d151062d0ba3df62953f2f697b16fe5dad9b0ac6191c7efc7b1d9dcaa4b768174b7b29da89d4428e64bc0a20ed
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"totalist@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "totalist@npm:1.1.0"
|
||||
|
@ -3493,6 +3986,46 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:1 || 2":
|
||||
version: 2.3.1
|
||||
resolution: "tslib@npm:2.3.1"
|
||||
checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^1.8.1":
|
||||
version: 1.14.1
|
||||
resolution: "tslib@npm:1.14.1"
|
||||
checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsutils@npm:3":
|
||||
version: 3.21.0
|
||||
resolution: "tsutils@npm:3.21.0"
|
||||
dependencies:
|
||||
tslib: ^1.8.1
|
||||
peerDependencies:
|
||||
typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
|
||||
checksum: 1843f4c1b2e0f975e08c4c21caa4af4f7f65a12ac1b81b3b8489366826259323feb3fc7a243123453d2d1a02314205a7634e048d4a8009921da19f99755cdc48
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-coverage-core@npm:^2.17.2":
|
||||
version: 2.18.3
|
||||
resolution: "type-coverage-core@npm:2.18.3"
|
||||
dependencies:
|
||||
fast-glob: 3
|
||||
minimatch: 3
|
||||
normalize-path: 3
|
||||
tslib: 1 || 2
|
||||
tsutils: 3
|
||||
peerDependencies:
|
||||
typescript: 2 || 3 || 4
|
||||
checksum: ba9f42fb9c71f49124bc0422dd14e7396806be1f4d4ead4af37d598d143cc03cb1fd5b951b685307f43a830d5288bf38ea0a8b5a3a40db5e87e7428d7430e939
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-fest@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "type-fest@npm:0.6.0"
|
||||
|
@ -3507,6 +4040,43 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typed-styles@npm:^0.0.7":
|
||||
version: 0.0.7
|
||||
resolution: "typed-styles@npm:0.0.7"
|
||||
checksum: 36a6ad6bee008c15ddb8c2425eaf9aee37d2841985b4c44406ea4cf57080a9c30b6f9f3feb842ac952354733ac53299ee44f68d83f734486e8344d413f8c8c0d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-coverage-report@npm:^0.6.1":
|
||||
version: 0.6.1
|
||||
resolution: "typescript-coverage-report@npm:0.6.1"
|
||||
dependencies:
|
||||
colors: ^1.4.0
|
||||
commander: ^5.0.0
|
||||
ncp: ^2.0.0
|
||||
react: ^16.13.1
|
||||
react-dom: ^16.13.1
|
||||
rimraf: ^3.0.2
|
||||
semantic-ui-react: ^0.88.2
|
||||
terminal-table: ^0.0.12
|
||||
type-coverage-core: ^2.17.2
|
||||
typescript: 4.1.3
|
||||
bin:
|
||||
typescript-coverage-report: dist/bin/typescript-coverage-report.js
|
||||
checksum: ee3703a56d53aeba723ca65563834becfa2e8ce67cf51fc4c839191db7215c4704f0affc3ca3095ff6baab724c5b0312c84af828e5f199ea6ffe49d1bf223400
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:4.1.3":
|
||||
version: 4.1.3
|
||||
resolution: "typescript@npm:4.1.3"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 0a25f7d7cebbc5ad23f41cb30918643460477be265bd3bcd400ffedb77d16e97d46f2b0c31393b2f990c5cf5b9f7a829ad6aff8636988b8f30abf81c656237c0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:^4.3.2, typescript@npm:^4.4.4":
|
||||
version: 4.4.4
|
||||
resolution: "typescript@npm:4.4.4"
|
||||
|
@ -3517,6 +4087,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@4.1.3#~builtin<compat/typescript>":
|
||||
version: 4.1.3
|
||||
resolution: "typescript@patch:typescript@npm%3A4.1.3#~builtin<compat/typescript>::version=4.1.3&hash=ddd1e8"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: ed3df76d9b6efb448e5e73bca671698cda353a3807199ad95c78782a857e7685ccc94b799ac885423ced65ee21c87cbbeb823642c5dfd715efae5ebbc6137070
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@^4.3.2#~builtin<compat/typescript>, typescript@patch:typescript@^4.4.4#~builtin<compat/typescript>":
|
||||
version: 4.4.4
|
||||
resolution: "typescript@patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"
|
||||
|
@ -3577,6 +4157,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"warning@npm:^4.0.2, warning@npm:^4.0.3":
|
||||
version: 4.0.3
|
||||
resolution: "warning@npm:4.0.3"
|
||||
dependencies:
|
||||
loose-envify: ^1.0.0
|
||||
checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"watchpack@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "watchpack@npm:2.2.0"
|
||||
|
|
Loading…
Reference in New Issue
Block a user