docs: cleaned up playground and mildly improved ux

This commit is contained in:
Ken Snyder
2022-02-01 00:16:38 -08:00
parent b9c2bea63d
commit e4ce4f813f
9 changed files with 131 additions and 65 deletions

View File

@@ -22,6 +22,7 @@
"@vueuse/head": "^0.7.5",
"date-fns": "^2.28.0",
"floating-vue": "^2.0.0-beta.3",
"inferred-types": "^0.18.4",
"nprogress": "^0.2.0",
"pinia": "^2.0.11",
"prism-theme-vars": "^0.2.2",

View File

@@ -1,50 +1,105 @@
<script setup lang="ts">
import { Keys } from "inferred-types";
import { useSearch } from "~/modules/search";
const search = useSearch();
const s = useSearch();
/**
* this allows all expected indexes to be shown and in a sensible order
*/
const knownIndexes = ["api", "prose", "repo", "consolidated"];
/** the name of the indexes current known by MeiliSearch */
const currentIndexes = computed(() => search.$state.indexes.map(i => i.name));
const currentIndexes = computed(() => s.$state.indexes.map(i => i.name));
/**
* we do want to see ANY index that exists though too
*/
const unknownIndexes = computed(() => {
return search.$state.indexes.filter(i => !knownIndexes.includes(i.name));
return s.$state.indexes.filter(i => !knownIndexes.includes(i.name));
});
const indexStateOptions = ["consolidated", "individual", "bespoke"] as const;
type IndexState = Keys<typeof indexStateOptions>;
const indexState = computed(() => {
if(s.$state.searchUsing.length === 1 && s.$state.searchUsing[0] === "consolidated") {
return "consolidated";
} else if(s.$state.searchUsing.length === 3 && ["api", "prose", "repo"].every(i => s.$state.searchUsing.includes(i))) {
return "individual";
} else {
return "bespoke";
}
});
const changeIndexes = (opt: IndexState) => {
switch(opt) {
case "consolidated":
s.setUseOfIndexes(["consolidated"]);
break;
case "individual":
s.setUseOfIndexes(["api", "prose", "repo"]);
}
};
const optionStyle = (opt: IndexState) => computed(() => {
const isSelected = indexState.value === opt;
const selected = isSelected ? "opacity-100 border-b-1" : "opacity-50";
switch(opt) {
case "bespoke":
return `${selected} cursor-default`;
default:
return isSelected ? `${selected} cursor-default` : `${selected} cursor-pointer`;
}
});
</script>
<template>
<div class="search-indexes flex flex-col flex-grow rounded-md bg-gray-100/25 dark:bg-gray-800/25">
<div class="search-indexes flex flex-col flex-grow rounded-md bg-gray-100/25 dark:bg-gray-800/25 ">
<div class="rounded-t-md overflow-hidden">
<div class="rounded-t-md overflow-hidden ">
<div class="font-bold text-lg rounded-t-md bg-gray-900/10 dark:bg-gray-100/10 py-3 px-4">
INDEXES
</div>
</div>
<div v-if="search.$state.health" class="text-xs mt-2" >
<div class="text-green-500">MeiliSearch Service Healthy!</div>
<div class="db-size">db size: {{search.dbSize}}</div>
</div>
<span v-else class="text-red-500 ">
Meilisearch is not available locally!
</span>
<div class="index-list p-4 w-full flex flex-col flex-grow ">
<div class="index-usage flex flex-col items-center p-1 bg-gray-500/10 rounded mb-4">
<div>Index Usage</div>
<div class="flex mt-2 flex-row space-x-3 text-gray-600 dark:text-gray-400 text-sm ">
<div
v-for="opt in indexStateOptions"
:key="opt"
class="option flex"
:class="optionStyle(opt).value"
@click="() => changeIndexes(opt)"
>
{{opt}}
</div>
</div>
</div>
<div class="index-list p-4 w-full flex flex-col ">
<template v-for="idx in knownIndexes" :key="idx">
<current-index v-if="currentIndexes.includes(idx)" :idx="search.$state.indexes.find(i => i.name === idx)"/>
<missing-index v-else :idx="idx" />
<current-index v-if="currentIndexes.includes(idx)" :idx="s.$state.indexes.find(i => i.name === idx)"/>
<missing-index v-else :idx="idx" />
</template>
<template v-for="idx in unknownIndexes" :key="idx">
<current-index :idx="idx" :unknown="true" />
<current-index :idx="idx" :unknown="true" />
</template>
<div class="mt-8">
<div v-if="s.$state.health" class="text-xs mt-2" >
<div class="text-green-500">MeiliSearch Service Healthy!</div>
<div class="db-size">db size: {{s.dbSize}}</div>
</div>
<span v-else class="text-red-500 ">
Meilisearch is not available locally!
</span>
</div>
<div class="flex flex-grow"></div>
</div>
</div>
</template>

