Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: convert Badge Checkbox and Navigation components to TS #3532

Merged
merged 4 commits into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions framework/core/js/src/admin/AdminApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Application, { ApplicationData } from '../common/Application';
import Navigation from '../common/components/Navigation';
import AdminNav from './components/AdminNav';
import ExtensionData from './utils/ExtensionData';
import IHistory from '../common/IHistory';

export type Extension = {
id: string;
Expand Down Expand Up @@ -47,13 +48,16 @@ export default class AdminApplication extends Application {
language: 10,
};

history = {
history: IHistory = {
canGoBack: () => true,
getPrevious: () => {},
getCurrent: () => null,
getPrevious: () => null,
push: () => {},
backUrl: () => this.forum.attribute<string>('baseUrl'),
back: function () {
window.location.assign(this.backUrl());
},
home: () => {},
};

/**
Expand Down
4 changes: 4 additions & 0 deletions framework/core/js/src/common/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import type Component from './Component';
import type { ComponentAttrs } from './Component';
import Model, { SavedModelData } from './Model';
import fireApplicationError from './helpers/fireApplicationError';
import IHistory from './IHistory';

export type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';

Expand Down Expand Up @@ -228,6 +229,9 @@ export default class Application {
*/
drawer!: Drawer;

history: IHistory | null = null;
pane: any = null;

data!: ApplicationData;

private _title: string = '';
Expand Down
2 changes: 1 addition & 1 deletion framework/core/js/src/common/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
static initAttrs<T>(attrs: T): void {}
static initAttrs(attrs: unknown): void {}
}
15 changes: 15 additions & 0 deletions framework/core/js/src/common/IHistory.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import Tooltip from './Tooltip';
import Component from '../Component';
import Component, { ComponentAttrs } from '../Component';
import icon from '../helpers/icon';
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
* status (e.g. a discussion is stickied, a user is an admin).
Expand All @@ -16,7 +23,7 @@ import classList from '../utils/classList';
*
* 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() {
const { type, icon: iconName, label, color, style = {}, ...attrs } = this.attrs;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import Component from '../Component';
import Component, { ComponentAttrs } from '../Component';
import LoadingIndicator from './LoadingIndicator';
import icon from '../helpers/icon';
import classList from '../utils/classList';
import withAttr from '../utils/withAttr';
import Mithril from 'mithril';
SychO9 marked this conversation as resolved.
Show resolved Hide resolved

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.
Expand All @@ -16,12 +24,8 @@ import withAttr from '../utils/withAttr';
* - `onchange` A callback to run when the checkbox is checked/unchecked.
* - `children` A text label to display next to the checkbox.
*/
export default class Checkbox extends Component {
view(vnode) {
// 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;

export default class Checkbox<CustomAttrs extends ICheckboxAttrs = ICheckboxAttrs> extends Component<CustomAttrs> {
view(vnode: Mithril.Vnode<CustomAttrs, this>) {
const className = classList([
'Checkbox',
this.attrs.state ? 'on' : 'off',
Expand All @@ -43,21 +47,15 @@ export default class Checkbox extends Component {

/**
* 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');
}

/**
* 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);
}
}
16 changes: 0 additions & 16 deletions framework/core/js/src/common/components/GroupBadge.js

This file was deleted.

21 changes: 21 additions & 0 deletions framework/core/js/src/common/components/GroupBadge.tsx
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import app from '../../common/app';
import Component from '../Component';
import Button from './Button';
import LinkButton from './LinkButton';
import Mithril from 'mithril';
SychO9 marked this conversation as resolved.
Show resolved Hide resolved

/**
* The `Navigation` component displays a set of navigation buttons. Typically
Expand All @@ -28,41 +29,35 @@ export default class Navigation extends Component {
onmouseenter={pane && pane.show.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>
);
}

/**
* Get the back button.
*
* @return {import('mithril').Children}
* @protected
*/
getBackButton() {
protected getBackButton(): Mithril.Children {
const { history } = app;
const previous = history.getPrevious() || {};
const previous = history?.getPrevious();

return LinkButton.component({
className: 'Button Navigation-back Button--icon',
href: history.backUrl(),
href: history?.backUrl(),
icon: 'fas fa-chevron-left',
'aria-label': previous.title,
onclick: (e) => {
'aria-label': previous?.title,
onclick: (e: MouseEvent) => {
if (e.shiftKey || e.ctrlKey || e.metaKey || e.which === 2) return;
e.preventDefault();
history.back();
history?.back();
},
});
}

/**
* Get the pane pinned toggle button.
*
* @return {import('mithril').Children}
* @protected
*/
getPaneButton() {
protected getPaneButton(): Mithril.Children {
const { pane } = app;

if (!pane || !pane.active) return '';
Expand All @@ -76,19 +71,16 @@ export default class Navigation extends Component {

/**
* Get the drawer toggle button.
*
* @return {import('mithril').Children}
* @protected
*/
getDrawerButton() {
protected getDrawerButton(): Mithril.Children {
if (!this.attrs.drawer) return '';

const { drawer } = app;
const user = app.session.user;

return Button.component({
className: 'Button Button--icon Navigation-drawer' + (user && user.newNotificationCount() ? ' new' : ''),
onclick: (e) => {
onclick: (e: MouseEvent) => {
e.stopPropagation();
drawer.show();
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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
* a tick/cross one.
*/
export default class Switch extends Checkbox {
static initAttrs(attrs) {
static initAttrs(attrs: ICheckboxAttrs) {
super.initAttrs(attrs);

attrs.className = (attrs.className || '') + ' Checkbox--switch';
Expand Down
9 changes: 2 additions & 7 deletions framework/core/js/src/forum/utils/History.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import setRouteWithForcedRefresh from '../../common/utils/setRouteWithForcedRefresh';

export interface HistoryEntry {
name: string;
title: string;
url: string;
}
import IHistory, { HistoryEntry } from '../../common/IHistory';

/**
* The `History` class keeps track and manages a stack of routes that the user
Expand All @@ -17,7 +12,7 @@ export interface HistoryEntry {
* popping the history stack will still take them back to the discussion list
* rather than the previous discussion.
*/
export default class History {
export default class History implements IHistory {
/**
* The stack of routes that have been navigated to.
*/
Expand Down