Skip to content

Commit 9012af9

Browse files
committed
File list contextual actions.
Signed-off-by: ubi de feo <me@ubidefeo.com>
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

Lines changed: 68 additions & 4 deletions
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

Lines changed: 3 additions & 0 deletions
Loading

ui/arduino/media/download.svg

Lines changed: 3 additions & 0 deletions
Loading

ui/arduino/media/upload.svg

Lines changed: 3 additions & 0 deletions
Loading

ui/arduino/store.js

Lines changed: 24 additions & 0 deletions
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

Lines changed: 73 additions & 7 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy