Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removes fuzzysearch and uses fuse.js #1888

Merged
merged 6 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 47 additions & 38 deletions assets/components/FuzzySearchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@
<o-autocomplete
ref="autocomplete"
v-model="query"
placeholder="Search containers using ⌘ + k or ctrl + k"
field="name"
:placeholder="$t('placeholder.search-containers')"
open-on-focus
keep-first
expanded
:data="results"
:data="data"
@select="selected"
>
<template #default="props">
<template #default="{ option: item }">
<div class="media">
<div class="media-left">
<span class="icon is-small" :class="props.option.state">
<span class="icon is-small" :class="item.state">
<octicon-container-24 />
</span>
</div>
<div class="media-content">
{{ props.option.name }}
{{ item.name }}
</div>
<div class="media-right">
<span class="icon is-small column-icon" @click.stop.prevent="addColumn(props.option)" title="Pin as column">
<span
class="icon is-small column-icon"
@click.stop.prevent="addColumn(item)"
:title="$t('tooltip.pin-column')"
>
<cil-columns />
</span>
</div>
Expand All @@ -33,55 +36,61 @@
</template>

<script lang="ts" setup>
import fuzzysort from "fuzzysort";
import { type Container } from "@/types/Container";
import { useFuse } from "@vueuse/integrations/useFuse";

