Skip to content

Commit b5950da

Browse files
committed
Bookmarks
1 parent 4f28d9b commit b5950da

File tree

7 files changed

+242
-70
lines changed

7 files changed

+242
-70
lines changed

src/bookmarks.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { BOOKMARKS_KEY } from "./commons";
2+
3+
export function getBookmarks() {
4+
// Get all bookmarks from localStorage
5+
const bookmarks = localStorage.getItem(BOOKMARKS_KEY);
6+
return bookmarks ? JSON.parse(bookmarks) : [];
7+
}
8+
9+
export function isBookmarked(target) {
10+
// Check if a bookmark already exists
11+
const bookmarks = getBookmarks();
12+
return bookmarks.some(bookmark => JSON.stringify(bookmark) === JSON.stringify(target));
13+
}
14+
15+
export function addBookmark(target) {
16+
// Add a bookmark if it doesn't already exist
17+
if (!isBookmarked(target)) {
18+
const bookmarks = getBookmarks();
19+
bookmarks.push(target);
20+
localStorage.setItem(BOOKMARKS_KEY, JSON.stringify(bookmarks));
21+
return true;
22+
} else {
23+
return false;
24+
}
25+
}
26+
27+
export function removeBookmark(target) {
28+
// Remove a bookmark
29+
let bookmarks = getBookmarks();
30+
const updatedBookmarks = bookmarks.filter(bookmark => JSON.stringify(bookmark) !== JSON.stringify(target));
31+
32+
if (bookmarks.length !== updatedBookmarks.length) {
33+
localStorage.setItem(BOOKMARKS_KEY, JSON.stringify(updatedBookmarks));
34+
return true;
35+
} else {
36+
return false;
37+
}
38+
}

src/commons.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ export const COLORS = [
2121
"#e47b07", "#e36920", "#d34e2a", "#ec3b24", "#ba3d99", "#9d45c9", "#4f5aec", "#615dcf", "#3286cf", "#00abca", "#279227", "#3a980c", "#6c7f00", "#ab8b0a", "#b56427", "#757575",
2222
"#ff911a", "#fc8120", "#e7623e", "#fa5236", "#ca4da9", "#a74fd3", "#5a68ff", "#6d69db", "#489bd9", "#00bcde", "#36a436", "#47a519", "#798d0a", "#c1a120", "#bf7730", "#8e8e8e"]
2323

24+
// LocalStorage keys
25+
export const EDITOR_THEME_KEY = 'airflow_code_editor_theme';
26+
export const EDITOR_MODE_KEY = 'airflow_code_editor_mode';
27+
export const EDITOR_COLOR_KEY = 'airflow_code_editor_color';
28+
export const SHOW_HIDDEN_FILES_KEY = 'airflow_code_editor_show_hidden_files';
29+
export const BOOKMARKS_KEY = 'airflow_code_editor_bookmarks';
30+
// Workspace UUID
31+
export const WORKSPACE_UUID = 'd15216ca-854d-4705-bff5-1887e8bf1180';
32+
2433
var csrfToken = null;
2534
var vueApp = null;
2635
var themesPath = null;

