aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/views/Blog/components/ContentSearch.vue
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-07-12 01:28:23 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-07-12 01:28:23 -0400
commitf64955c69d91e578e580b409ba31ac4b3477da96 (patch)
tree16f01392ddf1abfea13d7d1ede3bfb0459fe8f0d /front-end/src/views/Blog/components/ContentSearch.vue
Initial commit
Diffstat (limited to 'front-end/src/views/Blog/components/ContentSearch.vue')
-rw-r--r--front-end/src/views/Blog/components/ContentSearch.vue115
1 files changed, 115 insertions, 0 deletions
diff --git a/front-end/src/views/Blog/components/ContentSearch.vue b/front-end/src/views/Blog/components/ContentSearch.vue
new file mode 100644
index 0000000..37fd438
--- /dev/null
+++ b/front-end/src/views/Blog/components/ContentSearch.vue
@@ -0,0 +1,115 @@
+<template>
+ <div id="content-search" class="my-4">
+ <div class="">
+ <div class="">
+ <input class="w-full input primary" placeholder="Search..." v-model="search" />
+ </div>
+ </div>
+ <div class="search-results">
+ <div v-if="searchResults.length == 0" class="result">
+ No results found.
+ </div>
+ <div v-else v-for="result in searchResults" :key="result.id" @click.prevent="onSelected(result)" class="result">
+ <div class="flex-auto result name">
+ {{ result.shortName }}
+ </div>
+ <div class="result id">
+ {{ result.shortId }}
+ </div>
+ <div class="rseult controls">
+ <div v-if="waiting">
+ <fa-icon icon="spinner" spin />
+ </div>
+ <div v-else-if="result.copied.value" class="text-sm text-amber-500">
+ copied
+ </div>
+ <div v-else class="">
+ <button class="btn secondary sm borderless" @click="result.copyLink()">
+ <fa-icon icon="link" />
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { useClipboard } from '@vueuse/core';
+import { apiCall, useWait } from '@vnuge/vnlib.browser';
+import { computed, Ref, ref } from 'vue';
+import { map, slice, truncate } from 'lodash';
+import { ContentMeta } from '@vnuge/cmnext-admin';
+import { BlogState } from '../blog-api';
+
+const emit = defineEmits(['selected'])
+
+const props = defineProps<{
+ blog: BlogState,
+}>()
+
+const { createReactiveSearch, getPublicUrl } = props.blog.content
+const { waiting } = useWait()
+
+const search = ref('')
+const searcher = createReactiveSearch(search);
+
+interface ContentResult extends ContentMeta {
+ readonly shortId: string,
+ readonly shortName: string,
+ readonly copied: Ref<boolean>,
+ copyLink(): void
+}
+
+const searchResults = computed<ContentResult[]>(() => {
+ const current = slice(searcher.value, 0, 5);
+
+ //Copies the link to the clipboard from the server to insert into the editor
+ const copyLink = (result : ContentMeta, copy : (text: string) => Promise<void> ) => {
+ apiCall(async () =>{
+ const link = await getPublicUrl(result);
+ await copy(link);
+ })
+ }
+
+ //Formats the result for display
+ return map(current, content => {
+ //scoped clipboard for copy link
+ const { copied, copy } = useClipboard();
+ return {
+ ...content,
+ //truncate the id and name for display
+ shortId: truncate(content.id, { length: 15 }),
+ shortName: truncate(content.name, { length: 24 }),
+ copyLink: () => copyLink(content, copy),
+ copied
+ }
+ })
+})
+
+const onSelected = (result: ContentResult) => {
+ emit('selected', result)
+}
+
+</script>
+
+<style lang="scss">
+
+ .search-results{
+ @apply mt-3;
+ }
+
+ .result{
+ @apply flex flex-row items-center justify-between;
+ @apply p-1 cursor-pointer hover:bg-gray-100 dark:hover:bg-dark-600;
+
+ .id{
+ @apply text-sm;
+ }
+
+ .controls{
+ @apply min-w-[4rem] text-center;
+ }
+ }
+
+</style> \ No newline at end of file