diff --git a/components/global/Box.vue b/components/global/Box.vue new file mode 100644 index 0000000000000000000000000000000000000000..ae4b4cda2ff387a1811cbd7f2bebec3c89416f22 --- /dev/null +++ b/components/global/Box.vue @@ -0,0 +1,48 @@ +<template> + <component + :is="to ? 'nuxt-link' : 'div'" + :to="to" + class="block rounded p-4" + :class="computedClass" + > + <slot /> + </component> +</template> + +<script> +export default { + name: 'Box', + props: { + to: { + type: [String, Object], + default: null, + }, + color: { + type: String, + default: null, + }, + }, + computed: { + computedClass() { + let classes = '' + if (this.color) { + classes += `bg-${this.color}-100 dark:bg-${this.color}-900` + } else { + classes = `border dark:border-gray-700` + } + if (this.to) + classes += + ' transition transform hover:shadow-xl hover:-translate-y-0.5 is-box-with-link' + return classes + }, + }, +} +</script> + +<style lang="postcss" scoped> +.is-box-with-link { + text-decoration: none !important; + color: currentColor !important; + font-weight: normal !important; +} +</style> diff --git a/components/global/List.vue b/components/global/List.vue new file mode 100644 index 0000000000000000000000000000000000000000..1e01318c0d69389ecfff5bf9434f0bf71d81c48e --- /dev/null +++ b/components/global/List.vue @@ -0,0 +1,104 @@ +<template> + <div class="prose dark:prose-dark"> + <slot /> + + <slot v-if="Array.isArray(data)" name="items" :items="data"> + <ul class="list"> + <li v-for="(item, i) in data" :key="i"> + <nuxt-link :to="item.path"> + <div v-if="title">{{ item.title }}</div> + </nuxt-link> + <span v-if="description">{{ item.description }}</span> + </li> + </ul> + </slot> + <slot v-else-if="data" name="item"> + <h2 v-if="title">{{ data.title }}</h2> + <p v-if="description">{{ data.description }}</p> + </slot> + </div> +</template> + +<script> +export default { + name: 'List', + props: { + content: { + type: String, + default: null, + }, + only: { + type: [String, Array], + default: null, + }, + where: { + type: Object, + default: null, + }, + sortBy: { + type: String, + default: null, + }, + direction: { + type: String, + default: 'asc', + validator(value) { + return ['asc', 'desc'].includes(value) + }, + }, + skip: { + type: [Number, String], + default: null, + }, + search: { + type: String, + default: null, + }, + searchField: { + type: String, + default: null, + }, + limit: { + type: [Number, String], + default: null, + }, + + title: { + type: Boolean, + default: true, + }, + description: { + type: Boolean, + default: false, + }, + readingTime: { + type: Boolean, + default: false, + }, + }, + data() { + return { + data: null, + } + }, + async fetch() { + if (this.content) { + const content = this.$content(this.content) + if (this.only) content.only(this.only) + if (this.where) content.where(this.where) + if (this.sortBy) content.sortBy(this.sortBy, this.direction) + if (this.skip) content.skip(+this.skip) + if (this.search) { + this.searchField + ? content.search(this.searchField, this.search) + : content.search(this.search) + } + if (this.limit) content.limit(+this.limit) + + this.data = await content.fetch() + } + }, +} +</script> + +<style lang="postcss" scoped></style>