Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: focusable grid item #142

Merged
merged 4 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ interface DashKitProps {
- **context**: Object that will be propped up on all widgets.
- **overlayControls**: Object that overrides widget controls at the time of editing. If not transmitted, basic controls will be displayed.
- **noOverlay**: If `true`, overlay and controls are not displayed while editing.
- **focusable**: If `true`, grid items will be focusable.
- **draggableHandleClassName** : СSS class name of the element that makes the widget draggable.

## Usage
Expand Down
2 changes: 2 additions & 0 deletions src/components/DashKit/DashKit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface DashKitDefaultProps {
settings: SettingsProps;
context: ContextProps;
noOverlay: boolean;
focusable?: boolean;
}

export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {}
Expand All @@ -55,6 +56,7 @@ export class DashKit extends React.PureComponent<DashKitInnerProps> {
},
context: {},
noOverlay: false,
focusable: false,
};

static registerPlugins(...plugins: Plugin[]) {
Expand Down
1 change: 1 addition & 0 deletions src/components/DashKit/__stories__/DashKitShowcase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
}

render() {
console.log('customControlsActionData', this.state.customControlsActionData);

Check warning on line 72 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 72 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
const {editMode} = this.state;
const controls = {
custom: [
Expand All @@ -85,7 +85,7 @@
allWidgetsControls: true,
title: 'Icon tooltip 1',
icon: TickIcon,
handler: () => console.log('overlayControls::custom click'),

Check warning on line 88 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 88 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
iconSize: 16,
},
{
Expand All @@ -98,7 +98,7 @@
{
allWidgetsControls: true,
title: 'Icon tooltip 2',
handler: () => console.log('overlayControls::custom click'),

Check warning on line 101 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 101 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
},
],
};
Expand Down Expand Up @@ -208,6 +208,7 @@
settings={this.state.settings}
ref={this.dashKitRef}
overlayControls={controls}
focusable={!editMode}
/>
</DemoRow>
</Demo>
Expand All @@ -224,10 +225,10 @@
};

