Skip to content

Commit d7e8ad7

Browse files
committed
Bare foot Blank UI
1 parent 8af6328 commit d7e8ad7

27 files changed

+2912
-0
lines changed

ui/blank/app.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function App(state, emit) {
2+
return html`
3+
<div id="app" class="column fill">
4+
${Toolbar(state, emit)}
5+
${Editor(state, emit)}
6+
${Panel(state, emit)}
7+
${state.isPortDialogOpen ? PortDialog(state, emit) : null}
8+
</div>
9+
`
10+
}
11+
12+
window.addEventListener('load', () => {
13+
let app = Choo()
14+
app.use(store);
15+
app.route('*', App)
16+
app.mount('#app')
17+
})

ui/blank/components/buttons.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function Button(props, children) {
2+
const { className = '', onclick = () => false, disabled = false } = props
3+
return html`<button
4+
class="${className}"
5+
onclick=${onclick}
6+
disabled=${disabled}
7+
>
8+
${children}
9+
</button>
10+
`
11+
}
12+
13+
function RoundButton(props, children) {
14+
props.className += ' round'
15+
return Button(props, children)
16+
}
17+
18+
function SquareButton(props, children) {
19+
props.className += ' square'
20+
return Button(props, children)
21+
}
22+
23+
function TinyButton(props, children) {
24+
props.className += ' tiny'
25+
return Button(props, children)
26+
}

ui/blank/components/editor.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
function Editor(state, emit) {
3+
const diskIcon = Image({ src: 'icons/folder.png' })
4+
const serialIcon = Image({ src: 'icons/developer_board.png' })
5+
let icon = null
6+
if (state.selectedDevice === 'serial') icon = serialIcon
7+
if (state.selectedDevice === 'disk') icon = diskIcon
8+
9+
let fileName = html`
10+
<div class="file-name" onclick=${() => emit('start-renaming-file')}>
11+
${state.selectedFile || 'untitled'}
12+
</div>
13+
`
14+
if (state.renamingFile) {
15+
let input = html`
16+
<input
17+
type="text"
18+
name="filename"
19+
value="${state.selectedFile || 'untitled'}"
20+
onchange=${(e) => emit('end-renaming-file', e.target.value)}
21+
onblur=${(e) => emit('end-renaming-file', e.target.value)}
22+
/>
23+
`
24+
fileName = html`
25+
<div class="file-name" onclick=${() => emit('start-renaming-file')}>
26+
${input}
27+
</div>
28+
`
29+
setTimeout(() => {
30+
input.focus()
31+
input.select()
32+
}, 50)
33+
}
34+
35+
36+
return html`
37+
<div id="file-header" class="row lightgray align-center">
38+
<div class="device-icon">${icon}</div>
39+
${fileName}
40+
</div>
41+
${state.cache(AceEditor, 'editor').render()}
42+
`
43+
}
44+
45+
class AceEditor extends Component {
46+
constructor() {
47+
super()
48+
this.editor = null
49+
}
50+
51+
load(element) {
52+
this.editor = ace.edit("editor")
53+
this.editor.setFontSize(14)
54+
this.editor.setTheme("ace/theme/github")
55+
this.editor.session.setMode("ace/mode/python")
56+
}
57+
58+
createElement(content) {
59+
return html`<div id="editor" class="fill"></div>`
60+
}
61+
62+
update(newContent) {
63+
if (newContent) {
64+
this.editor.setValue(newContent)
65+
}
66+
return false
67+
}
68+
}

