Skip to content

Commit 0b04171

Browse files
Added Fuse lookup for the file palette
1 parent f2ca911 commit 0b04171

10 files changed

+389
-159
lines changed

functions/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"logs": "firebase functions:log"
1212
},
1313
"engines": {
14-
"node": "18"
14+
"node": "20"
1515
},
1616
"main": "lib/functions/src/index.js",
1717
"dependencies": {

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"fuse.js": "^7.1.0"
4+
}
5+
}

shared/viewModels.ts

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export type SourceFile = {
3636
* yet.
3737
*/
3838
ref?: MediaRef
39+
40+
/**
41+
* The number of top-level comments for this file.
42+
*/
43+
commentCount?: number
3944
}
4045

4146
/**

web/components/workspace/WorkspaceFileItem.vue

+45-46
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@
3333
font-size="0.7em"
3434
rounded
3535
size="md"
36-
/>
36+
>
37+
<QBadge
38+
v-if="file.commentCount && file.commentCount > 0"
39+
color="accent"
40+
:label="file.commentCount"
41+
transparent
42+
floating
43+
/>
44+
</QAvatar>
3745
</QItemSection>
3846
<!-- The file display name or actual name -->
3947
<QItemSection>
@@ -44,12 +52,7 @@
4452
<div>
4553
<!-- Button to delete file -->
4654
<QBtn
47-
v-show="
48-
!file.ref &&
49-
file.name !== readmeName &&
50-
selected &&
51-
unsaved
52-
"
55+
v-show="!file.ref && file.name !== readmeName && selected && unsaved"
5356
color="red-8"
5457
:icon="tabTrash"
5558
@click="emits('remove')"
@@ -58,23 +61,19 @@
5861
/>
5962
<!-- Button to edit file name -->
6063
<QBtn
61-
v-show="selected
62-
&& allowEdit
63-
&& file.name !== readmeName"
64+
v-show="selected && allowEdit && file.name !== readmeName"
6465
color="deep-purple-4"
6566
:icon="tabPencil"
6667
@click="handleStartEditing"
6768
dense
6869
flat
69-
/>
70+
/>
7071
</div>
7172
</QItemSection>
7273
</QItem>
7374

7475
<!-- Control for editing a file name -->
75-
<QItem
76-
v-else
77-
class="q-ma-sm q-px-none">
76+
<QItem v-else class="q-ma-sm q-px-none">
7877
<QItemSection>
7978
<QInput
8079
ref="existingFileName"
@@ -86,13 +85,10 @@
8685
stack-label
8786
outlined
8887
autofocus
89-
dense>
88+
dense
89+
>
9090
<template #after>
91-
<QBtn
92-
:icon="tabX"
93-
@click="handleCancelEdit"
94-
flat
95-
dense/>
91+
<QBtn :icon="tabX" @click="handleCancelEdit" flat dense />
9692
</template>
9793

9894
<template #append>
@@ -101,61 +97,64 @@
10197
:disable="(editedFileName?.trim().length ?? 0) === 0"
10298
@click="handleConfirmEditFileName"
10399
flat
104-
dense/>
100+
dense
101+
/>
105102
</template>
106103
</QInput>
107104
</QItemSection>
108105
</QItem>
109-
110106
</template>
111107

112108
<script setup lang="ts">
113-
import type { SourceFile } from '../../../shared/viewModels';
109+
import type { SourceFile } from "../../../shared/viewModels";
114110
115111
import {
116112
tabPencil,
117113
tabFileCheck,
118114
tabFileUpload,
119115
tabTrash,
120-
tabX
116+
tabX,
121117
} from "quasar-extras-svg-icons/tabler-icons";
122118
123119
const props = defineProps<{
124-
file: SourceFile,
125-
allowEdit: boolean,
126-
selected?: boolean,
127-
unsaved?: boolean
128-
}>()
120+
file: SourceFile;
121+
allowEdit: boolean;
122+
selected?: boolean;
123+
unsaved?: boolean;
124+
}>();
129125
130126
const emits = defineEmits<{
131-
selected: [],
132-
remove: [],
133-
changeName: [string]
134-
}>()
127+
selected: [];
128+
remove: [];
129+
changeName: [string];
130+
}>();
135131
136132
const readmeName = ".README.md";
137133
138134
const { dark } = storeToRefs(useAppStore());
139135
140-
const editedFileName = ref<string|undefined>()
136+
const editedFileName = ref<string | undefined>();
141137
142-
const editing = ref(false)
138+
const editing = ref(false);
143139
144140
/**
145141
* When the selected file changes, we cancel out any edits
146142
*/
147-
watch(() => props.selected, (currentlySelected) => {
148-
if (!currentlySelected) {
149-
handleCancelEdit()
143+
watch(
144+
() => props.selected,
145+
(currentlySelected) => {
146+
if (!currentlySelected) {
147+
handleCancelEdit();
148+
}
150149
}
151-
})
150+
);
152151
153152
/**
154153
* Start editing by copying over the name of the file as default.
155154
*/
156155
function handleStartEditing() {
157-
editedFileName.value = props.file.name.substring(0, props.file.name.lastIndexOf('.'))
158-
editing.value = true
156+
editedFileName.value = props.file.name.substring(0, props.file.name.lastIndexOf("."));
157+
editing.value = true;
159158
}
160159
161160
/**
@@ -164,19 +163,19 @@ function handleStartEditing() {
164163
*/
165164
async function handleConfirmEditFileName() {
166165
if (!editedFileName.value || (editedFileName.value?.length ?? 0) === 0) {
167-
return
166+
return;
168167
}
169168
170-
await emits('changeName', editedFileName.value)
169+
await emits("changeName", editedFileName.value);
171170
172-
handleCancelEdit()
171+
handleCancelEdit();
173172
}
174173
175174
/**
176175
* User cancels the edit or changes the selected item.
177176
*/
178177
function handleCancelEdit() {
179-
editing.value = false
180-
editedFileName.value = ''
178+
editing.value = false;
179+
editedFileName.value = "";
181180
}
182181
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<template>
2+
<QDialog
3+
v-model="visible"
4+
transition-show="jump-down"
5+
transition-hide="jump-up"
6+
transition-duration="150"
7+
>
8+
<QCard style="min-width: 440px">
9+
<QCardSection class="q-pa-md">
10+
<QSelect
11+
v-model="selectedFile"
12+
placeholder="Jump to file"
13+
hint="↓ Down arrow to see file list; ESC to close"
14+
:option-value="(opt) => opt.item"
15+
input-debounce="0"
16+
popup-content-style=""
17+
:menu-offset="[0, 38]"
18+
:options="fuseFileResults"
19+
@filter="handleFilterResults"
20+
map-options
21+
emit-value
22+
autofocus
23+
use-input
24+
hide-selected
25+
hide-dropdown-icon
26+
options-dense
27+
borderless
28+
dense
29+
>
30+
<template #no-option>
31+
<QItem>
32+
<QItemSection>
33+
<QItemLabel class="text-italic text-grey-6">No match</QItemLabel>
34+
</QItemSection>
35+
</QItem>
36+
</template>
37+
38+
<template #option="{ itemProps, opt, selected, toggleOption }">
39+
<QItem v-bind="itemProps" dense>
40+
<QItemSection>
41+
<QItemLabel
42+
><QIcon :name="tabFile" size="sm" color="grey-6" class="q-mr-md">
43+
<QBadge
44+
v-if="opt.item.commentCount && opt.item.commentCount > 0"
45+
color="accent"
46+
:label="opt.item.commentCount"
47+
floating
48+
transparent /></QIcon
49+
>{{ opt.item.name }}</QItemLabel
50+
>
51+
</QItemSection>
52+
</QItem>
53+
</template>
54+
</QSelect>
55+
</QCardSection>
56+
</QCard>
57+
</QDialog>
58+
</template>
59+
60+
<script setup lang="ts">
61+
import { tabFile } from "quasar-extras-svg-icons/tabler-icons-v2";
62+
import { useFuse } from "@vueuse/integrations/useFuse";
63+
64+
const visible = defineModel<boolean>({ required: true });
65+
66+
const props = defineProps<{
67+
files: SourceFile[];
68+
}>();
69+
70+
const emits = defineEmits<{
71+
fileSelected: [fileId: string];
72+
}>();
73+
74+
const filteredOptions = ref<SourceFile[]>(props.files);
75+
76+
const selectedFile = ref<SourceFile>();
77+
78+
const inputSearchTerm = ref("");
79+
80+
watch(
81+
selectedFile,
82+
(file) => {
83+
if (!file) {
84+
return;
85+
}
86+
87+
visible.value = false;
88+
89+
emits("fileSelected", file.hash ?? file.name);
90+
},
91+
{ flush: "post" }
92+
);
93+
94+
const { results: fuseFileResults } = useFuse(inputSearchTerm, filteredOptions, {
95+
fuseOptions: {
96+
isCaseSensitive: false,
97+
minMatchCharLength: 1,
98+
threshold: 0.4,
99+
useExtendedSearch: false,
100+
keys: ["name"],
101+
},
102+
resultLimit: 6,
103+
matchAllWhenSearchEmpty: true,
104+
});
105+
106+
function handleFilterResults(val: string, update: (callback: () => void) => void) {
107+
if (val === "") {
108+
update(() => {
109+
inputSearchTerm.value = "";
110+
update(() => {});
111+
return;
112+
});
113+
}
114+
115+
inputSearchTerm.value = val;
116+
117+
update(() => {});
118+
}
119+
</script>
120+
121+
<style scoped></style>

0 commit comments

Comments
 (0)