mirror of
https://github.com/devcode-it/openstamanager.git
synced 2025-06-05 22:09:38 +02:00
chore: Usa mithril-utilities
per Component, Form e Request
This commit is contained in:
@@ -1,174 +0,0 @@
|
|||||||
import classnames, {Argument as ClassNames} from 'classnames';
|
|
||||||
import collect, {Collection} from 'collect.js';
|
|
||||||
import Mithril from 'mithril';
|
|
||||||
import type {
|
|
||||||
Children,
|
|
||||||
ClassComponent,
|
|
||||||
Vnode,
|
|
||||||
VnodeDOM
|
|
||||||
} from 'mithril';
|
|
||||||
|
|
||||||
export interface Attributes {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AttributesCollection<T extends Attributes> extends Collection<T> {
|
|
||||||
addClassNames(...classNames: ClassNames[]): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// noinspection SpellCheckingInspection,JSUnusedGlobalSymbols,JSUnusedLocalSymbols
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @abstract
|
|
||||||
*
|
|
||||||
* The `Component` class defines a user interface 'building block'. A component
|
|
||||||
* generates a virtual DOM to be rendered on each redraw.
|
|
||||||
*
|
|
||||||
* Essentially, this is a wrapper for Mithril's components that adds several useful features:
|
|
||||||
*
|
|
||||||
* — In the `oninit` and `onbeforeupdate` lifecycle hooks, we store vnode attrs in `this.attrs.
|
|
||||||
* This allows us to use attrs across components without having to pass the vnode to every single
|
|
||||||
* method.
|
|
||||||
* — The static `initAttrs` method allows a convenient way to provide defaults (or to otherwise
|
|
||||||
* modify) the attrs that have been passed into a component.
|
|
||||||
* — When the component is created in the DOM, we store its DOM element under `this.element`;
|
|
||||||
* this lets us use Cash to modify child DOM state from internal methods via the `this.$()`
|
|
||||||
* method.
|
|
||||||
* — A convenience `component` method, which serves as an alternative to hyperscript and JSX.
|
|
||||||
*
|
|
||||||
* As with other Mithril components, components extending Component can be initialized
|
|
||||||
* and nested using JSX. The `component` method can also
|
|
||||||
* be used.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* return m('div', <MyComponent foo="bar"><p>Hello World</p></MyComponent>);
|
|
||||||
*
|
|
||||||
* @see https://mithril.js.org/components.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
export abstract class Component<
|
|
||||||
A extends Attributes = Attributes,
|
|
||||||
S = undefined
|
|
||||||
> implements ClassComponent<A> {
|
|
||||||
/**
|
|
||||||
* The root DOM element for the component.
|
|
||||||
*/
|
|
||||||
element!: Element;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes passed into the component.
|
|
||||||
*
|
|
||||||
* @see https://mithril.js.org/components.html#passing-data-to-components
|
|
||||||
* @see initAttrs
|
|
||||||
*/
|
|
||||||
attrs!: AttributesCollection<A>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class component state that is persisted between redraws.
|
|
||||||
*
|
|
||||||
* Updating this will **not** automatically trigger a redraw, unlike
|
|
||||||
* other frameworks.
|
|
||||||
*
|
|
||||||
* This is different to Vnode state, which is always an instance of your
|
|
||||||
* class component.
|
|
||||||
*
|
|
||||||
* This is `undefined` by default.
|
|
||||||
*/
|
|
||||||
state!: S;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for attribute code completion in JSX.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private __attrs!: A;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
abstract view(vnode: Vnode<A, this>): Children;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
oninit(vnode: Vnode<A, this>) {
|
|
||||||
this.setAttrs(vnode.attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
oncreate(vnode: VnodeDOM<A, this>) {
|
|
||||||
this.element = vnode.dom;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
onbeforeupdate(vnode: VnodeDOM<A, this>) {
|
|
||||||
this.setAttrs(vnode.attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
onupdate(vnode: VnodeDOM<A, this>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
onbeforeremove(vnode: VnodeDOM<A, this>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
onremove(vnode: VnodeDOM<A, this>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a reference to the vnode attrs after running them through initAttrs,
|
|
||||||
* and checking for common issues.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
setAttrs(attributes: A): void {
|
|
||||||
this.initAttrs(attributes);
|
|
||||||
|
|
||||||
if (attributes) {
|
|
||||||
if ('children' in attributes) {
|
|
||||||
// noinspection JSUnresolvedVariable
|
|
||||||
throw new Error(
|
|
||||||
`[${this.constructor.name}] The "children" attribute of attrs should never be used. Either pass children in as
|
|
||||||
the vnode children or rename the attribute`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('tag' in attributes) {
|
|
||||||
// noinspection JSUnresolvedVariable
|
|
||||||
throw new Error(
|
|
||||||
`[${this.constructor.name}] You cannot use the "tag" attribute name with Mithril 2.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const attributesCollection = collect<A>(attributes);
|
|
||||||
attributesCollection.macro('addClassNames', (...classNames: ClassNames[]) => {
|
|
||||||
attributesCollection.put(
|
|
||||||
'className',
|
|
||||||
classnames(attributesCollection.get('className') as ClassNames, ...classNames)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
this.attrs = attributesCollection as AttributesCollection<A>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// noinspection JSUnusedLocalSymbols
|
|
||||||
/**
|
|
||||||
* Initialize the component's attrs.
|
|
||||||
*
|
|
||||||
* This can be used to assign default values for missing, optional attrs.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
initAttrs(attributes: A): void {
|
|
||||||
}
|
|
||||||
}
|
|
@@ -14,7 +14,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '~/Components/Component';
|
} from 'mithril-utilities';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
|
|
||||||
export interface DataTableAttributes extends Attributes {
|
export interface DataTableAttributes extends Attributes {
|
||||||
|
@@ -10,7 +10,7 @@ import {Vnode} from 'mithril';
|
|||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '~/Components/Component';
|
} from 'mithril-utilities';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
|
|
||||||
export interface DataTableColumnAttributes extends Attributes, Partial<JSX.IntrinsicElements['md-data-table-column']> {
|
export interface DataTableColumnAttributes extends Attributes, Partial<JSX.IntrinsicElements['md-data-table-column']> {
|
||||||
|
@@ -5,12 +5,12 @@ import {
|
|||||||
Vnode,
|
Vnode,
|
||||||
VnodeDOM
|
VnodeDOM
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
|
|
||||||
|
import {Form} from 'mithril-utilities';
|
||||||
import Stream from 'mithril/stream';
|
import Stream from 'mithril/stream';
|
||||||
import {Class} from 'type-fest';
|
import {Class} from 'type-fest';
|
||||||
|
|
||||||
import Form from '~/Components/Form';
|
|
||||||
import MdIcon from '~/Components/MdIcon';
|
|
||||||
import RecordDialog, {RecordDialogAttributes} from '~/Components/Dialogs/RecordDialog';
|
import RecordDialog, {RecordDialogAttributes} from '~/Components/Dialogs/RecordDialog';
|
||||||
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import Model from '~/Models/Model';
|
import Model from '~/Models/Model';
|
||||||
import {
|
import {
|
||||||
VnodeCollection,
|
VnodeCollection,
|
||||||
|
@@ -2,10 +2,10 @@ import {
|
|||||||
Children,
|
Children,
|
||||||
Vnode
|
Vnode
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
|
import {RequestError} from 'mithril-utilities';
|
||||||
|
|
||||||
import Model from '~/Models/Model';
|
import Model from '~/Models/Model';
|
||||||
import {showSnackbar} from '~/utils/misc';
|
import {showSnackbar} from '~/utils/misc';
|
||||||
import {RequestError} from '~/utils/Request';
|
|
||||||
|
|
||||||
import RecordDialog, {RecordDialogAttributes} from './RecordDialog';
|
import RecordDialog, {RecordDialogAttributes} from './RecordDialog';
|
||||||
|
|
||||||
|
@@ -6,12 +6,12 @@ import {
|
|||||||
Vnode,
|
Vnode,
|
||||||
VnodeDOM
|
VnodeDOM
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '../Component';
|
} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
|
|
||||||
export interface DialogAttributes extends Attributes, Partial<Pick<MDDialog,
|
export interface DialogAttributes extends Attributes, Partial<Pick<MDDialog,
|
||||||
'fullscreen' | 'fullscreenBreakpoint' | 'footerHidden' | 'stacked' | 'defaultAction' |
|
'fullscreen' | 'fullscreenBreakpoint' | 'footerHidden' | 'stacked' | 'defaultAction' |
|
||||||
|
@@ -1,97 +0,0 @@
|
|||||||
import {
|
|
||||||
ChildArray,
|
|
||||||
Children,
|
|
||||||
Vnode,
|
|
||||||
VnodeDOM
|
|
||||||
} from 'mithril';
|
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
import {Without} from 'type-fest/source/merge-exclusive';
|
|
||||||
|
|
||||||
import {isVnode} from '~/utils/misc';
|
|
||||||
|
|
||||||
import {Component} from './Component';
|
|
||||||
|
|
||||||
export type FormSubmitEvent = SubmitEvent & {data: FormData};
|
|
||||||
|
|
||||||
export interface FormAttributes extends Partial<Omit<HTMLElementTagNameMap['form'], 'style' | 'onsubmit'>> {
|
|
||||||
onsubmit?: (event: FormSubmitEvent) => void,
|
|
||||||
state?: Record<string, Stream<string | any>> | Map<string, Stream<string | any>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Form<A extends FormAttributes = FormAttributes> extends Component<A, Record<string, Stream<string | any>> | Map<string, Stream<string | any>>> {
|
|
||||||
element!: HTMLFormElement;
|
|
||||||
onsubmitFunction?: A['onsubmit'];
|
|
||||||
// TODO: Change all states to Map?
|
|
||||||
state!: NonNullable<A['state']>;
|
|
||||||
|
|
||||||
oninit(vnode: Vnode<A, this>) {
|
|
||||||
super.oninit(vnode);
|
|
||||||
this.onsubmitFunction = vnode.attrs.onsubmit;
|
|
||||||
this.state = vnode.attrs.state ?? {};
|
|
||||||
delete vnode.attrs.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
view(vnode: Vnode<A>) {
|
|
||||||
const attributes = this.attrs.except(['onsubmit', 'state']);
|
|
||||||
return (
|
|
||||||
<form {...attributes.all()} onsubmit={this.onsubmit.bind(this)}>
|
|
||||||
{(vnode.children as ChildArray).map(this.attachStreamToInput.bind(this))}
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
attachStreamToInput(child: Children) {
|
|
||||||
// Check if child is a Vnode
|
|
||||||
if (isVnode<{name?: string, id: string, value: unknown, oninput?: (event: Event) => void, state?: Stream<any>}>(child)) {
|
|
||||||
const stream = child.attrs.state ?? this.getState(child.attrs.name ?? child.attrs.id);
|
|
||||||
if (stream) {
|
|
||||||
child.attrs.value = stream();
|
|
||||||
|
|
||||||
const originalOninput = child.attrs.oninput;
|
|
||||||
// This ESLint rule is disabled because it doesn't recognize that the `oninput` attribute is being set and Mithril uses it instead of adding an event listener
|
|
||||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
||||||
child.attrs.oninput = (event: Event) => {
|
|
||||||
stream((event.target as HTMLInputElement).value);
|
|
||||||
if (originalOninput) {
|
|
||||||
originalOninput(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
delete child.attrs.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if `child` has children and recursively call this function on them.
|
|
||||||
if (Array.isArray(child.children)) {
|
|
||||||
child.children = child.children.map(this.attachStreamToInput.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
oncreate(vnode: VnodeDOM<A, this>) {
|
|
||||||
super.oncreate(vnode);
|
|
||||||
const submitter = this.element.querySelector<HTMLElement>('[type="submit"]');
|
|
||||||
if (submitter) {
|
|
||||||
submitter.addEventListener('click', () => {
|
|
||||||
// TODO: Add submitter when https://github.com/material-components/material-web/issues/3941 is completed
|
|
||||||
this.element.requestSubmit();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onsubmit(e: FormSubmitEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.data = new FormData(e.target as HTMLFormElement);
|
|
||||||
if (this.onsubmitFunction) {
|
|
||||||
this.onsubmitFunction(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getState(key: string) {
|
|
||||||
if (this.state instanceof Map) {
|
|
||||||
return this.state.get(key);
|
|
||||||
}
|
|
||||||
return this.state[key];
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,7 +2,7 @@ import '@material/web/icon/icon.js';
|
|||||||
|
|
||||||
import type MaterialIcons from '@mdi/js';
|
import type MaterialIcons from '@mdi/js';
|
||||||
|
|
||||||
import {Component} from './Component';
|
import {Component} from 'mithril-utilities';
|
||||||
|
|
||||||
export interface Attributes extends Partial<SVGElement> {
|
export interface Attributes extends Partial<SVGElement> {
|
||||||
icon: typeof MaterialIcons | string;
|
icon: typeof MaterialIcons | string;
|
||||||
|
@@ -6,14 +6,14 @@ import {
|
|||||||
Vnode,
|
Vnode,
|
||||||
VnodeDOM
|
VnodeDOM
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
|
import {
|
||||||
|
Attributes,
|
||||||
|
Component
|
||||||
|
} from 'mithril-utilities';
|
||||||
|
|
||||||
import {Footer} from '~/Components/layout/Footer';
|
import {Footer} from '~/Components/layout/Footer';
|
||||||
|
|
||||||
import logoUrl from '../../images/logo_completo.png';
|
import logoUrl from '../../images/logo_completo.png';
|
||||||
import {
|
|
||||||
Attributes,
|
|
||||||
Component
|
|
||||||
} from './Component';
|
|
||||||
import TopAppBar from './layout/TopAppBar';
|
import TopAppBar from './layout/TopAppBar';
|
||||||
|
|
||||||
export interface PageAttributes<A extends Record<string, any> & {external?: boolean} = Record<string, any>> extends Attributes, Required<ComponentAttributes<A>> {
|
export interface PageAttributes<A extends Record<string, any> & {external?: boolean} = Record<string, any>> extends Attributes, Required<ComponentAttributes<A>> {
|
||||||
|
@@ -10,12 +10,12 @@ import {
|
|||||||
Children,
|
Children,
|
||||||
Vnode
|
Vnode
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '~/Components/Component';
|
} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import {VnodeCollectionItem} from '~/typings/jsx';
|
import {VnodeCollectionItem} from '~/typings/jsx';
|
||||||
import {isMobile} from '~/utils/misc';
|
import {isMobile} from '~/utils/misc';
|
||||||
|
@@ -4,12 +4,12 @@ import {ListItemLink} from '@material/web/list/lib/listitemlink/list-item-link';
|
|||||||
import '@material/web/list/list-item-link.js';
|
import '@material/web/list/list-item-link.js';
|
||||||
import type * as MaterialIcons from '@mdi/js';
|
import type * as MaterialIcons from '@mdi/js';
|
||||||
import {Vnode} from 'mithril';
|
import {Vnode} from 'mithril';
|
||||||
import {ValueOf} from 'type-fest';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '~/Components/Component';
|
} from 'mithril-utilities';
|
||||||
|
import {ValueOf} from 'type-fest';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
|
|
||||||
type Icons = ValueOf<typeof MaterialIcons>;
|
type Icons = ValueOf<typeof MaterialIcons>;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import {Component} from '../Component';
|
import {Component} from 'mithril-utilities';
|
||||||
|
|
||||||
export class Footer extends Component {
|
export class Footer extends Component {
|
||||||
view() {
|
view() {
|
||||||
|
@@ -1,8 +1,4 @@
|
|||||||
import '@material/web/iconbutton/standard-icon-button.js';
|
import '@material/web/iconbutton/standard-icon-button.js';
|
||||||
import '~/WebComponents/TopAppBar';
|
|
||||||
|
|
||||||
import {IconButton} from '@material/web/iconbutton/lib/icon-button';
|
|
||||||
import {Menu} from '@material/web/menu/lib/menu';
|
|
||||||
import {
|
import {
|
||||||
mdiMenu,
|
mdiMenu,
|
||||||
mdiMenuOpen
|
mdiMenuOpen
|
||||||
@@ -12,13 +8,13 @@ import {
|
|||||||
Vnode,
|
Vnode,
|
||||||
VnodeDOM
|
VnodeDOM
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import logo from '~/../images/logo.png';
|
|
||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '~/Components/Component';
|
} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
|
|
||||||
|
import logo from '~/../images/logo.png';
|
||||||
import Drawer from '~/Components/layout/Drawer';
|
import Drawer from '~/Components/layout/Drawer';
|
||||||
import NotificationsAction from '~/Components/layout/topappbar_actions/NotificationsAction';
|
import NotificationsAction from '~/Components/layout/topappbar_actions/NotificationsAction';
|
||||||
import PeriodSwitcherAction from '~/Components/layout/topappbar_actions/PeriodSwitcherAction';
|
import PeriodSwitcherAction from '~/Components/layout/topappbar_actions/PeriodSwitcherAction';
|
||||||
@@ -30,6 +26,7 @@ import {
|
|||||||
isMobile,
|
isMobile,
|
||||||
mobileMediaQuery
|
mobileMediaQuery
|
||||||
} from '~/utils/misc';
|
} from '~/utils/misc';
|
||||||
|
import '~/WebComponents/TopAppBar';
|
||||||
|
|
||||||
export default class TopAppBar extends Component {
|
export default class TopAppBar extends Component {
|
||||||
drawerOpenState = Stream(!isMobile());
|
drawerOpenState = Stream(!isMobile());
|
||||||
|
@@ -3,7 +3,7 @@ import {
|
|||||||
Vnode
|
Vnode
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
|
|
||||||
import {Component} from '~/Components/Component';
|
import {Component} from 'mithril-utilities';
|
||||||
import MdIcon, {Attributes as MdIconAttributes} from '~/Components/MdIcon';
|
import MdIcon, {Attributes as MdIconAttributes} from '~/Components/MdIcon';
|
||||||
|
|
||||||
export default abstract class TopAppBarAction extends Component {
|
export default abstract class TopAppBarAction extends Component {
|
||||||
|
@@ -1,18 +1,17 @@
|
|||||||
|
import {router} from '@maicol07/inertia-mithril';
|
||||||
import '@material/web/button/outlined-button.js';
|
import '@material/web/button/outlined-button.js';
|
||||||
import '@material/web/button/text-button.js';
|
import '@material/web/button/text-button.js';
|
||||||
|
|
||||||
import {router} from '@maicol07/inertia-mithril';
|
|
||||||
import {
|
import {
|
||||||
mdiAccountCircleOutline,
|
mdiAccountCircleOutline,
|
||||||
mdiAccountOutline,
|
mdiAccountOutline,
|
||||||
mdiLogoutVariant
|
mdiLogoutVariant
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import {Vnode} from 'mithril';
|
import {Vnode} from 'mithril';
|
||||||
|
import {Request} from 'mithril-utilities';
|
||||||
import Stream from 'mithril/stream';
|
import Stream from 'mithril/stream';
|
||||||
|
|
||||||
import Dialog from '~/Components/Dialogs/Dialog';
|
import Dialog from '~/Components/Dialogs/Dialog';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import Request from '~/utils/Request';
|
|
||||||
|
|
||||||
import TopAppBarAction from './TopAppBarAction';
|
import TopAppBarAction from './TopAppBarAction';
|
||||||
|
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import RequestHttpClientPromise from './RequestHttpClientPromise';
|
|
||||||
import Request from '~/utils/Request';
|
|
||||||
import type {
|
import type {
|
||||||
HttpClient,
|
HttpClient,
|
||||||
HttpClientPromise
|
HttpClientPromise
|
||||||
} from 'coloquent';
|
} from 'coloquent';
|
||||||
|
import {Request} from 'mithril-utilities';
|
||||||
|
|
||||||
|
import RequestHttpClientPromise from './RequestHttpClientPromise';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class RequestHttpClient
|
* @class RequestHttpClient
|
||||||
|
@@ -3,7 +3,6 @@ import '@material/web/button/filled-button.js';
|
|||||||
import '@material/web/button/text-button.js';
|
import '@material/web/button/text-button.js';
|
||||||
import '@material/web/checkbox/checkbox.js';
|
import '@material/web/checkbox/checkbox.js';
|
||||||
import '@material/web/dialog/dialog.js';
|
import '@material/web/dialog/dialog.js';
|
||||||
import '~/Components/m3/FilledTextField';
|
|
||||||
|
|
||||||
import {Dialog} from '@material/web/dialog/lib/dialog';
|
import {Dialog} from '@material/web/dialog/lib/dialog';
|
||||||
import {
|
import {
|
||||||
@@ -18,18 +17,19 @@ import type {
|
|||||||
Vnode,
|
Vnode,
|
||||||
VnodeDOM
|
VnodeDOM
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import Form, {FormSubmitEvent} from '~/Components/Form';
|
import {
|
||||||
|
Form,
|
||||||
|
FormSubmitEvent,
|
||||||
|
Request,
|
||||||
|
RequestError
|
||||||
|
} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
|
import '~/Components/m3/FilledTextField';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import Page, {
|
import Page, {PageAttributes} from '~/Components/Page';
|
||||||
PageAttributes
|
|
||||||
} from '~/Components/Page';
|
|
||||||
import {VnodeCollectionItem} from '~/typings/jsx';
|
import {VnodeCollectionItem} from '~/typings/jsx';
|
||||||
import {showSnackbar} from '~/utils/misc';
|
import {showSnackbar} from '~/utils/misc';
|
||||||
import Request, {
|
|
||||||
RequestError
|
|
||||||
} from '~/utils/Request';
|
|
||||||
|
|
||||||
export default class LoginPage extends Page {
|
export default class LoginPage extends Page {
|
||||||
form = {
|
form = {
|
||||||
|
@@ -1,30 +1,25 @@
|
|||||||
|
import {router} from '@maicol07/inertia-mithril';
|
||||||
import '@maicol07/material-web-additions/card/elevated-card.js';
|
import '@maicol07/material-web-additions/card/elevated-card.js';
|
||||||
import '@material/web/button/filled-button.js';
|
import '@material/web/button/filled-button.js';
|
||||||
import '~/Components/m3/FilledTextField';
|
|
||||||
|
|
||||||
import {router} from '@maicol07/inertia-mithril';
|
|
||||||
import {
|
import {
|
||||||
mdiAccountOutline,
|
mdiAccountOutline,
|
||||||
mdiLockCheckOutline,
|
mdiLockCheckOutline,
|
||||||
mdiLockOutline
|
mdiLockOutline
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import logoUrl from '~/../images/logo_completo.png';
|
|
||||||
import collect from 'collect.js';
|
import collect from 'collect.js';
|
||||||
import type {
|
import type {Vnode} from 'mithril';
|
||||||
Vnode,
|
import {
|
||||||
VnodeDOM
|
Form,
|
||||||
} from 'mithril';
|
FormSubmitEvent,
|
||||||
|
Request,
|
||||||
|
RequestError
|
||||||
|
} from 'mithril-utilities';
|
||||||
import Stream from 'mithril/stream';
|
import Stream from 'mithril/stream';
|
||||||
import Form, {FormSubmitEvent} from '~/Components/Form';
|
import '~/Components/m3/FilledTextField';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import Page, {
|
import Page, {PageAttributes} from '~/Components/Page';
|
||||||
PageAttributes
|
|
||||||
} from '~/Components/Page';
|
|
||||||
import {VnodeCollectionItem} from '~/typings/jsx';
|
import {VnodeCollectionItem} from '~/typings/jsx';
|
||||||
import {showSnackbar} from '~/utils/misc';
|
import {showSnackbar} from '~/utils/misc';
|
||||||
import Request, {
|
|
||||||
RequestError
|
|
||||||
} from '~/utils/Request';
|
|
||||||
|
|
||||||
export default class ResetPasswordPage extends Page {
|
export default class ResetPasswordPage extends Page {
|
||||||
form = {
|
form = {
|
||||||
|
@@ -1,18 +1,14 @@
|
|||||||
import '@maicol07/material-web-additions/card/elevated-card.js';
|
|
||||||
|
|
||||||
import {router} from '@maicol07/inertia-mithril';
|
import {router} from '@maicol07/inertia-mithril';
|
||||||
import type {
|
import '@maicol07/material-web-additions/card/elevated-card.js';
|
||||||
Vnode
|
import type {Vnode} from 'mithril';
|
||||||
} from 'mithril';
|
import {
|
||||||
|
Request,
|
||||||
|
RequestError
|
||||||
|
} from 'mithril-utilities';
|
||||||
import Stream from 'mithril/stream';
|
import Stream from 'mithril/stream';
|
||||||
|
|
||||||
import Page, {
|
import Page, {PageAttributes} from '~/Components/Page';
|
||||||
PageAttributes
|
|
||||||
} from '~/Components/Page';
|
|
||||||
import {showSnackbar} from '~/utils/misc';
|
import {showSnackbar} from '~/utils/misc';
|
||||||
import Request, {
|
|
||||||
RequestError
|
|
||||||
} from '~/utils/Request';
|
|
||||||
import AdminUserStep from '~/Views/Setup/Steps/AdminUserStep';
|
import AdminUserStep from '~/Views/Setup/Steps/AdminUserStep';
|
||||||
import DatabaseStep from '~/Views/Setup/Steps/DatabaseStep';
|
import DatabaseStep from '~/Views/Setup/Steps/DatabaseStep';
|
||||||
import RegionalSettings from '~/Views/Setup/Steps/RegionalSettings';
|
import RegionalSettings from '~/Views/Setup/Steps/RegionalSettings';
|
||||||
|
@@ -9,9 +9,12 @@ import {
|
|||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import collect from 'collect.js';
|
import collect from 'collect.js';
|
||||||
import {Vnode} from 'mithril';
|
import {Vnode} from 'mithril';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import Form, {FormSubmitEvent} from '~/Components/Form';
|
import {
|
||||||
|
Form,
|
||||||
|
FormSubmitEvent
|
||||||
|
} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import {VnodeCollectionItem} from '~/typings/jsx';
|
import {VnodeCollectionItem} from '~/typings/jsx';
|
||||||
import {
|
import {
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import '@maicol07/material-web-additions/layout-grid/layout-grid.js';
|
import '@maicol07/material-web-additions/layout-grid/layout-grid.js';
|
||||||
import '~/Components/m3/FilledTextField';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mdiAccountOutline,
|
mdiAccountOutline,
|
||||||
@@ -14,15 +13,14 @@ import {
|
|||||||
Children,
|
Children,
|
||||||
Vnode
|
Vnode
|
||||||
} from 'mithril';
|
} from 'mithril';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import Form from '~/Components/Form';
|
import Form from 'mithril-utilities';
|
||||||
|
import Request, {RequestError} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
|
import '~/Components/m3/FilledTextField';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import {VnodeCollectionItem} from '~/typings/jsx';
|
import {VnodeCollectionItem} from '~/typings/jsx';
|
||||||
import {showSnackbar} from '~/utils/misc';
|
import {showSnackbar} from '~/utils/misc';
|
||||||
import Request, {
|
|
||||||
RequestError
|
|
||||||
} from '~/utils/Request';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SetupStep,
|
SetupStep,
|
||||||
|
@@ -5,9 +5,9 @@ import {
|
|||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import collect from 'collect.js';
|
import collect from 'collect.js';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Stream from 'mithril/stream';
|
|
||||||
|
|
||||||
import Form from '~/Components/Form';
|
import {Form} from 'mithril-utilities';
|
||||||
|
import Stream from 'mithril/stream';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
import {VnodeCollectionItem} from '~/typings/jsx';
|
import {VnodeCollectionItem} from '~/typings/jsx';
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
Attributes,
|
Attributes,
|
||||||
Component
|
Component
|
||||||
} from '~/Components/Component';
|
} from 'mithril-utilities';
|
||||||
import MdIcon from '~/Components/MdIcon';
|
import MdIcon from '~/Components/MdIcon';
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
|
import type {MdCheckbox} from '@material/web/checkbox/checkbox';
|
||||||
import '@material/web/checkbox/checkbox.js';
|
import '@material/web/checkbox/checkbox.js';
|
||||||
import '@material/web/field/outlined-field.js';
|
import '@material/web/field/outlined-field.js';
|
||||||
|
|
||||||
import type {MdCheckbox} from '@material/web/checkbox/checkbox';
|
|
||||||
import {mdiLicense} from '@mdi/js';
|
import {mdiLicense} from '@mdi/js';
|
||||||
import {
|
import {
|
||||||
Vnode,
|
Vnode,
|
||||||
@@ -14,13 +13,7 @@ import {
|
|||||||
getFlag,
|
getFlag,
|
||||||
getLocaleDisplayName
|
getLocaleDisplayName
|
||||||
} from '~/utils/i18n';
|
} from '~/utils/i18n';
|
||||||
import {
|
import {capitalize} from '~/utils/misc';
|
||||||
capitalize,
|
|
||||||
showSnackbar
|
|
||||||
} from '~/utils/misc';
|
|
||||||
import Request, {
|
|
||||||
RequestError
|
|
||||||
} from '~/utils/Request';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SetupStep,
|
SetupStep,
|
||||||
|
@@ -1,142 +0,0 @@
|
|||||||
import {RequestOptions as MithrilRequestOptions} from 'mithril';
|
|
||||||
import {Cookies} from 'typescript-cookie';
|
|
||||||
|
|
||||||
export type RequestMethods = 'get' | 'head' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | 'patch' | string;
|
|
||||||
|
|
||||||
export interface RequestOptions<R = any> extends MithrilRequestOptions<R> {
|
|
||||||
url?: string,
|
|
||||||
renewCSRF?: boolean,
|
|
||||||
renewCSRFOnFailure?: boolean,
|
|
||||||
method?: RequestMethods
|
|
||||||
beforeRequest?: (options: RequestOptionsWithUrl) => Promise<void>
|
|
||||||
afterRequest?: (response: Promise<R>, options: RequestOptionsWithUrl) => void,
|
|
||||||
xsrfCookieName?: string,
|
|
||||||
xsrfHeaderName?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RequestOptionsWithUrl<R = any> extends RequestOptions<R> {
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RequestError<T = {message: string}> extends Error {
|
|
||||||
code: number;
|
|
||||||
response: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Request<R> {
|
|
||||||
options: RequestOptionsWithUrl<R> = {
|
|
||||||
url: '',
|
|
||||||
headers: {},
|
|
||||||
renewCSRF: false,
|
|
||||||
renewCSRFOnFailure: false, // Renew CSRF token if request fails with 419 status code (CSRF token expired; risky since it can cause an infinite loop)
|
|
||||||
withCredentials: true,
|
|
||||||
xsrfCookieName: 'XSRF-TOKEN',
|
|
||||||
xsrfHeaderName: 'X-XSRF-TOKEN'
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(options: RequestOptions) {
|
|
||||||
this.options = {...this.options, ...options};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the request
|
|
||||||
*
|
|
||||||
* @throws {RequestError} If request has an error
|
|
||||||
*/
|
|
||||||
public async send() {
|
|
||||||
await this.beforeSendingRequest();
|
|
||||||
const response = m.request<R>(this.options);
|
|
||||||
this.afterSendingRequest(response);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actions to perform before sending the request
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private async beforeSendingRequest() {
|
|
||||||
this.phpWorkaround();
|
|
||||||
this.xsfrAutoHeader();
|
|
||||||
|
|
||||||
await this.options.beforeRequest?.(this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for PHP issue with PUT/PATCH/DELETE requests and FormData
|
|
||||||
*
|
|
||||||
* @see https://bugs.php.net/bug.php?id=55815
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private phpWorkaround() {
|
|
||||||
if (this.options.method && !['get', 'post'].includes(this.options.method) && this.options.body instanceof FormData) {
|
|
||||||
this.options.body.append('_method', this.options.method);
|
|
||||||
this.options.method = 'post';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically set the XSRF header if the cookie is set
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private xsfrAutoHeader() {
|
|
||||||
const token = Cookies.get(this.options.xsrfCookieName) as string | undefined;
|
|
||||||
if (token && this.options.xsrfHeaderName) {
|
|
||||||
this.options.headers![this.options.xsrfHeaderName] = decodeURIComponent(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actions to perform after sending the request
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private afterSendingRequest(response: Promise<R>) {
|
|
||||||
this.options.afterRequest?.(response, this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get<R>(url: string, parameters?: RequestOptions['params'], options?: RequestOptions) {
|
|
||||||
return this.sendRequest<R>('get', url, parameters, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
static post<R>(url: string, data?: RequestOptions['body'], options?: RequestOptions) {
|
|
||||||
return this.sendRequest<R>('post', url, data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
static put<R>(url: string, data?: RequestOptions['body'], options?: RequestOptions) {
|
|
||||||
return this.sendRequest<R>('put', url, data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
static patch<R>(url: string, data?: RequestOptions['body'], options?: RequestOptions) {
|
|
||||||
return this.sendRequest<R>('patch', url, data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
static delete<R>(url: string, options?: RequestOptions) {
|
|
||||||
return this.sendRequest<R>('delete', url, undefined, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a request
|
|
||||||
* @param method The method of the request (get, post, put, patch, delete)
|
|
||||||
* @param url The URL of the request
|
|
||||||
* @param data The data to send
|
|
||||||
* @param options The options of the request
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
static sendRequest<R>(
|
|
||||||
method: RequestMethods,
|
|
||||||
url: string,
|
|
||||||
data?: RequestOptions['params'] | RequestOptions['body'],
|
|
||||||
options: RequestOptions = {}
|
|
||||||
) {
|
|
||||||
if (method === 'get') {
|
|
||||||
options.params = data as RequestOptions['params'];
|
|
||||||
} else {
|
|
||||||
options.body = data;
|
|
||||||
}
|
|
||||||
options.url ??= url;
|
|
||||||
|
|
||||||
return (new Request<R>({method, ...options})).send();
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user