From da22a222533d27bf8b54be387893e86d52814ace Mon Sep 17 00:00:00 2001 From: DerDorius <77979816+DerDorius@users.noreply.github.com> Date: Thu, 15 Sep 2022 16:10:43 +0200 Subject: [PATCH] fixes #3200 Add Generics to Dropdown, Listbox, Multiselect and Selectbutton (#3212) * #3200 Fresh Branch with changes * add Generic as any in Cascadeselect * #3200 revert copy error --- .../lib/cascadeselect/cascadeselect.d.ts | 2 +- components/lib/dropdown/dropdown.d.ts | 83 +++++++++----- components/lib/listbox/listbox.d.ts | 103 +++++++++++++----- components/lib/multiselect/multiselect.d.ts | 101 ++++++++++------- components/lib/selectbutton/selectbutton.d.ts | 53 ++++++--- components/lib/selectitem/selectitem.d.ts | 6 +- 6 files changed, 237 insertions(+), 111 deletions(-) diff --git a/components/lib/cascadeselect/cascadeselect.d.ts b/components/lib/cascadeselect/cascadeselect.d.ts index 198e56499a..00b97381e7 100644 --- a/components/lib/cascadeselect/cascadeselect.d.ts +++ b/components/lib/cascadeselect/cascadeselect.d.ts @@ -20,7 +20,7 @@ export interface CascadeSelectProps extends Omit; optionLabel?: string; optionValue?: string; optionGroupLabel?: string; diff --git a/components/lib/dropdown/dropdown.d.ts b/components/lib/dropdown/dropdown.d.ts index 42f72c1e37..df859ee5ac 100755 --- a/components/lib/dropdown/dropdown.d.ts +++ b/components/lib/dropdown/dropdown.d.ts @@ -1,37 +1,59 @@ import * as React from 'react'; -import TooltipOptions from '../tooltip/tooltipoptions'; import { CSSTransitionProps } from '../csstransition'; +import { NestedKeyOf, SelectItemOptionsType } from '../selectitem/selectitem'; +import TooltipOptions from '../tooltip/tooltipoptions'; import { VirtualScrollerProps } from '../virtualscroller'; -import { SelectItemOptionsType } from '../selectitem/selectitem'; -type DropdownOptionGroupTemplateType = React.ReactNode | ((option: any, index: number) => React.ReactNode); +type DropdownOptionGroupTemplateType = React.ReactNode | ((option: TOption, index: number) => React.ReactNode); -type DropdownValueTemplateType = React.ReactNode | ((option: any, props: DropdownProps) => React.ReactNode); +type DropdownValueTemplateType = React.ReactNode | ((option: TOption, props: DropdownProps) => React.ReactNode); -type DropdownItemTemplateType = React.ReactNode | ((option: any) => React.ReactNode); +type DropdownItemTemplateType = React.ReactNode | ((option: TOption) => React.ReactNode); type DropdownFilterTemplateType = React.ReactNode | ((options: DropdownFilterOptions) => React.ReactNode); -type DropdownEmptyMessageType = React.ReactNode | ((props: DropdownProps) => React.ReactNode); +type DropdownEmptyMessageType = React.ReactNode | ((props: DropdownProps) => React.ReactNode); -type DropdownEmptyFilterMessageType = React.ReactNode | ((props: DropdownProps) => React.ReactNode); +type DropdownEmptyFilterMessageType = React.ReactNode | ((props: DropdownProps) => React.ReactNode); -type DropdownOptionDisabledType = string | ((option: any) => boolean); +type DropdownOptionDisabledType = string | ((option: TOption) => boolean); type DropdownAppendToType = 'self' | HTMLElement | undefined | null; -interface DropdownChangeTargetOptions { +type DropdownValue = TGroupLabel extends undefined + ? TValue extends undefined + ? TOption extends { value: any } + ? TOption['value'] + : TOption + : TValue extends keyof TOption + ? TOption[TValue] + : any + : TGroupChildren extends keyof TOption + ? TValue extends undefined + ? TOption[TGroupChildren] extends { value: any }[] + ? TOption[TGroupChildren][0]['value'] + : TOption[TGroupChildren] extends any[] + ? TOption[TGroupChildren][0] + : any + : TOption[TGroupChildren] extends any[] + ? TValue extends keyof TOption[TGroupChildren][0] + ? TOption[TGroupChildren][0][TValue] + : any + : any + : any; + +interface DropdownChangeTargetOptions { name: string; id: string; - value: any; + value: TOption; } -interface DropdownChangeParams { +interface DropdownChangeParams { originalEvent: React.SyntheticEvent; - value: any; + value: DropdownValue; stopPropagation(): void; preventDefault(): void; - target: DropdownChangeTargetOptions; + target: DropdownChangeTargetOptions; } interface DropdownFilterParams { @@ -44,21 +66,21 @@ interface DropdownFilterOptions { reset?: () => void; } -export interface DropdownProps extends Omit, HTMLDivElement>, 'onChange' | 'ref'> { +export interface DropdownProps extends Omit, HTMLDivElement>, 'value' | 'onChange' | 'ref'> { id?: string; inputRef?: React.Ref; name?: string; - value?: any; - options?: SelectItemOptionsType; - optionLabel?: string; - optionValue?: string; - optionDisabled?: DropdownOptionDisabledType; - optionGroupLabel?: string; - optionGroupChildren?: string; - optionGroupTemplate?: DropdownOptionGroupTemplateType; - valueTemplate?: DropdownValueTemplateType; + value?: string | number | DropdownValue | undefined; + options?: SelectItemOptionsType; + optionLabel?: NestedKeyOf | Omit, string>; + optionValue?: TValue | Omit; + optionDisabled?: DropdownOptionDisabledType; + optionGroupLabel?: TGroupLabel | Omit, string>; + optionGroupChildren?: TGroupChildren | Omit, string>; + optionGroupTemplate?: DropdownOptionGroupTemplateType; + valueTemplate?: DropdownValueTemplateType; filterTemplate?: DropdownFilterTemplateType; - itemTemplate?: DropdownItemTemplateType; + itemTemplate?: DropdownItemTemplateType; style?: object; className?: string; virtualScrollerOptions?: VirtualScrollerProps; @@ -68,8 +90,8 @@ export interface DropdownProps extends Omit; + emptyFilterMessage?: DropdownEmptyFilterMessageType; editable?: boolean; placeholder?: string; required?: boolean; @@ -93,7 +115,7 @@ export interface DropdownProps extends Omit): void; onFocus?(event: React.FocusEvent): void; onBlur?(event: React.FocusEvent): void; onMouseDown?(event: React.MouseEvent): void; @@ -104,7 +126,12 @@ export interface DropdownProps extends Omit { +export declare class Dropdown< + TOption, + TValue extends NestedKeyOf | undefined = undefined, + TGroupLabel extends NestedKeyOf | undefined = undefined, + TGroupChildren extends NestedKeyOf | undefined = undefined +> extends React.Component, any> { public getElement(): HTMLDivElement; public getInput(): HTMLInputElement; public getFocusInput(): HTMLInputElement; diff --git a/components/lib/listbox/listbox.d.ts b/components/lib/listbox/listbox.d.ts index cd01da36a1..2022450528 100755 --- a/components/lib/listbox/listbox.d.ts +++ b/components/lib/listbox/listbox.d.ts @@ -1,33 +1,77 @@ import * as React from 'react'; -import { SelectItemOptionsType } from '../selectitem/selectitem'; +import { NestedKeyOf, SelectItemOptionsType } from '../selectitem/selectitem'; import TooltipOptions from '../tooltip/tooltipoptions'; import { VirtualScrollerProps, VirtualScroller } from '../virtualscroller'; -type ListBoxOptionGroupTemplateType = React.ReactNode | ((option: any, index: number) => React.ReactNode); +type ListBoxOptionGroupTemplateType = React.ReactNode | ((option: TOption, index: number) => React.ReactNode); -type ListBoxItemTemplateType = React.ReactNode | ((option: any) => React.ReactNode); +type ListBoxItemTemplateType = React.ReactNode | ((option: TOption) => React.ReactNode); type ListBoxFilterTemplateType = React.ReactNode | ((options: ListBoxFilterOptions) => React.ReactNode); -type ListBoxOptionDisabledType = string | ((option: any) => boolean); +type ListBoxOptionDisabledType = string | ((option: TOption) => boolean); -interface ListBoxChangeTargetOptions { +type ListBoxValueType = TMultiple extends undefined + ? TGroupLabel extends undefined + ? TValue extends undefined + ? TOption extends { value: any } + ? TOption['value'] + : TOption + : TValue extends keyof TOption + ? TOption[TValue] + : any + : TGroupChildren extends keyof TOption + ? TValue extends undefined + ? TOption[TGroupChildren] extends { value: any }[] + ? TOption[TGroupChildren][0]['value'] + : TOption[TGroupChildren] extends any[] + ? TOption[TGroupChildren][0] + : any + : TOption[TGroupChildren] extends any[] + ? TValue extends keyof TOption[TGroupChildren][0] + ? TOption[TGroupChildren][0][TValue] + : any + : any + : any + : TGroupLabel extends undefined + ? TValue extends undefined + ? TOption extends { value: any } + ? TOption['value'][] + : TOption[] + : TValue extends keyof TOption + ? TOption[TValue][] + : any[] + : TGroupChildren extends keyof TOption + ? TValue extends undefined + ? TOption[TGroupChildren] extends { value: any }[] + ? TOption[TGroupChildren][0]['value'][] + : TOption[TGroupChildren] extends any[] + ? TOption[TGroupChildren][0] + : any[] + : TOption[TGroupChildren] extends any[] + ? TValue extends keyof TOption[TGroupChildren][0] + ? TOption[TGroupChildren][0][TValue][] + : any[] + : any[] + : any[]; + +interface ListBoxChangeTargetOptions { name: string; id: string; - value: any; + value: TOption; } -interface ListBoxChangeParams { +interface ListBoxChangeParams { originalEvent: React.SyntheticEvent; - value: any; + value: ListBoxValueType; stopPropagation(): void; preventDefault(): void; - target: ListBoxChangeTargetOptions; + target: ListBoxChangeTargetOptions; } -interface ListBoxFilterValueChangeParams { +interface ListBoxFilterValueChangeParams { originalEvent: React.SyntheticEvent; - value: any; + value: TOption; } interface ListBoxFilterOptions { @@ -35,23 +79,24 @@ interface ListBoxFilterOptions { reset?: () => void; } -export interface ListBoxProps extends Omit, HTMLDivElement>, 'onChange' | 'ref'> { - value?: any; - options?: SelectItemOptionsType; - optionLabel?: string; - optionValue?: string; - optionDisabled?: ListBoxOptionDisabledType; - optionGroupLabel?: string; - optionGroupChildren?: string; - optionGroupTemplate?: ListBoxOptionGroupTemplateType; - itemTemplate?: ListBoxItemTemplateType; +export interface ListBoxProps + extends Omit, HTMLDivElement>, 'multiple' | 'value' | 'onChange' | 'ref'> { + value?: ReadonlyArray | ListBoxValueType; + options?: SelectItemOptionsType; + optionLabel?: NestedKeyOf | Omit, string>; + optionValue?: TValue | Omit; + optionDisabled?: ListBoxOptionDisabledType; + optionGroupLabel?: TGroupLabel | Omit, string>; + optionGroupChildren?: TGroupChildren | Omit, string>; + optionGroupTemplate?: ListBoxOptionGroupTemplateType; + itemTemplate?: ListBoxItemTemplateType; filterTemplate?: ListBoxFilterTemplateType; listStyle?: object; listClassName?: string; virtualScrollerOptions?: VirtualScrollerProps; disabled?: boolean; - dataKey?: string; - multiple?: boolean; + dataKey?: NestedKeyOf | Omit, string>; + multiple?: TMultiple; metaKeySelection?: boolean; filter?: boolean; filterBy?: string; @@ -63,12 +108,18 @@ export interface ListBoxProps extends Omit): void; + onFilterValueChange?(e: ListBoxFilterValueChangeParams): void; children?: React.ReactNode; } -export declare class ListBox extends React.Component { +export declare class ListBox< + TOption, + TValue extends NestedKeyOf | undefined = undefined, + TMultiple = undefined, + TGroupLabel extends NestedKeyOf | undefined = undefined, + TGroupChildren extends NestedKeyOf | undefined = undefined +> extends React.Component, any> { public getElement(): HTMLDivElement; public getVirtualScroller(): VirtualScroller; } diff --git a/components/lib/multiselect/multiselect.d.ts b/components/lib/multiselect/multiselect.d.ts index 20101e2ab3..6632d1b561 100755 --- a/components/lib/multiselect/multiselect.d.ts +++ b/components/lib/multiselect/multiselect.d.ts @@ -1,19 +1,19 @@ import * as React from 'react'; -import TooltipOptions from '../tooltip/tooltipoptions'; import { CSSTransitionProps } from '../csstransition'; +import { NestedKeyOf, SelectItemOptionsType } from '../selectitem/selectitem'; +import TooltipOptions from '../tooltip/tooltipoptions'; import { IconType } from '../utils'; import { VirtualScrollerProps } from '../virtualscroller'; -import { SelectItemOptionsType } from '../selectitem/selectitem'; -type MultiSelectOptionGroupTemplateType = React.ReactNode | ((option: any, index: number) => React.ReactNode); +type MultiSelectOptionGroupTemplateType = React.ReactNode | ((option: TOption, index: number) => React.ReactNode); -type MultiSelectItemTemplateType = React.ReactNode | ((option: any) => React.ReactNode); +type MultiSelectItemTemplateType = React.ReactNode | ((option: TOption) => React.ReactNode); -type MultiSelectSelectedItemTemplateType = React.ReactNode | ((value: any) => React.ReactNode); +type MultiSelectSelectedItemTemplateType = React.ReactNode | ((value: TOption) => React.ReactNode); type MultiSelectFilterTemplateType = React.ReactNode | ((options: MultiSelectFilterOptions) => React.ReactNode); -type MultiSelectEmptyFilterMessageType = React.ReactNode | ((props: MultiSelectProps) => React.ReactNode); +type MultiSelectEmptyFilterMessageType = React.ReactNode | ((props: MultiSelectProps) => React.ReactNode); type MultiSelectDisplayType = 'comma' | 'chip'; @@ -22,7 +22,7 @@ interface MultiSelectHeaderCheckboxChangeParams { checked: boolean; } -interface MultiSelectPanelHeaderTemplateParams { +interface MultiSelectPanelHeaderTemplateParams { className: string; checkboxElement: HTMLElement; checked: boolean; @@ -33,29 +33,51 @@ interface MultiSelectPanelHeaderTemplateParams { closeIconClassName: string; onCloseClick(event: React.MouseEvent): void; element: JSX.Element; - props: MultiSelectProps; + props: MultiSelectProps; } -type MultiSelectPanelHeaderTemplateType = React.ReactNode | ((e: MultiSelectPanelHeaderTemplateParams) => React.ReactNode); - -type MultiSelectPanelFooterTemplateType = React.ReactNode | ((props: MultiSelectProps, hide: () => void) => React.ReactNode); - -type MultiSelectOptionDisabledType = string | ((option: any) => boolean); +type MultiSelectValue = TGroupLabel extends undefined + ? TValue extends undefined + ? TOption extends { value: any } + ? TOption['value'][] + : TOption[] + : TValue extends keyof TOption + ? TOption[TValue][] + : any[] + : TGroupChildren extends keyof TOption + ? TValue extends undefined + ? TOption[TGroupChildren] extends { value: any }[] + ? TOption[TGroupChildren][0]['value'][] + : TOption[TGroupChildren] extends any[] + ? TOption[TGroupChildren][0][] + : any[] + : TOption[TGroupChildren] extends any[] + ? TValue extends keyof TOption[TGroupChildren][0] + ? TOption[TGroupChildren][0][TValue][] + : any[] + : any[] + : any[]; + +type MultiSelectPanelHeaderTemplateType = React.ReactNode | ((e: MultiSelectPanelHeaderTemplateParams) => React.ReactNode); + +type MultiSelectPanelFooterTemplateType = React.ReactNode | ((props: MultiSelectProps, hide: () => void) => React.ReactNode); + +type MultiSelectOptionDisabledType = string | ((option: TOption) => boolean); type MultiSelectAppendToType = 'self' | HTMLElement | undefined | null; -interface MultiSelectChangeTargetOptions { +interface MultiSelectChangeTargetOptions { name: string; id: string; - value: any; + value: TOption; } -interface MultiSelectChangeParams { +interface MultiSelectChangeParams { originalEvent: React.SyntheticEvent; - value: any; + value: MultiSelectValue; stopPropagation(): void; preventDefault(): void; - target: MultiSelectChangeTargetOptions; + target: MultiSelectChangeTargetOptions; } interface MultiSelectFilterParams { @@ -73,18 +95,18 @@ interface MultiSelectFilterOptions { reset?: () => void; } -export interface MultiSelectProps extends Omit, HTMLDivElement>, 'onChange' | 'ref'> { +export interface MultiSelectProps extends Omit, HTMLDivElement>, 'value' | 'onChange' | 'ref'> { id?: string; inputRef?: React.Ref; name?: string; - value?: any; - options?: SelectItemOptionsType; - optionLabel?: string; - optionValue?: string; - optionDisabled?: MultiSelectOptionDisabledType; - optionGroupLabel?: string; - optionGroupChildren?: string; - optionGroupTemplate?: MultiSelectOptionGroupTemplateType; + value?: string | number | ReadonlyArray | ReadonlyArray | MultiSelectValue | ReadonlyArray | undefined; + options?: SelectItemOptionsType; + optionLabel?: NestedKeyOf | Omit, string>; + optionValue?: TValue | Omit; + optionDisabled?: MultiSelectOptionDisabledType; + optionGroupLabel?: TGroupLabel | Omit, string>; + optionGroupChildren?: TGroupChildren | Omit, string>; + optionGroupTemplate?: MultiSelectOptionGroupTemplateType; display?: MultiSelectDisplayType; style?: object; className?: string; @@ -101,10 +123,10 @@ export interface MultiSelectProps extends Omit; resetFilterOnHide?: boolean; tabIndex?: number; - dataKey?: string; + dataKey?: NestedKeyOf | Omit, string>; inputId?: string; appendTo?: MultiSelectAppendToType; tooltip?: string; @@ -113,17 +135,17 @@ export interface MultiSelectProps extends Omit; filterTemplate?: MultiSelectFilterTemplateType; - selectedItemTemplate?: MultiSelectSelectedItemTemplateType; - panelHeaderTemplate?: MultiSelectPanelHeaderTemplateType; - panelFooterTemplate?: MultiSelectPanelFooterTemplateType; + selectedItemTemplate?: MultiSelectSelectedItemTemplateType; + panelHeaderTemplate?: MultiSelectPanelHeaderTemplateType; + panelFooterTemplate?: MultiSelectPanelFooterTemplateType; transitionOptions?: CSSTransitionProps; - dropdownIcon?: IconType; - removeIcon?: IconType; + dropdownIcon?: IconType>; + removeIcon?: IconType>; showSelectAll?: boolean; selectAll?: boolean; - onChange?(e: MultiSelectChangeParams): void; + onChange?(e: MultiSelectChangeParams): void; onFocus?(event: React.FocusEvent): void; onBlur?(event: React.FocusEvent): void; onShow?(): void; @@ -133,7 +155,12 @@ export interface MultiSelectProps extends Omit { +export declare class MultiSelect< + TOption, + TValue extends NestedKeyOf | undefined = undefined, + TGroupLabel extends NestedKeyOf | undefined = undefined, + TGroupChildren extends NestedKeyOf | undefined = undefined +> extends React.Component, any> { public show(): void; public hide(): void; public getElement(): HTMLDivElement; diff --git a/components/lib/selectbutton/selectbutton.d.ts b/components/lib/selectbutton/selectbutton.d.ts index af61b90ee6..64127334fc 100644 --- a/components/lib/selectbutton/selectbutton.d.ts +++ b/components/lib/selectbutton/selectbutton.d.ts @@ -1,42 +1,59 @@ +import { NestedKeyOf } from './../selectitem/selectitem.d'; import * as React from 'react'; -import TooltipOptions from '../tooltip/tooltipoptions'; import { SelectItemOptionsType } from '../selectitem/selectitem'; +import TooltipOptions from '../tooltip/tooltipoptions'; -type SelectButtonOptionDisabledType = string | ((option: any) => boolean); +type SelectButtonOptionDisabledType = string | ((option: TOption) => boolean); -interface SelectButtonChangeTargetOptions { +interface SelectButtonChangeTargetOptions { name: string; id: string; - value: any; + value: TOption; } -interface SelectButtonChangeParams { +type SelectButtonValue = TMultiple extends undefined + ? TValue extends undefined + ? TOption extends { value: any } + ? TOption['value'] + : TOption + : TValue extends keyof TOption + ? TOption[TValue] + : any + : TValue extends undefined + ? TOption extends { value: any } + ? TOption['value'][] + : TOption[] + : TValue extends keyof TOption + ? TOption[TValue][] + : any[]; + +interface SelectButtonChangeParams { originalEvent: React.SyntheticEvent; - value: any; + value: SelectButtonValue; stopPropagation(): void; preventDefault(): void; - target: SelectButtonChangeTargetOptions; + target: SelectButtonChangeTargetOptions; } -export interface SelectButtonProps extends Omit, HTMLDivElement>, 'unselectable' | 'onChange' | 'ref'> { - value?: any; - options?: SelectItemOptionsType; - optionLabel?: string; - optionValue?: string; - optionDisabled?: SelectButtonOptionDisabledType; +export interface SelectButtonProps extends Omit, HTMLDivElement>, 'value' | 'multiple' | 'unselectable' | 'onChange' | 'ref'> { + value?: string | number | ReadonlyArray | ReadonlyArray | SelectButtonValue | ReadonlyArray | undefined; + options?: SelectItemOptionsType; + optionLabel?: NestedKeyOf | Omit, string>; + optionValue?: TValue | Omit; + optionDisabled?: SelectButtonOptionDisabledType; tabIndex?: number; - multiple?: boolean; + multiple?: TMultiple; unselectable?: boolean; disabled?: boolean; - dataKey?: string; + dataKey?: NestedKeyOf | Omit, string>; tooltip?: string; tooltipOptions?: TooltipOptions; ariaLabelledBy?: string; - itemTemplate?(option: any): React.ReactNode; - onChange?(e: SelectButtonChangeParams): void; + itemTemplate?(option: TOption): React.ReactNode; + onChange?(e: SelectButtonChangeParams): void; children?: React.ReactNode; } -export declare class SelectButton extends React.Component { +export declare class SelectButton | undefined = undefined, TMultiple = undefined> extends React.Component, any> { public getElement(): HTMLDivElement; } diff --git a/components/lib/selectitem/selectitem.d.ts b/components/lib/selectitem/selectitem.d.ts index 9d15f1de57..0326615146 100644 --- a/components/lib/selectitem/selectitem.d.ts +++ b/components/lib/selectitem/selectitem.d.ts @@ -1,6 +1,6 @@ import { IconType } from '../utils'; -export type SelectItemOptionsType = SelectItem[] | any[]; +export type SelectItemOptionsType = SelectItem[] | TOption[]; export interface SelectItem { label?: string; @@ -10,3 +10,7 @@ export interface SelectItem { title?: string; disabled?: boolean; } + +export type NestedKeyOf = { + [Key in keyof ObjectType & (string | number)]: ObjectType extends any[] ? 'length' : ObjectType[Key] extends object ? `${Key}` | `${Key}.${NestedKeyOf}` : `${Key}`; +}[keyof ObjectType & (string | number)];