Skip to content

Commit 988d4ec

Browse files
committed
CommandPalette - Add base command palette
1 parent c174e7d commit 988d4ec

File tree

7 files changed

+358
-2
lines changed

7 files changed

+358
-2
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React, { HTMLAttributes, FC } from 'react';
2+
import { KeyTag } from './KeyTag';
3+
4+
interface CommandButtonRowProps extends HTMLAttributes<HTMLButtonElement> {
5+
shortCut: string[];
6+
}
7+
8+
export const CommandButtonRow: FC<CommandButtonRowProps> = ({
9+
children,
10+
shortCut,
11+
...props
12+
}) => {
13+
return (
14+
<button type="button" {...props} className="command-palette-row">
15+
<span className="command-name">{children}</span>
16+
<span>
17+
{shortCut.map(shortcut => (
18+
<KeyTag mini>{shortcut}</KeyTag>
19+
))}
20+
</span>
21+
</button>
22+
);
23+
};

src/CommandPalette/CommandPalette.css

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
.command-palette {
2+
font-family: sans-serif;
3+
padding: 20px 0;
4+
background: #29313A;
5+
border: 1px solid #13181E;
6+
box-sizing: border-box;
7+
box-shadow: 0px 10px 42px rgba(0, 0, 0, 0.32), 0px 6px 24px rgba(0, 0, 0, 0.16);
8+
border-radius: 4px;
9+
}
10+
11+
.command-palette-key-tag {
12+
display: inline-flex;
13+
background-color: rgba(30, 36, 44, .5);
14+
border-radius: 2px;
15+
padding: 8px;
16+
color: white;
17+
display: flex;
18+
justify-content: center;
19+
align-items: center;
20+
}
21+
22+
.command-palette-key-tag.mini {
23+
background-color: #1E242C;
24+
border-radius: 2px;
25+
padding: 2px 4px;
26+
margin-left: 4px;
27+
color: white;
28+
min-width: 20px;
29+
min-height: 20px;
30+
font-size: 13px;
31+
}
32+
33+
.command-palette-row {
34+
padding: 6px 32px;
35+
display: flex;
36+
flex-direction: row;
37+
justify-content: space-between;
38+
align-items: center;
39+
}
40+
41+
button.command-palette-row {
42+
font-size: 14px;
43+
color: #fff;
44+
width: 100%;
45+
background: none;
46+
border: none;
47+
}
48+
49+
button.command-palette-row span {
50+
display: flex;
51+
align-items: center;
52+
}
53+
54+
button.command-palette-row span > *:not(:last-child) {
55+
margin-right: 4px;
56+
}
57+
58+
.command-palette-row:hover:not(:first-child) {
59+
background-color: rgba(143, 150, 157, .1);
60+
}
61+
62+
.command-palette-row .command-name > *:first-child {
63+
margin-right: 12px;
64+
}
65+
66+
.command-palette-input-row {
67+
padding: 8px 32px;
68+
}
69+
/* TODO: This could be a reusable class instead for hiding content */
70+
.command-palette-input-row label {
71+
position: absolute !important;
72+
height: 1px;
73+
width: 1px;
74+
overflow: hidden;
75+
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
76+
clip: rect(1px, 1px, 1px, 1px);
77+
white-space: nowrap; /* added line */
78+
}
79+
80+
.command-palette-input-row input {
81+
border: 1px solid rgba(253, 253, 253, .3);
82+
border-radius: 4px;
83+
background-color: transparent;
84+
color: #FFF;
85+
box-sizing: border-box;
86+
width: 100%;
87+
font-size: 24px;
88+
padding: 4px;
89+
}
90+
91+
.command-palette-input-row input:focus {
92+
outline: none;
93+
border: 1px solid transparent;
94+
border-radius: unset;
95+
border-bottom: 3px solid rgba(253, 253, 253, .1);
96+
97+
}

