Skip to content

Commit 51ec501

Browse files
add help panel with hotkeys descr. refactor url state management (#25)
1 parent 3157cb5 commit 51ec501

15 files changed

Lines changed: 199 additions & 91 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.rcv-hotkey-pill {
2+
font-size: 12px;
3+
4+
margin-right: 4px;
5+
margin-left: 4px;
6+
7+
padding: 3px;
8+
border: 1px solid var(--rcv-secondary-border-color);
9+
border-radius: 4px;
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react';
2+
3+
import './HotKeyPill.css';
4+
5+
export interface Props {
6+
hotKey: string;
7+
}
8+
9+
export function HotKeyPill({hotKey}: Props) {
10+
return (
11+
<div className="rcv-hotkey-pill">
12+
{hotKey}
13+
</div>
14+
);
15+
}

src/components/viewer/ComponentViewer.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,11 @@ body {
8989
margin: 10px 10px 10px 20px;
9090

9191
overflow: auto;
92+
}
93+
94+
.rcv-help {
95+
grid-area: preview;
96+
justify-self: right;
97+
98+
z-index: 1000;
9299
}

src/components/viewer/ComponentViewer.tsx

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import { HotKeyBoundActions } from '../hotkeys/HotKeyBoundActions';
2121

2222
import { ComponentViewerDropDown } from './ComponentViewerDropDown';
2323

24+
import { ComponentViewerHelp } from './help/ComponentViewerHelp';
25+
import { globalActionDefaultKeys } from './GlobalActions';
26+
2427
import './ComponentViewer.css';
2528

2629
export interface Props {
@@ -44,11 +47,11 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
4447

4548
this.state = this.stateFromUrl();
4649
this.hotKeyBoundActions = {
47-
'Alt F': this.onFullScreenToggle,
48-
'Ctrl Alt Down': this.onNextDemo,
49-
'Ctrl Alt Up': this.onPrevDemo,
50-
'Ctrl Alt Right': this.onNextDemoEntry,
51-
'Ctrl Alt Left': this.onPrevDemoEntry,
50+
[globalActionDefaultKeys.fullScreenToggle]: this.onFullScreenToggle,
51+
[globalActionDefaultKeys.nextDemo]: this.onNextDemo,
52+
[globalActionDefaultKeys.prevDemo]: this.onPrevDemo,
53+
[globalActionDefaultKeys.nextDemoEntry]: this.onNextDemoEntry,
54+
[globalActionDefaultKeys.prevDemoEntry]: this.onPrevDemoEntry,
5255
...this.dropDownKeyBoundActions()
5356
};
5457
}
@@ -81,7 +84,8 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
8184
registryName,
8285
demoName,
8386
filterText,
84-
selectedToolbarItem
87+
selectedToolbarItem,
88+
isHelpOn
8589
} = this.state;
8690

8791
return (
@@ -96,7 +100,8 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
96100

97101
<div className="rcv-toolbar-panel">
98102
<Toolbar
99-
onFullScreen={this.onFullScreenToggle}
103+
questionMarkToggledOn={isHelpOn}
104+
onQuestionMarkClick={this.onQuestionMarkToggle}
100105
dropDownLabel={dropDown ? dropDown.label : undefined}
101106
dropDownSelected={selectedToolbarItem}
102107
dropDownItems={dropDown ? dropDown.items : undefined}
@@ -124,6 +129,10 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
124129
<div className="rcv-preview">
125130
{this.renderDemo(demoEntry, false)}
126131
</div>
132+
133+
{isHelpOn && <div className="rcv-help">
134+
<ComponentViewerHelp/>
135+
</div>}
127136
</div>
128137
);
129138
}
@@ -200,12 +209,11 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
200209
}
201210

202211
private onFullScreenToggle = () => {
203-
this.pushUrl(
204-
this.state.registryName,
205-
this.state.demoName,
206-
this.state.entryTitle,
207-
!this.state.isFullScreen,
208-
this.state.selectedToolbarItem);
212+
this.pushUrl({isFullScreen: !this.state.isFullScreen});
213+
}
214+
215+
private onQuestionMarkToggle = () => {
216+
this.pushUrl({isHelpOn: !this.state.isHelpOn});
209217
}
210218