private onItemEdit = ({id, type, namespace, data}: ConfigItem) => {
console.log('id', id);

Check warning on line 228 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 228 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
console.log('type', type);

Check warning on line 229 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 229 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
console.log('namespace', namespace);

Check warning on line 230 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 230 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
console.log('data', data);

Check warning on line 231 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 231 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
this.setState({
lastAction: `[onItemEdit] Widget (id = '${id}') has been changed: ${new Date().toISOString()}`,
});
Expand All @@ -240,8 +241,8 @@
config: DashKitProps['config'];
itemsStateAndParams: DashKitProps['itemsStateAndParams'];
}) => {
console.log('config', config);

Check warning on line 244 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 244 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
console.log('itemsStateAndParams', itemsStateAndParams);

Check warning on line 245 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 245 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement

this.setState({
lastAction: `[onChange] config or itemsStateAndParams has been changed: ${new Date().toISOString()}`,
Expand Down Expand Up @@ -337,7 +338,7 @@
if (this.dashKitRef.current) {
const itemsMetas = await Promise.all(this.dashKitRef.current.getItemsMeta());
// There is no such method in the Title and Text plugin
console.log(itemsMetas);

Check warning on line 341 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

Unexpected console statement

Check warning on line 341 in src/components/DashKit/__stories__/DashKitShowcase.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
}
};

Expand Down
67 changes: 67 additions & 0 deletions src/components/GridItem/GridItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,39 @@ import './GridItem.scss';

const b = cn('dashkit-grid-item');

class WindowFocusObserver {
constructor() {
this.subscribers = 0;
this.isFocused = !document.hidden;

window.addEventListener('blur', this.blurHandler, true);
window.addEventListener('focus', this.focusHandler, true);
}

blurHandler = (e) => {
if (e.target === window) {
this.isFocused = false;
}
};

focusHandler = (e) => {
if (e.target === window) {
this.isFocused = true;
}
};

// Method to get state after all blur\focus events in document are triggered
async getFocuseState() {
return new Promise((resolve) => {
requestAnimationFrame(() => {
resolve(this.isFocused);
});
});
}
}

const windowFocusObserver = new WindowFocusObserver();

class GridItem extends React.PureComponent {
static propTypes = {
adjustWidgetLayout: PropTypes.func.isRequired,
Expand All @@ -28,6 +61,7 @@ class GridItem extends React.PureComponent {
className: PropTypes.string,
style: PropTypes.object,
noOverlay: PropTypes.bool,
focusable: PropTypes.bool,
withCustomHandle: PropTypes.bool,
onMouseDown: PropTypes.func,
onMouseUp: PropTypes.func,
Expand All @@ -37,6 +71,10 @@ class GridItem extends React.PureComponent {

static contextType = DashKitContext;

state = {
isFocused: false,
};

renderOverlay() {
const {overlayControls} = this.props;
const {editMode} = this.context;
Expand All @@ -60,6 +98,26 @@ class GridItem extends React.PureComponent {
);
}

onFocusHandler = () => {
this.setState({isFocused: true});

if (this.controller) {
this.controller.abort();
}
};

onBlurHandler = () => {
this.controller = new AbortController();

windowFocusObserver.getFocuseState().then((isWindowFocused) => {
if (!this.controller.signal.aborted && isWindowFocused) {
this.setState({isFocused: false});
}

this.controller = null;
});
};

render() {
// из-за бага, что Grid Items unmounts при изменении static, isDraggable, isResaizable
// https://github.com/STRML/react-grid-layout/issues/721
Expand All @@ -73,6 +131,7 @@ class GridItem extends React.PureComponent {
className,
isDragging,
noOverlay,
focusable,
withCustomHandle,
} = this.props;
const {editMode} = this.context;
Expand All @@ -92,6 +151,7 @@ class GridItem extends React.PureComponent {
className={b(
{
'is-dragging': isDragging,
'is-focused': this.state.isFocused,
'with-custom-handle': withCustomHandle,
},
preparedClassName,
Expand All @@ -100,6 +160,13 @@ class GridItem extends React.PureComponent {
style={style}
ref={this.props.forwardedRef}
{...reactGridLayoutProps}
{...(focusable
? {
onFocus: this.onFocusHandler,
onBlur: this.onBlurHandler,
tabIndex: -1,
}
: {})}
>
<div className={b('item', {editMode: editMode && !_editActive && !noOverlay})}>
<Item
Expand Down
8 changes: 8 additions & 0 deletions src/components/GridItem/GridItem.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
position: relative;
$_border-radius: 3px;

&:focus {
outline: none;
}

&__overlay {
--_-dashkit-overlay-color: var(--dashkit-overlay-color, var(--g-color-base-generic));
--_-dashkit-overlay-border-color: var(--dashkit-overlay-border-color, rgba(0, 0, 0, 0.1));
Expand Down Expand Up @@ -62,6 +66,10 @@
will-change: transform;
}

.react-grid-item.dashkit-grid-item_is-focused {
z-index: 2;
}

.react-grid-item.react-grid-placeholder {
--_-dashkit-placeholder-color: var(--dashkit-placeholder-color, #fc0);
--_-dashkit-placeholder-opacity: var(--dashkit-placeholder-opacity, 0.2);
Expand Down
12 changes: 10 additions & 2 deletions src/components/GridLayout/GridLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,15 @@ export default class GridLayout extends React.PureComponent {
};

render() {
const {layout, config, registerManager, editMode, noOverlay, draggableHandleClassName} =
this.context;
const {
layout,
config,
registerManager,
editMode,
noOverlay,
focusable,
draggableHandleClassName,
} = this.context;
this.pluginsRefs.length = config.items.length;

return (
Expand Down Expand Up @@ -140,6 +147,7 @@ export default class GridLayout extends React.PureComponent {
adjustWidgetLayout={this.adjustWidgetLayout}
isDragging={this.state.isDragging}
noOverlay={noOverlay}
focusable={focusable}
withCustomHandle={Boolean(draggableHandleClassName)}
overlayControls={this.props.overlayControls}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/hocs/withContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ function useMemoStateContext(props) {
config: props.config,
context: props.context,
noOverlay: props.noOverlay,
focusable: props.focusable,
defaultGlobalParams: props.globalParams,
globalParams: props.globalParams,
editMode: props.editMode,
Expand All @@ -207,6 +208,7 @@ function useMemoStateContext(props) {
props.config,
props.context,
props.noOverlay,
props.focusable,
props.globalParams,
props.editMode,
props.settings,
Expand Down
Loading