diff --git a/packages/jsapi-components/src/index.ts b/packages/jsapi-components/src/index.ts index 27b3769e5..2c519d75a 100644 --- a/packages/jsapi-components/src/index.ts +++ b/packages/jsapi-components/src/index.ts @@ -2,6 +2,7 @@ export * from './HookTestUtils'; export { default as TableInput } from './TableInput'; export * from './RefreshTokenBootstrap'; export * from './RefreshTokenUtils'; +export * from './spectrum'; export * from './TableDropdown'; export { default as useBroadcastChannel } from './useBroadcastChannel'; export { default as useBroadcastLoginListener } from './useBroadcastLoginListener'; diff --git a/packages/jsapi-components/src/spectrum/Picker.tsx b/packages/jsapi-components/src/spectrum/Picker.tsx new file mode 100644 index 000000000..6782bc8b4 --- /dev/null +++ b/packages/jsapi-components/src/spectrum/Picker.tsx @@ -0,0 +1,94 @@ +import { + NormalizedPickerItemData, + Picker as PickerBase, + PickerProps as PickerPropsBase, +} from '@deephaven/components'; +import { dh } from '@deephaven/jsapi-types'; +import { useCallback, useMemo } from 'react'; +import { useViewportData } from '../useViewportData'; + +function formatValue(value: unknown): string | number | boolean { + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + // TODO: Add support for formatting date values + return String(value); +} + +function usePickerItemRowDeserializer({ + table, + keyColumnName, + labelColumnName, +}: { + table: dh.Table; + keyColumnName?: string; + labelColumnName?: string; +}) { + const keyColumn = useMemo( + () => + table.findColumn( + keyColumnName == null ? table.columns[0].name : keyColumnName + ), + [keyColumnName, table] + ); + + const labelColumn = useMemo( + () => + labelColumnName == null ? keyColumn : table.findColumn(labelColumnName), + [keyColumn, labelColumnName, table] + ); + + const deserializeRow = useCallback( + (row: dh.Row): NormalizedPickerItemData => { + const key = formatValue(row.get(keyColumn)); + const content = formatValue(row.get(labelColumn)); + + return { + key, + content, + }; + }, + [keyColumn, labelColumn] + ); + + return deserializeRow; +} + +export interface PickerProps extends PickerPropsBase { + table: dh.Table; + /* The column of values to use as item keys. Defaults to the first column. */ + keyColumn?: string; + /* The column of values to display as primary text. Defaults to the `keyColumn` value. */ + labelColumn?: string; +} + +export function Picker({ + table, + keyColumn: keyColumnName, + labelColumn: labelColumnName, + ...props +}: PickerProps): JSX.Element { + const deserializeRow = usePickerItemRowDeserializer({ + table, + keyColumnName, + labelColumnName, + }); + + const { viewportData } = useViewportData({ + reuseItemsOnTableResize: true, + table, + deserializeRow, + }); + + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + {viewportData.items} + ); +} + +export default Picker; diff --git a/packages/jsapi-components/src/spectrum/index.ts b/packages/jsapi-components/src/spectrum/index.ts new file mode 100644 index 000000000..c434d5d81 --- /dev/null +++ b/packages/jsapi-components/src/spectrum/index.ts @@ -0,0 +1 @@ +export * from './Picker';