ui/blank/components/filebrowser.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
function FileBrowser(state, emit) {
2+
function BoardFile(file) {
3+
let selectedClass = ''
4+
if (state.selectedDevice === 'board' && state.selectedFile === file) {
5+
selectedClass = 'selected'
6+
}
7+
return html`
8+
<li
9+
class=${selectedClass}
10+
onclick=${() => emit('select-board-file', file)}
11+
>
12+
${file}
13+
</li>
14+
`
15+
}
16+
function DiskFile(file) {
17+
let selectedClass = ''
18+
if (state.selectedDevice === 'disk' && state.selectedFile === file) {
19+
selectedClass = 'selected'
20+
}
21+
return html`
22+
<li
23+
class=${selectedClass}
24+
onclick=${() => emit('select-disk-file', file)}
25+
>
26+
${file}
27+
</li>
28+
`
29+
}
30+
31+
let canSendToBoard = state.selectedDevice === 'disk'
32+
&& state.connected === true
33+
&& state.selectedFile
34+
&& state.diskFolder
35+
let canSendToDisk = state.selectedDevice === 'board'
36+
&& state.connected === true
37+
&& state.selectedFile
38+
&& state.diskFolder
39+
40+
let sendToBoardButton = SquareButton(
41+
{
42+
onclick: () => emit('send-file-to-board'),
43+
disabled: !canSendToBoard
44+
},
45+
Image({src: 'icons/left.png'})
46+
)
47+
let sendToDiskButton = SquareButton(
48+
{
49+
onclick: () => emit('send-file-to-disk'),
50+
disabled: !canSendToDisk
51+
},
52+
Image({src: 'icons/right.png'})
53+
)
54+
let removeButton = SquareButton(
55+
{
56+
onclick: () => emit('remove-file'),
57+
disabled: !state.selectedFile
58+
},
59+
Image({src: 'icons/delete.png'})
60+
)
61+
62+
return html`
63+
<div id="files" class="row fill">
64+
<div id="board-files" class="fill">
65+
<ul id="file-list" class="fill white column">
66+
${state.boardFiles.map(BoardFile)}
67+
</ul>
68+
</div>
69+
<div id="file-actions" class="column fill-vertical align-center">
70+
${sendToBoardButton}
71+
${sendToDiskButton}
72+
${removeButton}
73+
</div>
74+
<div id="system-files" class="fill">
75+
<ul id="file-list" class="fill white column">
76+
${state.diskFiles.map(DiskFile)}
77+
</ul>
78+
</div>
79+
</div>
80+
`
81+
}

ui/blank/components/image.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
function Image(props) {
2+
let { src } = props
3+
return html`<img src=${src} />`
4+
}

ui/blank/components/panel.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
function Panel(state, emit) {
2+
let isTerminalSelected = (state.panel === 'terminal') && !state.panelCollapsed
3+
let isFilesSelected = (state.panel === 'files') && !state.panelCollapsed
4+
5+
// Dragging event handlers
6+
function onMouseDown(e) {
7+
if (e.target.id !== 'bar') return
8+
window.addEventListener('mousemove', onMouseMove)
9+
window.addEventListener('mouseup', onMouseUp, { once: true })
10+
}
11+
function onMouseMove(e) {
12+
emit('resize-panel', e.clientY)
13+
}
14+
function onMouseUp(e) {
15+
window.removeEventListener('mousemove', onMouseMove)
16+
}
17+
18+
// Panel selection handlers
19+
function togglePanel() {
20+
emit('toggle-panel')
21+
}
22+
function selectFiles() {
23+
if (state.panel === 'files') {
24+
emit('toggle-panel')
25+
} else {
26+
if (state.panelCollapsed) {
27+
emit('toggle-panel')
28+
}
29+
emit('select-panel', 'files')
30+
}
31+
}
32+
function selectTerminal() {
33+
if (state.panel === 'terminal') {
34+
emit('toggle-panel')
35+
} else {
36+
if (state.panelCollapsed) {
37+
emit('toggle-panel')
38+
}
39+
emit('select-panel', 'terminal')
40+
}
41+
}
42+
43+
// Bar buttons
44+
let fileButton = SquareButton(
45+
{ onclick: selectFiles, className: isFilesSelected ? 'active' : 'inactive'},
46+
Image({ src: 'icons/folder.png' })
47+
)
48+
let terminalButton = SquareButton(
49+
{ onclick: selectTerminal , className: isTerminalSelected ? 'active' : 'inactive' },
50+
Image( { src: 'icons/developer_board.png' } )
51+
)
52+
53+
let panelHeight = state.panelCollapsed ? 0 : state.panelHeight
54+
let background = isTerminalSelected ? 'black' : 'gray'
55+
56+
if (state.panelCollapsed) {
57+
return html`
58+
<div id="bar" class="gray row align-center justify-end">
59+
${fileButton}
60+
${terminalButton}
61+
</div>
62+
`
63+
} else {
64+
return html`
65+
<div
66+
id="bar"
67+
class="gray row align-center justify-end resizable"
68+
onmousedown=${onMouseDown}
69+
>
70+
${fileButton}
71+
${terminalButton}
72+
</div>
73+
<div
74+
id="panel"
75+
class="${background} column fill"
76+
style="height: ${panelHeight}px"
77+
>
78+
${isTerminalSelected ? state.cache(XTerm, 'terminal').render() : null}
79+
${isFilesSelected ? FileBrowser(state, emit) : null}
80+
</div>
81+
`
82+
}
83+
}