View File

@@ -4,20 +4,22 @@ const props = defineProps({
query: {type: String, default: ""}
});
const s = useSearch();
const notRealQuery = computed(() => props.query.trim().length === 0);
</script>
<template>
<div class="search-stats flex flex-col rounded-md h-full min-h-128">
<div class="search-stats flex flex-col rounded-md h-full min-h-128 bg-gray-100/25 dark:bg-gray-800/25">
<div class="rounded-t-md overflow-hidden">
<div class="grid grid-cols-3 rounded-t-md bg-gray-900/10 dark:bg-gray-100/10 py-3 px-4 ">
<div class="block text-sm font-light place-self-start self-center">
<div class="rounded-t-md overflow-hidden bg-gray-900/10 dark:bg-gray-100/10 ">
<div class="grid grid-cols-3 rounded-t-md py-3 px-4 ">
<div class="block text-sm font-light place-self-start self-center" :class="notRealQuery ? 'opacity-50' : ''">
{{s.$state.searchUsing.length}} <span class="italic">index(s) searched</span>
</div>
<div class="block font-bold text-lg place-self-center self-center">
Results
RESULTS
</div>
<div class="block text-sm font-light place-self-end self-center">
<div class="block text-sm font-light place-self-end self-center" :class="notRealQuery ? 'opacity-50' : ''">
{{s.$state.searchResults.length}} <span class="italic">docs found</span>
</div>
</div>
@@ -27,7 +29,7 @@ const s = useSearch();
<search-hit v-for="hit in s.searchResults" :key="hit.id" :document="hit" />
</div>
<div v-else class="flex flex-col flex-grow justify-center italic">
<div v-if="query.length > 0" >
<div v-if="props.query.length > 0" >
no results
</div>
<div v-else>

View File

