From 65d502f6a3a128809e32752db2519c1256847da5 Mon Sep 17 00:00:00 2001 From: Asa Baylus Date: Thu, 19 Dec 2019 12:22:07 -0500 Subject: [PATCH 01/36] add vscode theme --- README.md | 9 +- examples/sampleVSCodeCommand.css | 63 + examples/sampleVSCodeCommand.js | 25 + examples/sampleVSCodeCommandPalette.js | 94 + src/__mocks__/categories.js | 27 + src/__mocks__/global_commands.js | 86 + .../command-palette.test.js.snap | 17018 +++++++++++++++- src/command-palette.test.js | 93 +- stories/index.stories.js | 7 + themes/vscode-theme.js | 19 + themes/vscode.css | 127 + 11 files changed, 16824 insertions(+), 744 deletions(-) create mode 100644 examples/sampleVSCodeCommand.css create mode 100644 examples/sampleVSCodeCommand.js create mode 100644 examples/sampleVSCodeCommandPalette.js create mode 100644 src/__mocks__/categories.js create mode 100644 src/__mocks__/global_commands.js create mode 100644 themes/vscode-theme.js create mode 100644 themes/vscode.css diff --git a/README.md b/README.md index 102bfa99..9f8e6f7f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,9 @@ const commands = [{ * ```placeholder``` a _string_ that contains a short text description which is displayed inside the the input field until the user provides input. Defaults to "Type a command". -* ```hotKeys``` a _string_ that contains a keyboard shortcut for opening/closing the palette. Defaults to "_command+shift+p_". Uses [mousetrap key combos](https://craig.is/killing/mice) +* ```hotKeys``` a _string_ that contains a keyboard shortcut for opening/closing the palette. Defaults to "_command+shift+p_". Uses [mousetrap key combos](https://craig.is/killing/mice). + +* ```defaultInputValue``` a _string_ that determines the value of the text in the input field. By default the defaultInputValue is an empty string. * ```defaultInputValue``` a _string_ that determines the value of the text in the input field. By default the defaultInputValue is an empty string. @@ -84,6 +86,7 @@ const commands = [{ allowTypo: true, scoreFn: null ``` + * ```onChange``` a function that's called when the input value changes. It returns the current value of the input field. ```js @@ -121,7 +124,7 @@ const commands = [{ /> ``` -* ```onRequestClose``` a function that will be run when the modal is requested to be closed (either by clicking on overlay or pressing ESC) +* ```onRequestClose``` a function that will be run when the modal is requested to be closed (either by clicking on overlay or pressing ESC). Note: It is not called if _open_ is changed by other means. Passes through to the [react-modal prop](http://reactcommunity.org/react-modal/examples/on_request_close.html). ```js @@ -191,7 +194,7 @@ Note: It is not called if _open_ is changed by other means. Passes through to th * ```showSpinnerOnSelect``` a _boolean_ which displays a loading indicator immediatley after a command has been selected. When true the spinner is enabled when false the spinner is disabled. Useful when dynamicaly loading lists of a commands based upon user selections. Setting both _showSpinnerOnSelect_ and _closeOnSelect_ to false will keep the palette open and allow a new list of commands to be loaded, see the [dynamic lists example](https://codesandbox.io/s/react-command-palette-dynamic-lists-p2xo9?fontsize=14&hidenavigation=1&theme=dark). * ```theme``` enables you to apply a sample or custom look-n-feel. - Two themes are included with the command palette, Chrome and Atom. The CommandPalette comes with the Atom theme enabled default. + Several themes are included with the command palette, Chrome, VS Code and Atom. The CommandPalette comes with the Atom theme enabled default. 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. diff --git a/examples/sampleVSCodeCommand.css b/examples/sampleVSCodeCommand.css new file mode 100644 index 00000000..173d8596 --- /dev/null +++ b/examples/sampleVSCodeCommand.css @@ -0,0 +1,63 @@ + +.vscode-category { + float: left; + margin-right: 2px; + color: rgb(204, 204, 204); + margin-right: 6px; + border-radius: 2px; + padding: 1.2px 3px; + font-weight: 450; +} +.vscode-shortcut { + float: right; + margin-right: 2px; +} + +.vscode-name { + color: rgb(153, 153, 153); +} + +.vscode-hidden { + display: none; +} + +.vscode-shortcut > kbd { +} + +.vscode-shortcut-part { + color: rgb(204, 204, 204) ; + background-color: rgb(63, 63, 64); + border-radius: 3px; + display: inline-block; + font-family: inherit; + font-size: .85em; + letter-spacing: 0.1em; + line-height: 1.8; + margin-left: 0.45454545em; + padding: 0 0.375em; + margin-top: -0.375em; + -webkit-box-shadow: inset 0px -2px 2px 0px rgba(163,163,163,0.15); + -moz-box-shadow: inset 0px -2px 2px 0px rgba(163,163,163,0.15); + box-shadow: inset 0px -2px 2px 0px rgba(163,163,163,0.15); +} + +.vscode-suggestionHighlighted .vscode-shortcut-part, +.vscode-suggestionFirst .vscode-shortcut-part { + background-color: rgb(27, 61, 84); +} + +.vscode-category.Command { + background: rgb(67, 130, 207); +} +.vscode-category.Navigate { + background: rgb(165, 22, 134); +} +.vscode-category.Network { + background: rgb(46, 41, 194); +} +.vscode-category.System { + background: rgb(49, 177, 79); +} +.vscode-category.Drawer { + background: rgb(206, 64, 206); +} \ No newline at end of file diff --git a/examples/sampleVSCodeCommand.js b/examples/sampleVSCodeCommand.js new file mode 100644 index 00000000..657808dc --- /dev/null +++ b/examples/sampleVSCodeCommand.js @@ -0,0 +1,25 @@ +import React from "react"; +import "./sampleVSCodeCommand.css"; + +export default function sampleVSCodeCommand(suggestion) { + const { name, highlight, shortcut, category } = suggestion; + let shortcutsArray = []; + if (typeof shortcut !== 'undefined') { + shortcutsArray = shortcut.toString().split(" ") || ""; + } + return ( +
+ {category} + {highlight ? ( + + ) : ( + {name} + )} + + {shortcutsArray.map(item => { + return {item}; + })} + +
+ ); +} diff --git a/examples/sampleVSCodeCommandPalette.js b/examples/sampleVSCodeCommandPalette.js new file mode 100644 index 00000000..af45ee12 --- /dev/null +++ b/examples/sampleVSCodeCommandPalette.js @@ -0,0 +1,94 @@ +import React, { Component } from 'react' +import CommandPalette from '../src/command-palette'; +import sampleVSCodeCommand from './sampleVSCodeCommand'; +import vscode from "../themes/vscode-theme"; + +// sample lists of commands, we can use these to demonstrate how to dynamicaly swap out +// the contents of the command palette as the user navigates from one list to another. +import categories from "../src/__mocks__/categories"; +import globalCommands from '../src/__mocks__/global_commands'; + +export class DynamicListCommandPalette extends Component { + constructor(props) { + super(props); + + this.state = { + commands: categories, + showSpinnerOnSelect: false + }; + + this.handleChange = this.handleChange.bind(this); + this.handleSelect = this.handleSelect.bind(this); + } + + // The VS code theme has "action" command keys such as "?, :, >, #". When a user + // types these characters the palette immediately displays the resulting "action" + // commands. The user need not select or press enter. Simply typing the character + // trigger the action command. + handleChange(value) { + // given the user enters a special character eg "?" or ">" + // it should dynamicaly load the appropriate list of commands. + // note that the user will not need to press the enter key or select + // the special character to trigger this action. Where navigating sublists, eg: "Command >> Start All Data Imports" requires the user to select or press enter to navigate to the next sublist + if (value === "?" || value == ">") { + this.setState({ + showSpinnerOnSelect: false, + commands: (function() { + if (value === "?") { + return globalCommands; + } + + if (value === ">") { + return categories; + } + + if (value === "") { + return []; + } + + // else { + // allCommands.filter(command => { + // if (command.category !== value.name) { + // return false; + // } + // return command; + // }); + // } + })() + }); + } + } + + // The action command keys also has a plain language command equivalent ex: "> Show + // and Run Commands". To trigger these actions the user must select the command in + // the UI by clicking it or pressing enter on the keyboard. + handleSelect(command) { + if(command.name === "Show and Run Commands") { + this.setState({ + showSpinnerOnSelect: false, + commands: categories + }); + } + } + + render() { + return ( +
+ +
+ ); + } + } + +export default DynamicListCommandPalette diff --git a/src/__mocks__/categories.js b/src/__mocks__/categories.js new file mode 100644 index 00000000..2c263317 --- /dev/null +++ b/src/__mocks__/categories.js @@ -0,0 +1,27 @@ +export default [ + { + id: 1, + name: "Command", + command() {} + }, + { + id: 2, + name: "Network", + command() {} + }, + { + id: 3, + name: "Navigate", + command() {} + }, + { + id: 4, + name: "System", + command() {} + }, + { + id: 5, + name: "Drawer", + command() {} + } +]; diff --git a/src/__mocks__/global_commands.js b/src/__mocks__/global_commands.js new file mode 100644 index 00000000..f1f15ccb --- /dev/null +++ b/src/__mocks__/global_commands.js @@ -0,0 +1,86 @@ +export default [ + { + id: 1, + name: "Go to File", + category: "…", + command() {} + }, + { + id: 2, + name: "Go to Symbol in Workspace", + category: "#", + command() {} + }, + { + id: 3, + name: "Show and Run Commands", + category: ">", + command() {} + }, + { + id: 4, + name: "Debug Configuration", + category: "debug", + command() {} + }, + { + id: 5, + name: "Show All Opened Editors", + category: "edit", + command() {} + }, + { + id: 6, + name: "Show Editors in Active Group", + category: "edit active", + command() {} + }, + { + id: 7, + name: "Manage Extensions", + category: "ext", + command() {} + }, + { + id: 8, + name: "Install Gallery Extensions", + category: "ext install", + command() {} + }, + { + id: 9, + name: "Run Task", + category: "task", + command() {} + }, + { + id: 10, + name: "Show All Opened Terminals", + category: "term", + command() {} + }, + { + id: 11, + name: "Open View", + category: "view", + command() {} + }, + { + id: 12, + name: "Go to Line", + category: ":", + command() {} + }, + { + id: 13, + name: "Go to Symbol in File", + category: "@", + command() {} + }, + { + id: 14, + name: "Go to Symbol in File by Category", + category: "@:", + command() {} + } +]; diff --git a/src/__snapshots__/command-palette.test.js.snap b/src/__snapshots__/command-palette.test.js.snap index aec2caaf..254f6778 100644 --- a/src/__snapshots__/command-palette.test.js.snap +++ b/src/__snapshots__/command-palette.test.js.snap @@ -1416,6 +1416,81 @@ exports[`Command List renders a command 1`] = ` +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
@@ -5652,143 +5727,218 @@ exports[`Command List renders a command 1`] = ` role="dialog" tabindex="-1" > -
-
-
+ - -
-
    -
  • -
    - - Start All Data Imports - -
    -
  • -
  • -
    - - Stop All Data Imports - -
    -
  • -
  • -
    - - Delete All Tenant - -
    -
  • -
  • -
    - - Go offline - -
    -
  • -
  • -
    - - Go online - -
    -
  • -
  • -
    - - Jump to Tenant - -
    -
  • -
  • -
    - - View Logs - -
    -
  • -
