Skip to content

Commit a7d4c4f

Browse files
ykornilovYury Kornilov
andauthored
fix!: move GridItem from JS to TS (#279)
* fix!: move GridItem from JS to TS --------- Co-authored-by: Yury Kornilov <[email protected]>
1 parent b215753 commit a7d4c4f

File tree

3 files changed

+81
-75
lines changed

3 files changed

+81
-75
lines changed

src/components/GridItem/GridItem.js renamed to src/components/GridItem/GridItem.tsx

Lines changed: 64 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React from 'react';
22

3-
import PropTypes from 'prop-types';
4-
53
import {FOCUSED_CLASS_NAME} from '../../constants';
64
import {DashKitContext} from '../../context';
5+
import type {ConfigItem, ConfigLayout} from '../../shared';
6+
import type {PluginRef, ReactGridLayoutProps} from '../../typings';
77
import {cn} from '../../utils/cn';
88
import Item from '../Item/Item';
99
import OverlayControls from '../OverlayControls/OverlayControls';
@@ -13,28 +13,28 @@ import './GridItem.scss';
1313
const b = cn('dashkit-grid-item');
1414

1515
class WindowFocusObserver {
16-
constructor() {
17-
this.subscribers = 0;
18-
this.isFocused = !document.hidden;
16+
subscribers = 0;
17+
isFocused = !document.hidden;
1918

19+
constructor() {
2020
window.addEventListener('blur', this.blurHandler, true);
2121
window.addEventListener('focus', this.focusHandler, true);
2222
}
2323

24-
blurHandler = (e) => {
24+
blurHandler = (e: FocusEvent) => {
2525
if (e.target === window) {
2626
this.isFocused = false;
2727
}
2828
};
2929

30-
focusHandler = (e) => {
30+
focusHandler = (e: FocusEvent) => {
3131
if (e.target === window) {
3232
this.isFocused = true;
3333
}
3434
};
3535

3636
// Method to get state after all blur\focus events in document are triggered
37-
async getFocusedState() {
37+
async getFocusedState(): Promise<boolean> {
3838
return new Promise((resolve) => {
3939
requestAnimationFrame(() => {
4040
resolve(this.isFocused);
@@ -45,43 +45,53 @@ class WindowFocusObserver {
4545

4646
const windowFocusObserver = new WindowFocusObserver();
4747

48-
class GridItem extends React.PureComponent {
49-
static propTypes = {
50-
adjustWidgetLayout: PropTypes.func.isRequired,
51-
gridLayout: PropTypes.object,
52-
id: PropTypes.string,
53-
item: PropTypes.object,
54-
isDragging: PropTypes.bool,
55-
isDraggedOut: PropTypes.bool,
56-
layout: PropTypes.array,
57-
58-
forwardedRef: PropTypes.any,
59-
forwardedPluginRef: PropTypes.any,
60-
isPlaceholder: PropTypes.bool,
61-
62-
onItemMountChange: PropTypes.func,
63-
onItemRender: PropTypes.func,
64-
65-
// from react-grid-layout:
66-
children: PropTypes.node,
67-
className: PropTypes.string,
68-
style: PropTypes.object,
69-
noOverlay: PropTypes.bool,
70-
focusable: PropTypes.bool,
71-
withCustomHandle: PropTypes.bool,
72-
onMouseDown: PropTypes.func,
73-
onMouseUp: PropTypes.func,
74-
onTouchEnd: PropTypes.func,
75-
onTouchStart: PropTypes.func,
76-
onItemFocus: PropTypes.func,
77-
onItemBlur: PropTypes.func,
78-
};
79-
48+
type GridItemProps = {
49+
adjustWidgetLayout: (data: {
50+
widgetId: string;
51+
needSetDefault?: boolean;
52+
adjustedWidgetLayout?: ConfigLayout;
53+
}) => void;
54+
gridLayout?: ReactGridLayoutProps;
55+
id?: string;
56+
item: ConfigItem;
57+
isDragging?: boolean;
58+
isDraggedOut?: boolean;
59+
layout?: ConfigLayout[];
60+
61+
forwardedRef?: React.Ref<HTMLDivElement>;
62+
forwardedPluginRef?: (pluginRef: PluginRef) => void;
63+
isPlaceholder?: boolean;
64+
65+
onItemMountChange?: (item: ConfigItem, meta: {isAsync: boolean; isMounted: boolean}) => void;
66+
onItemRender?: (item: ConfigItem) => void;
67+
68+
// from react-grid-layout:
69+
children?: React.ReactNode;
70+
className?: string;
71+
style?: React.CSSProperties;
72+
noOverlay?: boolean;
73+
focusable?: boolean;
74+
withCustomHandle?: boolean;
75+
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
76+
onMouseUp?: (e: React.MouseEvent<HTMLDivElement>) => void;
77+
onTouchEnd?: (e: React.TouchEvent<HTMLDivElement>) => void;
78+
onTouchStart?: (e: React.TouchEvent<HTMLDivElement>) => void;
79+
onItemFocus?: (item: ConfigItem) => void;
80+
onItemBlur?: (item: ConfigItem) => void;
81+
};
82+
83+
type GridItemState = {
84+
isFocused: boolean;
85+
};
86+
87+
class GridItem extends React.PureComponent<GridItemProps, GridItemState> {
8088
static contextType = DashKitContext;
89+
context!: React.ContextType<typeof DashKitContext>;
8190

8291
_isAsyncItem = false;
92+
controller: AbortController | null = null;
8393

84-
state = {
94+
state: GridItemState = {
8595
isFocused: false,
8696
};
8797

@@ -105,7 +115,7 @@ class GridItem extends React.PureComponent {
105115
<div className={b('overlay')} />
106116
<OverlayControls
107117
configItem={item}
108-
onItemClick={focusable ? this.onOverlayItemClick : null}
118+
onItemClick={focusable ? this.onOverlayItemClick : undefined}
109119
/>
110120
</React.Fragment>
111121
);
@@ -114,17 +124,13 @@ class GridItem extends React.PureComponent {
114124
onOverlayItemClick = () => {
115125
// Creating button element to trigger focus out
116126
const focusDummy = document.createElement('button');
117-
const styles = {
127+
Object.assign(focusDummy.style, {
118128
width: '0',
119129
height: '0',
120130
opacity: '0',
121131
position: 'fixed',
122132
top: '0',
123133
left: '0',
124-
};
125-
126-
Object.entries(styles).forEach(([key, value]) => {
127-
focusDummy.style[key] = value;
128134
});
129135

130136
// requestAnimationFrame to make call after alert() or confirm()
@@ -187,14 +193,16 @@ class GridItem extends React.PureComponent {
187193
const {editMode} = this.context;
188194
const {isFocused} = this.state;
189195

190-
const width = Number.parseInt(style.width, 10);
191-
const height = Number.parseInt(style.height, 10);
192-
const transform = style.transform;
196+
const width =
197+
style?.width === undefined ? undefined : Number.parseInt(String(style.width), 10);
198+
const height =
199+
style?.height === undefined ? undefined : Number.parseInt(String(style.height), 10);
200+
const transform = style?.transform;
193201
const preparedClassName =
194202
(editMode
195203
? className
196204
: className
197-
.replace('react-resizable', '')
205+
?.replace('react-resizable', '')
198206
.replace('react-draggable', '')
199207
.replace(FOCUSED_CLASS_NAME, '')) +
200208
(isFocused ? ` ${FOCUSED_CLASS_NAME}` : '');
@@ -254,9 +262,11 @@ class GridItem extends React.PureComponent {
254262
}
255263
}
256264

257-
const GridItemForwarderRef = React.forwardRef((props, ref) => {
258-
return <GridItem {...props} forwardedRef={ref} />;
259-
});
265+
const GridItemForwarderRef = React.forwardRef<HTMLDivElement, Omit<GridItemProps, 'forwardedRef'>>(
266+
(props, ref) => {
267+
return <GridItem {...props} forwardedRef={ref} />;
268+
},
269+
);
260270

261271
GridItemForwarderRef.displayName = 'forwardRef(GridItem)';
262272

src/components/GridLayout/GridLayout.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,8 @@ export default class GridLayout extends React.PureComponent<GridLayoutProps, Gri
712712
return (
713713
<GridItem
714714
key={id}
715-
// TODO remove the expected error after translating GridItem to TS
716-
// @ts-expect-error
717715
id={id}
718-
item={{id, type, data: {}}}
716+
item={{id, type, data: {}} as ConfigItem}
719717
layout={temporaryLayout.data}
720718
adjustWidgetLayout={this.adjustWidgetLayout}
721719
isDragging={this.state.isDragging}
@@ -815,23 +813,21 @@ export default class GridLayout extends React.PureComponent<GridLayoutProps, Gri
815813
return (
816814
<GridItem
817815
key={keyId}
818-
{...({
819-
forwardedPluginRef: this.getMemoForwardRefCallback(offset + i),
820-
id: keyId,
821-
item,
822-
layout,
823-
adjustWidgetLayout: this.adjustWidgetLayout,
824-
isDragging,
825-
isDraggedOut,
826-
noOverlay: itemNoOverlay,
827-
focusable,
828-
withCustomHandle: Boolean(draggableHandleClassName),
829-
onItemMountChange,
830-
onItemRender,
831-
gridLayout: properties,
832-
onItemFocus,
833-
onItemBlur,
834-
} as any)} // TODO remove any after translating GridItem to TS
816+
forwardedPluginRef={this.getMemoForwardRefCallback(offset + i)} // forwarded ref to plugin
817+
id={keyId}
818+
item={item}
819+
layout={layout}
820+
adjustWidgetLayout={this.adjustWidgetLayout}
821+
isDragging={isDragging}
822+
isDraggedOut={isDraggedOut || undefined}
823+
noOverlay={itemNoOverlay}
824+
focusable={focusable}
825+
withCustomHandle={Boolean(draggableHandleClassName)}
826+
onItemMountChange={onItemMountChange}
827+
onItemRender={onItemRender}
828+
gridLayout={properties}
829+
onItemFocus={onItemFocus}
830+
onItemBlur={onItemBlur}
835831
/>
836832
);
837833
})}

src/components/OverlayControls/OverlayControls.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ interface OverlayControlsDefaultProps {
7575

7676
interface OverlayControlsProps extends OverlayControlsDefaultProps {
7777
configItem: ConfigItem;
78-
onItemClick?: () => void | null;
78+
onItemClick?: () => void;
7979
}
8080

8181
type PreparedCopyItemOptionsArg = Pick<ConfigItem, 'data' | 'type' | 'defaults' | 'namespace'> & {

0 commit comments

Comments
 (0)