@@ -25,6 +25,8 @@ export interface SearchState {
searchStatus: "ready" | "searching" | "error" | "not-ready";
searchQuery: string;
searchResults: {id: string; _idx: string; [key: string]: unknown}[];
}
@@ -35,15 +37,23 @@ export const useSearch = defineStore("search", ({
indexSettings: {},
searchUsing: ["consolidated"],
stats: undefined,
searchQuery: "",
searchStatus: "not-ready",
searchResults: [],
}) as SearchState,
actions: {
async search(text: string) {
async search(text: string, force: boolean = false) {
if(text.trim()==="") {
this.$state.searchResults = [];
this.$state.searchQuery = text.trim();
return;
}
if(text.trim() === this.searchQuery.trim() && !force) {
// no change
return;
} else {
this.searchQuery = text.trim();
}
const indexes = this.$state.searchUsing;
console.group(`searching for: "${text}"`);
console.info(`search on ${indexes.length} indexes`, indexes);
@@ -82,10 +92,16 @@ export const useSearch = defineStore("search", ({
toggleUseOfIndex(idx: string) {
if(this.$state.searchUsing.includes(idx)) {
this.$state.searchUsing = this.$state.searchUsing.filter(i => i !== idx);
this.search(this.searchQuery, true);
} else {
this.$state.searchUsing = [...this.$state.searchUsing, idx];
this.search(this.searchQuery, true);
}
},
setUseOfIndexes(indexes: string[]) {
this.$state.searchUsing = indexes;
this.search(this.searchQuery, true);
},
statsUpdate(s: MeiliSearchStats) {
this.$state.stats = s;
},
@@ -117,6 +133,7 @@ if (import.meta.hot) {
import.meta.hot)
);
}
//#endregion STORE
//#region SERVICE
@@ -141,7 +158,6 @@ export const install: UserModule = ({ isClient }) => {
s.$state.searchStatus = !h ? "not-ready": "ready";
if(h === true && !isActive.value) {
createIndexes();
resume();
};
if(h === false && isActive.value) pause();

View File

@@ -2,8 +2,8 @@
import { onStartTyping } from "@vueuse/core";
import { useSearch } from "~/modules/search";
const el = ref();
const searchText = ref("");
const s = useSearch();
const searchText = ref(s.$state.searchQuery);
const { t } = useI18n();
@@ -26,28 +26,41 @@ onStartTyping(() => {
<h1 class="text-xl mb-4">
MeiliSearch Playground
</h1>
<div class="grid grid-cols-3 px-4 gap-x-4">
<div class="left">
<!-- -->
</div>
<div class="centered">
<input
id="input"
ref="el"
v-model="searchText"
:placeholder="t('home.ask-for-search')"
:aria-label="t('home.ask-for-search')"
type="text"
autocomplete="false"
spellcheck="false"
class="px-auto px-4 py-4 w-350px text-center bg-transparent self-center"
border="~ rounded gray-200 dark:gray-700"
outline="none active:none"
>
<label class="hidden" for="input">{{ t('home.ask-for-search') }}</label>
</div>
<div class="right flex flex-col items-center p-1 rounded">
</div>
</div>
<input
id="input"
ref="el"
v-model="searchText"
:placeholder="t('home.ask-for-search')"
:aria-label="t('home.ask-for-search')"
type="text"
autocomplete="false"
spellcheck="false"
class="px-auto px-4 py-3 w-350px text-center bg-transparent self-center"
border="~ rounded gray-200 dark:gray-700"
outline="none active:none"
>
<label class="hidden" for="input">{{ t('home.ask-for-search') }}</label>
<div class="results-area grid grid-cols-3 gap-4 h-full mt-8 mx-4">
<div class="flex flex-col col-span-1 space-y-4">
<search-indexes class="" />
</div>
<div class="col-span-2 rounded-md bg-gray-100/25 dark:bg-gray-900/25">
<div class="col-span-2 rounded-md">
<search-results :query="searchText" />
</div>
</div>

View File

@@ -23,6 +23,7 @@
"push-typescript": "node bin/push-typescript.js",
"push-consolidated": "node bin/push-consolidated.js",
"restart": "docker compose restart",
"build": "run-p build:*",
"build:cli": "tsup src/cli/*.ts --format=esm,cjs --clean --sourcemap -d bin",
"build:npm": "tsup src/index.ts --dts --format=esm,cjs --sourcemap --clean -d dist ",
"test": "vitest run",

View File

@@ -1,5 +0,0 @@
import { githubPipeline } from "~/pipelines/githubPipeline";
(async () => {
await githubPipeline();
})();

View File

@@ -1,19 +0,0 @@
import { GithubMapper } from "~/mappers/GithubMapper";
import { RepoModel } from "~/models/RepoModel";
import { IMeilisearchAddOrReplace } from "~/types";
import { getRepo } from "~/utils/github/getRepo";
/**
* Iterates over all marked repos and updates Meilisearch documents
* associated to them.
*/
export async function githubPipeline() {
const model = await RepoModel;
const waitFor: Promise<IMeilisearchAddOrReplace>[] = [];
for (const repo of REPOS) {
const resp = await getRepo(repo);
waitFor.push(model.query.addOrReplaceDoc(GithubMapper(resp)));
}
await Promise.all(waitFor);
}

2
pnpm-lock.yaml generated
View File

@@ -40,6 +40,7 @@ importers:
eslint-plugin-cypress: ^2.12.1
floating-vue: ^2.0.0-beta.3
https-localhost: ^4.7.0
inferred-types: ^0.18.4
jsdom: ^19.0.0
markdown-it-collapsible: ^1.0.0
markdown-it-link-attributes: ^4.0.0
@@ -73,6 +74,7 @@ importers:
'@vueuse/head': 0.7.5_vue@3.2.29
date-fns: 2.28.0
floating-vue: 2.0.0-beta.3_vue@3.2.29
inferred-types: 0.18.4
nprogress: 0.2.0
pinia: 2.0.11_typescript@4.5.5+vue@3.2.29
prism-theme-vars: 0.2.2