Skip to content

Commit 0236de2

Browse files
authored
Add support custom themes (#83)
* add chrome theme * add atom theme * add props.theme and customizable styles
1 parent c27a16b commit 0236de2

33 files changed

+9139
-4966
lines changed

README.md

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ $ npm i --save react-command-palette
3535

3636
Import into your react app and pass commands
3737

38-
```js
38+
```jsx
3939
import CommandPalette from 'react-command-palette';
4040

4141
const commands = [{
@@ -98,9 +98,7 @@ const commands = [{
9898
9999
* ```renderCommand``` a _React.func_. By default, react-command-palette will render the suggestion.name_ for each command. However, if passed a custom react component _renderCommand_ will display the command using any template you can imageine. The _renderCommand_ code signature follows the same coding pattern defined by react-autosuggest's renderSuggestion property.
100100
101-
```js
102-
import "./commandStyles.css"; // or use inline styles
103-
101+
```jsx
104102
function RenderCommand(suggestion) {
105103
// A suggestion object will be passed to your custom component for each command
106104
const { id, color, name } = suggestion;
@@ -131,36 +129,76 @@ const commands = [{
131129
132130
Note: the _suggestion.hightlight_ will be passed and contains the rendered markup from (fuzzysort)[farzher/fuzzysort#fuzzysorthighlightresult-openb-closeb], see the ```options``` prop.
133131
134-
See [a full example](examples/sampleCustomCommand.js)
132+
See [a full example](examples/sampleAtomCommand.js)
135133
136134
*Important:* _renderCommand_ must be a pure function (react-autosuggest, upon which this is based will optimize rendering performance based on this assumption).
137135
138136
* ```maxDisplayed``` a _number_ between 1 and 500 that determines the maxium number of commands that will be rendered on screen. Defaults to 7
139137
140138
* ```spinner``` a _string_ or a _React.ComponentType_ that is displayed when the user selects an item. If a custom spinner is not set then the default spinner will be used. If a custom component or string is provided then it will automatically be wrapped inside a div with a _role="status"_ attribute. If a component is provided then it will be be wrapped in a div that also contains a sibling node with a div contain "Loading..." visible only to screen readers.
141139
142-
* ```trigger``` a _string_ or a _React.ComponentType_ the opens the command palette when clicked. If a custom trigger is not set then by default a button will be used. If a custom component or string is provided then it will automatically be wrapped inside an accessible div that will allow it be keyboard accessible, clickable and focusable for assistive technologies.
140+
* ```theme``` enables you to apply a sample or custom look-n-feel.
141+
Two themes are included with the command palette, Chrome and Atom. The CommandPalette comes with the Atom theme enabled default.
143142
144-
Example with a component:
145-
```
146-
// jsx trigger prop
147-
<CommandPalette commands={data} trigger={<b>Click Me!</b>}>
148-
149-
// html generated trigger
150-
<div role="button" tabindex="0"><b>Click Me!</b></div>
151-
```
143+
Creating a new theme is also possible. There are four base components that should be styled, the _trigger_, _spinner_, _react-modal_ and _react-autosuggest_ components. All four can be styled at once via the `theme` prop.
144+
145+
There are two steps to styling. First create a theme object to map your custom class names to their associated components. Then add styles that use the rules mapped in the `theme` prop.
146+
147+
For example, to style the CommandPalette using CSS Modules, do:
152148
153-
Example with a string:
149+
```css
150+
/* theme.css */
151+
.my-modal { ... }
152+
.my-overlay { ... }
153+
.my-container { ... }
154+
.my-input { ... }
155+
...
154156
```
155-
// jsx trigger prop
156-
<CommandPalette commands={data} trigger="Click Me!">
157157
158-
// html generated trigger
159-
<div role="button" tabindex="0">Click Me!</div>
158+
```jsx
159+
/* my-component.js */
160+
const theme = {
161+
modal: "my-modal",
162+
overlay: "my-overlay",
163+
container: "my-container",
164+
content: "my-content",
165+
input: "my-input",
166+
...
167+
}
168+
169+
import theme from 'theme.css';
170+
171+
<CommandPalette theme={theme} ... />
160172
```
161-
162173
163-
When the trigger is clicked it will open the command palette, no custom handlers or events are required.
174+
When not specified, `theme` defaults to the included _Atom_ theme. Complete sample themes are provided, see: [Chrome](examples/sampleChromeTheme.md) and [Atom](examples/sampleAtomTheme.md)
175+
176+
The following picture illustrates how `theme` keys correspond to CommandPalette DOM structure:
177+
178+
![DOM structure](./docs/images/dom-structure.png)
179+
180+
```trigger``` a _string_ or a _React.ComponentType_ the opens the command palette when clicked. If a custom trigger is not set then by default a button will be used. If a custom component or string is provided then it will automatically be wrapped inside an accessible div that will allow it be keyboard accessible, clickable and focusable for assistive technologies.
181+
182+
Example with a component:
183+
```
184+
// jsx trigger prop
185+
<CommandPalette commands={data} trigger={<b>Click Me!</b>}>
186+
187+
// html generated trigger
188+
<div role="button" tabindex="0"><b>Click Me!</b></div>
189+
```
190+
191+
Example with a string:
192+
```
193+
// jsx trigger prop
194+
<CommandPalette commands={data} trigger="Click Me!">
195+
196+
// html generated trigger
197+
<div role="button" tabindex="0">Click Me!</div>
198+
```
199+
200+
201+
When the trigger is clicked it will open the command palette, no custom handlers or events are required.
164202
165203
## Developer Setup
166204
```

docs/images/dom-structure.afdesign

4.93 MB
Binary file not shown.

docs/images/dom-structure.png

67.1 KB
Loading

examples/sampleAtomCommand.css

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
.atom-category {
3+
color: #fff;
4+
margin-right: 6px;
5+
border-radius: 2px;
6+
padding: 1.2px 3px;
7+
}
8+
.atom-shortcut {
9+
float: right;
10+
margin-right: 2px;
11+
display: inline-block;
12+
margin-left: 0.45454545em;
13+
padding: 0 0.375em;
14+
line-height: 2;
15+
margin-top: -0.375em;
16+
font-family: inherit;
17+
font-size: 1em;
18+
letter-spacing: 0.1em;
19+
border-radius: 3px;
20+
color: #fff;
21+
background-color: #4d78cc;
22+
}
23+
24+
.atom-category.Command {
25+
background: rgb(67, 130, 207);
26+
}
27+
.atom-category.Navigate {
28+
background: rgb(165, 22, 134);
29+
}
30+
.atom-category.Network {
31+
background: rgb(46, 41, 194);
32+
}
33+
.atom-category.System {
34+
background: rgb(49, 177, 79);
35+
}
36+
.atom-category.Drawer {
37+
background: rgb(206, 64, 206);
38+
}

examples/sampleAtomCommand.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from "react";
2+
import "./sampleAtomCommand.css";
3+
4+
export default function sampleAtomCommand(suggestion) {
5+
const { name, highlight, shortcut } = suggestion;
6+
return (
7+
<div className="atom-item">
8+
{highlight ? (
9+
<span dangerouslySetInnerHTML={{ __html: highlight }} />
10+
) : (
11+
<span>{name}</span>
12+
)}
13+
<kbd className="atom-shortcut">{shortcut}</kbd>
14+
</div>
15+
);
16+
}

examples/sampleAtomTheme.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#### Building an Atom inspired Command Palette
2+
3+
The easiest way to do this, is to do nothing because Atom is the default theme. However you may wish to tweak the theme to better meet your projects needs.
4+
5+
The CommandPalette comes with the Atom theme by default. There are four base components that need to be styled, the _trigger_, _spinner_, _react-modal_ and _react-autosuggest_ components. All three can be styled at once via the `theme` prop.
6+
7+
Try it on [CodeSandbox](https://codesandbox.io/s/hfqjn)
8+
9+
To custom style the CommandPalette you'll need a CSS file with rules that map to your _theme_ props' key/value pairs, ex:
10+
11+
```js
12+
import React from "react";
13+
import CommandPalette from "react-command-palette";
14+
15+
// map CSS class names to CommandPalette components
16+
// Note that we dont need to do this for the Atom theme because its the default
17+
// When not otherwise specified, the theme defaults to:
18+
// const atom = {
19+
// modal: "atom-modal",
20+
// overlay: "atom-overlay",
21+
// container: "atom-container",
22+
// content: "atom-content",
23+
// containerOpen: "atom-containerOpen",
24+
// input: "atom-input",
25+
// inputOpen: "atom-inputOpen",
26+
// inputFocused: "atom-inputFocused",
27+
// spinner: "atom-spinner",
28+
// suggestionsContainer: "atom-suggestionsContainer",
29+
// suggestionsContainerOpen: "atom-suggestionsContainerOpen",
30+
// suggestionsList: "atom-suggestionsList",
31+
// suggestion: "atom-suggestion",
32+
// suggestionFirst: "atom-suggestionFirst",
33+
// suggestionHighlighted: "atom-suggestionHighlighted",
34+
// trigger: "atom-trigger"
35+
// }
36+
37+
// or use a theme from those provided ...
38+
import atom from "./node_modules/react-command-palette/themes/atom-theme";
39+
import "./node_modules/react-command-palette/themes/atom.css";
40+
```
41+
42+
The layout for each of the commands that appears in the command list can also be customized. For instance, the _Atom_ command palette has a list of commands that includes a command and associated keyboard shortcut when applicable. Because the default command is limited to just displaying the command's _name_ you'll need to make your own _renderCommand_ like the component included in [_sampleAtomCommand.js_](../examples/sampleAtomCommand.js).
43+
44+
The [_sampleAtomCommands.css_](../examples/sampleAtomCommand.css) file must be imported into the _renderCommand_ component. Of coure you can use your imagination to create any layout you like for each command. Note that `suggestion.highlight` will contain the raw HTML of the matching value.
45+
46+
```jsx
47+
import React from "react";
48+
import "./sampleAtomCommand.css";
49+
50+
function sampleAtomCommand(suggestion) {
51+
const { name, highlight, shortcut } = suggestion;
52+
return (
53+
<div className="atom-suggestion">
54+
{highlight ? (
55+
<span dangerouslySetInnerHTML={{ __html: highlight }} />
56+
) : (
57+
<span>{name}</span>
58+
)}
59+
<kbd className="atom-shortcut">{shortcut}</kbd>
60+
</div>
61+
);
62+
}
63+
64+
const commands = [{
65+
id: 1,
66+
shortcut: '⌘ Esc',
67+
name: "Close pannel",
68+
command() {
69+
// do something
70+
}
71+
} ...];
72+
73+
React.render(
74+
<CommandPalette theme={atom}
75+
commands={commands}
76+
renderCommand={sampleAtomCommand} />,
77+
document.getElementById('root')
78+
)
79+
```
80+

examples/sampleChromeCommand.css

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.chrome-category {
2+
color: #fff;
3+
margin-right: 6px;
4+
border-radius: 2px;
5+
padding: 1.2px 3px;
6+
}
7+
8+
.chrome-shortcut {
9+
float: right;
10+
margin-right: 2px;
11+
color: rgb(150, 150, 150);
12+
display: inline-block;
13+
}
14+
15+
.chrome-category.Command {
16+
background: rgb(0, 188, 212);
17+
}
18+
19+
.chrome-category.System {
20+
background: rgb(76, 174, 80);
21+
}
22+
23+
.chrome-category.Network {
24+
background: rgb(63, 81, 181);
25+
}
26+
27+
.chrome-category.Navigate {
28+
background: rgb(255, 182, 0);
29+
}
30+
31+
.chrome-category.Drawer {
32+
background: rgb(0, 149, 136);
33+
}

examples/sampleChromeCommand.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from "react";
2+
import "./sampleChromeCommand.css";
3+
4+
export default function sampleChromeCommand(suggestion) {
5+
const { name, highlight, category, shortcut } = suggestion;
6+
return (
7+
<div className="chrome-suggestion">
8+
<span className={`chrome-category ${category}`}>{category}</span>
9+
{highlight ? (
10+
<span dangerouslySetInnerHTML={{ __html: highlight }} />
11+
) : (
12+
<span>{name}</span>
13+
)}
14+
<kbd className="chrome-shortcut">{shortcut}</kbd>
15+
</div>
16+
);
17+
}

examples/sampleChromeTheme.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#### Building a Chrome inspired Command Palette
2+
3+
A Chrome theme is available in the the _themes_ directory. There are four base components that need to be styled, _trigger_, _spinner_, _react-modal_ and _react-autosuggest_ components. All three can be styled at once via the `theme` prop.
4+
5+
For a complete example see this [CodeSandbox](https://codesandbox.io/s/gfx7l)
6+
7+
The simplest way to get started is to _import_ the Chrome [theme](../themes/chrome-theme.js) and [CSS](../themes/chrome.css) from the examples directory as follows:
8+
9+
```js
10+
import React from "react";
11+
import CommandPalette from "react-command-palette";
12+
13+
// import the theme from those provided ...
14+
import chrome from "./node_modules/react-command-palette/themes/chrome-theme";
15+
16+
// then import the CSS
17+
import "./node_modules/react-command-palette/themes/chrome.css";
18+
```
19+
20+
Alternatively to custom style the CommandPalette you'll need a CSS file with rules that map to your _theme_ props' key/value pairs, ex:
21+
22+
```js
23+
// map CSS class names to CommandPalette components
24+
const chrome = {
25+
modal: "chrome-modal",
26+
overlay: "chrome-overlay",
27+
container: "chrome-container",
28+
content: "chrome-content",
29+
containerOpen: "chrome-containerOpen",
30+
input: "chrome-input",
31+
inputOpen: "chrome-inputOpen",
32+
inputFocused: "chrome-inputFocused",
33+
spinner: "chrome-spinner",
34+
suggestionsContainer: "chrome-suggestionsContainer",
35+
suggestionsContainerOpen: "chrome-suggestionsContainerOpen",
36+
suggestionsList: "chrome-suggestionsList",
37+
suggestion: "chrome-suggestion",
38+
suggestionFirst: "chrome-suggestionFirst",
39+
suggestionHighlighted: "chrome-suggestionHighlighted",
40+
trigger: "chrome-trigger"
41+
}
42+
```
43+
44+
The layout for each of the commands that appears in the command list can also be customized. For instance, _Chrome dev tools_ command palette has a list of commands that includes a category, command and associated keyboard shortcut when applicable. Because the default command is limited to just displaying the command's _name_ you'll need to make your own _renderCommand_ like the component included in [_sampleChromeCommand.js_](../examples/sampleChromeCommand.js).
45+
46+
The [_sampleChromeCommands.css_](../examples/sampleChromeCommand.css) file must be imported into the _renderCommand_ component. Of coure you can use your imagination to create any layout you like for each command. Note that `suggestion.highlight` will contain the raw HTML of the matching value.
47+
48+
```jsx
49+
import React from "react";
50+
import "./sampleChromeCommand.css";
51+
52+
export default function sampleChromeCommand(suggestion) {
53+
const { name, highlight, category, shortcut } = suggestion;
54+
return (
55+
<div className="chrome-suggestion">
56+
<span className={`chrome-category ${category}`}>{category}</span>
57+
{highlight ? (
58+
<span dangerouslySetInnerHTML={{ __html: highlight }} />
59+
) : (
60+
<span>{name}</span>
61+
)}
62+
<kbd className="chrome-shortcut">{shortcut}</kbd>
63+
</div>
64+
);
65+
}
66+
67+
68+
const commands = [{
69+
id: 1,
70+
shortcut: '⌘ Esc',
71+
name: "Close pannel",
72+
command() {
73+
// do something
74+
}
75+
} ...];
76+
77+
React.render(
78+
<CommandPalette theme={chrome}
79+
commands={commands}
80+
renderCommand={sampleChromeCommand} />,
81+
document.getElementById('root')
82+
)
83+
```
84+

0 commit comments

Comments
 (0)