mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-09 16:06:21 +02:00
Merge pull request #350 from chinchang/settings-ui-refactor
Settings ui refactor
This commit is contained in:
@ -1,32 +1,21 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { editorThemes } from '../editorThemes';
|
import { editorThemes } from '../editorThemes';
|
||||||
|
import Switch from './Switch';
|
||||||
|
import Tabs, { TabPanel } from './Tabs';
|
||||||
|
import { Divider } from './common';
|
||||||
|
|
||||||
function CheckboxSetting({
|
function CheckboxSetting({ label, onChange, pref }) {
|
||||||
title,
|
|
||||||
label,
|
|
||||||
onChange,
|
|
||||||
pref,
|
|
||||||
name,
|
|
||||||
showWhenExtension
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<label
|
<Switch checked={pref} onChange={onChange}>
|
||||||
class={`line ${showWhenExtension ? 'show-when-extension' : ''} `}
|
|
||||||
title={title}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={pref}
|
|
||||||
onChange={onChange}
|
|
||||||
data-setting={name}
|
|
||||||
/>{' '}
|
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</Switch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default class Settings extends Component {
|
export default class Settings extends Component {
|
||||||
updateSetting(e) {
|
updateSetting(e, settingName) {
|
||||||
this.props.onChange(e);
|
const value =
|
||||||
|
e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||||
|
this.props.onChange(settingName, value);
|
||||||
}
|
}
|
||||||
shouldComponentUpdate() {
|
shouldComponentUpdate() {
|
||||||
// TODO: add check on prefs
|
// TODO: add check on prefs
|
||||||
@ -38,304 +27,335 @@ export default class Settings extends Component {
|
|||||||
<div>
|
<div>
|
||||||
<h1>Settings</h1>
|
<h1>Settings</h1>
|
||||||
|
|
||||||
<h3>Indentation</h3>
|
<Tabs>
|
||||||
<div
|
<TabPanel label="General">
|
||||||
class="line"
|
|
||||||
title="I know this is tough, but you have to decide one!"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="indentation"
|
|
||||||
value="spaces"
|
|
||||||
checked={prefs.indentWith === 'spaces'}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
data-setting="indentWith"
|
|
||||||
/>{' '}
|
|
||||||
Spaces
|
|
||||||
</label>
|
|
||||||
<label class="ml-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="indentation"
|
|
||||||
value="tabs"
|
|
||||||
checked={prefs.indentWith === 'tabs'}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
data-setting="indentWith"
|
|
||||||
/>{' '}
|
|
||||||
Tabs
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<label class="line" title="">
|
|
||||||
Indentation Size{' '}
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
class="va-m ml-1"
|
|
||||||
value={prefs.indentSize}
|
|
||||||
min="1"
|
|
||||||
max="7"
|
|
||||||
list="indentationSizeList"
|
|
||||||
data-setting="indentSize"
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<span id="indentationSizeValueEl">{prefs.indentSize}</span>
|
|
||||||
<datalist id="indentationSizeList">
|
|
||||||
<option>1</option>
|
|
||||||
<option>2</option>
|
|
||||||
<option>3</option>
|
|
||||||
<option>4</option>
|
|
||||||
<option>5</option>
|
|
||||||
<option>6</option>
|
|
||||||
<option>7</option>
|
|
||||||
</datalist>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<h3>Editor</h3>
|
|
||||||
<div class="flex block--mobile">
|
|
||||||
<div>
|
|
||||||
<label class="line">Default Preprocessors</label>
|
|
||||||
<div class="flex line">
|
|
||||||
<select
|
|
||||||
style="flex:1;margin-left:20px"
|
|
||||||
data-setting="htmlMode"
|
|
||||||
value={prefs.htmlMode}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
>
|
|
||||||
<option value="html">HTML</option>
|
|
||||||
<option value="markdown">Markdown</option>
|
|
||||||
<option value="jade">Pug</option>
|
|
||||||
</select>
|
|
||||||
<select
|
|
||||||
style="flex:1;margin-left:20px"
|
|
||||||
data-setting="cssMode"
|
|
||||||
value={prefs.cssMode}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
>
|
|
||||||
<option value="css">CSS</option>
|
|
||||||
<option value="scss">SCSS</option>
|
|
||||||
<option value="sass">SASS</option>
|
|
||||||
<option value="less">LESS</option>
|
|
||||||
<option value="stylus">Stylus</option>
|
|
||||||
<option value="acss">Atomic CSS</option>
|
|
||||||
</select>
|
|
||||||
<select
|
|
||||||
style="flex:1;margin-left:20px"
|
|
||||||
data-setting="jsMode"
|
|
||||||
value={prefs.jsMode}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
>
|
|
||||||
<option value="js">JS</option>
|
|
||||||
<option value="coffee">CoffeeScript</option>
|
|
||||||
<option value="es6">ES6 (Babel)</option>
|
|
||||||
<option value="typescript">TypeScript</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<label class="line">
|
|
||||||
Theme
|
|
||||||
<select
|
|
||||||
style="flex:1;margin:0 20px"
|
|
||||||
data-setting="editorTheme"
|
|
||||||
value={prefs.editorTheme}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
>
|
|
||||||
{editorThemes.map(theme => (
|
|
||||||
<option value={theme}>{theme}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label class="line">
|
|
||||||
Font
|
|
||||||
<select
|
|
||||||
style="flex:1;margin:0 20px"
|
|
||||||
data-setting="editorFont"
|
|
||||||
value={prefs.editorFont}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
>
|
|
||||||
<option value="FiraCode">Fira Code</option>
|
|
||||||
<option value="Inconsolata">Inconsolata</option>
|
|
||||||
<option value="Monoid">Monoid</option>
|
|
||||||
<option value="FixedSys">FixedSys</option>
|
|
||||||
<option disabled="disabled">----</option>
|
|
||||||
<option value="other">Other font from system</option>
|
|
||||||
</select>
|
|
||||||
{prefs.editorFont === 'other' && (
|
|
||||||
<input
|
|
||||||
id="customEditorFontInput"
|
|
||||||
type="text"
|
|
||||||
value={prefs.editorCustomFont}
|
|
||||||
placeholder="Custom font name here"
|
|
||||||
data-setting="editorCustomFont"
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
<label class="line">
|
|
||||||
Font Size{' '}
|
|
||||||
<input
|
|
||||||
style="width:70px"
|
|
||||||
type="number"
|
|
||||||
value={prefs.fontSize}
|
|
||||||
data-setting="fontSize"
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>{' '}
|
|
||||||
px
|
|
||||||
</label>
|
|
||||||
<div class="line">
|
|
||||||
Key bindings
|
|
||||||
<label class="ml-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="keymap"
|
|
||||||
value="sublime"
|
|
||||||
checked={prefs.keymap === 'sublime'}
|
|
||||||
data-setting="keymap"
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>{' '}
|
|
||||||
Sublime
|
|
||||||
</label>
|
|
||||||
<label class="ml-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="keymap"
|
|
||||||
value="vim"
|
|
||||||
checked={prefs.keymap === 'vim'}
|
|
||||||
data-setting="keymap"
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>{' '}
|
|
||||||
Vim
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ml-2 ml-0--mobile">
|
|
||||||
<CheckboxSetting
|
<CheckboxSetting
|
||||||
name="lineWrap"
|
|
||||||
title="Toggle wrapping of long sentences onto new line"
|
|
||||||
label="Line wrap"
|
|
||||||
pref={prefs.lineWrap}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="refreshOnResize"
|
|
||||||
title="Your Preview will refresh when you resize the preview split"
|
|
||||||
label="Refresh preview on resize"
|
|
||||||
pref={prefs.refreshOnResize}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="autoComplete"
|
|
||||||
title="Turns on the auto-completion suggestions as you type"
|
|
||||||
label="Auto-complete suggestions"
|
|
||||||
pref={prefs.autoComplete}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="autoPreview"
|
|
||||||
title="Refreshes the preview as you code. Otherwise use the Run button"
|
|
||||||
label="Auto-preview"
|
|
||||||
pref={prefs.autoPreview}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="autoSave"
|
|
||||||
title="Auto-save keeps saving your code at regular intervals after you hit the first save manually"
|
|
||||||
label="Auto-save"
|
|
||||||
pref={prefs.autoSave}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="preserveLastCode"
|
|
||||||
title="Loads the last open creation when app starts"
|
|
||||||
label="Preserve last written code"
|
label="Preserve last written code"
|
||||||
pref={prefs.preserveLastCode}
|
pref={prefs.preserveLastCode}
|
||||||
onChange={this.updateSetting.bind(this)}
|
onChange={e => this.updateSetting(e, 'preserveLastCode')}
|
||||||
/>
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Loads the last open creation when app starts
|
||||||
|
</p>
|
||||||
|
<Divider />
|
||||||
<CheckboxSetting
|
<CheckboxSetting
|
||||||
name="replaceNewTab"
|
|
||||||
title="Turning this on will start showing Web Maker in every new tab you open"
|
|
||||||
label="Replace new tab page"
|
|
||||||
pref={prefs.replaceNewTab}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
showWhenExtension
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="preserveConsoleLogs"
|
|
||||||
title="Preserves the console logs across your preview refreshes"
|
|
||||||
label="Preserve console logs"
|
|
||||||
pref={prefs.preserveConsoleLogs}
|
|
||||||
onChange={this.updateSetting.bind(this)}
|
|
||||||
/>
|
|
||||||
<CheckboxSetting
|
|
||||||
name="lightVersion"
|
|
||||||
title="Switch to lighter version for better performance. Removes things like blur etc."
|
|
||||||
label="Fast/light version"
|
label="Fast/light version"
|
||||||
pref={prefs.lightVersion}
|
pref={prefs.lightVersion}
|
||||||
onChange={this.updateSetting.bind(this)}
|
onChange={e => this.updateSetting(e, 'lightVersion')}
|
||||||
/>
|
/>
|
||||||
</div>
|
<p class="help-text">
|
||||||
</div>
|
Switch to lighter version for better performance. Removes things
|
||||||
|
like blur etc.
|
||||||
|
</p>
|
||||||
|
<Divider />
|
||||||
|
<CheckboxSetting
|
||||||
|
label="Auto-preview"
|
||||||
|
pref={prefs.autoPreview}
|
||||||
|
onChange={e => this.updateSetting(e, 'autoPreview')}
|
||||||
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Refreshes the preview as you code. Otherwise use the 'Run' button
|
||||||
|
</p>
|
||||||
|
<Divider />
|
||||||
|
<CheckboxSetting
|
||||||
|
label="Auto-save"
|
||||||
|
pref={prefs.autoSave}
|
||||||
|
onChange={e => this.updateSetting(e, 'autoSave')}
|
||||||
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Auto-save keeps saving your code at regular intervals after you
|
||||||
|
hit save manually the first time
|
||||||
|
</p>
|
||||||
|
<Divider />
|
||||||
|
<CheckboxSetting
|
||||||
|
label="Refresh preview on resize"
|
||||||
|
pref={prefs.refreshOnResize}
|
||||||
|
onChange={e => this.updateSetting(e, 'refreshOnResize')}
|
||||||
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Preview will refresh when you resize the preview pane
|
||||||
|
</p>
|
||||||
|
<div class="show-when-extension">
|
||||||
|
<Divider />
|
||||||
|
<CheckboxSetting
|
||||||
|
label="Replace new tab page"
|
||||||
|
pref={prefs.replaceNewTab}
|
||||||
|
onChange={e => this.updateSetting(e, 'replaceNewTab')}
|
||||||
|
showWhenExtension
|
||||||
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Turning this on will start showing Web Maker in every new tab
|
||||||
|
you open
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<CheckboxSetting
|
||||||
|
label="Preserve console logs"
|
||||||
|
pref={prefs.preserveConsoleLogs}
|
||||||
|
onChange={e => this.updateSetting(e, 'preserveConsoleLogs')}
|
||||||
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Preserves the console logs across your preview refreshes
|
||||||
|
</p>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel label="Indentation">
|
||||||
|
<div class="line">
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="indentation"
|
||||||
|
value="spaces"
|
||||||
|
checked={prefs.indentWith === 'spaces'}
|
||||||
|
onChange={e => this.updateSetting(e, 'indentWith')}
|
||||||
|
/>{' '}
|
||||||
|
Spaces
|
||||||
|
</label>
|
||||||
|
<label class="ml-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="indentation"
|
||||||
|
value="tabs"
|
||||||
|
checked={prefs.indentWith === 'tabs'}
|
||||||
|
onChange={e => this.updateSetting(e, 'indentWith')}
|
||||||
|
/>{' '}
|
||||||
|
Tabs
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label class="line" title="">
|
||||||
|
Indentation Size
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
class="va-m ml-1"
|
||||||
|
value={prefs.indentSize}
|
||||||
|
min="1"
|
||||||
|
max="7"
|
||||||
|
list="indentationSizeList"
|
||||||
|
onChange={e => this.updateSetting(e, 'indentSize')}
|
||||||
|
/>
|
||||||
|
<span id="indentationSizeValueEl">{prefs.indentSize}</span>
|
||||||
|
<datalist id="indentationSizeList">
|
||||||
|
<option>1</option>
|
||||||
|
<option>2</option>
|
||||||
|
<option>3</option>
|
||||||
|
<option>4</option>
|
||||||
|
<option>5</option>
|
||||||
|
<option>6</option>
|
||||||
|
<option>7</option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel label="Editor">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div class="line">
|
||||||
|
<span>Default Preprocessors</span>
|
||||||
|
<div class="flex">
|
||||||
|
<select
|
||||||
|
aria-label="Default HTML preprocessor"
|
||||||
|
style="flex:1;margin-left:20px"
|
||||||
|
value={prefs.htmlMode}
|
||||||
|
onChange={e => this.updateSetting(e, 'htmlMode')}
|
||||||
|
>
|
||||||
|
<option value="html">HTML</option>
|
||||||
|
<option value="markdown">Markdown</option>
|
||||||
|
<option value="jade">Pug</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
aria-label="Default CSS preprocessor"
|
||||||
|
style="flex:1;margin-left:20px"
|
||||||
|
value={prefs.cssMode}
|
||||||
|
onChange={e => this.updateSetting(e, 'cssMode')}
|
||||||
|
>
|
||||||
|
<option value="css">CSS</option>
|
||||||
|
<option value="scss">SCSS</option>
|
||||||
|
<option value="sass">SASS</option>
|
||||||
|
<option value="less">LESS</option>
|
||||||
|
<option value="stylus">Stylus</option>
|
||||||
|
<option value="acss">Atomic CSS</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
aria-label="Default JavaScript preprocessor"
|
||||||
|
style="flex:1;margin-left:20px"
|
||||||
|
value={prefs.jsMode}
|
||||||
|
onChange={e => this.updateSetting(e, 'jsMode')}
|
||||||
|
>
|
||||||
|
<option value="js">JS</option>
|
||||||
|
<option value="coffee">CoffeeScript</option>
|
||||||
|
<option value="es6">ES6 (Babel)</option>
|
||||||
|
<option value="typescript">TypeScript</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<label class="line">
|
||||||
|
Theme
|
||||||
|
<div>
|
||||||
|
<select
|
||||||
|
value={prefs.editorTheme}
|
||||||
|
onChange={e => this.updateSetting(e, 'editorTheme')}
|
||||||
|
>
|
||||||
|
{editorThemes.map(theme => (
|
||||||
|
<option value={theme}>{theme}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<hr />
|
<label class="line">
|
||||||
|
Font
|
||||||
|
<div>
|
||||||
|
<select
|
||||||
|
value={prefs.editorFont}
|
||||||
|
onChange={e => this.updateSetting(e, 'editorFont')}
|
||||||
|
>
|
||||||
|
<option value="FiraCode">Fira Code</option>
|
||||||
|
<option value="Inconsolata">Inconsolata</option>
|
||||||
|
<option value="Monoid">Monoid</option>
|
||||||
|
<option value="FixedSys">FixedSys</option>
|
||||||
|
<option disabled="disabled">----</option>
|
||||||
|
<option value="other">Other font from system</option>
|
||||||
|
</select>
|
||||||
|
{prefs.editorFont === 'other' && (
|
||||||
|
<input
|
||||||
|
style="margin-left:20px"
|
||||||
|
id="customEditorFontInput"
|
||||||
|
type="text"
|
||||||
|
value={prefs.editorCustomFont}
|
||||||
|
placeholder="Custom font name here"
|
||||||
|
onChange={e =>
|
||||||
|
this.updateSetting(e, 'editorCustomFont')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<h3>Fun</h3>
|
<label class="line">
|
||||||
<p>
|
Font Size
|
||||||
<CheckboxSetting
|
<div>
|
||||||
title="Enjoy wonderful particle blasts while you type"
|
<input
|
||||||
label="Code blast!"
|
style="width:70px"
|
||||||
name="isCodeBlastOn"
|
type="number"
|
||||||
pref={prefs.isCodeBlastOn}
|
value={prefs.fontSize}
|
||||||
onChange={this.updateSetting.bind(this)}
|
onChange={e => this.updateSetting(e, 'fontSize')}
|
||||||
/>
|
/>{' '}
|
||||||
|
px
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<CheckboxSetting
|
<div class="line">
|
||||||
title="Get ready to build some games at JS13KGames"
|
Key bindings
|
||||||
label="Js13kGames Mode"
|
<div>
|
||||||
name="isJs13kModeOn"
|
<label class="ml-1">
|
||||||
pref={prefs.isJs13kModeOn}
|
<input
|
||||||
onChange={this.updateSetting.bind(this)}
|
type="radio"
|
||||||
/>
|
name="keymap"
|
||||||
</p>
|
value="sublime"
|
||||||
|
checked={prefs.keymap === 'sublime'}
|
||||||
|
onChange={e => this.updateSetting(e, 'keymap')}
|
||||||
|
/>{' '}
|
||||||
|
Sublime
|
||||||
|
</label>
|
||||||
|
<label class="ml-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="keymap"
|
||||||
|
value="vim"
|
||||||
|
checked={prefs.keymap === 'vim'}
|
||||||
|
onChange={e => this.updateSetting(e, 'keymap')}
|
||||||
|
/>{' '}
|
||||||
|
Vim
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<hr />
|
<div class="flex-grow">
|
||||||
|
<CheckboxSetting
|
||||||
|
title="Toggle wrapping of long sentences onto new line"
|
||||||
|
label="Line wrap"
|
||||||
|
pref={prefs.lineWrap}
|
||||||
|
onChange={e => this.updateSetting(e, 'lineWrap')}
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<h3>Advanced</h3>
|
<CheckboxSetting
|
||||||
<p>
|
title="Turns on the auto-completion suggestions as you type"
|
||||||
<label
|
label="Auto-complete suggestions"
|
||||||
class="line"
|
pref={prefs.autoComplete}
|
||||||
title="This timeout is used to indentify a possible infinite loop and prevent it."
|
onChange={e => this.updateSetting(e, 'autoComplete')}
|
||||||
>
|
/>
|
||||||
Maximum time allowed in a loop iteration{' '}
|
</div>
|
||||||
<input
|
</div>
|
||||||
type="number"
|
</TabPanel>
|
||||||
value={prefs.infiniteLoopTimeout}
|
<TabPanel label="Fun">
|
||||||
data-setting="infiniteLoopTimeout"
|
<CheckboxSetting
|
||||||
onChange={this.updateSetting.bind(this)}
|
label="Code blast!"
|
||||||
/>{' '}
|
pref={prefs.isCodeBlastOn}
|
||||||
ms
|
onChange={e => this.updateSetting(e, 'isCodeBlastOn')}
|
||||||
</label>
|
/>
|
||||||
<div class="help-text">
|
<p class="help-text">
|
||||||
If any loop iteration takes more than the defined time, it is
|
Enjoy wonderful particle blasts while you type
|
||||||
detected as a potential infinite loop and further iterations are
|
</p>
|
||||||
stopped.
|
<Divider />
|
||||||
</div>
|
<CheckboxSetting
|
||||||
</p>
|
label="Js13kGames Mode"
|
||||||
|
pref={prefs.isJs13kModeOn}
|
||||||
|
onChange={e => this.updateSetting(e, 'isJs13kModeOn')}
|
||||||
|
/>
|
||||||
|
<p class="help-text">
|
||||||
|
Make the app ready to build some games for{' '}
|
||||||
|
<a href="https://js13kgames.com/" target="_blank" rel="noopener">
|
||||||
|
Js13kGames
|
||||||
|
</a>.
|
||||||
|
</p>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel label="Advanced">
|
||||||
|
<div>
|
||||||
|
<label class="line">
|
||||||
|
Maximum time allowed in a loop iteration
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
style="width:120px"
|
||||||
|
value={prefs.infiniteLoopTimeout}
|
||||||
|
onChange={e => this.updateSetting(e, 'infiniteLoopTimeout')}
|
||||||
|
/>{' '}
|
||||||
|
ms
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<p class="help-text">
|
||||||
|
If any loop iteration takes more than the defined time, it is
|
||||||
|
detected as a potential infinite loop and further iterations are
|
||||||
|
stopped.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<p>
|
<div>
|
||||||
<label class="line">
|
<label class="line">
|
||||||
Language
|
Language
|
||||||
<select
|
<select
|
||||||
data-setting="lang"
|
value={prefs.lang}
|
||||||
value={prefs.lang}
|
onChange={e => this.updateSetting(e, 'lang')}
|
||||||
onChange={this.updateSetting.bind(this)}
|
>
|
||||||
>
|
<option value="en">English</option>
|
||||||
<option value="en">English</option>
|
<option value="hi">Hindi</option>
|
||||||
<option value="hi">Hindi</option>
|
<option value="ja">Japanese</option>
|
||||||
<option value="ja">Japanese</option>
|
<option value="sa">Sanskrit</option>
|
||||||
<option value="sa">Sanskrit</option>
|
</select>
|
||||||
</select>
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</p>
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
35
src/components/Switch.jsx
Normal file
35
src/components/Switch.jsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { h, Component } from 'preact';
|
||||||
|
|
||||||
|
export default class Switch extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<label class="check-switch">
|
||||||
|
<input
|
||||||
|
role="switch"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.props.checked}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
/>
|
||||||
|
<div class="check-switch__toggle-wrap">
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="check-switch__status"
|
||||||
|
style={`visibility:${this.props.checked ? 'hidden' : 'visible'}`}
|
||||||
|
>
|
||||||
|
Off
|
||||||
|
</span>
|
||||||
|
<span aria-hidden="true" class="check-switch__toggle" />
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="check-switch__status"
|
||||||
|
style={`visibility:${this.props.checked ? 'visible' : 'hidden'}`}
|
||||||
|
>
|
||||||
|
On
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{this.props.children}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
89
src/components/Tabs.jsx
Normal file
89
src/components/Tabs.jsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { h, Component } from 'preact';
|
||||||
|
|
||||||
|
function hyphenate(text) {
|
||||||
|
return text.replace(/\s/g, '-');
|
||||||
|
}
|
||||||
|
const ID_PREFIX = 'tab-panel-';
|
||||||
|
|
||||||
|
export function TabPanel({ label }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="tabs__tabpanel"
|
||||||
|
role="tabpanel"
|
||||||
|
id={`${ID_PREFIX}${hyphenate(label)}`}
|
||||||
|
>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function Tab({ label, isSelected, onKeyUp, onClick }) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
class={`tabs__tab ${isSelected ? 'tabs__tab--selected' : ''}`}
|
||||||
|
role="tab"
|
||||||
|
tabindex={isSelected ? null : -1}
|
||||||
|
aria-selected={`${isSelected}`}
|
||||||
|
aria-controls={`${ID_PREFIX}${hyphenate(label)}`}
|
||||||
|
onKeyUp={onKeyUp}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default class Tabs extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selectedTab: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
isSelected(index) {
|
||||||
|
return this.state.selectedTab === index;
|
||||||
|
}
|
||||||
|
switchTab(selectedTab) {
|
||||||
|
this.setState({ selectedTab: selectedTab });
|
||||||
|
this.tabListEl.querySelectorAll('[role=tab]')[selectedTab].focus();
|
||||||
|
}
|
||||||
|
keyUpHandler(e) {
|
||||||
|
let { selectedTab } = this.state;
|
||||||
|
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
||||||
|
selectedTab--;
|
||||||
|
selectedTab =
|
||||||
|
selectedTab < 0 ? this.props.children.length - 1 : selectedTab;
|
||||||
|
this.switchTab(selectedTab);
|
||||||
|
e.preventDefault();
|
||||||
|
} else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
||||||
|
selectedTab++;
|
||||||
|
selectedTab %= this.props.children.length;
|
||||||
|
this.switchTab(selectedTab);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const tabs = this.props.children;
|
||||||
|
return (
|
||||||
|
<div class="tabs">
|
||||||
|
<div
|
||||||
|
class="tabs__tablist"
|
||||||
|
role="tablist"
|
||||||
|
ref={el => (this.tabListEl = el)}
|
||||||
|
>
|
||||||
|
{tabs.map((child, index) => (
|
||||||
|
<Tab
|
||||||
|
isSelected={this.isSelected(index)}
|
||||||
|
label={child.props.label}
|
||||||
|
onKeyUp={this.keyUpHandler.bind(this)}
|
||||||
|
onClick={() => this.setState({ selectedTab: index })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div class="tabs__tabpanel-wrap">
|
||||||
|
{tabs.map(
|
||||||
|
(child, index) => (this.state.selectedTab === index ? child : null)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -887,15 +887,14 @@ export default class App extends Component {
|
|||||||
/**
|
/**
|
||||||
* Handles all user triggered preference changes in the UI.
|
* Handles all user triggered preference changes in the UI.
|
||||||
*/
|
*/
|
||||||
updateSetting(e) {
|
updateSetting(settingName, value) {
|
||||||
// If this was triggered from user interaction, save the setting
|
// If this was triggered from user interaction, save the setting
|
||||||
if (e) {
|
if (settingName) {
|
||||||
var settingName = e.target.dataset.setting;
|
// var settingName = e.target.dataset.setting;
|
||||||
var obj = {};
|
var obj = {};
|
||||||
var el = e.target;
|
log(settingName, value);
|
||||||
log(settingName, el.type === 'checkbox' ? el.checked : el.value);
|
|
||||||
const prefs = { ...this.state.prefs };
|
const prefs = { ...this.state.prefs };
|
||||||
prefs[settingName] = el.type === 'checkbox' ? el.checked : el.value;
|
prefs[settingName] = value;
|
||||||
obj[settingName] = prefs[settingName];
|
obj[settingName] = prefs[settingName];
|
||||||
this.setState({ prefs });
|
this.setState({ prefs });
|
||||||
|
|
||||||
|
@ -32,3 +32,7 @@ export function AutoFocusInput(props) {
|
|||||||
<input ref={el => el && setTimeout(() => el.focus(), 100)} {...props} />
|
<input ref={el => el && setTimeout(() => el.focus(), 100)} {...props} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Divider(props) {
|
||||||
|
return <div class="divider" />;
|
||||||
|
}
|
||||||
|
120
src/style.css
120
src/style.css
@ -212,6 +212,11 @@ hr {
|
|||||||
label {
|
label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.divider {
|
||||||
|
margin: 10px 0;
|
||||||
|
height: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
[class*='hint--']:after {
|
[class*='hint--']:after {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
@ -221,8 +226,10 @@ label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
display: block;
|
/* display: block; */
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.caret {
|
.caret {
|
||||||
@ -249,6 +256,86 @@ a > svg {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.check-switch {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* breathing space for outline */
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch .check-switch__toggle {
|
||||||
|
position: relative;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
.check-switch .check-switch__toggle:after {
|
||||||
|
background: #fff;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 100%;
|
||||||
|
width: 1.1em;
|
||||||
|
height: 1.1em;
|
||||||
|
top: 3px;
|
||||||
|
right: 1.5em;
|
||||||
|
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
transition: right 0.1825s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch .check-switch__toggle:before,
|
||||||
|
.check-switch .check-switch__toggle:after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch .check-switch__toggle:before {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 1.75em;
|
||||||
|
width: 2.75em;
|
||||||
|
height: 1.45em;
|
||||||
|
right: 0.25em;
|
||||||
|
transition: background 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch input:not([role='button']) {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch input {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.0001;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch__status {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch input:focus + * .check-switch__toggle:before {
|
||||||
|
outline: 2px solid;
|
||||||
|
outline-color: var(--color-focus-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch input:checked + * .check-switch__toggle:after {
|
||||||
|
right: 0.15em;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-switch input:checked + * .check-switch__toggle:before {
|
||||||
|
background: #61ad1c;
|
||||||
|
}
|
||||||
|
.check-switch__toggle-wrap {
|
||||||
|
float: right;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--color-button);
|
color: var(--color-button);
|
||||||
@ -907,10 +994,9 @@ body > #demo-frame {
|
|||||||
|
|
||||||
/* Make settings modal smaller */
|
/* Make settings modal smaller */
|
||||||
|
|
||||||
@media screen and (min-width: 600px) {
|
@media screen and (min-width: 850px) {
|
||||||
.modal--settings {
|
.modal--settings > .modal__content {
|
||||||
/* width: 600px; */
|
width: 850px;
|
||||||
/* margin-lef.t: -300px; */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1577,6 +1663,7 @@ body:not(.is-app) .show-when-app {
|
|||||||
.help-text {
|
.help-text {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: rgba(255, 255, 255, 0.5);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
margin: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.social-login-btn:after,
|
.social-login-btn:after,
|
||||||
@ -1822,6 +1909,29 @@ while the theme CSS file is loading */
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.tabs__tablist {
|
||||||
|
margin-right: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.tabs__tab {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.tabs__tab--selected {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
.tabs__tabpanel-wrap {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
body {
|
body {
|
||||||
font-size: 70%;
|
font-size: 70%;
|
||||||
|
Reference in New Issue
Block a user