Skip to content

Commit

Permalink
chore(types): allow containers to constrain children
Browse files Browse the repository at this point in the history
  • Loading branch information
eliandoran committed Jan 5, 2025
1 parent 4cfb0d6 commit 6d41af9
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 41 deletions.
12 changes: 7 additions & 5 deletions src/public/app/components/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { CommandMappings, CommandNames } from './app_context.js';
* - although the execution is async, we are collecting all the promises, and therefore it is possible to wait until the
* event / command is executed in all components - by simply awaiting the `triggerEvent()`.
*/
export default class Component {
export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
$widget!: JQuery<HTMLElement>;
componentId: string;
children: Component[];
children: ChildT[];
initialized: Promise<void> | null;
parent?: Component;
parent?: TypedComponent<any>;
position!: number;

constructor() {
Expand All @@ -31,12 +31,12 @@ export default class Component {
return this.constructor.name.replace(/[^A-Z0-9]/ig, "_");
}

setParent(parent: Component) {
setParent(parent: TypedComponent<any>) {
this.parent = parent;
return this;
}

child(...components: Component[]) {
child(...components: ChildT[]) {
for (const component of components) {
component.setParent(this);

Expand Down Expand Up @@ -122,3 +122,5 @@ export default class Component {
return promise;
}
}

export default class Component extends TypedComponent<Component> {}
22 changes: 12 additions & 10 deletions src/public/app/widgets/basic_widget.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import Component from "../components/component.js";
import Component, { TypedComponent } from "../components/component.js";
import froca from "../services/froca.js";
import { t } from "../services/i18n.js";
import toastService from "../services/toast.js";

/**
* This is the base widget for all other widgets.
*
* For information on using widgets, see the tutorial {@tutorial widget_basics}.
*/
class BasicWidget extends Component {
export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedComponent<T> {
protected attrs: Record<string, string>;
private classes: string[];
private childPositionCounter: number;
Expand All @@ -27,7 +22,7 @@ class BasicWidget extends Component {
this.childPositionCounter = 10;
}

child(...components: Component[]) {
child(...components: T[]) {
if (!components) {
return this;
}
Expand All @@ -53,7 +48,7 @@ class BasicWidget extends Component {
* @param components the components to be added as children to this component provided the condition is truthy.
* @returns self for chaining.
*/
optChild(condition: boolean, ...components: Component[]) {
optChild(condition: boolean, ...components: T[]) {
if (condition) {
return this.child(...components);
} else {
Expand Down Expand Up @@ -259,4 +254,11 @@ class BasicWidget extends Component {
cleanup() {}
}

export default BasicWidget;
/**
* This is the base widget for all other widgets.
*
* For information on using widgets, see the tutorial {@tutorial widget_basics}.
*/
export default class BasicWidget extends TypedBasicWidget<Component> {

}
18 changes: 0 additions & 18 deletions src/public/app/widgets/containers/container.js

This file was deleted.

30 changes: 30 additions & 0 deletions src/public/app/widgets/containers/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Component, { TypedComponent } from "../../components/component.js";
import BasicWidget, { TypedBasicWidget } from "../basic_widget.js";

export default class Container<T extends TypedComponent<any>> extends TypedBasicWidget<T> {

doRender() {
this.$widget = $(`<div>`);
this.renderChildren();
}

renderChildren() {
for (const widget of this.children) {
if (!("render" in widget)) {
throw "Non-renderable widget encountered.";
}

const typedWidget = widget as unknown as TypedBasicWidget<any>;

try {
if ("render" in widget) {
this.$widget.append(typedWidget.render());
}

} catch (e: any) {
typedWidget.logRenderingError(e);
}
}
}

}
4 changes: 3 additions & 1 deletion src/public/app/widgets/containers/flex_container.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { TypedComponent } from "../../components/component.js";
import Container from "./container.js";

export type FlexDirection = "row" | "column";

export default class FlexContainer extends Container {
export default class FlexContainer<T extends TypedComponent<any>> extends Container<T> {

constructor(direction: FlexDirection) {
super();
Expand All @@ -13,4 +14,5 @@ export default class FlexContainer extends Container {

this.attrs.style = `display: flex; flex-direction: ${direction};`;
}

}
2 changes: 1 addition & 1 deletion src/public/app/widgets/containers/launcher_container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import appContext, { EventData } from "../../components/app_context.js";
import LauncherWidget from "./launcher.js";
import utils from "../../services/utils.js";

export default class LauncherContainer extends FlexContainer {
export default class LauncherContainer extends FlexContainer<LauncherWidget> {
private isHorizontalLayout: boolean;

constructor(isHorizontalLayout: boolean) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import FlexContainer from "./flex_container.js";
import splitService from "../../services/resizer.js";
import RightPanelWidget from "../right_panel_widget.js";

export default class RightPaneContainer extends FlexContainer<RightPanelWidget> {

private rightPaneHidden: boolean;

export default class RightPaneContainer extends FlexContainer {
constructor() {
super('column');

Expand All @@ -19,7 +23,7 @@ export default class RightPaneContainer extends FlexContainer {
&& !!this.children.find(ch => ch.isEnabled() && ch.canBeShown());
}

handleEventInChildren(name, data) {
handleEventInChildren(name: string, data: unknown) {
const promise = super.handleEventInChildren(name, data);

if (['activeContextChanged', 'noteSwitchedAndActivated', 'noteSwitched'].includes(name)) {
Expand Down
3 changes: 2 additions & 1 deletion src/public/app/widgets/mobile_widgets/sidebar_container.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EventData } from "../../components/app_context.js";
import { Screen } from "../../components/mobile_screen_switcher.js";
import BasicWidget from "../basic_widget.js";
import FlexContainer, { FlexDirection } from "../containers/flex_container.js";

const DRAG_STATE_NONE = 0;
Expand All @@ -14,7 +15,7 @@ const DRAG_CLOSED_START_THRESHOLD = 10;
/** The number of pixels the user has to drag across the screen to the left when the sidebar is opened to trigger the drag close animation. */
const DRAG_OPENED_START_THRESHOLD = 80;

export default class SidebarContainer extends FlexContainer {
export default class SidebarContainer extends FlexContainer<BasicWidget> {

private screenName: Screen;
/** The screen name that is currently active, according to the screen changed event. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import BasicWidget from "./basic_widget.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import toastService from "../services/toast.js";
import { t } from "../services/i18n.js";

const WIDGET_TPL = `
<div class="card widget">
Expand All @@ -19,6 +18,12 @@ const WIDGET_TPL = `
* @extends {NoteContextAwareWidget}
*/
class RightPanelWidget extends NoteContextAwareWidget {

private $bodyWrapper!: JQuery<HTMLElement>;
$body!: JQuery<HTMLElement>;
private $title!: JQuery<HTMLElement>;
private $buttons!: JQuery<HTMLElement>;

/** Title to show in the panel. */
get widgetTitle() { return "Untitled widget"; }

Expand Down Expand Up @@ -53,7 +58,7 @@ class RightPanelWidget extends NoteContextAwareWidget {
this.$buttons.empty();

for (const buttonWidget of this.children) {
this.$buttons.append(buttonWidget.render());
this.$buttons.append((buttonWidget as BasicWidget).render());
}

this.initialized = this.doRenderBody().catch(e => {
Expand Down

0 comments on commit 6d41af9

Please sign in to comment.