1
0
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:
Kushagra Gour
2018-10-23 19:56:33 +05:30
parent 03e8e3e463
commit 78468f8a5d
6 changed files with 197 additions and 1 deletions

View 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
View 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'
}
];

View 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>
);
}
}

View File

@@ -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 = '') {

View File

@@ -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"

View File

@@ -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} />
);
}