src/CommandPalette/CommandPalette.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { FC, ReactNode, ReactNodeArray, HTMLAttributes } from 'react';
2+
import './CommandPalette.css';
3+
import { Commands } from '../Icons';
4+
import { KeyTag } from './KeyTag';
5+
6+
export interface Props
7+
extends HTMLAttributes<HTMLDivElement & HTMLInputElement> {
8+
shortCut: string[];
9+
children: ReactNode | ReactNodeArray;
10+
}
11+
12+
export const CommandPalette: FC<Props> = ({
13+
children,
14+
shortCut,
15+
onChange,
16+
...props
17+
}) => {
18+
return (
19+
<div className="command-palette">
20+
<div className="command-palette-row">
21+
<Commands muted />
22+
<KeyTag>
23+
Hide Menu Bar
24+
{shortCut.map(shortcutKey => (
25+
<KeyTag mini>{shortcutKey}</KeyTag>
26+
))}
27+
</KeyTag>
28+
</div>
29+
<div className="command-palette-input-row">
30+
<label htmlFor="commandFilter">Filter commands</label>
31+
<input
32+
onChange={onChange}
33+
type="text"
34+
name="commandFilter"
35+
id="commandFilter"
36+
{...props}
37+
/>
38+
</div>
39+
{children}
40+
</div>
41+
);
42+
};

src/CommandPalette/KeyTag.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React, { HTMLAttributes, FC } from 'react';
2+
import classnames from 'classnames';
3+
4+
interface KeyTagProps extends HTMLAttributes<HTMLDivElement> {
5+
mini?: boolean;
6+
}
7+
export const KeyTag: FC<KeyTagProps> = ({ children, mini }) => {
8+
return (
9+
<div className={classnames('command-palette-key-tag', { mini: !!mini })}>
10+
{children}
11+
</div>
12+
);
13+
};

src/CommandPalette/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './CommandPalette';
2+
export * from './CommandButtonRow';

src/Icons/Icons.tsx

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { HTMLAttributes } from 'react';
1+
import React, { HTMLAttributes, FC } from 'react';
22

