-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
990 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { Aircraft } from '../../lib/api/api.model'; | ||
import React, { useMemo } from 'react'; | ||
import { Select, SelectProps } from '@cloudscape-design/components'; | ||
|
||
export interface AircraftSelectProps { | ||
aircraft: ReadonlyArray<Aircraft>; | ||
selectedAircraftCode: string | null; | ||
loading: boolean; | ||
disabled: boolean; | ||
onChange: (v: string | null) => void; | ||
placeholder?: string; | ||
} | ||
|
||
export function AircraftSelect({ aircraft, selectedAircraftCode, loading, disabled, onChange, placeholder }: AircraftSelectProps) { | ||
const [options, optionByAircraftCode] = useMemo(() => { | ||
const optionByAircraftCode: Record<string, SelectProps.Option> = {}; | ||
const otherOptions: Array<SelectProps.Option> = []; | ||
const groups: ReadonlyArray<{ name: string, options: Array<SelectProps.Option> }> = [ | ||
{ name: 'Airbus', options: [] }, | ||
{ name: 'Boeing', options: [] }, | ||
{ name: 'Embraer', options: [] }, | ||
{ name: 'BAE Systems', options: [] }, | ||
{ name: 'Antonov', options: [] }, | ||
{ name: 'Bombardier', options: [] }, | ||
{ name: 'Tupolev', options: [] }, | ||
]; | ||
|
||
for (const a of aircraft) { | ||
const option = { | ||
label: a.name, | ||
value: a.code, | ||
description: a.equipCode, | ||
} satisfies SelectProps.Option; | ||
|
||
let addedToGroup = false; | ||
for (const group of groups) { | ||
if (a.name.toLowerCase().includes(group.name.toLowerCase())) { | ||
group.options.push(option); | ||
addedToGroup = true; | ||
break; | ||
} | ||
} | ||
|
||
if (!addedToGroup) { | ||
otherOptions.push(option); | ||
} | ||
|
||
optionByAircraftCode[a.code] = option; | ||
} | ||
|
||
const options: Array<SelectProps.Option | SelectProps.OptionGroup> = []; | ||
for (const group of groups) { | ||
if (group.options.length > 0) { | ||
options.push({ | ||
label: group.name, | ||
options: group.options, | ||
}); | ||
} | ||
} | ||
|
||
options.push(...otherOptions); | ||
|
||
return [options, optionByAircraftCode]; | ||
}, [aircraft]); | ||
|
||
const selectedOption = useMemo(() => { | ||
if (!selectedAircraftCode) { | ||
return null; | ||
} | ||
|
||
return optionByAircraftCode[selectedAircraftCode]; | ||
}, [optionByAircraftCode, selectedAircraftCode]); | ||
|
||
return ( | ||
<Select | ||
options={options} | ||
selectedOption={selectedOption} | ||
onChange={(e) => onChange(e.detail.selectedOption?.value ?? null)} | ||
virtualScroll={true} | ||
filteringType={'auto'} | ||
disabled={disabled} | ||
statusType={loading ? 'loading' : 'finished'} | ||
placeholder={placeholder} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React, { useMemo } from 'react'; | ||
import { Multiselect, MultiselectProps } from '@cloudscape-design/components'; | ||
|
||
export interface AirlineMultiselectProps { | ||
airlines: ReadonlyArray<string>; | ||
selectedAirlines: ReadonlyArray<string>; | ||
loading: boolean; | ||
disabled: boolean; | ||
onChange: (options: ReadonlyArray<string>) => void; | ||
placeholder?: string; | ||
} | ||
|
||
export function AirlineMultiselect({ airlines, selectedAirlines, loading, disabled, onChange, placeholder }: AirlineMultiselectProps) { | ||
const [options, optionByAirline] = useMemo(() => { | ||
const options: Array<MultiselectProps.Option> = []; | ||
const optionByAirline: Record<string, MultiselectProps.Option> = {}; | ||
|
||
for (const airline of airlines) { | ||
const option = { | ||
label: airline, | ||
value: airline, | ||
} satisfies MultiselectProps.Option; | ||
|
||
|
||
options.push(option); | ||
optionByAirline[airline] = option; | ||
} | ||
|
||
return [options, optionByAirline]; | ||
}, [airlines]); | ||
|
||
const selectedOptions = useMemo(() => { | ||
const result: Array<MultiselectProps.Option> = []; | ||
for (const airline of selectedAirlines) { | ||
const option = optionByAirline[airline]; | ||
if (option) { | ||
result.push(option); | ||
} | ||
} | ||
|
||
return result; | ||
}, [optionByAirline, selectedAirlines]); | ||
|
||
return ( | ||
<Multiselect | ||
options={options} | ||
selectedOptions={selectedOptions} | ||
onChange={(e) => onChange(e.detail.selectedOptions.flatMap((v) => v.value ? [v.value] : []))} | ||
keepOpen={true} | ||
filteringType={'auto'} | ||
disabled={disabled} | ||
statusType={loading ? 'loading' : 'finished'} | ||
placeholder={placeholder} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import { Airports } from '../../lib/api/api.model'; | ||
import React, { useMemo, useState } from 'react'; | ||
import { Select, SelectProps } from '@cloudscape-design/components'; | ||
|
||
export interface AirportSelectProps { | ||
airports: Airports; | ||
selectedAirportCode: string | null; | ||
loading: boolean; | ||
disabled: boolean; | ||
onChange: (value: string | null) => void; | ||
placeholder?: string; | ||
} | ||
|
||
export function AirportSelect({ airports, selectedAirportCode, loading, disabled, onChange, placeholder }: AirportSelectProps) { | ||
const [filterText, setFilterText] = useState(''); | ||
const [options, optionByAirportCode] = useMemo(() => { | ||
const options: Array<SelectProps.Option | SelectProps.OptionGroup> = []; | ||
const optionByAirportCode: Record<string, SelectProps.Option> = {}; | ||
|
||
for (const airport of airports.airports) { | ||
const option = { | ||
label: airport.code, | ||
value: airport.code, | ||
description: airport.name, | ||
} satisfies SelectProps.Option; | ||
|
||
options.push(option); | ||
optionByAirportCode[airport.code] = option; | ||
} | ||
|
||
for (const metroArea of airports.metropolitanAreas) { | ||
const airportOptions: Array<SelectProps.Option> = []; | ||
|
||
for (const airport of metroArea.airports) { | ||
const option = { | ||
label: airport.code, | ||
value: airport.code, | ||
description: airport.name, | ||
} satisfies SelectProps.Option; | ||
|
||
airportOptions.push(option); | ||
optionByAirportCode[airport.code] = option; | ||
} | ||
|
||
options.push({ | ||
label: metroArea.code, | ||
description: metroArea.name, | ||
options: airportOptions, | ||
}); | ||
} | ||
|
||
return [options, optionByAirportCode]; | ||
}, [airports]); | ||
|
||
const displayOptions = useMemo(() => { | ||
const filter = filterText.trim(); | ||
if (filter === '') { | ||
return options; | ||
} | ||
|
||
return filterOptions(filter.toUpperCase(), options)[0]; | ||
}, [filterText, options]); | ||
|
||
const selectedOption = useMemo(() => { | ||
if (!selectedAirportCode) { | ||
return null; | ||
} | ||
|
||
return optionByAirportCode[selectedAirportCode]; | ||
}, [optionByAirportCode, selectedAirportCode]); | ||
|
||
return ( | ||
<Select | ||
options={displayOptions} | ||
selectedOption={selectedOption} | ||
onChange={(e) => onChange(e.detail.selectedOption?.value ?? null)} | ||
virtualScroll={true} | ||
disabled={disabled} | ||
statusType={loading ? 'loading' : 'finished'} | ||
filteringType={'manual'} | ||
onLoadItems={(e) => setFilterText(e.detail.filteringText)} | ||
placeholder={placeholder} | ||
/> | ||
); | ||
} | ||
|
||
function filterOptions(filter: string, options: ReadonlyArray<SelectProps.Option | SelectProps.OptionGroup>): [ReadonlyArray<SelectProps.Option | SelectProps.OptionGroup>, boolean] { | ||
const matchByLabel: Array<SelectProps.Option | SelectProps.OptionGroup> = []; | ||
const matchByDescription: Array<SelectProps.Option | SelectProps.OptionGroup> = []; | ||
|
||
for (const option of options) { | ||
const label = option.label?.toUpperCase(); | ||
const description = option.description?.toUpperCase(); | ||
let matched = false; | ||
|
||
if (label?.includes(filter)) { | ||
matchByLabel.push(option); | ||
matched = true; | ||
} else if (isOptionGroup(option)) { | ||
const [filtered, anyMatchedByLabel] = filterOptions(filter, option.options); | ||
if (filtered.length > 0) { | ||
const filteredOption = { | ||
...option, | ||
options: filtered, | ||
} satisfies SelectProps.OptionGroup; | ||
|
||
if (anyMatchedByLabel) { | ||
matchByLabel.push(filteredOption); | ||
} else { | ||
matchByDescription.push(filteredOption); | ||
} | ||
|
||
matched = true; | ||
} | ||
} | ||
|
||
if (!matched && description?.includes(filter)) { | ||
matchByDescription.push(option); | ||
} | ||
} | ||
|
||
return [[...matchByLabel, ...matchByDescription], matchByLabel.length > 0]; | ||
} | ||
|
||
function isOptionGroup(option: SelectProps.Option | SelectProps.OptionGroup): option is SelectProps.OptionGroup { | ||
return !!(option as SelectProps.OptionGroup).options; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.