-
-
+ Loading... + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ @@ -10465,6 +10615,81 @@ exports[`Opening the palette displays the suggestion list 1`] = `
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
@@ -13175,6 +13400,81 @@ exports[`Opening the palette displays the suggestion list 1`] = `
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
@@ -19655,6 +19955,81 @@ exports[`props.reactModalParentSelector should render render reat-modal in the t
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
@@ -21759,143 +22134,218 @@ exports[`props.reactModalParentSelector should render render reat-modal in the t role="dialog" tabindex="-1" > -
-
-
+ - -
-
    -
  • -
    - - Start All Data Imports - -
    -
  • -
  • -
    - - Stop All Data Imports - -
    -
  • -
  • -
    - - Delete All Tenant - -
    -
  • -
  • -
    - - Go offline - -
    -
  • -
  • -
    - - Go online - -
    -
  • -
  • -
    - - Jump to Tenant - -
    -
  • -
  • -
    - - View Logs - -
    -
  • -
-
-
+ Loading... + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ @@ -26274,13 +26724,15049 @@ exports[`props.renderCommand should render a custom command component 1`] = ` >
+ class="atom-header" + /> +
+ +
+
    +
  • +
    + + Start All Data Imports + +
    +
  • +
  • +
    + + Stop All Data Imports + +
    +
  • +
  • +
    + + Delete All Tenant + +
    +
  • +
  • +
    + + Go offline + +
    +
  • +
  • +
    + + Go online + +
    +
  • +
  • +
    + + Jump to Tenant + +
    +
  • +
  • +
    + + View Logs + +
    +
  • +
+
+
+
+
+
+
+ + } + ariaHideApp={true} + bodyOpenClassName="ReactModal__Body--open" + className="atom-modal" + closeTimeoutMS={1} + contentLabel="Command Palette" + defaultStyles={ + Object { + "content": Object { + "WebkitOverflowScrolling": "touch", + "background": "#fff", + "border": "1px solid #ccc", + "borderRadius": "4px", + "bottom": "40px", + "left": "40px", + "outline": "none", + "overflow": "auto", + "padding": "20px", + "position": "absolute", + "right": "40px", + "top": "40px", + }, + "overlay": Object { + "backgroundColor": "rgba(255, 255, 255, 0.75)", + "bottom": 0, + "left": 0, + "position": "fixed", + "right": 0, + "top": 0, + }, + } + } + isOpen={true} + onAfterOpen={[Function]} + onRequestClose={[Function]} + overlayClassName="atom-overlay" + parentSelector={[Function]} + portalClassName="ReactModalPortal" + role="dialog" + shouldCloseOnEsc={true} + shouldCloseOnOverlayClick={true} + shouldFocusAfterRender={true} + shouldReturnFocusAfterClose={true} + style={ + Object { + "content": Object {}, + "overlay": Object {}, + } + } + > +
+
+
+
+
+
+ + +
+ +
+ +
    + +
  • + + +
    + + Start All Data Imports + +
    +
    +
    +
  • +
    + +
  • + + +
    + + Stop All Data Imports + +
    +
    +
    +
  • +
    + +
  • + + +
    + + Delete All Tenant + +
    +
    +
    +
  • +
    + +
  • + + +
    + + Go offline + +
    +
    +
    +
  • +
    + +
  • + + +
    + + Go online + +
    +
    +
    +
  • +
    + +
  • + + +
    + + Jump to Tenant + +
    +
    +
    +
  • +
    + +
  • + + +
    + + View Logs + +
    +
    +
    +
  • +
    +
+
+
+
+
+
+
+
+
+ + + + + +`; + +exports[`props.theme should render a chrome theme 1`] = ` + +
+ + + + + +