blob: fef7f05fe9035eea770a640fd72287ea3117556e [file] [edit]
// Copyright (C) 2026 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import m from 'mithril';
import {Setting, EnumOption} from './settings_types';
import {Select} from '../../widgets/select';
import {TextInput} from '../../widgets/text_input';
import {
PopupMultiSelect,
MultiSelectDiff,
MultiSelectOption,
} from '../../widgets/multiselect';
import {Checkbox} from '../../widgets/checkbox';
import {Editor} from '../../widgets/editor';
export function renderSetting(setting: Setting<unknown>): m.Children {
const currentValue = setting.get();
const disabled = setting.isDisabled();
switch (setting.type) {
case 'number':
return m(TextInput, {
type: 'number',
value: String(currentValue),
placeholder: setting.placeholder,
disabled,
onChange: (value: string) => {
const numValue = parseFloat(value);
if (!isNaN(numValue)) {
setting.set(numValue);
}
},
});
case 'string':
if (setting.format === 'sql') {
return m(Editor, {
text: String(currentValue),
language: 'perfetto-sql',
disabled,
onUpdate: (text: string) => {
setting.set(text);
},
});
}
return m(TextInput, {
value: String(currentValue),
placeholder: setting.placeholder,
disabled,
onChange: (value: string) => {
setting.set(value);
},
});
case 'boolean':
return m(Checkbox, {
checked: Boolean(currentValue),
disabled,
onchange: (e: Event) => {
if (e.currentTarget instanceof HTMLLabelElement) {
const input = e.currentTarget.querySelector('input');
if (input) {
setting.set(input.checked);
}
}
},
});
case 'enum':
const options = setting.options || [];
return m(
Select,
{
value: String(currentValue),
disabled,
onchange: (e: Event) => {
const target = e.target as HTMLSelectElement;
setting.set(target.value);
},
},
options.map((option: string | EnumOption) => {
const value = typeof option === 'string' ? option : option.value;
const label = typeof option === 'string' ? option : option.label;
return m(
'option',
{
value: value,
selected: currentValue === value,
},
label,
);
}),
);
case 'multi-select':
const multiSelectOptions: MultiSelectOption[] = (
setting.options || []
).map((option) => {
const value = typeof option === 'string' ? option : option.value;
const label = typeof option === 'string' ? option : option.label;
return {
id: value,
name: label,
checked: (currentValue as string[]).includes(value),
};
});
const validSelectedCount = multiSelectOptions.filter(
(o) => o.checked,
).length;
return m(PopupMultiSelect, {
label: `${setting.name} (${validSelectedCount} selected)`,
options: multiSelectOptions,
onChange: (diffs: MultiSelectDiff[]) => {
const newValue = [...(currentValue as string[])];
for (const diff of diffs) {
if (diff.checked) {
if (!newValue.includes(diff.id)) {
newValue.push(diff.id);
}
} else {
const index = newValue.indexOf(diff.id);
if (index > -1) {
newValue.splice(index, 1);
}
}
}
setting.set(newValue);
},
});
case 'string-array':
return m('textarea.pf-bt-textarea', {
placeholder: setting.placeholder || 'Comma-separated values',
disabled,
rows: 3,
spellcheck: false,
oncreate: (vnode: m.VnodeDOM) => {
(vnode.dom as HTMLTextAreaElement).value = (
currentValue as string[]
).join(', ');
},
onblur: (e: Event) => {
const target = e.target as HTMLTextAreaElement;
setting.set(
target.value
.split(',')
.map((s) => s.trim())
.filter((s) => s !== ''),
);
},
});
default:
return `Unsupported setting type: ${setting.type}`;
}
}