mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-18 04:21:12 +02:00
Command pallete: first draft!
This commit is contained in:
29
src/commandPaletteService.js
Normal file
29
src/commandPaletteService.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { deferred } from './deferred';
|
||||
import { log } from 'util';
|
||||
|
||||
export const SWITCH_FILE_EVENT = 'switchFileEvent';
|
||||
export const OPEN_SAVED_CREATIONS_EVENT = 'openSavedCreationsEvent';
|
||||
export const SAVE_EVENT = 'saveEvent';
|
||||
|
||||
export const commandPaletteService = {
|
||||
subscriptions: {},
|
||||
subscribe(eventName, callback) {
|
||||
console.log('subscribed for ', eventName);
|
||||
this.subscriptions[eventName] = this.subscriptions[eventName] || [];
|
||||
this.subscriptions[eventName].push(callback);
|
||||
return () => {
|
||||
console.log('Unsubscribing ', eventName);
|
||||
this.subscriptions[eventName].splice(
|
||||
this.subscriptions[eventName].indexOf(callback),
|
||||
1
|
||||
);
|
||||
};
|
||||
},
|
||||
publish(eventName, ...args) {
|
||||
console.log('published ', eventName, args);
|
||||
const callbacks = this.subscriptions[eventName] || [];
|
||||
callbacks.forEach(callback => {
|
||||
callback.apply(null, args);
|
||||
});
|
||||
}
|
||||
};
|
22
src/commands.js
Normal file
22
src/commands.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
OPEN_SAVED_CREATIONS_EVENT,
|
||||
SAVE_EVENT
|
||||
} from './commandPaletteService';
|
||||
|
||||
export const commands = [
|
||||
{
|
||||
name: 'Open Creation',
|
||||
event: OPEN_SAVED_CREATIONS_EVENT,
|
||||
keyboardShortcut: 'Cmd+O'
|
||||
},
|
||||
{
|
||||
name: 'Save Creation',
|
||||
event: SAVE_EVENT,
|
||||
keyboardShortcut: 'Cmd+S'
|
||||
},
|
||||
{
|
||||
name: 'Add Library',
|
||||
run: '',
|
||||
keyboardShortcut: 'Cmd+F'
|
||||
}
|
||||
];
|
96
src/components/CommandPalette.jsx
Normal file
96
src/components/CommandPalette.jsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { h, Component } from 'preact';
|
||||
import Modal from './Modal';
|
||||
import { AutoFocusInput } from './common';
|
||||
import { commands } from '../commands';
|
||||
import {
|
||||
commandPaletteService,
|
||||
SWITCH_FILE_EVENT
|
||||
} from '../commandPaletteService';
|
||||
import { FileIcon } from './FileIcon';
|
||||
|
||||
function getFolder(filePath) {
|
||||
const split = filePath.split('/');
|
||||
if (split.length > 1) {
|
||||
split.length = split.length - 1;
|
||||
return split.join('/');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function Row({ item, onClick }) {
|
||||
return (
|
||||
<li>
|
||||
<button style="background:0;border:0;" onClick={onClick}>
|
||||
{item.path ? <FileIcon file={item} /> : null}
|
||||
{item.name}
|
||||
{item.path ? (
|
||||
<span style="color:#ccc;margin-left:10px;font-size:0.8em;">
|
||||
{getFolder(item.path)}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
export class CommandPalette extends Component {
|
||||
state = { list: [], search: '' };
|
||||
componentDidUpdate(previousProps) {
|
||||
if (this.props.show && !previousProps.show) {
|
||||
this.state.search = '';
|
||||
|
||||
this.isCommandMode = this.props.isCommandMode;
|
||||
if (this.isCommandMode) {
|
||||
this.setState({ search: '>' });
|
||||
}
|
||||
|
||||
this.setState({
|
||||
list: this.getFilteredList()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getFilteredList(search = '') {
|
||||
const list = this.isCommandMode ? commands : this.props.files;
|
||||
return list.filter(
|
||||
item =>
|
||||
item.name
|
||||
.toLowerCase()
|
||||
.indexOf(this.isCommandMode ? search.substr(1) : search) !== -1
|
||||
);
|
||||
}
|
||||
|
||||
inputHandler(e) {
|
||||
const search = e.target.value;
|
||||
this.setState({ search });
|
||||
if (search.indexOf('>') === 0) {
|
||||
this.isCommandMode = true;
|
||||
}
|
||||
this.setState({
|
||||
list: this.getFilteredList(search)
|
||||
});
|
||||
}
|
||||
optionClickHandler(option) {
|
||||
commandPaletteService.publish(
|
||||
option.path ? SWITCH_FILE_EVENT : option.event,
|
||||
option
|
||||
);
|
||||
this.props.closeHandler();
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Modal show={this.props.show} closeHandler={this.props.closeHandler}>
|
||||
<AutoFocusInput
|
||||
value={this.state.search}
|
||||
onInput={this.inputHandler.bind(this)}
|
||||
/>
|
||||
<ul style="padding:0;list-style:none;">
|
||||
{this.state.list.map(item => (
|
||||
<Row
|
||||
item={item}
|
||||
onClick={this.optionClickHandler.bind(this, item)}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
@@ -17,6 +17,10 @@ import 'codemirror/mode/meta';
|
||||
import { deferred } from '../deferred';
|
||||
import { SidePane } from './SidePane';
|
||||
import { Console } from './Console';
|
||||
import {
|
||||
commandPaletteService,
|
||||
SWITCH_FILE_EVENT
|
||||
} from '../commandPaletteService';
|
||||
|
||||
const minCodeWrapSize = 33;
|
||||
|
||||
@@ -105,6 +109,21 @@ export default class ContentWrapFiles extends Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.onRef(this);
|
||||
this.commandPaletteSubscriptions = [];
|
||||
this.commandPaletteSubscriptions.push(
|
||||
commandPaletteService.subscribe(SWITCH_FILE_EVENT, file => {
|
||||
const targetFile = getFileFromPath(
|
||||
this.props.currentItem.files,
|
||||
file.path
|
||||
);
|
||||
if (targetFile.file) {
|
||||
this.fileSelectHandler(targetFile.file);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.commandPaletteSubscriptions.forEach(unsubscribeFn => unsubscribeFn());
|
||||
}
|
||||
|
||||
getEditorOptions(fileName = '') {
|
||||
|
@@ -55,6 +55,11 @@ import { Js13KModal } from './Js13KModal';
|
||||
import { CreateNewModal } from './CreateNewModal';
|
||||
import { Icons } from './Icons';
|
||||
import JSZip from 'jszip';
|
||||
import { CommandPalette } from './CommandPalette';
|
||||
import {
|
||||
commandPaletteService,
|
||||
OPEN_SAVED_CREATIONS_EVENT
|
||||
} from '../commandPaletteService';
|
||||
|
||||
if (module.hot) {
|
||||
require('preact/debug');
|
||||
@@ -84,7 +89,8 @@ export default class App extends Component {
|
||||
isAskToImportModalOpen: false,
|
||||
isOnboardModalOpen: false,
|
||||
isJs13KModalOpen: false,
|
||||
isCreateNewModalOpen: false
|
||||
isCreateNewModalOpen: false,
|
||||
isCommandPaletteOpen: false
|
||||
};
|
||||
this.state = {
|
||||
isSavedItemPaneOpen: false,
|
||||
@@ -532,6 +538,12 @@ export default class App extends Component {
|
||||
// We might be listening on keydown for some input inside the app. In that case
|
||||
// we don't want this to trigger which in turn focuses back the last editor.
|
||||
this.closeSavedItemsPane();
|
||||
} else if ((event.ctrlKey || event.metaKey) && event.keyCode === 80) {
|
||||
this.setState({
|
||||
isCommandPaletteOpen: true,
|
||||
isCommandPaletteInCommandMode: !!event.shiftKey
|
||||
});
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -548,6 +560,10 @@ export default class App extends Component {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commandPaletteService.subscribe(OPEN_SAVED_CREATIONS_EVENT, () => {
|
||||
this.openSavedItemsPane();
|
||||
});
|
||||
}
|
||||
|
||||
closeAllOverlays() {
|
||||
@@ -1553,6 +1569,14 @@ export default class App extends Component {
|
||||
onTemplateSelect={this.templateSelectHandler.bind(this)}
|
||||
/>
|
||||
|
||||
<CommandPalette
|
||||
show={this.state.isCommandPaletteOpen}
|
||||
closeHandler={() => this.setState({ isCommandPaletteOpen: false })}
|
||||
files={linearizeFiles(this.state.currentItem.files || [])}
|
||||
isCommandMode={this.state.isCommandPaletteInCommandMode}
|
||||
closeHandler={() => this.setState({ isCommandPaletteOpen: false })}
|
||||
/>
|
||||
|
||||
<Portal into="body">
|
||||
<div
|
||||
class="modal-overlay"
|
||||
|
@@ -26,3 +26,9 @@ export function A(props) {
|
||||
export function Button(props) {
|
||||
return <Clickable Tag={'button'} {...props} />;
|
||||
}
|
||||
|
||||
export function AutoFocusInput(props) {
|
||||
return (
|
||||
<input ref={el => el && setTimeout(() => el.focus(), 100)} {...props} />
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user