blob: 7d59a79408924f6ebb007923ad1a45036489d5de [file] [log] [blame]
// Copyright (C) 2023 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 {exists} from '../base/utils';
import {HTMLInputAttrs} from './common';
import {Menu, MenuItem} from './menu';
import {scheduleFullRedraw} from './raf';
import {TextInput} from './text_input';
export class Select implements m.ClassComponent<HTMLInputAttrs> {
view({attrs, children}: m.CVnode<HTMLInputAttrs>) {
return m('select.pf-select', attrs, children);
}
}
export interface FilterableSelectAttrs {
// Whether to show a search box. Defaults to false.
filterable?: boolean;
// The values to show in the select.
values: string[];
// Called when the user selects an option.
onSelected: (value: string) => void;
// If set, only the first maxDisplayedItems will be shown.
maxDisplayedItems?: number;
// Whether the input field should be focused when the widget is created.
autofocusInput?: boolean;
}
// A select widget with a search box, allowing the user to filter the options.
export class FilterableSelect
implements m.ClassComponent<FilterableSelectAttrs>
{
searchText = '';
view({attrs}: m.CVnode<FilterableSelectAttrs>) {
const filteredValues = attrs.values.filter((name) => {
return name.toLowerCase().includes(this.searchText.toLowerCase());
});
const displayedValues =
attrs.maxDisplayedItems === undefined
? filteredValues
: filteredValues.slice(0, attrs.maxDisplayedItems);
const extraItems =
exists(attrs.maxDisplayedItems) &&
Math.max(0, filteredValues.length - attrs.maxDisplayedItems);
// TODO(altimin): when the user presses enter and there is only one item,
// select the first one.
// MAYBE(altimin): when the user presses enter and there are multiple items,
// select the first one.
return m(
'div',
m(
'.pf-search-bar',
m(TextInput, {
oninput: (event: Event) => {
const eventTarget = event.target as HTMLTextAreaElement;
this.searchText = eventTarget.value;
scheduleFullRedraw();
},
onload: (event: Event) => {
if (!attrs.autofocusInput) return;
const eventTarget = event.target as HTMLTextAreaElement;
eventTarget.focus();
},
value: this.searchText,
placeholder: 'Filter...',
className: 'pf-search-box',
}),
m(
Menu,
...displayedValues.map((value) =>
m(MenuItem, {
label: value,
onclick: () => attrs.onSelected(value),
}),
),
Boolean(extraItems) && m('i', `+${extraItems} more`),
),
),
);
}
}