From bf1719d75dbfd96e4f2a0035a3aec5ca7a8f900b Mon Sep 17 00:00:00 2001 From: Talon Date: Sat, 24 Aug 2024 20:23:10 +0200 Subject: [PATCH] Make ui components more chainable --- frontend/src/ui/audio-recorder.ts | 2 ++ frontend/src/ui/audio.ts | 8 ++++++++ frontend/src/ui/button.ts | 4 ++++ frontend/src/ui/canvas.ts | 2 ++ frontend/src/ui/checkbox.ts | 4 ++++ frontend/src/ui/collapsable-container.ts | 4 +++- frontend/src/ui/container.ts | 4 +++- frontend/src/ui/date-picker.ts | 3 +++ frontend/src/ui/dialog.ts | 6 ++++-- frontend/src/ui/dropdown.ts | 7 +++++++ frontend/src/ui/file-input.ts | 3 +++ frontend/src/ui/image.ts | 3 +++ frontend/src/ui/list-item.ts | 3 +++ frontend/src/ui/list.ts | 7 +++++++ frontend/src/ui/multiline-input.ts | 4 ++++ frontend/src/ui/node.ts | 6 ++++++ frontend/src/ui/progress-bar.ts | 3 +++ frontend/src/ui/radio-group.ts | 3 +++ frontend/src/ui/slider.ts | 4 ++++ frontend/src/ui/tab-bar.ts | 5 +++++ frontend/src/ui/tab.ts | 4 ++++ frontend/src/ui/tabbed-view.ts | 1 + frontend/src/ui/text-input.ts | 4 ++++ frontend/src/ui/text.ts | 3 +++ frontend/src/ui/time-picker.ts | 3 +++ frontend/src/ui/video.ts | 7 +++++++ frontend/src/ui/window.ts | 2 ++ 27 files changed, 105 insertions(+), 4 deletions(-) diff --git a/frontend/src/ui/audio-recorder.ts b/frontend/src/ui/audio-recorder.ts index 5edd9d5..aecd9ce 100644 --- a/frontend/src/ui/audio-recorder.ts +++ b/frontend/src/ui/audio-recorder.ts @@ -61,11 +61,13 @@ export class AudioRecorder extends UINode { const customEvent = event as CustomEvent; callback(customEvent.detail.audioUrl); }); + return this; } protected triggerRecordingComplete(audioUrl: string) { const event = new CustomEvent("recording-complete", { detail: { audioUrl } }); this.element.dispatchEvent(event); + return this; } public getRecording() { diff --git a/frontend/src/ui/audio.ts b/frontend/src/ui/audio.ts index 033ee67..3169bd6 100644 --- a/frontend/src/ui/audio.ts +++ b/frontend/src/ui/audio.ts @@ -26,33 +26,41 @@ export class Audio extends UINode { } else if (src instanceof MediaStream) { this.audioElement.srcObject = src; } + return this; } public play() { this.audioElement.play(); + return this; } public pause() { this.audioElement.pause(); + return this; } public setControls(show: boolean) { this.audioElement.controls = show; + return this; } public setLoop(loop: boolean) { this.audioElement.loop = loop; + return this; } public setMuted(muted: boolean) { this.audioElement.muted = muted; + return this; } public setAutoplay(autoplay: boolean) { this.audioElement.autoplay = autoplay; + return this; } public setVolume(volume: number) { this.audioElement.volume = volume; + return this; } } diff --git a/frontend/src/ui/button.ts b/frontend/src/ui/button.ts index aa88544..d11c509 100644 --- a/frontend/src/ui/button.ts +++ b/frontend/src/ui/button.ts @@ -13,10 +13,12 @@ export class Button extends UINode { public focus() { this.buttonElement.focus(); + return this; } public click() { this.buttonElement.click(); + return this; } public getElement(): HTMLElement { @@ -27,9 +29,11 @@ export class Button extends UINode { this.title = text; this.buttonElement.innerText = text; this.element.setAttribute("aria-label", this.title); + return this; } public setDisabled(val: boolean) { this.buttonElement.disabled = val; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/canvas.ts b/frontend/src/ui/canvas.ts index fdb48f0..d8ea3ea 100644 --- a/frontend/src/ui/canvas.ts +++ b/frontend/src/ui/canvas.ts @@ -12,10 +12,12 @@ export class Canvas extends UINode { public focus() { this.canvasElement.focus(); + return this; } public click() { this.canvasElement.click(); + return this; } public getElement(): HTMLElement { diff --git a/frontend/src/ui/checkbox.ts b/frontend/src/ui/checkbox.ts index 468eff5..b4731fc 100644 --- a/frontend/src/ui/checkbox.ts +++ b/frontend/src/ui/checkbox.ts @@ -19,10 +19,12 @@ export class Checkbox extends UINode { public focus() { this.checkboxElement.focus(); + return this; } public click() { this.checkboxElement.click(); + return this; } public getElement(): HTMLElement { @@ -34,6 +36,7 @@ export class Checkbox extends UINode { this.titleElement.innerText = text; this.element.setAttribute("aria-label", this.title); this.element.setAttribute("aria-roledescription", "checkbox"); + return this; } public isChecked(): boolean { @@ -42,5 +45,6 @@ export class Checkbox extends UINode { public setChecked(value: boolean) { this.checkboxElement.checked = value; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/collapsable-container.ts b/frontend/src/ui/collapsable-container.ts index 34a5308..77249fd 100644 --- a/frontend/src/ui/collapsable-container.ts +++ b/frontend/src/ui/collapsable-container.ts @@ -21,9 +21,10 @@ export class CollapsableContainer extends Container { return this.wrapperElement; } - public setTitle(text: string): void { + public setTitle(text: string) { this.title = text; this.summaryElement.innerText = text; + return this; } public isCollapsed(): boolean { @@ -36,5 +37,6 @@ export class CollapsableContainer extends Container { } else { this.detailsElement.removeAttribute("open"); } + return this; } } diff --git a/frontend/src/ui/container.ts b/frontend/src/ui/container.ts index c051d47..9f93545 100644 --- a/frontend/src/ui/container.ts +++ b/frontend/src/ui/container.ts @@ -15,6 +15,7 @@ export class Container extends UINode { public focus() { this.containerElement.focus(); + return this; } public _onFocus() { @@ -45,7 +46,8 @@ export class Container extends UINode { return this.containerElement; } - public setAriaLabel(text: string): void { + public setAriaLabel(text: string) { this.containerElement.setAttribute("aria-label", text); + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/date-picker.ts b/frontend/src/ui/date-picker.ts index f91a442..4b5f6f5 100644 --- a/frontend/src/ui/date-picker.ts +++ b/frontend/src/ui/date-picker.ts @@ -20,6 +20,7 @@ export class DatePicker extends UINode { public focus() { this.inputElement.focus(); + return this; } public getElement(): HTMLElement { @@ -29,6 +30,7 @@ export class DatePicker extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getValue(): string { @@ -37,5 +39,6 @@ export class DatePicker extends UINode { public setValue(value: string) { this.inputElement.value = value; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/dialog.ts b/frontend/src/ui/dialog.ts index bcaa135..799d4a4 100644 --- a/frontend/src/ui/dialog.ts +++ b/frontend/src/ui/dialog.ts @@ -31,20 +31,22 @@ export class Dialog extends UIWindow { } } - public setOkAction(action: () => T): void { + public setOkAction(action: () => T) { if (!this.okButton) return; this.okButton.onClick(() => { const result = action(); this.choose(result); }); + return this; } - public setCancelAction(action: () => void): void { + public setCancelAction(action: () => void) { if (!this.cancelButton) return; this.cancelButton.onClick(() => { action(); this.cancel(); }); + return this; } public choose(item: T | undefined) { diff --git a/frontend/src/ui/dropdown.ts b/frontend/src/ui/dropdown.ts index 6832f26..0e7558b 100644 --- a/frontend/src/ui/dropdown.ts +++ b/frontend/src/ui/dropdown.ts @@ -21,6 +21,7 @@ export class Dropdown extends UINode { public focus() { this.selectElement.focus(); + return this; } public getElement(): HTMLElement { @@ -30,6 +31,7 @@ export class Dropdown extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getSelectedValue(): string { @@ -38,6 +40,7 @@ export class Dropdown extends UINode { public setSelectedValue(value: string) { this.selectElement.value = value; + return this; } public setOptions(options: { key: string; value: string }[]) { @@ -45,6 +48,7 @@ export class Dropdown extends UINode { options.forEach((option) => { this.addOption(option.key, option.value); }); + return this; } public addOption(key: string, value: string) { @@ -52,6 +56,7 @@ export class Dropdown extends UINode { optionElement.value = key; optionElement.innerText = value; this.selectElement.appendChild(optionElement); + return this; } public removeOption(key: string) { @@ -60,11 +65,13 @@ export class Dropdown extends UINode { if (optionToRemove) { this.selectElement.removeChild(optionToRemove); } + return this; } public clearOptions() { while (this.selectElement.firstChild) { this.selectElement.removeChild(this.selectElement.firstChild); } + return this } } diff --git a/frontend/src/ui/file-input.ts b/frontend/src/ui/file-input.ts index 6862837..0f9754c 100644 --- a/frontend/src/ui/file-input.ts +++ b/frontend/src/ui/file-input.ts @@ -23,6 +23,7 @@ export class FileInput extends UINode { public focus() { this.inputElement.focus(); + return this; } public getElement(): HTMLElement { @@ -32,6 +33,7 @@ export class FileInput extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getFiles(): FileList | null { @@ -40,5 +42,6 @@ export class FileInput extends UINode { public setAccept(accept: string) { this.inputElement.accept = accept; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/image.ts b/frontend/src/ui/image.ts index 53c2403..b02a374 100644 --- a/frontend/src/ui/image.ts +++ b/frontend/src/ui/image.ts @@ -18,13 +18,16 @@ export class Image extends UINode { public setText(text: string) { this.title = text; this.element.setAttribute("aria-label", text); + return this; } public setSource(src: string) { this.imgElement.src = src; + return this; } public setAltText(altText: string) { this.imgElement.alt = altText; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/list-item.ts b/frontend/src/ui/list-item.ts index cfbe4d0..e4c124e 100644 --- a/frontend/src/ui/list-item.ts +++ b/frontend/src/ui/list-item.ts @@ -15,10 +15,12 @@ export class ListItem extends UINode { public focus() { this.listElement.focus(); + return this; } public click() { this.listElement.click(); + return this; } public getElement(): HTMLElement { @@ -30,5 +32,6 @@ export class ListItem extends UINode { this.listElement.innerText = text; this.element.setAttribute("aria-label", this.title); this.listElement.setAttribute("aria-label", this.title); + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/list.ts b/frontend/src/ui/list.ts index f9943ba..6b221e8 100644 --- a/frontend/src/ui/list.ts +++ b/frontend/src/ui/list.ts @@ -23,6 +23,7 @@ export class List extends UINode { this.listElement.appendChild(node.render()); if (this.children.length === 1) this.calculateTabIndex(); node.onFocus(() => this.calculateFocused(node)); + return this; } public addNodeAtIndex(node: UINode, index: number) { @@ -32,6 +33,7 @@ export class List extends UINode { this.listElement.insertBefore(node.render(), this.listElement.children[index]); if (this.children.length === 1) this.calculateTabIndex(); node.onFocus(() => this.calculateFocused(node)); + return this; } public remove(node: UINode) { @@ -43,11 +45,13 @@ export class List extends UINode { if (this.focused > 0) this.focused--; this.calculateTabIndex(); } + return this; } public _onFocus() { super._onFocus(); this.children[this.focused].focus(); + return this; } public _onClick() { @@ -133,6 +137,7 @@ export class List extends UINode { this.children = []; this.listElement.innerHTML = ''; this.focused = 0; + return this; } public getFocusedChild() { @@ -145,6 +150,7 @@ export class List extends UINode { public onSelect(f: (id: number) => void) { this.selectCallback = f; + return this; } protected calculateFocused(node: UINode) { @@ -160,5 +166,6 @@ export class List extends UINode { // set the focused element for tab index without focusing directly. this.focused = this.children.length - 1; this.children[this.focused].setTabbable(true); + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/multiline-input.ts b/frontend/src/ui/multiline-input.ts index 155e851..0444840 100644 --- a/frontend/src/ui/multiline-input.ts +++ b/frontend/src/ui/multiline-input.ts @@ -18,10 +18,12 @@ export class MultilineInput extends UINode { public focus() { this.textareaElement.focus(); + return this; } public click() { this.textareaElement.click(); + return this; } public getElement(): HTMLElement { @@ -31,6 +33,7 @@ export class MultilineInput extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getValue(): string { @@ -39,5 +42,6 @@ export class MultilineInput extends UINode { public setValue(value: string) { this.textareaElement.value = value; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/node.ts b/frontend/src/ui/node.ts index bd0556c..deadb89 100644 --- a/frontend/src/ui/node.ts +++ b/frontend/src/ui/node.ts @@ -100,6 +100,7 @@ export class UINode { this.positionType = type; this.calculateOwnStyle = true; this.calculateStyle(); + return this; } public onClick(f: () => void) { @@ -131,14 +132,17 @@ export class UINode { this.getElement().setAttribute("tabindex", (val === true) ? "0" : "-1"); + return this; } public setAriaLabel(text: string) { this.element.setAttribute("aria-label", text); + return this; } public setRole(role: string) { this.getElement().setAttribute("role", role); + return this; } public getUserData(): any { @@ -147,9 +151,11 @@ export class UINode { public setUserData(obj: any) { this.userdata = obj; + return this; } public setAccessKey(key: string) { this.getElement().accessKey = key; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/progress-bar.ts b/frontend/src/ui/progress-bar.ts index 080e1b6..ab8d3e1 100644 --- a/frontend/src/ui/progress-bar.ts +++ b/frontend/src/ui/progress-bar.ts @@ -17,6 +17,7 @@ export class ProgressBar extends UINode { public setText(text: string) { this.title = text; this.element.setAttribute("aria-label", text); + return this; } public getValue(): number { @@ -25,6 +26,7 @@ export class ProgressBar extends UINode { public setValue(value: number) { this.progressElement.value = value; + return this; } public getMax(): number { @@ -33,5 +35,6 @@ export class ProgressBar extends UINode { public setMax(max: number) { this.progressElement.max = max; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/radio-group.ts b/frontend/src/ui/radio-group.ts index 7a872e1..d415fec 100644 --- a/frontend/src/ui/radio-group.ts +++ b/frontend/src/ui/radio-group.ts @@ -47,6 +47,7 @@ export class RadioGroup extends UINode { if (firstRadioElement) { firstRadioElement.focus(); } + return this; } public getElement(): HTMLElement { @@ -56,6 +57,7 @@ export class RadioGroup extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getSelectedValue(): string | null { @@ -72,5 +74,6 @@ export class RadioGroup extends UINode { if (radioElement) { radioElement.checked = true; } + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/slider.ts b/frontend/src/ui/slider.ts index fa08fcf..aab86bd 100644 --- a/frontend/src/ui/slider.ts +++ b/frontend/src/ui/slider.ts @@ -22,10 +22,12 @@ export class Slider extends UINode { public focus() { this.sliderElement.focus(); + return this; } public click() { this.sliderElement.click(); + return this; } public getElement(): HTMLElement { @@ -35,6 +37,7 @@ export class Slider extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getValue(): number { @@ -43,5 +46,6 @@ export class Slider extends UINode { public setValue(value: number) { this.sliderElement.value = value.toString(); + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/tab-bar.ts b/frontend/src/ui/tab-bar.ts index 44318de..136a050 100644 --- a/frontend/src/ui/tab-bar.ts +++ b/frontend/src/ui/tab-bar.ts @@ -23,10 +23,12 @@ export class TabBar extends UINode { public _onFocus() { this.tabs[this.focused].focus(); + return this; } public focus() { this.tabs[this.focused].focus(); + return this; } public add(title: string) { @@ -39,10 +41,12 @@ export class TabBar extends UINode { this.tabBarContainer.appendChild(elem.render()); elem._onConnect(); if (this.tabs.length === 1) this.calculateTabIndex(); + return this; } public onTabChange(f: (index: number) => void) { this.onTabChangeCallback = f; + return this; } private selectTab(idx: number) { @@ -93,5 +97,6 @@ export class TabBar extends UINode { public calculateTabIndex() { this.tabs[this.focused].setTabbable(true); + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/tab.ts b/frontend/src/ui/tab.ts index c8b7b31..7059dec 100644 --- a/frontend/src/ui/tab.ts +++ b/frontend/src/ui/tab.ts @@ -18,10 +18,12 @@ export class UITab extends UINode { public focus() { this.textElement.focus(); + return this; } public click() { this.textElement.click(); + return this; } public getElement(): HTMLElement { @@ -31,10 +33,12 @@ export class UITab extends UINode { public setText(text: string) { this.title = text; this.textElement.innerText = text; + return this; } public setSelected(val: boolean) { this.selected = val; this.textElement.setAttribute("aria-selected", this.selected.toString()); + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/tabbed-view.ts b/frontend/src/ui/tabbed-view.ts index a96a7de..47d30e7 100644 --- a/frontend/src/ui/tabbed-view.ts +++ b/frontend/src/ui/tabbed-view.ts @@ -26,6 +26,7 @@ export class TabbedView extends UINode { this.bar.add(name); container.setRole("tabpanel"); this.containers.push(container); + return this; } private onTabChanged(idx: number) { diff --git a/frontend/src/ui/text-input.ts b/frontend/src/ui/text-input.ts index 3020449..dd5a37b 100644 --- a/frontend/src/ui/text-input.ts +++ b/frontend/src/ui/text-input.ts @@ -19,10 +19,12 @@ export class TextInput extends UINode { public focus() { this.inputElement.focus(); + return this; } public click() { this.inputElement.click(); + return this; } public getElement(): HTMLElement { @@ -32,6 +34,7 @@ export class TextInput extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getValue(): string { @@ -40,5 +43,6 @@ export class TextInput extends UINode { public setValue(value: string) { this.inputElement.value = value; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/text.ts b/frontend/src/ui/text.ts index e37bb8b..35fc384 100644 --- a/frontend/src/ui/text.ts +++ b/frontend/src/ui/text.ts @@ -12,10 +12,12 @@ export class Text extends UINode { public focus() { this.textElement.focus(); + return this; } public click() { this.textElement.click(); + return this; } public getElement(): HTMLElement { @@ -25,5 +27,6 @@ export class Text extends UINode { public setText(text: string) { this.title = text; this.textElement.innerText = text; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/time-picker.ts b/frontend/src/ui/time-picker.ts index f39aad9..23eb5f2 100644 --- a/frontend/src/ui/time-picker.ts +++ b/frontend/src/ui/time-picker.ts @@ -19,6 +19,7 @@ export class TimePicker extends UINode { public focus() { this.inputElement.focus(); + return this; } public getElement(): HTMLElement { @@ -28,6 +29,7 @@ export class TimePicker extends UINode { public setText(text: string) { this.title = text; this.titleElement.innerText = text; + return this; } public getValue(): string { @@ -36,5 +38,6 @@ export class TimePicker extends UINode { public setValue(value: string) { this.inputElement.value = value; + return this; } } \ No newline at end of file diff --git a/frontend/src/ui/video.ts b/frontend/src/ui/video.ts index 29b61b6..42fb547 100644 --- a/frontend/src/ui/video.ts +++ b/frontend/src/ui/video.ts @@ -26,29 +26,36 @@ export class Video extends UINode { } else if (src instanceof MediaStream) { this.videoElement.srcObject = src; } + return this; } public play() { this.videoElement.play(); + return this; } public pause() { this.videoElement.pause(); + return this; } public setControls(show: boolean) { this.videoElement.controls = show; + return this; } public setLoop(loop: boolean) { this.videoElement.loop = loop; + return this; } public setMuted(muted: boolean) { this.videoElement.muted = muted; + return this; } public setAutoplay(autoplay: boolean) { this.videoElement.autoplay = autoplay; + return this; } } diff --git a/frontend/src/ui/window.ts b/frontend/src/ui/window.ts index 2fcecac..1ca00bf 100644 --- a/frontend/src/ui/window.ts +++ b/frontend/src/ui/window.ts @@ -30,10 +30,12 @@ export class UIWindow { public add(node: UINode) { this.container.add(node); + return this; } public remove(node: UINode) { if (this.container.children.includes(node)) this.container.remove(node); + return this; } public show(): HTMLElement|undefined {