ui/blank/components/portdialog.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function PortDialog(state, emit) {
2+
function PortItem(port) {
3+
return html`<li onclick=${() => emit('connect', port)}>${port.path}</li>`
4+
}
5+
function closeBackdrop(e) {
6+
if (e.target.id == 'backdrop') {
7+
emit('close-port-dialog')
8+
}
9+
}
10+
return html`
11+
<div id="backdrop" onclick=${closeBackdrop}>
12+
<div id="dialog">
13+
<ul>
14+
${state.ports.map(PortItem)}
15+
<li onclick=${() => window.serialBus.emit('load-ports')}>Refresh</li>
16+
</ul>
17+
</div>
18+
</div>
19+
`
20+
}

ui/blank/components/toolbar.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
function Toolbar(state, emit) {
2+
const runButton = RoundButton(
3+
{ onclick: () => emit('run'), disabled: !state.connected },
4+
Image({src: 'icons/play_arrow.png'})
5+
)
6+
const stopButton = RoundButton(
7+
{ onclick: () => emit('stop'), disabled: !state.connected },
8+
Image({src: 'icons/stop.png'})
9+
)
10+
const resetButton = RoundButton(
11+
{ onclick: () => emit('reset'), disabled: !state.connected },
12+
Image({src: 'icons/reset.png'})
13+
)
14+
const connectButton = RoundButton(
15+
{ onclick: () => emit('open-port-dialog') },
16+
Image({src: 'icons/cable.png'})
17+
)
18+
19+
const newButton = RoundButton(
20+
{ onclick: () => emit('new-file') },
21+
Image({src: 'icons/add.png'})
22+
)
23+
const openFolderButton = RoundButton(
24+
{ onclick: () => emit('open-disk-folder') },
25+
Image({src: 'icons/folder.png'})
26+
)
27+
const canSave = (state.selectedDevice === 'board' && state.connected)
28+
|| (state.selectedDevice === 'disk' && state.diskFolder)
29+
const saveButton = RoundButton(
30+
{ onclick: () => emit('save-file'), disabled: !canSave },
31+
Image({ src: 'icons/sd_storage.png' })
32+
)
33+
34+
return html`
35+
<div id="toolbar" class="row gray">
36+
<div class="row">
37+
${runButton}
38+
${stopButton}
39+
${resetButton}
40+
</div>
41+
<div class="row fill justify-start align-center">
42+
${newButton}
43+
${openFolderButton}
44+
${saveButton}
45+
</div>
46+
<div class="row">
47+
${connectButton}
48+
</div>
49+
</div>
50+
`
51+
}

ui/blank/components/xterm.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class XTerm extends Component {
2+
constructor(id, state, emit) {
3+
super(id)
4+
this.term = new Terminal()
5+
this.term.onData((data) => emit('terminal-input', data))
6+
this.resizeTerm = this.resizeTerm.bind(this)
7+
}
8+
9+
load(element) {
10+
this.term.open(element)
11+
this.resizeTerm()
12+
window.addEventListener('resize', this.resizeTerm)
13+
}
14+
15+
createElement() {
16+
return html`<div id="terminal" class="black column fill"></div>`
17+
}
18+
19+
update() {
20+
this.resizeTerm()
21+
return false
22+
}
23+
24+
resizeTerm() {
25+
const parentStyle = window.getComputedStyle(this.term.element.parentElement)
26+
const parentWidth = parseInt(parentStyle.getPropertyValue('width')) - (window.innerHeight*0.0175)
27+
const parentHeight = parseInt(parentStyle.getPropertyValue('height')) - (window.innerHeight*0.0175)
28+
const cols = Math.floor(parentWidth / this.term._core._renderService.dimensions.actualCellWidth)
29+
const rows = Math.floor(parentHeight / this.term._core._renderService.dimensions.actualCellHeight)
30+
this.term.resize(cols, rows)
31+
}
32+
}

ui/blank/icons/add.png

204 Bytes
Loading

0 commit comments

Comments
 (0)