33
export const Markdown = () => {
44
return (
@@ -41,6 +41,71 @@ export const Delete = () => {
4141
</svg>
4242
);
4343
};
44+
45+
interface MutedProps extends HTMLAttributes<SVGElement> {
46+
muted: boolean;
47+
}
48+
export const Commands: FC<MutedProps> = ({ muted = false }) => (
49+
<svg
50+
width="22px"
51+
height="25px"
52+
viewBox="0 0 22 25"
53+
version="1.1"
54+
xmlns="http://www.w3.org/2000/svg"
55+
>
56+
<title>Group</title>
57+
<g
58+
id="Page-1"
59+
stroke="none"
60+
stroke-width="1"
61+
fill="none"
62+
fill-rule="evenodd"
63+
>
64+
<g id="Group" transform="translate(0.000000, 0.292248)">
65+
<rect
66+
id="Rectangle"
67+
fill-opacity="0"
68+
fill="#D8D8D8"
69+
x="0"
70+
y="0.7077525"
71+
width="22"
72+
height="23"
73+
></rect>
74+
<path
75+
d="M0,20.5077525 L3.96,22.7077525 L11,18.7077525 L18.04,22.7077525 L22,20.5077525 L22,6.8677525 L11.977,1.2549925 C11.37,0.9150025 10.63,0.9150025 10.023,1.2549925 L0,6.8677525 L0,20.5077525 Z"
76+
id="Path"
77+
fill={muted ? '#475059' : '#E2E5E7'}
78+
fill-rule="nonzero"
79+
></path>
80+
<polygon
81+
id="Path"
82+
fill="#8F969D"
83+
fill-rule="nonzero"
84+
points="10.998 12.0560525 5 9.0572525 10.998 5.7077525 16.995 9.0572525"
85+
></polygon>
86+
<polygon
87+
id="Path"
88+
fill="#29313A"
89+
fill-rule="nonzero"
90+
points="5 9.0587625 10.998 12.0529525 10.998 18.4066525 5 15.4078525"
91+
></polygon>
92+
<polygon
93+
id="Path"
94+
fill="#BABFC4"
95+
fill-rule="nonzero"
96+
points="16.995 9.0587625 10.997 12.0529525 10.997 18.4066525 16.995 15.4078525"
97+
></polygon>
98+
<path
99+
d="M10.998,18.4021525 L10.998,12.0437525 M10.998,12.0437525 L5,9.0542525 M10.998,12.0437525 L17,9.0542525"
100+
id="Shape"
101+
stroke="#E2E5E7"
102+
stroke-width="1.5"
103+
></path>
104+
</g>
105+
</g>
106+
</svg>
107+
);
108+
44109
export const AddCell = ({ below = true }) => {
45110
return below ? (
46111
<svg
@@ -81,7 +146,46 @@ export const AddCell = ({ below = true }) => {
81146
stroke-linejoin="round"
82147
/>
83148
</svg>
84-
) : null;
149+
) : (
150+
<svg
151+
width="16"
152+
height="16"
153+
viewBox="0 0 16 16"
154+
fill="none"
155+
xmlns="http://www.w3.org/2000/svg"
156+
>
157+
<path
158+
d="M13.5 7V12.1667C13.5 12.903 12.903 13.5 12.1667 13.5H3.83333C3.09695 13.5 2.5 12.903 2.5 12.1667L2.5 3.83333C2.5 3.09695 3.09695 2.5 3.83333 2.5H9"
159+
stroke="#E2E5E7"
160+
stroke-linecap="round"
161+
stroke-linejoin="round"
162+
/>
163+
<path
164+
d="M13.5 4.5V0.5"
165+
stroke="#E2E5E7"
166+
stroke-linecap="round"
167+
stroke-linejoin="round"
168+
/>
169+
<path
170+
d="M11.5 2.5H15.5"
171+
stroke="#E2E5E7"
172+
stroke-linecap="round"
173+
stroke-linejoin="round"
174+
/>
175+
<path
176+
d="M8 10.5V6.5"
177+
stroke="#E2E5E7"
178+
stroke-linecap="round"
179+
stroke-linejoin="round"
180+
/>
181+
<path
182+
d="M10 7.5L8 5.5L6 7.5"
183+
stroke="#E2E5E7"
184+
stroke-linecap="round"
185+
stroke-linejoin="round"
186+
/>
187+
</svg>
188+
);
85189
};
86190

87191
export const Clear = () => {
@@ -233,3 +337,26 @@ export const Chevron = (props: HTMLAttributes<SVGElement>) => (
233337
/>
234338
</svg>
235339
);
340+
341+
export const Eye = (props: HTMLAttributes<SVGElement>) => (
342+
<svg
343+
width="16"
344+
height="16"
345+
viewBox="0 0 16 16"
346+
fill="none"
347+
xmlns="http://www.w3.org/2000/svg"
348+
>
349+
<path
350+
fill-rule="evenodd"
351+
clip-rule="evenodd"
352+
d="M12.5786 3.96431C11.4224 2.97694 9.89608 2.16675 7.99984 2.16675C5.46121 2.16675 3.58574 3.61884 2.37362 5.00417C1.76412 5.70077 1.30868 6.39519 1.00558 6.91482C0.853717 7.17517 0.739274 7.39306 0.662149 7.54732C0.623568 7.62449 0.594269 7.68584 0.574245 7.72875C0.564231 7.75021 0.556532 7.76707 0.551145 7.779L0.544798 7.79316L0.542924 7.79739L0.542312 7.79878L0.542087 7.79929C0.541997 7.7995 0.541916 7.79968 1 8.00008L0.541916 7.79968C0.486028 7.92744 0.486028 8.07273 0.541916 8.20048L1 8.00008C0.541916 8.20048 0.541997 8.20066 0.542087 8.20087L0.542312 8.20138L0.542924 8.20277L0.544798 8.207L0.551145 8.22116C0.556532 8.23309 0.564231 8.24995 0.574245 8.27141C0.594269 8.31432 0.623568 8.37568 0.662149 8.45284C0.739274 8.6071 0.853717 8.82499 1.00558 9.08534C1.30868 9.60497 1.76412 10.2994 2.37362 10.996C2.83137 11.5192 3.38372 12.0518 4.03066 12.5122L4.74894 11.7939C4.12432 11.3715 3.58341 10.86 3.12621 10.3375C2.56909 9.70077 2.14957 9.06186 1.86937 8.58149C1.72958 8.34184 1.62529 8.14306 1.55659 8.00565L1.5538 8.00008L1.55659 7.99451C1.62529 7.8571 1.72958 7.65832 1.86937 7.41867C2.14957 6.93831 2.56909 6.29939 3.12621 5.66266C4.24735 4.38132 5.8718 3.16675 7.99984 3.16675C9.56041 3.16675 10.8502 3.81992 11.8693 4.67361L12.5786 3.96431ZM10.3754 6.16746C9.82679 5.45733 8.9668 5 8 5C6.34315 5 5 6.34315 5 8C5 8.9668 5.45733 9.82679 6.16746 10.3754L6.88338 9.65951C6.35048 9.30024 6 8.69105 6 8C6 6.89543 6.89543 6 8 6C8.69105 6 9.30024 6.35048 9.65951 6.88338L10.3754 6.16746ZM9.99956 7.95755L10.8613 7.09578C10.9514 7.38112 11 7.68488 11 8C11 9.65685 9.65685 11 8 11C7.68488 11 7.38112 10.9514 7.09578 10.8613L7.95755 9.99956C7.97166 9.99985 7.98581 10 8 10C9.10457 10 10 9.10457 10 8C10 7.98581 9.99985 7.97166 9.99956 7.95755ZM5.65047 12.3066C6.35009 12.632 7.1332 12.8334 7.99984 12.8334C10.1279 12.8334 11.7524 11.6188 12.8736 10.3375C13.4308 9.70075 13.8504 9.06184 14.1306 8.58147C14.2704 8.34182 14.3747 8.14304 14.4434 8.00563L14.4462 8.00008L14.4434 7.99453C14.3747 7.85712 14.2704 7.65834 14.1306 7.41869C13.8504 6.93832 13.4308 6.29941 12.8736 5.66267C12.7847 5.56107 12.6927 5.45988 12.5974 5.35969L13.3047 4.65243C13.4166 4.76954 13.5238 4.88709 13.6262 5.00416C14.2357 5.70075 14.6912 6.39517 14.9944 6.9148C15.1462 7.17515 15.2607 7.39304 15.3378 7.5473C15.3764 7.62446 15.4057 7.68582 15.4257 7.72873C15.4358 7.75019 15.4435 7.76705 15.4488 7.77898L15.4552 7.79313L15.4571 7.79737L15.4577 7.79876L15.4579 7.79927C15.458 7.79948 15.4581 7.79966 15 8.00008C15.4581 8.2005 15.458 8.20069 15.4579 8.20089L15.4577 8.2014L15.4571 8.2028L15.4552 8.20703L15.4488 8.22119C15.4435 8.23311 15.4358 8.24997 15.4257 8.27143C15.4057 8.31434 15.3764 8.3757 15.3378 8.45287C15.2607 8.60712 15.1462 8.82501 14.9944 9.08536C14.6912 9.60499 14.2357 10.2994 13.6262 10.996C12.414 12.3813 10.5385 13.8334 7.99984 13.8334C6.82635 13.8334 5.79457 13.5231 4.90451 13.0526L5.65047 12.3066ZM15 8.00008L15.4581 8.2005C15.514 8.07274 15.514 7.92743 15.4581 7.79966L15 8.00008Z"
353+
fill="#E2E5E7"
354+
/>
355+
<path
356+
d="M14.9993 1L0.666016 15.3333"
357+
stroke="#E2E5E7"
358+
stroke-linecap="round"
359+
stroke-linejoin="round"
360+
/>
361+
</svg>
362+
);

stories/CommandPalette.stories.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { action } from '@storybook/addon-actions';
3+
import { CommandPalette, CommandButtonRow, Props } from '../src/CommandPalette';
4+
import { Play, Markdown, AddCell, Eye } from '../src/Icons';
5+
6+
export default {
7+
title: 'CommandPalette',
8+
};
9+
10+
export const Standard = (props?: Partial<Props>) => (
11+
<CommandPalette
12+
placeholder="Filter commands"
13+
onChange={action('ON CHANGE')}
14+
shortCut={['⌘', 'H']}
15+
>
16+
<CommandButtonRow
17+
onClick={action('Run Cell')}
18+
shortCut={['Shift', 'Enter']}
19+
>
20+
<Play /> Run Cell
21+
</CommandButtonRow>
22+
<CommandButtonRow
23+
onClick={action('Add Cell Below')}
24+
shortCut={['Shift', 'M']}
25+
>
26+
<AddCell below={false} />
27+
Add Cell Below
28+
</CommandButtonRow>
29+
<CommandButtonRow
30+
onClick={action('Add Cell Above')}
31+
shortCut={['Shift', 'Backspace']}
32+
>
33+
<AddCell />
34+
Add Cell Above
35+
</CommandButtonRow>
36+
<CommandButtonRow
37+
onClick={action('Convert to Markdown')}
38+
shortCut={['⌘', 'M']}
39+
>
40+
<Markdown />
41+
Convert to Markdown
42+
</CommandButtonRow>
43+
<CommandButtonRow onClick={action('Hide Output')} shortCut={['Shift', 'O']}>
44+
<Eye />
45+
Hide Output
46+
</CommandButtonRow>
47+
<CommandButtonRow onClick={action('Hide Input')} shortCut={['Shift', 'I']}>
48+
<Eye />
49+
Hide Input
50+
</CommandButtonRow>
51+
</CommandPalette>
52+
);

0 commit comments

Comments
 (0)