buildSelectOrInput: load default only when page is constructed

This commit is contained in:
TsXor 2023-09-25 16:53:43 +08:00
parent 8e5576db94
commit e94a7b1058
4 changed files with 30 additions and 10 deletions

2
js/dist/admin.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,6 @@
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import type Stream from 'mithril/stream';
import app from 'flarum/admin/app';
import AdminPage, { CommonSettingsItemOptions } from 'flarum/admin/components/AdminPage'; import AdminPage, { CommonSettingsItemOptions } from 'flarum/admin/components/AdminPage';
import SelectOrInput from "../../common/Components/SelectOrInput"; import SelectOrInput from "../../common/Components/SelectOrInput";
import generateElementId from 'flarum/admin/utils/generateElementId'; import generateElementId from 'flarum/admin/utils/generateElementId';
@ -14,7 +16,9 @@ export interface SelectOrInputSettingComponentOptions extends SelectOrInputSetti
type: 'select-or-input'; type: 'select-or-input';
} }
var firstLoadCompletedSet: Set<string> = new Set(); interface StreamWithMark<T> extends Stream<T> {
mark?: true;
}
export default function (this: AdminPage, _entry: CommonSettingsItemOptions) { export default function (this: AdminPage, _entry: CommonSettingsItemOptions) {
// do this cast because function signature is restricted // do this cast because function signature is restricted
@ -22,25 +26,38 @@ export default function (this: AdminPage, _entry: CommonSettingsItemOptions) {
const [inputId, helpTextId] = [generateElementId(), generateElementId()]; const [inputId, helpTextId] = [generateElementId(), generateElementId()];
const { setting, help, label, ...componentAttrs } = entry; const { setting, help, label, ...componentAttrs } = entry;
const { default: defaultValue, options, checkboxDescription, ...otherAttrs } = componentAttrs; const { default: defaultValue, options, checkboxDescription, ...otherAttrs } = componentAttrs;
const value = this.setting(setting)();
let settingElement: Mithril.Children; let settingElement: Mithril.Children;
// Trick: add a marker property to the AdminPage's setting stream,
const firstLoadCompleted = firstLoadCompletedSet.has(entry.setting); // which contructs and destructs with AdminPage so that default is
// only loaded when the page is created.
// Default should only be loaded once in a page because when checkbox
// is checked, SelectOrInput will set its value to empty string so that
// it becomes an empty input. If default is loaded every time when value
// is falsy, SelectOrInput will not work properly.
const settingStream = this.settings[setting] as StreamWithMark<string>;
let value = settingStream();
if (!settingStream.mark) {
settingStream.mark = true;
if (!value) {
app.data.settings[setting] = defaultValue;
settingStream(defaultValue);
value = settingStream();
}
}
settingElement = ( settingElement = (
<SelectOrInput <SelectOrInput
id={inputId} id={inputId}
aria-describedby={helpTextId} aria-describedby={helpTextId}
value={(!firstLoadCompleted && !value) ? defaultValue : value} value={value}
options={options} options={options}
onchange={this.settings[setting]} onchange={settingStream}
checkboxDescription={checkboxDescription} checkboxDescription={checkboxDescription}
{...otherAttrs} {...otherAttrs}
/> />
); );
if (!firstLoadCompleted) firstLoadCompletedSet.add(entry.setting);
return ( return (
<div className="Form-group"> <div className="Form-group">
{label && <label for={inputId}>{label}</label>} {label && <label for={inputId}>{label}</label>}

View File

@ -30,6 +30,9 @@ export interface ISelectOrInputAttrs<
rawTextInputAttrs?: ComponentAttrs; rawTextInputAttrs?: ComponentAttrs;
} }
// when value is `undefined`, choose the first item of select
// when value is in select, use value as value of select
// otherwise, use value as value of input
export default class SelectOrInput<CustomAttrs extends ISelectOrInputAttrs> extends Component<CustomAttrs> { export default class SelectOrInput<CustomAttrs extends ISelectOrInputAttrs> extends Component<CustomAttrs> {
private onCheckboxChange(checked: boolean, component: Checkbox) { private onCheckboxChange(checked: boolean, component: Checkbox) {
this.attrs.value = checked ? '' : undefined; this.attrs.value = checked ? '' : undefined;