Skip to content

Commit 923afd2

Browse files
Menu: improve types (#30906)
1 parent c88f058 commit 923afd2

File tree

5 files changed

+53
-42
lines changed

5 files changed

+53
-42
lines changed

packages/devextreme/js/__internal/ui/chat/messagelist.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type { WidgetOptions } from '@js/ui/widget/ui.widget';
2020
import { getPublicElement } from '@ts/core/m_element';
2121
import type { OptionChanged } from '@ts/core/widget/types';
2222
import Widget from '@ts/core/widget/widget';
23+
import type { ClickableCollectionWidgetItem } from '@ts/ui/collection/item';
2324
import ContextMenu from '@ts/ui/context_menu/context_menu';
2425
import type {
2526
ScrollView as ScrollViewType,
@@ -301,14 +302,14 @@ class MessageList extends Widget<Properties> {
301302
const editText = messageLocalization.format('dxChat-editingEditMessage');
302303
const deleteText = messageLocalization.format('dxChat-editingDeleteMessage');
303304

304-
const buttons: ContextMenuItem[] = [];
305+
const buttons: ClickableCollectionWidgetItem<ContextMenuItem>[] = [];
305306

306307
if (allowUpdating(message) && message.type !== 'image') {
307308
buttons.push({
308309
icon: 'edit',
309310
text: editText,
310311
disabled: isEditActionDisabled(message),
311-
// @ts-expect-error ts-error
312+
// @ts-expect-error itemElement
312313
onClick: (e: ItemClick): void => {
313314
const onMessageEditStarted = onMessageEditingStart?.({
314315
event: e.event, message: message as TextMessage,
@@ -328,7 +329,7 @@ class MessageList extends Widget<Properties> {
328329
buttons.push({
329330
icon: 'trash',
330331
text: deleteText,
331-
// @ts-expect-error ts-error
332+
// @ts-expect-error itemElement
332333
onClick(e: ItemClick): void {
333334
onMessageDeleting?.({ event: e.event, message });
334335
},

packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import type { ActionConfig } from '@ts/core/widget/component';
3737
import type { OptionChanged } from '@ts/core/widget/types';
3838
import type { SupportedKeys, WidgetProperties } from '@ts/core/widget/widget';
3939
import Widget from '@ts/core/widget/widget';
40+
import type { ClickableCollectionWidgetItem, ItemClickEvent } from '@ts/ui/collection/item';
4041
import type CollectionItem from '@ts/ui/collection/item';
4142
import CollectionWidgetItem from '@ts/ui/collection/item';
4243

@@ -117,8 +118,7 @@ export type Constructor<T> = new (...args: unknown[]) => T;
117118
export interface CollectionWidgetBaseProperties<
118119
// eslint-disable-next-line @typescript-eslint/no-explicit-any
119120
TComponent extends CollectionWidget<any, TItem, TKey> | any,
120-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
121-
TItem extends ItemLike = any,
121+
TItem extends ItemLike = CollectionWidgetItemProperties,
122122
// eslint-disable-next-line @typescript-eslint/no-explicit-any
123123
TKey extends CollectionItemKey = any,
124124
> extends CollectionWidgetOptions<TComponent, TItem, TKey>, Omit<
@@ -145,8 +145,7 @@ export interface CollectionWidgetBaseProperties<
145145
class CollectionWidget<
146146
// eslint-disable-next-line @typescript-eslint/no-explicit-any
147147
TProperties extends CollectionWidgetBaseProperties<any, TItem, TKey>,
148-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
149-
TItem extends ItemLike = any,
148+
TItem extends ItemLike = CollectionWidgetItemProperties,
150149
// eslint-disable-next-line @typescript-eslint/no-explicit-any
151150
TKey extends CollectionItemKey = any,
152151
> extends Widget<TProperties> {
@@ -231,12 +230,10 @@ class CollectionWidget<
231230
}
232231

233232
const itemData = this._getItemData($itemElement);
234-
// @ts-expect-error ts-error
235-
if (itemData?.onClick) {
233+
if (CollectionWidgetItem.isClickableItem(itemData)) {
236234
const actionArgs: ActionArgs<TItem> = {
237235
event: e,
238236
};
239-
// @ts-expect-error
240237
this._itemEventHandlerByHandler($itemElement, itemData.onClick, actionArgs);
241238
}
242239
// @ts-expect-error ts-error
@@ -1240,8 +1237,7 @@ class CollectionWidget<
12401237
}
12411238

12421239
_attachItemClickEvent(itemData: TItem, $itemElement: dxElementWrapper): void {
1243-
// @ts-expect-error ts-error
1244-
if (!itemData || !itemData.onClick) {
1240+
if (!itemData || !CollectionWidgetItem.isClickableItem(itemData)) {
12451241
return;
12461242
}
12471243

@@ -1252,7 +1248,6 @@ class CollectionWidget<
12521248
const actionArgs = {
12531249
event: e,
12541250
};
1255-
// @ts-expect-error ts-error
12561251
this._itemEventHandlerByHandler($itemElement, itemData.onClick, actionArgs);
12571252
},
12581253
);
@@ -1487,7 +1482,7 @@ class CollectionWidget<
14871482

14881483
_itemEventHandlerByHandler(
14891484
initiator: dxElementWrapper | Element,
1490-
handler: () => void,
1485+
handler: (e: ItemClickEvent<ClickableCollectionWidgetItem>) => void,
14911486
actionArgs: ActionArgs<TItem>,
14921487
actionConfig?: ActionConfig,
14931488
): void {

packages/devextreme/js/__internal/ui/collection/item.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import type { ItemInfo, NativeEventInfo } from '@js/common/core/events';
12
import type { dxElementWrapper } from '@js/core/renderer';
23
import $ from '@js/core/renderer';
34
import { each } from '@js/core/utils/iterator';
45
import { attachInstanceToElement, getInstanceByElement } from '@js/core/utils/public_component';
5-
import type { CollectionWidgetItem } from '@js/ui/collection/ui.collection_widget.base';
6+
import { isObject } from '@js/core/utils/type';
7+
import type { CollectionWidgetItem, ItemLike } from '@js/ui/collection/ui.collection_widget.base';
68

79
const INVISIBLE_STATE_CLASS = 'dx-state-invisible';
810
const DISABLED_STATE_CLASS = 'dx-state-disabled';
@@ -51,6 +53,17 @@ export interface ItemExtraOption<TProperties> {
5153
) => () => void;
5254
}
5355

56+
export type ItemClickEvent<TProperties> =
57+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
58+
NativeEventInfo<any, KeyboardEvent | MouseEvent | PointerEvent>
59+
& ItemInfo<TProperties>;
60+
61+
export type ClickableCollectionWidgetItem<
62+
TProperties extends CollectionWidgetItem = CollectionWidgetItem,
63+
> = TProperties & {
64+
onClick: (e: ItemClickEvent<TProperties>) => void;
65+
};
66+
5467
class CollectionItem<
5568
TProperties extends CollectionWidgetItem = CollectionWidgetItem,
5669
> {
@@ -159,9 +172,14 @@ class CollectionItem<
159172

160173
// eslint-disable-next-line @typescript-eslint/no-explicit-any
161174
static getInstance<T = CollectionItem<any>>($element: dxElementWrapper): T {
162-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
163175
return getInstanceByElement($element, this);
164176
}
177+
178+
static isClickableItem(
179+
item: ItemLike,
180+
): item is ClickableCollectionWidgetItem {
181+
return isObject(item) && 'onClick' in item;
182+
}
165183
}
166184

167185
export default CollectionItem;

packages/devextreme/js/__internal/ui/context_menu/context_menu.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,12 @@ import type {
2929
HiddenEvent,
3030
HidingEvent,
3131
Item,
32-
ItemClickEvent,
3332
PositioningEvent,
3433
Properties,
3534
ShowingEvent,
3635
ShownEvent,
3736
} from '@js/ui/context_menu';
38-
import type dxContextMenu from '@js/ui/context_menu';
37+
import type dxMenuBase from '@js/ui/context_menu/ui.menu_base';
3938
import type {
4039
dxMenuBaseItem,
4140
SubmenuHiddenEvent,
@@ -45,10 +44,11 @@ import type {
4544
} from '@js/ui/menu';
4645
import type { Properties as OverlayProperties } from '@js/ui/overlay';
4746
import { current as currentTheme, isGeneric } from '@js/ui/themes';
48-
import type { ActionArguments } from '@ts/core/m_action';
4947
import type { OptionChanged } from '@ts/core/widget/types';
5048
import type { SupportedKeys } from '@ts/core/widget/widget';
51-
import type { ClickEvent, HoverEvent, MenuBaseProperties } from '@ts/ui/context_menu/menu_base';
49+
import type {
50+
ClickEvent, HoverEvent, ItemClickActionArguments, MenuBaseProperties,
51+
} from '@ts/ui/context_menu/menu_base';
5252
import MenuBase from '@ts/ui/context_menu/menu_base';
5353
import type { InternalNode } from '@ts/ui/hierarchical_collection/data_converter';
5454
import Overlay from '@ts/ui/overlay/overlay';
@@ -109,8 +109,6 @@ interface SubmenuCreatedEvent<TItem extends dxMenuBaseItem = dxMenuBaseItem> {
109109
submenuElement: Element;
110110
itemData: TItem;
111111
}
112-
type ItemClickActionArguments =
113-
ActionArguments<dxContextMenu<ContextMenuProperties>, ItemClickEvent>;
114112

115113
interface ContextMenuActions {
116114
onShowing?: ((e: ShowingEvent | SubmenuShowingEvent | ChatMenuShowingEvent) => void);
@@ -127,20 +125,18 @@ interface ContextMenuActions {
127125

128126
type ContextMenuPropertiesKeys = Exclude<keyof Properties, keyof MenuBaseProperties>;
129127

130-
export interface ContextMenuProperties<
131-
TItem extends dxMenuBaseItem = Item,
132-
> extends
128+
export interface ContextMenuProperties<TItem extends Item = Item> extends
133129
MenuBaseProperties<TItem>,
134130
Pick<Properties<TItem>, ContextMenuPropertiesKeys> {
135131
hideOnParentScroll?: boolean;
136132
visualContainer?: string | Element | Window | null;
137133
overlayContainer?: string | Element | null;
138134
boundaryOffset?: PositionConfig['boundaryOffset'];
139-
onSubmenuCreated?: ((e) => void) | null;
140-
onLeftFirstItem?: ((e) => void) | null;
141-
onLeftLastItem?: ((e) => void) | null;
142-
onCloseRootSubmenu?: ((e) => void) | null;
143-
onExpandLastSubmenu?: ((e) => void) | null;
135+
onSubmenuCreated?: ((e: SubmenuCreatedEvent) => void) | null;
136+
onLeftFirstItem?: (($item?: dxElementWrapper) => void) | null;
137+
onLeftLastItem?: (($item?: dxElementWrapper) => void) | null;
138+
onCloseRootSubmenu?: (($item?: dxElementWrapper) => void) | null;
139+
onExpandLastSubmenu?: (($item?: dxElementWrapper) => void) | null;
144140
}
145141

146142
class ContextMenu<
@@ -973,8 +969,9 @@ class ContextMenu<
973969
}
974970

975971
// TODO: try to simplify it
976-
// @ts-expect-error ts-error
977-
_updateSubmenuVisibilityOnClick(actionArgs: ItemClickActionArguments): void {
972+
_updateSubmenuVisibilityOnClick(
973+
actionArgs: ItemClickActionArguments<dxMenuBase<ContextMenuProperties>, Item>,
974+
): void {
978975
if (!actionArgs.args?.length) {
979976
return;
980977
}
@@ -1005,7 +1002,6 @@ class ContextMenu<
10051002
return;
10061003
}
10071004

1008-
// @ts-expect-error ts-error
10091005
this._updateSelectedItemOnClick(actionArgs);
10101006

10111007
// T238943. Give the workaround with e.cancel and remove this hack

packages/devextreme/js/__internal/ui/context_menu/menu_base.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import type dxMenuBase from '@js/ui/context_menu/ui.menu_base';
1212
import type {
1313
dxMenuBaseItem,
1414
Item,
15-
ItemClickEvent as MenuItemClickEvent,
1615
SubmenuShowMode,
1716
} from '@js/ui/menu';
1817
import type { ActionArguments } from '@ts/core/m_action';
@@ -51,20 +50,22 @@ const DX_ICON_WITH_URL_CLASS = 'dx-icon-with-url';
5150
const ITEM_URL_CLASS = 'dx-item-url';
5251
const DX_MENU_ITEM_DATA_KEY = 'dxMenuItemDataKey';
5352

54-
type BaseItemClickEvent =
55-
NativeEventInfo<dxMenuBase<MenuBaseProperties>, MouseEvent | PointerEvent | TouchEvent>
56-
& ItemInfo<dxMenuBaseItem>;
53+
type ItemClickEvent<TComponent, TItem> =
54+
NativeEventInfo<TComponent, MouseEvent | PointerEvent | TouchEvent>
55+
& ItemInfo<TItem>;
5756
export type HoverEvent = DxEvent<MouseEvent | PointerEvent>;
5857
export type ClickEvent = DxEvent<MouseEvent | PointerEvent | TouchEvent>;
59-
export type ItemClickActionArguments = ActionArguments<
60-
dxMenuBase<MenuBaseProperties>,
61-
BaseItemClickEvent | MenuItemClickEvent<Item>
58+
export type ItemClickActionArguments<
59+
TComponent extends dxMenuBase<MenuBaseProperties> = dxMenuBase<MenuBaseProperties>,
60+
TItem extends dxMenuBaseItem = dxMenuBaseItem,
61+
> = ActionArguments<
62+
TComponent,
63+
ItemClickEvent<TComponent, TItem>
6264
>;
6365
type MenuBaseNode = InternalNode & dxMenuBaseItem;
6466

6567
export interface MenuBaseProperties<
66-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
67-
TItem extends dxMenuBaseItem | any = any,
68+
TItem extends dxMenuBaseItem = dxMenuBaseItem,
6869
// @ts-expect-error ts-error
6970
> extends dxMenuBaseOptions<MenuBase, TItem> {
7071
focusedElement?: Element | null;

0 commit comments

Comments
 (0)