mirror of
https://github.com/flarum/framework.git
synced 2025-02-27 13:44:26 +08:00
refactor: convert Badge
, Checkbox
and Navigation
components to TS (#3532)
* chore: convert badge components to TypeScript Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com> * chore: convert checkbox components to TypeScript Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com> * chore: convert `Navigation` component to TypeScript Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com> * chore: import mithril type instead
This commit is contained in:
parent
707ca2d16d
commit
7471ef64d5
@ -5,6 +5,7 @@ import Application, { ApplicationData } from '../common/Application';
|
|||||||
import Navigation from '../common/components/Navigation';
|
import Navigation from '../common/components/Navigation';
|
||||||
import AdminNav from './components/AdminNav';
|
import AdminNav from './components/AdminNav';
|
||||||
import ExtensionData from './utils/ExtensionData';
|
import ExtensionData from './utils/ExtensionData';
|
||||||
|
import IHistory from '../common/IHistory';
|
||||||
|
|
||||||
export type Extension = {
|
export type Extension = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -47,13 +48,16 @@ export default class AdminApplication extends Application {
|
|||||||
language: 10,
|
language: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
history = {
|
history: IHistory = {
|
||||||
canGoBack: () => true,
|
canGoBack: () => true,
|
||||||
getPrevious: () => {},
|
getCurrent: () => null,
|
||||||
|
getPrevious: () => null,
|
||||||
|
push: () => {},
|
||||||
backUrl: () => this.forum.attribute<string>('baseUrl'),
|
backUrl: () => this.forum.attribute<string>('baseUrl'),
|
||||||
back: function () {
|
back: function () {
|
||||||
window.location.assign(this.backUrl());
|
window.location.assign(this.backUrl());
|
||||||
},
|
},
|
||||||
|
home: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,6 +34,7 @@ import type Component from './Component';
|
|||||||
import type { ComponentAttrs } from './Component';
|
import type { ComponentAttrs } from './Component';
|
||||||
import Model, { SavedModelData } from './Model';
|
import Model, { SavedModelData } from './Model';
|
||||||
import fireApplicationError from './helpers/fireApplicationError';
|
import fireApplicationError from './helpers/fireApplicationError';
|
||||||
|
import IHistory from './IHistory';
|
||||||
|
|
||||||
export type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
|
export type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
|
||||||
|
|
||||||
@ -228,6 +229,9 @@ export default class Application {
|
|||||||
*/
|
*/
|
||||||
drawer!: Drawer;
|
drawer!: Drawer;
|
||||||
|
|
||||||
|
history: IHistory | null = null;
|
||||||
|
pane: any = null;
|
||||||
|
|
||||||
data!: ApplicationData;
|
data!: ApplicationData;
|
||||||
|
|
||||||
private _title: string = '';
|
private _title: string = '';
|
||||||
|
@ -156,5 +156,5 @@ export default abstract class Component<Attrs extends ComponentAttrs = Component
|
|||||||
*
|
*
|
||||||
* This can be used to assign default values for missing, optional attrs.
|
* This can be used to assign default values for missing, optional attrs.
|
||||||
*/
|
*/
|
||||||
static initAttrs<T>(attrs: T): void {}
|
static initAttrs(attrs: unknown): void {}
|
||||||
}
|
}
|
||||||
|
15
framework/core/js/src/common/IHistory.ts
Normal file
15
framework/core/js/src/common/IHistory.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export interface HistoryEntry {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default interface IHistory {
|
||||||
|
canGoBack(): boolean;
|
||||||
|
getCurrent(): HistoryEntry | null;
|
||||||
|
getPrevious(): HistoryEntry | null;
|
||||||
|
push(name: string, title: string, url: string): void;
|
||||||
|
back(): void;
|
||||||
|
backUrl(): string;
|
||||||
|
home(): void;
|
||||||
|
}
|
@ -1,8 +1,15 @@
|
|||||||
import Tooltip from './Tooltip';
|
import Tooltip from './Tooltip';
|
||||||
import Component from '../Component';
|
import Component, { ComponentAttrs } from '../Component';
|
||||||
import icon from '../helpers/icon';
|
import icon from '../helpers/icon';
|
||||||
import classList from '../utils/classList';
|
import classList from '../utils/classList';
|
||||||
|
|
||||||
|
export interface IBadgeAttrs extends ComponentAttrs {
|
||||||
|
icon: string;
|
||||||
|
type?: string;
|
||||||
|
label?: string;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Badge` component represents a user/discussion badge, indicating some
|
* The `Badge` component represents a user/discussion badge, indicating some
|
||||||
* status (e.g. a discussion is stickied, a user is an admin).
|
* status (e.g. a discussion is stickied, a user is an admin).
|
||||||
@ -16,7 +23,7 @@ import classList from '../utils/classList';
|
|||||||
*
|
*
|
||||||
* All other attrs will be assigned as attributes on the badge element.
|
* All other attrs will be assigned as attributes on the badge element.
|
||||||
*/
|
*/
|
||||||
export default class Badge extends Component {
|
export default class Badge<CustomAttrs extends IBadgeAttrs = IBadgeAttrs> extends Component<CustomAttrs> {
|
||||||
view() {
|
view() {
|
||||||
const { type, icon: iconName, label, color, style = {}, ...attrs } = this.attrs;
|
const { type, icon: iconName, label, color, style = {}, ...attrs } = this.attrs;
|
||||||
|
|
@ -1,8 +1,16 @@
|
|||||||
import Component from '../Component';
|
import Component, { ComponentAttrs } from '../Component';
|
||||||
import LoadingIndicator from './LoadingIndicator';
|
import LoadingIndicator from './LoadingIndicator';
|
||||||
import icon from '../helpers/icon';
|
import icon from '../helpers/icon';
|
||||||
import classList from '../utils/classList';
|
import classList from '../utils/classList';
|
||||||
import withAttr from '../utils/withAttr';
|
import withAttr from '../utils/withAttr';
|
||||||
|
import type Mithril from 'mithril';
|
||||||
|
|
||||||
|
export interface ICheckboxAttrs extends ComponentAttrs {
|
||||||
|
state?: boolean;
|
||||||
|
loading?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
onchange: (checked: boolean, component: Checkbox<this>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Checkbox` component defines a checkbox input.
|
* The `Checkbox` component defines a checkbox input.
|
||||||
@ -16,12 +24,8 @@ import withAttr from '../utils/withAttr';
|
|||||||
* - `onchange` A callback to run when the checkbox is checked/unchecked.
|
* - `onchange` A callback to run when the checkbox is checked/unchecked.
|
||||||
* - `children` A text label to display next to the checkbox.
|
* - `children` A text label to display next to the checkbox.
|
||||||
*/
|
*/
|
||||||
export default class Checkbox extends Component {
|
export default class Checkbox<CustomAttrs extends ICheckboxAttrs = ICheckboxAttrs> extends Component<CustomAttrs> {
|
||||||
view(vnode) {
|
view(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||||
// Sometimes, false is stored in the DB as '0'. This is a temporary
|
|
||||||
// conversion layer until a more robust settings encoding is introduced
|
|
||||||
if (this.attrs.state === '0') this.attrs.state = false;
|
|
||||||
|
|
||||||
const className = classList([
|
const className = classList([
|
||||||
'Checkbox',
|
'Checkbox',
|
||||||
this.attrs.state ? 'on' : 'off',
|
this.attrs.state ? 'on' : 'off',
|
||||||
@ -43,21 +47,15 @@ export default class Checkbox extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the template for the checkbox's display (tick/cross icon).
|
* Get the template for the checkbox's display (tick/cross icon).
|
||||||
*
|
|
||||||
* @return {import('mithril').Children}
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
getDisplay() {
|
protected getDisplay(): Mithril.Children {
|
||||||
return this.attrs.loading ? <LoadingIndicator display="unset" size="small" /> : icon(this.attrs.state ? 'fas fa-check' : 'fas fa-times');
|
return this.attrs.loading ? <LoadingIndicator display="unset" size="small" /> : icon(this.attrs.state ? 'fas fa-check' : 'fas fa-times');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a callback when the state of the checkbox is changed.
|
* Run a callback when the state of the checkbox is changed.
|
||||||
*
|
|
||||||
* @param {boolean} checked
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
onchange(checked) {
|
protected onchange(checked: boolean): void {
|
||||||
if (this.attrs.onchange) this.attrs.onchange(checked, this);
|
if (this.attrs.onchange) this.attrs.onchange(checked, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,16 +0,0 @@
|
|||||||
import Badge from './Badge';
|
|
||||||
|
|
||||||
export default class GroupBadge extends Badge {
|
|
||||||
static initAttrs(attrs) {
|
|
||||||
super.initAttrs(attrs);
|
|
||||||
|
|
||||||
if (attrs.group) {
|
|
||||||
attrs.icon = attrs.group.icon();
|
|
||||||
attrs.color = attrs.group.color();
|
|
||||||
attrs.label = typeof attrs.label === 'undefined' ? attrs.group.nameSingular() : attrs.label;
|
|
||||||
attrs.type = 'group--' + attrs.group.id();
|
|
||||||
|
|
||||||
delete attrs.group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
21
framework/core/js/src/common/components/GroupBadge.tsx
Normal file
21
framework/core/js/src/common/components/GroupBadge.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Badge, { IBadgeAttrs } from './Badge';
|
||||||
|
import Group from '../models/Group';
|
||||||
|
|
||||||
|
export interface IGroupAttrs extends IBadgeAttrs {
|
||||||
|
group?: Group;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class GroupBadge<CustomAttrs extends IGroupAttrs = IGroupAttrs> extends Badge<CustomAttrs> {
|
||||||
|
static initAttrs(attrs: IGroupAttrs): void {
|
||||||
|
super.initAttrs(attrs);
|
||||||
|
|
||||||
|
if (attrs.group) {
|
||||||
|
attrs.icon = attrs.group.icon() || '';
|
||||||
|
attrs.color = attrs.group.color() || '';
|
||||||
|
attrs.label = typeof attrs.label === 'undefined' ? attrs.group.nameSingular() : attrs.label;
|
||||||
|
attrs.type = 'group--' + attrs.group.id();
|
||||||
|
|
||||||
|
delete attrs.group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import app from '../../common/app';
|
|||||||
import Component from '../Component';
|
import Component from '../Component';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import LinkButton from './LinkButton';
|
import LinkButton from './LinkButton';
|
||||||
|
import type Mithril from 'mithril';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Navigation` component displays a set of navigation buttons. Typically
|
* The `Navigation` component displays a set of navigation buttons. Typically
|
||||||
@ -28,41 +29,35 @@ export default class Navigation extends Component {
|
|||||||
onmouseenter={pane && pane.show.bind(pane)}
|
onmouseenter={pane && pane.show.bind(pane)}
|
||||||
onmouseleave={pane && pane.onmouseleave.bind(pane)}
|
onmouseleave={pane && pane.onmouseleave.bind(pane)}
|
||||||
>
|
>
|
||||||
{history.canGoBack() ? [this.getBackButton(), this.getPaneButton()] : this.getDrawerButton()}
|
{history?.canGoBack() ? [this.getBackButton(), this.getPaneButton()] : this.getDrawerButton()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the back button.
|
* Get the back button.
|
||||||
*
|
|
||||||
* @return {import('mithril').Children}
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
getBackButton() {
|
protected getBackButton(): Mithril.Children {
|
||||||
const { history } = app;
|
const { history } = app;
|
||||||
const previous = history.getPrevious() || {};
|
const previous = history?.getPrevious();
|
||||||
|
|
||||||
return LinkButton.component({
|
return LinkButton.component({
|
||||||
className: 'Button Navigation-back Button--icon',
|
className: 'Button Navigation-back Button--icon',
|
||||||
href: history.backUrl(),
|
href: history?.backUrl(),
|
||||||
icon: 'fas fa-chevron-left',
|
icon: 'fas fa-chevron-left',
|
||||||
'aria-label': previous.title,
|
'aria-label': previous?.title,
|
||||||
onclick: (e) => {
|
onclick: (e: MouseEvent) => {
|
||||||
if (e.shiftKey || e.ctrlKey || e.metaKey || e.which === 2) return;
|
if (e.shiftKey || e.ctrlKey || e.metaKey || e.which === 2) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
history.back();
|
history?.back();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the pane pinned toggle button.
|
* Get the pane pinned toggle button.
|
||||||
*
|
|
||||||
* @return {import('mithril').Children}
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
getPaneButton() {
|
protected getPaneButton(): Mithril.Children {
|
||||||
const { pane } = app;
|
const { pane } = app;
|
||||||
|
|
||||||
if (!pane || !pane.active) return '';
|
if (!pane || !pane.active) return '';
|
||||||
@ -76,11 +71,8 @@ export default class Navigation extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the drawer toggle button.
|
* Get the drawer toggle button.
|
||||||
*
|
|
||||||
* @return {import('mithril').Children}
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
getDrawerButton() {
|
protected getDrawerButton(): Mithril.Children {
|
||||||
if (!this.attrs.drawer) return '';
|
if (!this.attrs.drawer) return '';
|
||||||
|
|
||||||
const { drawer } = app;
|
const { drawer } = app;
|
||||||
@ -88,7 +80,7 @@ export default class Navigation extends Component {
|
|||||||
|
|
||||||
return Button.component({
|
return Button.component({
|
||||||
className: 'Button Button--icon Navigation-drawer' + (user && user.newNotificationCount() ? ' new' : ''),
|
className: 'Button Button--icon Navigation-drawer' + (user && user.newNotificationCount() ? ' new' : ''),
|
||||||
onclick: (e) => {
|
onclick: (e: MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
drawer.show();
|
drawer.show();
|
||||||
},
|
},
|
@ -1,11 +1,11 @@
|
|||||||
import Checkbox from './Checkbox';
|
import Checkbox, { ICheckboxAttrs } from './Checkbox';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Switch` component is a `Checkbox`, but with a switch display instead of
|
* The `Switch` component is a `Checkbox`, but with a switch display instead of
|
||||||
* a tick/cross one.
|
* a tick/cross one.
|
||||||
*/
|
*/
|
||||||
export default class Switch extends Checkbox {
|
export default class Switch extends Checkbox {
|
||||||
static initAttrs(attrs) {
|
static initAttrs(attrs: ICheckboxAttrs) {
|
||||||
super.initAttrs(attrs);
|
super.initAttrs(attrs);
|
||||||
|
|
||||||
attrs.className = (attrs.className || '') + ' Checkbox--switch';
|
attrs.className = (attrs.className || '') + ' Checkbox--switch';
|
@ -1,10 +1,5 @@
|
|||||||
import setRouteWithForcedRefresh from '../../common/utils/setRouteWithForcedRefresh';
|
import setRouteWithForcedRefresh from '../../common/utils/setRouteWithForcedRefresh';
|
||||||
|
import IHistory, { HistoryEntry } from '../../common/IHistory';
|
||||||
export interface HistoryEntry {
|
|
||||||
name: string;
|
|
||||||
title: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `History` class keeps track and manages a stack of routes that the user
|
* The `History` class keeps track and manages a stack of routes that the user
|
||||||
@ -17,7 +12,7 @@ export interface HistoryEntry {
|
|||||||
* popping the history stack will still take them back to the discussion list
|
* popping the history stack will still take them back to the discussion list
|
||||||
* rather than the previous discussion.
|
* rather than the previous discussion.
|
||||||
*/
|
*/
|
||||||
export default class History {
|
export default class History implements IHistory {
|
||||||
/**
|
/**
|
||||||
* The stack of routes that have been navigated to.
|
* The stack of routes that have been navigated to.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user