Skip to content

Commit 9012af9

Browse files
committed
File list contextual actions.
Signed-off-by: ubi de feo <[email protected]>
1 parent e0db436 commit 9012af9

File tree

6 files changed

+174
-11
lines changed

6 files changed

+174
-11
lines changed

ui/arduino/main.css

+68-4
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ button[disabled]:hover {
7373
button:hover, button.active {
7474
background: rgba(255, 255, 255, 1);
7575
}
76-
/* button.inactive:hover {
77-
background: rgba(255, 255, 255, 0.2);
78-
} */
7976

8077
button .icon {
8178
width: 63%;
@@ -101,7 +98,6 @@ button.small .icon {
10198
}
10299
.button .label {
103100
text-align: center;
104-
/* color: #eee; */
105101
color: rgba(255, 255, 255, 0.2);
106102
font-family: "OpenSans", sans-serif;
107103
}
@@ -624,6 +620,7 @@ button.small .icon {
624620
background: #ECF1F1;
625621
height: 100%;
626622
overflow-y: scroll;
623+
position: relative;
627624
}
628625

629626
.file-list .list {
@@ -646,6 +643,7 @@ button.small .icon {
646643
gap: 10px;
647644
align-self: stretch;
648645
transition: all 0.1s;
646+
position: relative;
649647
}
650648

651649
.file-list .item.selected,
@@ -659,6 +657,7 @@ button.small .icon {
659657
align-items: center;
660658
align-self: stretch;
661659
cursor: pointer;
660+
width: 22px;
662661
transition: all 0.1s;
663662
}
664663

@@ -715,6 +714,71 @@ button.small .icon {
715714
outline-color: #F4BA00;
716715
}
717716

717+
.popup-menu {
718+
position: absolute;
719+
top: auto;
720+
bottom: auto;
721+
background: white;
722+
border: 1px solid #ddd;
723+
border-radius: 8px;
724+
display: flex;
725+
z-index: 1000;
726+
gap: 4px;
727+
align-items: stretch;
728+
height: 28px;
729+
padding: 4px 4px;
730+
margin: 0;
731+
flex-direction: row;
732+
right: 0px;
733+
}
734+
735+
.popup-menu-item {
736+
cursor: pointer;
737+
background: #ddd;
738+
border-radius: 6px;
739+
flex: auto;
740+
display: flex;
741+
width: 32px;
742+
justify-content: center;
743+
flex-direction: column;
744+
align-items: center;
745+
}
746+
747+
.popup-menu-item img {
748+
width: 24px;
749+
height: 24px;
750+
max-width: 24px;
751+
max-height: 24px;
752+
}
753+
754+
.popup-menu-item:last-child {
755+
flex: 0 0 18px;
756+
}
757+
.popup-menu-item:last-child:hover {
758+
background-color: #bbb;
759+
}
760+
.popup-menu-item:hover {
761+
background-color: #f5f5f5;
762+
}
763+
764+
.popup-menu-item.disabled {
765+
color: #ccc;
766+
cursor: default;
767+
background: #eee;
768+
opacity: 0.5;
769+
}
770+
771+
.options {
772+
cursor: pointer;
773+
}
774+
775+
.options img{
776+
width: 28px;
777+
height: 28px;
778+
max-width: 28px;
779+
max-height: 28px;
780+
}
781+
718782
#code-editor .cm-panels {
719783
border-color: #DAE3E3;
720784
padding: 0 10px;

ui/arduino/media/More.svg

+3
Loading

ui/arduino/media/download.svg

+3
Loading

ui/arduino/media/upload.svg

+3
Loading

ui/arduino/store.js

+24
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ async function store(state, emitter) {
3939
state.boardFiles = []
4040
state.openFiles = []
4141
state.selectedFiles = []
42+
state.fileContextMenu = null
4243

4344
state.newTabFileName = null
4445
state.editingFile = null
@@ -1136,6 +1137,29 @@ async function store(state, emitter) {
11361137
emitter.emit('render')
11371138
})
11381139

1140+
emitter.on('file-context-menu', (file, source, event) => {
1141+
state.selectedFiles = []
1142+
let parentFolder = source == 'board' ? state.boardNavigationPath : state.diskNavigationPath
1143+
log('file-contextual-menu', file, source, event)
1144+
const isSelected = state.selectedFiles.find((f) => {
1145+
return f.fileName === file.fileName && f.source === source
1146+
})
1147+
if (isSelected) {
1148+
state.selectedFiles = state.selectedFiles.filter((f) => {
1149+
return !(f.fileName === file.fileName && f.source === source)
1150+
})
1151+
} else {
1152+
state.selectedFiles.push({
1153+
fileName: file.fileName,
1154+
type: file.type,
1155+
source: source,
1156+
parentFolder: parentFolder
1157+
})
1158+
}
1159+
state.fileContextMenu = state.selectedFiles[state.selectedFiles.length - 1]
1160+
emitter.emit('render')
1161+
})
1162+
11391163
emitter.on('toggle-file-selection', (file, source, event) => {
11401164
log('toggle-file-selection', file, source, event)
11411165
let parentFolder = source == 'board' ? state.boardNavigationPath : state.diskNavigationPath

ui/arduino/views/components/file-list.js

+73-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ function generateFileList(source) {
1919
<div class="text">
2020
<input type="text" onkeydown=${onKeyEvent} onblur=${(e) => emit('finish-creating-file', e.target.value)}/>
2121
</div>
22+
<div class="popup-menu">
23+
<div class="popup-menu-item">rename</div>
24+
<div class="popup-menu-item">delete</div>
25+
<div class="popup-menu-item">upload</div>
26+
</div>
2227
</div>
2328
`
2429
const newFolderItem = html`
@@ -27,8 +32,50 @@ function generateFileList(source) {
2732
<div class="text">
2833
<input type="text" onkeydown=${onKeyEvent} onblur=${(e) => emit('finish-creating-folder', e.target.value)}/>
2934
</div>
35+
<div class="popup-menu">
36+
<div class="popup-menu-item">rename</div>
37+
<div class="popup-menu-item">delete</div>
38+
<div class="popup-menu-item">upload</div>
39+
</div>
3040
</div>
3141
`
42+
function dismissContextMenu(e, item) {
43+
console.log("click action", e, item)
44+
e.stopPropagation()
45+
state.fileContextMenu = null
46+
state.selectedFiles = []
47+
emit('render')
48+
}
49+
function triggerRemove() {
50+
emit('remove-files')
51+
state.fileContextMenu = null
52+
emit('render')
53+
}
54+
function triggerRename(item) {
55+
emit('rename-file', source, item)
56+
57+
state.fileContextMenu = null
58+
emit('render')
59+
}
60+
function triggerTransfer() {
61+
if (source === 'disk') {
62+
emit('upload-files')
63+
}else{
64+
emit('download-files')
65+
}
66+
state.fileContextMenu = null
67+
emit('render')
68+
}
69+
function FileOptions(item, i){
70+
const popupMenu = html`
71+
<div class="popup-menu">
72+
<div class="popup-menu-item ${state.isConnected ? '' : 'disabled'}" onclick=${triggerTransfer}><img src="media/${source === 'disk' ? 'upload' : 'download'}.svg" /></div>
73+
<div class="popup-menu-item" onclick=${() => triggerRename(item)}><img src="media/cursor.svg" /></div>
74+
<div class="popup-menu-item" onclick=${triggerRemove}><img src="media/delete.svg" /></div>
75+
<div class="popup-menu-item" onclick=${(e) => {dismissContextMenu(e, item)}}><img src="media/arrow-right-white.svg" /></div>
76+
</div>`
77+
return popupMenu
78+
}
3279

3380
function FileItem(item, i) {
3481
const renamingFileItem = html`
@@ -43,6 +90,8 @@ function generateFileList(source) {
4390
const isChecked = state.selectedFiles.find(
4491
f => f.fileName === item.fileName && f.source === source
4592
)
93+
94+
const hasContextMenu = state.fileContextMenu && state.fileContextMenu.fileName === item.fileName && state.fileContextMenu.source === source
4695
function renameItem(e) {
4796
e.preventDefault()
4897
emit('rename-file', source, item)
@@ -54,12 +103,26 @@ function generateFileList(source) {
54103
function openFile() {
55104
if (!state.renamingFile) emit(`open-file`, source, item)
56105
}
106+
107+
function toggleContextMenu(item, source, e) {
108+
e.stopPropagation()
109+
console.log("show file options", item, source, e)
110+
// const popupMenu = e.currentTarget.parentElement.querySelector('.popup-menu')
111+
// popupMenu.classList.add('visible')
112+
emit('file-context-menu', item, source, e)
113+
}
57114
let fileName = item.fileName
58115
const isSelected = state.selectedFiles.find(f => f.fileName === fileName)
59116

60117
if (state.renamingFile == source && isSelected) {
61118
fileName = renamingFileItem
62119
}
120+
121+
contextMenuHtml = html``
122+
if (hasContextMenu) {
123+
contextMenuHtml = html`${FileOptions(item, i)}`
124+
}
125+
63126
if (item.type === 'folder') {
64127
return html`
65128
<div
@@ -69,12 +132,14 @@ function generateFileList(source) {
69132
>
70133
<img class="icon" src="media/folder.svg" />
71134
<div class="text">${fileName}</div>
72-
<div class="options" onclick=${renameItem}>
73-
<img src="media/cursor.svg" />
135+
<div class="options" onclick=${(e) => toggleContextMenu(item, source, e)}>}>
136+
<img src="media/more.svg" />
74137
</div>
138+
${contextMenuHtml}
75139
</div>
76140
`
77141
} else {
142+
//<div class="options" onclick=${(e) => {contextualMenu(e, item)}}>
78143
return html`
79144
<div
80145
class="item ${isChecked ? 'selected' : ''}"
@@ -83,14 +148,15 @@ function generateFileList(source) {
83148
>
84149
<img class="icon" src="media/file.svg" />
85150
<div class="text">${fileName}</div>
86-
<div class="options" onclick=${renameItem}>
87-
<img src="media/cursor.svg" />
151+
<div class="options" onclick=${(e) => toggleContextMenu(item, source, e)}>
152+
<img src="media/more.svg" />
88153
</div>
154+
${contextMenuHtml}
89155
</div>
90156
`
91157
}
92158
}
93-
159+
//
94160
// XXX: Use `source` to filter an array of files with a `source` as proprety
95161
const files = state[`${source}Files`].sort((a, b) => {
96162
const nameA = a.fileName.toUpperCase()
@@ -116,8 +182,8 @@ function generateFileList(source) {
116182
<div class="list">
117183
${source === 'disk' && state.diskNavigationPath != '/' ? parentNavigationDots : ''}
118184
${source === 'board' && state.boardNavigationPath != '/' ? parentNavigationDots : ''}
119-
${state.creatingFile == source ? newFileItem : null}
120-
${state.creatingFolder == source ? newFolderItem : null}
185+
${source == state.creatingFile ? newFileItem : null}
186+
${source == state.creatingFolder ? newFolderItem : null}
121187
${files.map(FileItem)}
122188
</div>
123189
</div>

0 commit comments

Comments
 (0)