const { maxResults = 20 } = defineProps<{
const { maxResults: resultLimit = 20 } = defineProps<{
maxResults?: number;
}>();

const emit = defineEmits(["close"]);
const emit = defineEmits<{
(e: "close"): void;
}>();

const query = ref("");
const autocomplete = ref<HTMLElement>();
const router = useRouter();
const store = useContainerStore();
const { containers } = storeToRefs(store);
const preparedContainers = computed(() =>
containers.value.map(({ name, id, created, state }) =>
reactive({
name,

const list = computed(() => {
return containers.value.map(({ id, created, name, state }) => {
return {
id,
created,
name,
state,
preparedName: fuzzysort.prepare(name),
})
)
);
};
});
});

const results = computed(() => {
const options = {
limit: maxResults,
key: "preparedName",
};
if (query.value) {
const results = fuzzysort.go(query.value, preparedContainers.value, options);
results.forEach((result) => {
if (result.obj.state === "running") {
// @ts-ignore
result.score += 1;
}
});
return [...results].sort((a, b) => b.score - a.score).map((i) => i.obj);
} else {
return [...preparedContainers.value].sort((a, b) => b.created - a.created);
}
const { results } = useFuse(query, list, {
fuseOptions: { keys: ["name"], includeScore: true },
resultLimit,
matchAllWhenSearchEmpty: true,
});

onMounted(() => nextTick(() => autocomplete.value?.focus()));
const data = computed(() => {
return results.value
.sort((a, b) => {
if (a.score === b.score) {
if (a.item.state === "running" && b.item.state !== "running") {
return -1;
} else {
return 1;
}
} else if (a.score && b.score) {
return a.score - b.score;
} else {
return 0;
}
})
.map(({ item }) => item);
});
watchOnce(autocomplete, () => autocomplete.value?.focus());

function selected(item: { id: string; name: string }) {
router.push({ name: "container-id", params: { id: item.id } });
function selected({ id }: { id: string }) {
router.push({ name: "container-id", params: { id } });
emit("close");
}
function addColumn(container: Container) {
Expand Down
49 changes: 27 additions & 22 deletions assets/pages/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,22 @@
class="input"
type="text"
:placeholder="$t('placeholder.search-containers')"
v-model="search"
@keyup.esc="search.value = null"
v-model="query"
@keyup.esc="query = ''"
@keyup.enter="onEnter()"
/>
<span class="icon is-left">
<search-icon />
</span>
</p>
</div>
<p class="panel-tabs" v-if="!search">
<p class="panel-tabs" v-if="query === ''">
<a :class="{ 'is-active': sort === 'running' }" @click="sort = 'running'">{{ $t("label.running") }}</a>
<a :class="{ 'is-active': sort === 'all' }" @click="sort = 'all'">{{ $t("label.all") }}</a>
</p>
<router-link
:to="{ name: 'container-id', params: { id: item.id } }"
v-for="item in results.slice(0, 10)"
v-for="item in data.slice(0, 10)"
:key="item.id"
class="panel-block"
>
Expand All @@ -86,54 +86,59 @@
</template>

<script lang="ts" setup>
import fuzzysort from "fuzzysort";
import SearchIcon from "~icons/mdi-light/magnify";
import { useFuse } from "@vueuse/integrations/useFuse";

const { base, version, secured } = config;
const containerStore = useContainerStore();
const { containers } = storeToRefs(containerStore);
const router = useRouter();

const sort = ref("running");
const search = ref();
const sort = $ref("running");
const query = ref("");

const results = computed(() => {
if (search.value) {
return fuzzysort.go(search.value, containers.value, { key: "name" }).map((i) => i.obj);
const mostRecentContainers = $computed(() => [...containers.value].sort((a, b) => b.created - a.created));
const runningContainers = $computed(() => mostRecentContainers.filter((c) => c.state === "running"));

const { results } = useFuse(query, containers, {
fuseOptions: { keys: ["name"] },
matchAllWhenSearchEmpty: false,
});
const data = computed(() => {
if (results.value.length) {
return results.value.map(({ item }) => item);
}
switch (sort.value) {
switch (sort) {
case "all":
return mostRecentContainers.value;
return mostRecentContainers;
case "running":
return runningContainers.value;
return runningContainers;
default:
throw `Invalid sort order: ${sort.value}`;
throw `Invalid sort order: ${sort}`;
}
});

const mostRecentContainers = computed(() => [...containers.value].sort((a, b) => b.created - a.created));
const runningContainers = computed(() => mostRecentContainers.value.filter((c) => c.state === "running"));
const totalCpu = ref(0);
let totalCpu = $ref(0);
useIntervalFn(
() => {
totalCpu.value = runningContainers.value.reduce((acc, c) => acc + (c.stat?.cpu ?? 0), 0);
totalCpu = runningContainers.reduce((acc, c) => acc + (c.stat?.cpu ?? 0), 0);
},
1000,
{ immediate: true }
);
const totalMem = ref(0);

let totalMem = $ref(0);
useIntervalFn(
() => {
totalMem.value = runningContainers.value.reduce((acc, c) => acc + (c.stat?.memoryUsage ?? 0), 0);
totalMem = runningContainers.reduce((acc, c) => acc + (c.stat?.memoryUsage ?? 0), 0);
},
1000,
{ immediate: true }
);

function onEnter() {
if (results.value.length == 1) {
const [item] = results.value;
if (data.value.length > 0) {
const item = data.value[0];
router.push({ name: "container-id", params: { id: item.id } });
}
}
Expand Down
49 changes: 27 additions & 22 deletions assets/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,22 @@
class="input"
type="text"
:placeholder="$t('placeholder.search-containers')"
v-model="search"
@keyup.esc="search.value = null"
v-model="query"
@keyup.esc="query = ''"
@keyup.enter="onEnter()"
/>
<span class="icon is-left">
<search-icon />
</span>
</p>
</div>
<p class="panel-tabs" v-if="!search">
<p class="panel-tabs" v-if="query === ''">
<a :class="{ 'is-active': sort === 'running' }" @click="sort = 'running'">{{ $t("label.running") }}</a>
<a :class="{ 'is-active': sort === 'all' }" @click="sort = 'all'">{{ $t("label.all") }}</a>
</p>
<router-link
:to="{ name: 'container-id', params: { id: item.id } }"
v-for="item in results.slice(0, 10)"
v-for="item in data.slice(0, 10)"
:key="item.id"
class="panel-block"
>
Expand All @@ -86,54 +86,59 @@
</template>

<script lang="ts" setup>
import fuzzysort from "fuzzysort";
import SearchIcon from "~icons/mdi-light/magnify";
import { useFuse } from "@vueuse/integrations/useFuse";

const { base, version, secured } = config;
const containerStore = useContainerStore();
const { containers } = storeToRefs(containerStore);
const router = useRouter();

const sort = ref("running");
const search = ref();
const sort = $ref("running");
const query = ref("");

const results = computed(() => {
if (search.value) {
return fuzzysort.go(search.value, containers.value, { key: "name" }).map((i) => i.obj);
const mostRecentContainers = $computed(() => [...containers.value].sort((a, b) => b.created - a.created));
const runningContainers = $computed(() => mostRecentContainers.filter((c) => c.state === "running"));

const { results } = useFuse(query, containers, {
fuseOptions: { keys: ["name"] },
matchAllWhenSearchEmpty: false,
});
const data = computed(() => {
if (results.value.length) {
return results.value.map(({ item }) => item);
}
switch (sort.value) {
switch (sort) {
case "all":
return mostRecentContainers.value;
return mostRecentContainers;
case "running":
return runningContainers.value;
return runningContainers;
default:
throw `Invalid sort order: ${sort.value}`;
throw `Invalid sort order: ${sort}`;
}
});

const mostRecentContainers = computed(() => [...containers.value].sort((a, b) => b.created - a.created));
const runningContainers = computed(() => mostRecentContainers.value.filter((c) => c.state === "running"));
const totalCpu = ref(0);
let totalCpu = $ref(0);
useIntervalFn(
() => {
totalCpu.value = runningContainers.value.reduce((acc, c) => acc + (c.stat?.cpu ?? 0), 0);
totalCpu = runningContainers.reduce((acc, c) => acc + (c.stat?.cpu ?? 0), 0);
},
1000,
{ immediate: true }
);
const totalMem = ref(0);

let totalMem = $ref(0);
useIntervalFn(
() => {
totalMem.value = runningContainers.value.reduce((acc, c) => acc + (c.stat?.memoryUsage ?? 0), 0);
totalMem = runningContainers.reduce((acc, c) => acc + (c.stat?.memoryUsage ?? 0), 0);
},
1000,
{ immediate: true }
);

function onEnter() {
if (results.value.length == 1) {
const [item] = results.value;
if (data.value.length > 0) {
const item = data.value[0];
router.push({ name: "container-id", params: { id: item.id } });
}
}
Expand Down
2 changes: 1 addition & 1 deletion e2e/cypress/e2e/dozze_default.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ context("Dozzle default mode", { baseUrl: Cypress.env("DOZZLE_DEFAULT") }, () =>
it("shortcut for fuzzy search works", () => {
cy.get("body").type("{ctrl}k");

cy.get("input[placeholder='Search containers using ⌘ + k or ctrl + k']").should("be.visible");
cy.get("input[placeholder='Search containers (⌘ + k, ⌃k)']").should("be.visible");
});
});
2 changes: 1 addition & 1 deletion locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ button:
logout: Logout
login: Login
placeholder:
search-containers: Search Containers
search-containers: Search containers (⌘ + k, ⌃k)
settings:
display: Display
small-scrollbars: Use smaller scrollbars
Expand Down
2 changes: 1 addition & 1 deletion locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ button:
logout: Cerrar la sesión
login: Iniciar sesión
placeholder:
search-containers: Buscar Contenedores
search-containers: Buscar contenedores (⌘ + K, CTRL + K)
settings:
display: Vista
small-scrollbars: Utilizar barras de desplazamiento más pequeñas
Expand Down
2 changes: 1 addition & 1 deletion locales/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ button:
logout: Terminar sessão
login: Iniciar sessão
placeholder:
search-containers: Pesquisar Contentores
search-containers: Pesquisar contentores (⌘ + K, CTRL + K)
settings:
display: Visão
small-scrollbars: Usar barras de rolagem mais pequenas
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@
"@vitejs/plugin-vue": "3.1.0",
"@vue/compiler-sfc": "^3.2.39",
"@vueuse/core": "^9.2.0",
"@vueuse/integrations": "^9.2.0",
"@vueuse/router": "^9.2.0",
"ansi-to-html": "^0.7.2",
"bulma": "^0.9.4",
"date-fns": "^2.29.3",
"fuzzysort": "^2.0.1",
"fuse.js": "^6.6.2",
"lodash.debounce": "^4.0.8",
"pinia": "^2.0.22",
"sass": "^1.54.9",
Expand Down
Loading