211219
private onNextDemo = () => {
@@ -260,41 +268,24 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
260268
}
261269

262270
private selectRegistry = (registryName: string) => {
263-
this.pushUrl(
271+
this.pushUrl({
264272
registryName,
265-
'',
266-
'',
267-
this.state.isFullScreen,
268-
this.state.selectedToolbarItem);
273+
demoName: '',
274+
entryTitle: ''});
269275
}
270276

271277
private selectDemo = (demoName: string) => {
272-
this.pushUrl(
273-
this.state.registryName,
274-
demoName,
275-
'',
276-
this.state.isFullScreen,
277-
this.state.selectedToolbarItem);
278+
this.pushUrl({demoName, entryTitle: ''});
278279
}
279280

280281
private selectInstanceByTitle = (title: string) => {
281-
this.pushUrl(
282-
this.state.registryName,
283-
this.state.demoName,
284-
title,
285-
this.state.isFullScreen,
286-
this.state.selectedToolbarItem);
282+
this.pushUrl({entryTitle: title});
287283
}
288284

289285
private selectToolbarItem = (label: string) => {
290286
const {dropDown} = this.props;
291287

292-
this.pushUrl(
293-
this.state.registryName,
294-
this.state.demoName,
295-
this.state.entryTitle,
296-
this.state.isFullScreen,
297-
label);
288+
this.pushUrl({selectedToolbarItem: label});
298289

299290
if (dropDown) {
300291
dropDown.onSelect(label);
@@ -316,15 +307,9 @@ class ComponentViewer extends Component<Props, ComponentViewerState> {
316307
[];
317308
}
318309

319-
private pushUrl(
320-
registryName: string,
321-
demoName: string,
322-
entryTitle: string,
323-
isFullScreen: boolean,
324-
selectedToolbarItem: string
325-
) {
326-
const searchParams = this.stateCreator.buildUrlSearchParams(
327-
{registryName, demoName, entryTitle, isFullScreen, selectedToolbarItem, filterText: ''});
310+
private pushUrl(newState: Partial<ComponentViewerState>) {
311+
const fullState = {...this.state, ...newState};
312+
const searchParams = this.stateCreator.buildUrlSearchParams({...fullState, filterText: ''});
328313
const newUrl = '?' + searchParams;
329314

330315
window.history.pushState({}, '', newUrl);

src/components/viewer/ComponentViewerState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export interface ComponentViewerState {
33
demoName: string;
44
entryTitle: string;
55
isFullScreen: boolean;
6+
isHelpOn: boolean;
67
filterText: string;
78
selectedToolbarItem: string;
89
}

src/components/viewer/ComponentViewerStateCreator.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('ComponentViewerStateCreator', () => {
2929
entryTitle: 'title-c-a',
3030
filterText: '',
3131
isFullScreen: false,
32+
isHelpOn: false,
3233
selectedToolbarItem: ''
3334
});
3435
});
@@ -47,6 +48,7 @@ describe('ComponentViewerStateCreator', () => {
4748
demoName: 'demo-name',
4849
entryTitle: '',
4950
isFullScreen: true,
51+
isHelpOn: false,
5052
filterText: '',
5153
selectedToolbarItem: ''
5254
});

src/components/viewer/ComponentViewerStateCreator.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ const queryParamNames = {
88
registryName: 'registryName',
99
demoName: 'demoName',
1010
entryTitle: 'entryTitle',
11+
selectedToolbarItem: 'toolbarItem',
1112
isFullScreen: 'fullScreen',
12-
selectedToolbarItem: 'toolbarItem'
13+
isHelpOn: 'help'
1314
};
1415

1516
export class ComponentViewerStateCreator {
@@ -40,6 +41,7 @@ export class ComponentViewerStateCreator {
4041
demoName: miniAppByUrl.demoEntry.name,
4142
entryTitle: miniAppByUrl.demoEntry.firstEntryTitle,
4243
isFullScreen: true,
44+
isHelpOn: false,
4345
filterText: '',
4446
selectedToolbarItem
4547
};
@@ -57,11 +59,15 @@ export class ComponentViewerStateCreator {
5759
const fullScreenValue = searchParams.get(queryParamNames.isFullScreen) || 'false';
5860
const isFullScreen = fullScreenValue === 'true';
5961

62+
const helpOnValue = searchParams.get(queryParamNames.isHelpOn) || 'false';
63+
const isHelpOn = helpOnValue === 'true';
64+
6065
return {
6166
registryName,
6267
demoName,
6368
entryTitle,
6469
isFullScreen,
70+
isHelpOn,
6571
selectedToolbarItem,
6672
filterText: ''
6773
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export interface GlobalActions {
2+
fullScreenToggle: string;
3+
nextDemo: string;
4+
prevDemo: string;
5+
nextDemoEntry: string;
6+
prevDemoEntry: string;
7+
}
8+
9+
export const globalActionDescription: GlobalActions = {
10+
fullScreenToggle: 'toggle full screen',
11+
nextDemo: 'select next demo',
12+
prevDemo: 'select previous demo',
13+
nextDemoEntry: 'select next demo entry',
14+
prevDemoEntry: 'select prev demo entry',
15+
};
16+
17+
export const globalActionDefaultKeys: GlobalActions = {
18+
fullScreenToggle: 'Alt F',
19+
nextDemo: 'Ctrl Alt Down',
20+
prevDemo: 'Ctrl Alt Up',
21+
nextDemoEntry: 'Ctrl Alt Right',
22+
prevDemoEntry: 'Ctrl Alt Left',
23+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.rcv-component-viewer-help {
2+
font-size: 14px;
3+
font-family: var(--rcv-font-family);
4+
5+
width: 400px;
6+
background-color: var(--rcv-primary-background-color);
7+
8+
height: 100%;
9+
border-left: 1px solid var(--rcv-primary-border-color);
10+
box-sizing: border-box;
11+
12+
padding-left: 16px;
13+
padding-top: 8px;
14+
15+
overflow: auto;
16+
}
17+
18+
.rcv-component-viewer-help > h1 {
19+
font-size: 20px;
20+
}
21+
22+
.rcv-component-viewer-help > h1:not(:first-child) {
23+
margin-top: 30px;
24+
}
25+
26+
.rcv-component-viewer-help > a,
27+
.rcv-component-viewer-help > a:visited {
28+
color: #618be1;
29+
}
30+
31+
.rcv-component-viewer-help-hotkeys-grid {
32+
display: grid;
33+
34+
grid-template-columns: auto 1fr;
35+
grid-gap: 8px 32px;
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as React from 'react';
2+
3+
import { globalActionDefaultKeys, globalActionDescription } from '../GlobalActions';
4+
import { HotKeyPill } from '../../hotkeys/HotKeyPill';
5+
6+
import './ComponentViewerHelp.css';
7+
8+
export function ComponentViewerHelp() {
9+
return (
10+
<div className="rcv-component-viewer-help">
11+
<h1>Hotkeys</h1>
12+
<div className="rcv-component-viewer-help-hotkeys-grid">
13+
{Object.keys(globalActionDefaultKeys).map(actionKey =>
14+
<HotKeyAndDescription
15+
key={actionKey}
16+
actionKey={actionKey}
17+
/>)}
18+
</div>
19+
<h1>GitHub</h1>
20+
<a href="https://github.com/MykolaGolubyev/react-component-viewer" target="_blank">Repository</a>
21+
</div>
22+
);
23+
}
24+
25+
interface HotKeyAndDescriptionProps {
26+
actionKey: string;
27+
}
28+
29+
function HotKeyAndDescription({actionKey}: HotKeyAndDescriptionProps) {
30+
return (
31+
<React.Fragment>
32+
<HotKeyPill hotKey={globalActionDefaultKeys[actionKey]}/>
33+
<div>{globalActionDescription[actionKey]}</div>
34+
</React.Fragment>
35+
);
36+
}

0 commit comments

Comments
 (0)