From 8fd8d7fa1dde1e6061fcab641e1b8a17ef75dfba Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Wed, 23 Nov 2022 17:59:37 +0100 Subject: [PATCH] feat(ui): Add support for radio groups This allows us to build UI where we provide a set of options and the user can select one of the options. --- .../userscript/source/settings/Settings.ts | 13 ++++ .../source/ui/components/Fieldset.ts | 22 ++++++ .../source/ui/components/OptionsListItem.ts | 48 ++++++++++++ .../source/ui/components/RadioItem.ts | 74 +++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 packages/userscript/source/ui/components/Fieldset.ts create mode 100644 packages/userscript/source/ui/components/OptionsListItem.ts create mode 100644 packages/userscript/source/ui/components/RadioItem.ts diff --git a/packages/userscript/source/settings/Settings.ts b/packages/userscript/source/settings/Settings.ts index 381fd35c3..ffe212d86 100644 --- a/packages/userscript/source/settings/Settings.ts +++ b/packages/userscript/source/settings/Settings.ts @@ -120,3 +120,16 @@ export class SettingTriggerMax extends SettingTrigger implements SettingMax { this.max = setting.max ?? this.max; } } + +export class SettingOptions { + readonly #options: Array<{ label: string; value: T }>; + selected: T | undefined; + + get options() { + return this.#options; + } + + constructor(options = new Array<{ label: string; value: T }>()) { + this.#options = options; + } +} diff --git a/packages/userscript/source/ui/components/Fieldset.ts b/packages/userscript/source/ui/components/Fieldset.ts new file mode 100644 index 000000000..44cab6e11 --- /dev/null +++ b/packages/userscript/source/ui/components/Fieldset.ts @@ -0,0 +1,22 @@ +import { UserScript } from "../../UserScript"; +import { UiComponent } from "./UiComponent"; + +export class Fieldset extends UiComponent { + readonly element: JQuery; + + /** + * Constructs a `Fieldset`. + * + * @param host A reference to the host. + * @param label The label on the fieldset. + */ + constructor(host: UserScript, label: string) { + super(host); + + const element = $("
").addClass("ks-fieldset"); + const legend = $("").text(label).addClass("ks-label"); + element.append(legend); + + this.element = element; + } +} diff --git a/packages/userscript/source/ui/components/OptionsListItem.ts b/packages/userscript/source/ui/components/OptionsListItem.ts new file mode 100644 index 000000000..dc345b56f --- /dev/null +++ b/packages/userscript/source/ui/components/OptionsListItem.ts @@ -0,0 +1,48 @@ +import { SettingOptions } from "../../settings/Settings"; +import { UserScript } from "../../UserScript"; +import { Fieldset } from "./Fieldset"; +import { RadioItem } from "./RadioItem"; +import { UiComponent } from "./UiComponent"; + +export class OptionsListItem extends UiComponent { + readonly fieldset: Fieldset; + readonly element: JQuery; + + /** + * Construct a new options setting element. + * This is a list of options, where the selected option will be put into the setting. + * + * @param host The userscript instance. + * @param label The label on the setting element. + * @param setting The setting this element is linked to. + * @param handler The event handlers for this setting element. + * @param handler.onCheck Will be invoked when the user selects an option. + * @param readOnly Should the user be prevented from changing the value of the input? + */ + constructor( + host: UserScript, + label: string, + setting: TSetting, + handler: { + onCheck: () => void; + }, + readOnly = false + ) { + super(host); + + this.element = $(`
  • `); + + this.fieldset = new Fieldset(host, label); + this.addChild(this.fieldset); + + for (const option of setting.options) { + this.fieldset.addChild( + new RadioItem(host, setting, option, label, handler, false, false, readOnly) + ); + } + } + + refreshUi() { + super.refreshUi(); + } +} diff --git a/packages/userscript/source/ui/components/RadioItem.ts b/packages/userscript/source/ui/components/RadioItem.ts new file mode 100644 index 000000000..0935ae27a --- /dev/null +++ b/packages/userscript/source/ui/components/RadioItem.ts @@ -0,0 +1,74 @@ +import { SettingOptions } from "../../settings/Settings"; +import { UserScript } from "../../UserScript"; +import { UiComponent } from "./UiComponent"; + +export class RadioItem extends UiComponent { + readonly setting: TSetting; + readonly element: JQuery; + readonly input: JQuery; + + readOnly: boolean; + + /** + * Construct a new radio setting element. + * This is a radio input that is expected to be hosted in a `Fieldset`. + * + * @param host The userscript instance. + * @param setting The setting this element is linked to. + * @param option The specific option out of the setting that this radio item represents. + * @param groupKey A unique name for the group of radio items this one belongs to. + * @param handler The event handlers for this setting element. + * @param handler.onCheck Will be invoked when the user selects this radio item. + * @param delimiter Should there be additional padding below this element? + * @param upgradeIndicator Should an indicator be rendered in front of the elemnt, + * to indicate that this is an upgrade of a prior setting? + * @param readOnly Should the user be prevented from changing the value of the input? + */ + constructor( + host: UserScript, + setting: TSetting, + option: TSetting["options"][0], + groupKey: string, + handler: { + onCheck: () => void; + }, + delimiter = false, + upgradeIndicator = false, + readOnly = false + ) { + super(host); + + const element = $(`
    `); + for (const cssClass of ["ks-setting", delimiter ? "ks-delimiter" : ""]) { + element.addClass(cssClass); + } + + const elementLabel = $("