Skip to content

Commit

Permalink
chore: extract FormModal from Modal (#3922)
Browse files Browse the repository at this point in the history
  • Loading branch information
SychO9 authored Nov 10, 2023
1 parent 9ef3664 commit eaabeab
Show file tree
Hide file tree
Showing 22 changed files with 126 additions and 93 deletions.
4 changes: 2 additions & 2 deletions extensions/flags/js/src/forum/components/FlagPostModal.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import app from 'flarum/forum/app';
import Modal from 'flarum/common/components/Modal';
import FormModal from 'flarum/common/components/FormModal';
import Form from 'flarum/common/components/Form';
import Button from 'flarum/common/components/Button';

import Stream from 'flarum/common/utils/Stream';
import withAttr from 'flarum/common/utils/withAttr';
import ItemList from 'flarum/common/utils/ItemList';

export default class FlagPostModal extends Modal {
export default class FlagPostModal extends FormModal {
oninit(vnode) {
super.oninit(vnode);

Expand Down
4 changes: 2 additions & 2 deletions extensions/nicknames/js/src/forum/components/NicknameModal.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import app from 'flarum/forum/app';
import Modal from 'flarum/common/components/Modal';
import FormModal from 'flarum/common/components/FormModal';
import Button from 'flarum/common/components/Button';
import Stream from 'flarum/common/utils/Stream';
import Form from '@flarum/core/src/common/components/Form';

export default class NicknameModal extends Modal {
export default class NicknameModal extends FormModal {
oninit(vnode) {
super.oninit(vnode);
this.nickname = Stream(app.session.user.displayName());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import app from 'flarum/admin/app';
import ItemList from 'flarum/common/utils/ItemList';
import generateElementId from 'flarum/admin/utils/generateElementId';
import Modal, { IInternalModalAttrs } from 'flarum/common/components/Modal';
import FormModal, { IFormModalAttrs } from 'flarum/common/components/FormModal';

import Mithril from 'mithril';
import Button from 'flarum/common/components/Button';
Expand All @@ -22,7 +22,7 @@ export interface IDateSelection {
end: number;
}

export interface IStatisticsWidgetDateSelectionModalAttrs extends IInternalModalAttrs {
export interface IStatisticsWidgetDateSelectionModalAttrs extends IFormModalAttrs {
onModalSubmit: (dates: IDateSelection) => void;
value?: IDateSelection;
}
Expand All @@ -38,7 +38,7 @@ interface IStatisticsWidgetDateSelectionModalState {
};
}

export default class StatisticsWidgetDateSelectionModal extends Modal<IStatisticsWidgetDateSelectionModalAttrs> {
export default class StatisticsWidgetDateSelectionModal extends FormModal<IStatisticsWidgetDateSelectionModalAttrs> {
/* @ts-expect-error core typings don't allow us to set the type of the state attr :( */
state: IStatisticsWidgetDateSelectionModalState = {
inputs: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import app from 'flarum/forum/app';
import Modal from 'flarum/common/components/Modal';
import FormModal from 'flarum/common/components/FormModal';
import Button from 'flarum/common/components/Button';
import Stream from 'flarum/common/utils/Stream';
import withAttr from 'flarum/common/utils/withAttr';
Expand All @@ -9,7 +9,7 @@ import { getPermanentSuspensionDate } from '../helpers/suspensionHelper';
import Form from '@flarum/core/src/common/components/Form';
import FieldSet from '@flarum/core/src/common/components/FieldSet';

export default class SuspendUserModal extends Modal {
export default class SuspendUserModal extends FormModal {
oninit(vnode) {
super.oninit(vnode);

Expand Down
6 changes: 3 additions & 3 deletions extensions/tags/js/src/admin/components/EditTagModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import app from 'flarum/admin/app';
import Modal, { IInternalModalAttrs } from 'flarum/common/components/Modal';
import FormModal, { IFormModalAttrs } from 'flarum/common/components/FormModal';
import Button from 'flarum/common/components/Button';
import ColorPreviewInput from 'flarum/common/components/ColorPreviewInput';
import ItemList from 'flarum/common/utils/ItemList';
Expand All @@ -12,7 +12,7 @@ import type Mithril from 'mithril';
import tagLabel from '../../common/helpers/tagLabel';
import type Tag from '../../common/models/Tag';

export interface EditTagModalAttrs extends IInternalModalAttrs {
export interface EditTagModalAttrs extends IFormModalAttrs {
primary?: boolean;
model?: Tag;
}
Expand All @@ -21,7 +21,7 @@ export interface EditTagModalAttrs extends IInternalModalAttrs {
* The `EditTagModal` component shows a modal dialog which allows the user
* to create or edit a tag.
*/
export default class EditTagModal extends Modal<EditTagModalAttrs> {
export default class EditTagModal extends FormModal<EditTagModalAttrs> {
tag!: Tag;

name!: Stream<string>;
Expand Down
10 changes: 5 additions & 5 deletions extensions/tags/js/src/common/components/TagSelectionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import extractText from 'flarum/common/utils/extractText';
import highlight from 'flarum/common/helpers/highlight';
import KeyboardNavigatable from 'flarum/common/utils/KeyboardNavigatable';
import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
import Modal from 'flarum/common/components/Modal';
import FormModal from 'flarum/common/components/FormModal';
import Stream from 'flarum/common/utils/Stream';

import sortTags from '../utils/sortTags';
Expand All @@ -14,7 +14,7 @@ import tagIcon from '../helpers/tagIcon';
import ToggleButton from '../../forum/components/ToggleButton';

import type Tag from '../models/Tag';
import type { IInternalModalAttrs } from 'flarum/common/components/Modal';
import type { IFormModalAttrs } from 'flarum/common/components/FormModal';
import type Mithril from 'mithril';

export interface ITagSelectionModalLimits {
Expand All @@ -34,7 +34,7 @@ export interface ITagSelectionModalLimits {
};
}

export interface ITagSelectionModalAttrs extends IInternalModalAttrs {
export interface ITagSelectionModalAttrs extends IFormModalAttrs {
/** Custom modal className to use. */
className?: string;
/** Modal title, defaults to 'Choose Tags'. */
Expand Down Expand Up @@ -64,7 +64,7 @@ export type ITagSelectionModalState = undefined;
export default class TagSelectionModal<
CustomAttrs extends ITagSelectionModalAttrs = ITagSelectionModalAttrs,
CustomState extends ITagSelectionModalState = ITagSelectionModalState
> extends Modal<CustomAttrs, CustomState> {
> extends FormModal<CustomAttrs, CustomState> {
protected loading = true;
protected tags!: Tag[];
protected selected: Tag[] = [];
Expand Down Expand Up @@ -108,7 +108,7 @@ export default class TagSelectionModal<
.onSelect(this.select.bind(this))
.onRemove(() => this.selected.splice(this.selected.length - 1, 1));

app.tagList.load(['parent']).then((tags) => {
app.tagList.load(['parent']).then((tags: Tag[]) => {
this.loading = false;

if (this.attrs.selectableTags) {
Expand Down
2 changes: 1 addition & 1 deletion extensions/tags/js/src/common/states/TagListState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class TagListState {
async query(includes: string[] = []): Promise<Tag[]> {
this.loadedIncludes ??= new Set();

return app.store.find<Tag[]>('tags', { include: includes.join(',') }).then((val) => {
return app.store.find<Tag[]>('tags', { include: includes.join(',') }).then((val: Tag) => {
includes.forEach((include) => this.loadedIncludes!.add(include));
return val;
});
Expand Down
6 changes: 3 additions & 3 deletions framework/core/js/src/admin/components/CreateUserModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import app from '../../admin/app';
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import FormModal, { IFormModalAttrs } from '../../common/components/FormModal';
import Button from '../../common/components/Button';
import extractText from '../../common/utils/extractText';
import ItemList from '../../common/utils/ItemList';
Expand All @@ -9,7 +9,7 @@ import Switch from '../../common/components/Switch';
import { generateRandomString } from '../../common/utils/string';
import Form from '../../common/components/Form';

export interface ICreateUserModalAttrs extends IInternalModalAttrs {
export interface ICreateUserModalAttrs extends IFormModalAttrs {
username?: string;
email?: string;
password?: string;
Expand All @@ -24,7 +24,7 @@ export type SignupBody = {
password: string;
};

export default class CreateUserModal<CustomAttrs extends ICreateUserModalAttrs = ICreateUserModalAttrs> extends Modal<CustomAttrs> {
export default class CreateUserModal<CustomAttrs extends ICreateUserModalAttrs = ICreateUserModalAttrs> extends FormModal<CustomAttrs> {
/**
* The value of the username input.
*/
Expand Down
6 changes: 3 additions & 3 deletions framework/core/js/src/admin/components/EditGroupModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import app from '../../admin/app';
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import FormModal, { IFormModalAttrs } from '../../common/components/FormModal';
import Button from '../../common/components/Button';
import Badge from '../../common/components/Badge';
import Group from '../../common/models/Group';
Expand All @@ -11,15 +11,15 @@ import extractText from '../../common/utils/extractText';
import ColorPreviewInput from '../../common/components/ColorPreviewInput';
import Form from '../../common/components/Form';

export interface IEditGroupModalAttrs extends IInternalModalAttrs {
export interface IEditGroupModalAttrs extends IFormModalAttrs {
group?: Group;
}

/**
* The `EditGroupModal` component shows a modal dialog which allows the user
* to create or edit a group.
*/
export default class EditGroupModal<CustomAttrs extends IEditGroupModalAttrs = IEditGroupModalAttrs> extends Modal<CustomAttrs> {
export default class EditGroupModal<CustomAttrs extends IEditGroupModalAttrs = IEditGroupModalAttrs> extends FormModal<CustomAttrs> {
group!: Group;
nameSingular!: Stream<string>;
namePlural!: Stream<string>;
Expand Down
4 changes: 0 additions & 4 deletions framework/core/js/src/admin/components/LoadingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,4 @@ export default class LoadingModal<ModalAttrs extends ILoadingModalAttrs = ILoadi
content() {
return null;
}

onsubmit(e: Event): void {
throw new Error('LoadingModal should not throw errors.');
}
}
6 changes: 3 additions & 3 deletions framework/core/js/src/admin/components/SettingsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import app from '../../admin/app';
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import FormModal, { IFormModalAttrs } from '../../common/components/FormModal';
import Button from '../../common/components/Button';
import Stream from '../../common/utils/Stream';
import saveSettings from '../utils/saveSettings';
import Mithril from 'mithril';
import { MutableSettings, SettingValue } from './AdminPage';
import Form from '../../common/components/Form';

export interface ISettingsModalAttrs extends IInternalModalAttrs {}
export interface ISettingsModalAttrs extends IFormModalAttrs {}

export default abstract class SettingsModal<CustomAttrs extends ISettingsModalAttrs = ISettingsModalAttrs> extends Modal<CustomAttrs> {
export default abstract class SettingsModal<CustomAttrs extends ISettingsModalAttrs = ISettingsModalAttrs> extends FormModal<CustomAttrs> {
settings: MutableSettings = {};
loading: boolean = false;

Expand Down
1 change: 1 addition & 0 deletions framework/core/js/src/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import './components/SelectDropdown';
import './components/ModalManager';
import './components/Button';
import './components/Modal';
import './components/FormModal';
import './components/GroupBadge';
import './components/TextEditor';
import './components/TextEditorButton';
Expand Down
6 changes: 3 additions & 3 deletions framework/core/js/src/common/components/EditUserModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import app from '../../common/app';
import Modal, { IInternalModalAttrs } from './Modal';
import FormModal, { IFormModalAttrs } from '../../common/components/FormModal';
import Button from './Button';
import GroupBadge from './GroupBadge';
import Group from '../models/Group';
Expand All @@ -11,11 +11,11 @@ import type User from '../models/User';
import type { SaveAttributes, SaveRelationships } from '../Model';
import Form from './Form';

export interface IEditUserModalAttrs extends IInternalModalAttrs {
export interface IEditUserModalAttrs extends IFormModalAttrs {
user: User;
}

export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEditUserModalAttrs> extends Modal<CustomAttrs> {
export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEditUserModalAttrs> extends FormModal<CustomAttrs> {
protected username!: Stream<string>;
protected email!: Stream<string>;
protected isEmailConfirmed!: Stream<boolean>;
Expand Down
51 changes: 51 additions & 0 deletions framework/core/js/src/common/components/FormModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Modal from './Modal';
import type { IInternalModalAttrs } from './Modal';
import RequestError from '../utils/RequestError';
import Mithril from 'mithril';

export interface IFormModalAttrs extends IInternalModalAttrs {}

/**
* The `FormModal` component displays a modal dialog, wrapped in a form.
* Subclasses should implement the `className`, `title`, and `content` methods.
*/
export default abstract class FormModal<ModalAttrs extends IFormModalAttrs = IFormModalAttrs, CustomState = undefined> extends Modal<
ModalAttrs,
CustomState
> {
wrapper(children: Mithril.Children): Mithril.Children {
return <form onsubmit={this.onsubmit.bind(this)}>{children}</form>;
}

/**
* Handle the modal form's submit event.
*/
onsubmit(e: SubmitEvent): void {
// ...
}

/**
* Callback executed when the modal is shown and ready to be interacted with.
*
* @remark Focuses the first input in the modal.
*/
onready(): void {
this.$().find('input, select, textarea').first().trigger('focus').trigger('select');
}

/**
* Shows an alert describing an error returned from the API, and gives focus to
* the first relevant field involved in the error.
*/
onerror(error: RequestError): void {
this.alertAttrs = error.alert;

m.redraw();

if (error.status === 422 && error.response?.errors) {
this.$('form [name=' + (error.response.errors as any[])[0].source.pointer.replace('/data/attributes/', '') + ']').trigger('select');
} else {
this.onready();
}
}
}
Loading

0 comments on commit eaabeab

Please sign in to comment.