src/components/App.vue

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<sidebar class="app-sidebar"
66
@show="show"
77
:config="config"
8+
:bookmarks="bookmarks"
9+
ref="sidebar"
810
></sidebar>
911
</pane>
1012
<pane key="2" :size="100 - sidebarSize" class="app-main">
@@ -19,7 +21,7 @@
1921
</ul>
2022
<vue-simple-context-menu
2123
element-id="app-main-nav-menu"
22-
:options="options"
24+
:options="menuOptions"
2325
ref="appMainNavMenu"
2426
@option-clicked="menuOptionClicked"
2527
/>
@@ -134,7 +136,9 @@ import axios from 'axios';
134136
import { v4 as uuidv4 } from 'uuid';
135137
import { defineComponent, ref } from 'vue';
136138
import { Splitpanes, Pane } from 'splitpanes';
137-
import { setColor, prepareHref, splitPath } from "../commons";
139+
import { setColor, prepareHref, splitPath, EDITOR_THEME_KEY, EDITOR_MODE_KEY, EDITOR_COLOR_KEY, SHOW_HIDDEN_FILES_KEY, BOOKMARKS_KEY, WORKSPACE_UUID } from "../commons";
140+
import { TabState } from '../tabs.js';
141+
138142
import VueSimpleContextMenu from 'vue-simple-context-menu';
139143
import Sidebar from './Sidebar.vue';
140144
import FilesEditorContainer from './FilesEditorContainer.vue';
@@ -144,22 +148,6 @@ import Workspace from './Workspace.vue';
144148
import ErrorDialog from './dialogs/ErrorDialog.vue';
145149
import CloseTabDialog from './dialogs/CloseTabDialog.vue';
146150
147-
const WORKSPACE_UUID = 'd15216ca-854d-4705-bff5-1887e8bf1180';
148-
149-
class TabState {
150-
constructor(name, component, target) {
151-
if (component == Workspace) {
152-
this.uuid = WORKSPACE_UUID;
153-
} else {
154-
this.uuid = uuidv4();
155-
}
156-
this.name = name;
157-
this.component = component;
158-
this.target = target;
159-
this.closed = false;
160-
}
161-
}
162-
163151
export default defineComponent({
164152
components: {
165153
'splitpanes': Splitpanes,
@@ -178,23 +166,14 @@ export default defineComponent({
178166
tabs: [],
179167
selectedTab: null, // selected tab
180168
config: {
181-
theme: localStorage.getItem('airflow_code_editor_theme') || 'default', // editor theme
182-
mode: localStorage.getItem('airflow_code_editor_mode') || 'default', // edit mode (default, vim, etc...)
183-
color: localStorage.getItem('airflow_code_editor_color') || 'Light', // light/dark mode
184-
showHiddenFiles: localStorage.getItem('airflow_code_editor_show_hidden_files') == 'true',
169+
theme: localStorage.getItem(EDITOR_THEME_KEY) || 'default', // editor theme
170+
mode: localStorage.getItem(EDITOR_MODE_KEY) || 'default', // edit mode (default, vim, etc...)
171+
color: localStorage.getItem(EDITOR_COLOR_KEY) || 'Light', // light/dark mode
172+
showHiddenFiles: localStorage.getItem(SHOW_HIDDEN_FILES_KEY) == 'true',
185173
singleTab: false,
186174
},
187175
sidebarSize: 190 * 100 / document.documentElement.clientWidth, // sidebar size (percentage)
188-
options: [
189-
{
190-
name: '<span class="material-icons">close</span> Close',
191-
slug: 'close',
192-
},
193-
{
194-
name: '<span class="material-icons">cancel</span> Close other tabs',
195-
slug: 'close_others',
196-
},
197-
],
176+
menuOptions: [],
198177
};
199178
},
200179
methods: {
@@ -227,7 +206,7 @@ export default defineComponent({
227206
this.$refs[tab.uuid][0].refresh();
228207
}
229208
} else {
230-
tab = new TabState(target.path, FilesEditorContainer, target);
209+
tab = new TabState(target.path, FilesEditorContainer, target, uuidv4());
231210
this.tabs.push(tab);
232211
}
233212
this.selectedTab = tab.uuid;
@@ -236,7 +215,7 @@ export default defineComponent({
236215
if (tab) {
237216
this.$refs[tab.uuid][0].refresh();
238217
} else {
239-
tab = new TabState('Workspace', Workspace);
218+
tab = new TabState('Workspace', Workspace, target, WORKSPACE_UUID);
240219
this.tabs.push(tab);
241220
}
242221
this.selectedTab = tab.uuid;
@@ -245,7 +224,7 @@ export default defineComponent({
245224
if (tab) {
246225
this.$refs[tab.uuid][0].refresh();
247226
} else {
248-
tab = new TabState(target.path, Search, target);
227+
tab = new TabState(target.path, Search, target, uuidv4());
249228
this.tabs.push(tab);
250229
}
251230
this.selectedTab = tab.uuid;
@@ -254,7 +233,7 @@ export default defineComponent({
254233
if (tab) {
255234
this.$refs[tab.uuid][0].refresh();
256235
} else {
257-
tab = new TabState(target.name, HistoryView, target);
236+
tab = new TabState(target.name, HistoryView, target, uuidv4());
258237
this.tabs.push(tab);
259238
}
260239
this.selectedTab = tab.uuid;
@@ -271,17 +250,51 @@ export default defineComponent({
271250
showMenu(event, tab) {
272251
// Show tab menu
273252
if (tab) {
253+
this.menuOptions = this.prepareMenuOptions(tab);
274254
this.$refs.appMainNavMenu.showMenu(event, tab);
275255
}
276256
},
257+
prepareMenuOptions(tab) {
258+
// Prepare tab menu options
259+
const label = tab.isBookmarked() ? 'Remove bookmark' : 'Bookmark tab';
260+
return [
261+
{
262+
name: '<span class="material-icons">bookmark</span> ' + label,
263+
slug: 'bookmark',
264+
},
265+
{
266+
type: 'divider'
267+
},
268+
{
269+
name: '<span class="material-icons">close</span> Close',
270+
slug: 'close',
271+
},
272+
{
273+
name: '<span class="material-icons">cancel</span> Close other tabs',
274+
slug: 'close_others',
275+
},
276+
];
277+
},
277278
async menuOptionClicked(event) {
278279
// Menu click
279-
if (event.option.slug == 'close_others') {
280-
for (const tab of this.tabs.filter(x => x != event.item && !x.closed)) {
281-
await this.closeTab(tab);
282-
}
283-
} else if (event.option.slug == 'close') {
284-
await this.closeTab(event.item);
280+
switch (event.option.slug) {
281+
case 'bookmark':
282+
if (event.item.isBookmarked()) {
283+
event.item.removeBookmark();
284+
} else {
285+
event.item.addBookmark();
286+
}
287+
// Refresh bookmarks in the sidebar
288+
this.$refs.sidebar.refreshBookmarks();
289+
break;
290+
case 'close_others':
291+
for (const tab of this.tabs.filter(x => x != event.item && !x.closed)) {
292+
await this.closeTab(tab);
293+
}
294+
break;
295+
case 'close':
296+
await this.closeTab(event.item);
297+
break
285298
}
286299
},
287300
selectTab(tab) {

src/components/Files.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ import { defineComponent } from 'vue';
157157
import { VueGoodTable } from 'vue-good-table-next';
158158
// import 'vue-good-table-next/dist/vue-good-table-next.css';
159159
import VueSimpleContextMenu from 'vue-simple-context-menu';
160-
import { basename, normalize, prepareHref, git_async, showNotification, parseErrorResponse } from '../commons';
160+
import { basename, normalize, prepareHref, git_async, showNotification, parseErrorResponse, SHOW_HIDDEN_FILES_KEY } from '../commons';
161161
import { TreeEntry, prepareMenuOptions } from '../tree_entry';
162162
import Icon from './Icon.vue';
163163
import Breadcrumb from './Breadcrumb.vue';
@@ -372,7 +372,7 @@ export default defineComponent({
372372
} else if (event.option.slug == 'show_hidden') {
373373
// Save setting on the local storage
374374
this.config.showHiddenFiles = !this.config.showHiddenFiles;
375-
localStorage.setItem('airflow_code_editor_show_hidden_files', this.config.showHiddenFiles);
375+
localStorage.setItem(SHOW_HIDDEN_FILES_KEY, this.config.showHiddenFiles);
376376
this.refresh();
377377
} else if (event.option.slug == 'new') {
378378
this.newAction();

0 commit comments

Comments
 (0)