Skip to content
Snippets Groups Projects
Commit 395bbef0 authored by Gilgard's avatar Gilgard
Browse files

Docs and code updates to base comps

parent f52672f5
No related branches found
No related tags found
1 merge request!165Basecomponent docs and tests
Showing with 168 additions and 76 deletions
<template>
<!-- Button with custom text and color -->
<button
class="flex items-center px-2 py-2 font-medium tracking-wide capitalize text-white transition-colors duration-200 transform rounded-md focus:outline-none focus:ring focus:ring-opacity-80 min-w-{20px}"
:class="color"
......@@ -8,6 +9,13 @@
</template>
<script>
/**
* Colored button component with customizable text and 3 possible colors.
* Colors:
* blue (default),
* red,
* green
*/
export default {
name: "ColoredButton",
props: {
......@@ -18,6 +26,9 @@ export default {
},
},
computed: {
/**
* Formats the tailwind css tags based on the color passed from parent
*/
color() {
if (this.buttonColor === "red") {
return "bg-error-medium hover:bg-error-dark focus:ring-error-light";
......
......@@ -14,22 +14,12 @@
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
{{ title }}
</h3>
<!-- Close button -->
<button
@click="close()"
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
>
<svg
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
<XIcon class="w-5 h-5" />
</button>
</div>
<!-- Modal body -->
......@@ -40,7 +30,7 @@
</div>
<!-- Modal footer -->
<div class="rounded-b border-t border-gray-200 dark:border-gray-600">
<!-- Slot: Add any html you want here -->
<!-- Slot: Add any html you want here (Must be one div) -->
<slot />
</div>
</div>
......@@ -49,14 +39,27 @@
</template>
<script>
import { XIcon } from "@heroicons/vue/outline";
/**
* CustomFooterModal component for adding buttons and such to the footer of the modals.
* Buttons are added to slot so must be in a single div if multiple html elements are needed.
*/
export default {
name: "CustomFooterModal",
emits: ["close"],
components: {
XIcon,
},
props: {
visible: Boolean,
title: String,
message: String,
},
methods: {
/**
* Method that emits close to parent.
*/
close() {
this.$emit("close");
},
......
<template>
<!-- Footer -->
<footer
class="w-full bg-white dark:bg-gray-800 sm:flex-row border-1 border-t border-gray-600 h-10"
>
<!-- Copyright -->
<p class="float-left text-xs my-3 ml-4 text-primary-dark">
© BoCo 2022 - All rights reserved
</p>
<!-- Icon link to help page -->
<QuestionMarkCircleIcon
class="md:mt-0 mt-1 mr-4 float-right cursor-pointer h-8 md:h-10 text-primary-medium"
alt="Hjelp"
......@@ -15,9 +18,12 @@
<script>
import { QuestionMarkCircleIcon } from "@heroicons/vue/outline";
/**
* FooterBar for homepage
*/
export default {
name: "FooterBar",
components: {
QuestionMarkCircleIcon,
},
......
<template>
<!-- Image -->
<img
:src="image"
class="w-2/5 inline"
alt="Bilde av gjenstanden"
@click="toggleModal"
/>
<custom-footer-modal
<!-- Modal -->
<CustomFooterModal
@close="toggleModal"
:message="'Bilder som fjernes kan ikke lastes opp på nytt uten å laste opp et annet bilde først.'"
:title="'Er du sikker på at du vil fjene bilde?'"
:visible="show"
>
<!-- Buttons for modal footer -->
<div class="flex justify-center p-2">
<colored-button :text="'Avbryt'" @click="toggleModal" class="m-2" />
<colored-button :text="'Slett'" @click="removeImage" class="m-2" />
<ColoredButton :text="'Avbryt'" @click="toggleModal" class="m-2" />
<ColoredButton
:text="'Slett'"
:color="'red'"
@click="removeImage"
class="m-2"
/>
</div>
</custom-footer-modal>
</CustomFooterModal>
</template>
<script>
import ColoredButton from "@/components/BaseComponents/ColoredButton";
import CustomFooterModal from "@/components/BaseComponents/CustomFooterModal.vue";
/**
* Displays uploaded image for forms.
*/
export default {
components: {
ColoredButton,
......@@ -36,9 +47,15 @@ export default {
image: String,
},
methods: {
/**
* Emits remove image to parent
*/
removeImage() {
this.$emit("remove");
},
/**
* Toggles modal
*/
toggleModal() {
this.show = !this.show;
},
......
<template>
<!-- Button -->
<button
class="flex items-center px-2 py-2 font-medium tracking-wide capitalize text-white transition-colors duration-200 transform rounded-md focus:outline-none focus:ring focus:ring-opacity-80"
:class="color"
>
<div class="w-5 h-5 mx-1">
<!-- Slot for icon (Default BanIcon) -->
<slot><BanIcon /></slot>
</div>
<span class="mx-1">{{ text }}</span>
......@@ -13,6 +15,13 @@
<script>
import { BanIcon } from "@heroicons/vue/outline";
/**
* IconButton component that can be customized with text, color and a slot for passing an icon.
* Colors:
* blue (default),
* red,
* green
*/
export default {
name: "IconButton",
props: {
......@@ -23,6 +32,9 @@ export default {
BanIcon,
},
computed: {
/**
* Formats the tailwind css tags based on the color passed from parent
*/
color() {
if (this.buttonColor === "red") {
return "bg-error-medium hover:bg-error-dark focus:ring-error-light";
......
<template>
<!-- PacMan for indicating loading -->
<div class="loadingio-spinner-bean-eater-o5tefvffeqm">
<div class="ldio-sweozsnwol">
<div>
......@@ -16,6 +17,11 @@
</template>
<style scoped type="text/css">
/**
* LoadSpinner component for indicating loading in the form of PacMan.
*
* generated by https://loading.io/
*/
@keyframes ldio-sweozsnwol-1 {
0% {
transform: rotate(0deg);
......@@ -107,10 +113,9 @@
position: relative;
transform: translateZ(0) scale(1);
backface-visibility: hidden;
transform-origin: 0 0; /* see note above */
transform-origin: 0 0;
}
.ldio-sweozsnwol div {
box-sizing: content-box;
}
/* generated by https://loading.io/ */
</style>
<template>
<!-- NavBar -->
<nav
class="flex items-center bg-white justify-between h-10 md:h-14 border-1 border-b border-gray-300 border-solid sticky top-0 z-50"
>
<!-- Logo reroutes to homepage -->
<div class="logo">
<img
class="m-1 ml-4 cursor-pointer h-9 md:h-12"
......@@ -11,6 +13,7 @@
/>
</div>
<ul class="flex justify-between">
<!-- New listing button -->
<li
class="cursor-pointer"
v-if="this.$store.state.user.token !== null"
......@@ -22,25 +25,35 @@
/>
<a class="hidden md:block mt-7 text-sm float-right">Legg til</a>
</li>
<!-- My messages button -->
<li
class="cursor-pointer"
v-if="this.$store.state.user.token !== null"
@click="loadMessages"
>
<div class="notification-container">
<div class="notification-container relative">
<ChatAlt2Icon
class="m-6 md:mr-2 h-7 text-primary-medium float-left"
alt="Meldinger"
/>
<p class="notification" v-if="newMessages > 0">{{ notifications }}</p>
<p
class="notification absolute bg-secondary top-0 p-1 min-w-[20px] min-h-[20px] rounded-full text-xs text-white font-bold text-center right-0 cursor-pointer"
v-if="newMessages > 0"
>
{{ notifications }}
</p>
<a class="hidden md:block mt-7 text-sm float-right">Meldinger</a>
</div>
</li>
<!-- User profile button -->
<li class="cursor-pointer" @click="loadProfile">
<UserCircleIcon
class="m-6 md:mr-2 h-7 text-primary-medium float-left"
alt="Profil"
/>
<!-- Shows "Profil" if user is logged in, else "Logg inn" -->
<a
v-if="this.$store.state.user.token !== null"
class="hidden md:block mr-4 mt-7 text-sm float-right"
......@@ -59,16 +72,27 @@ import { parseUserFromToken } from "@/utils/token-utils";
import { PlusIcon, ChatAlt2Icon, UserCircleIcon } from "@heroicons/vue/outline";
import ws from "@/services/ws";
/**
* NavBar component used in App
*/
export default {
name: "NavBar.vue",
name: "NavBar",
components: {
PlusIcon,
ChatAlt2Icon,
UserCircleIcon,
},
data() {
return {
newMessages: 0,
};
},
computed: {
/**
* Format method for notification number on messages.
* Shows "+99" the user has more than 99 new messages.
*/
notifications() {
// if new messages is greater than 99 show +99
if (this.newMessages > 99) {
return "+99";
} else {
......@@ -76,13 +100,10 @@ export default {
}
},
},
components: {
PlusIcon,
ChatAlt2Icon,
UserCircleIcon,
},
methods: {
/**
* Method for routing to user profile. Routes to user profile if logged in, otherwise to login.
*/
async loadProfile() {
if (this.$store.state.user.token !== null) {
let user = parseUserFromToken(this.$store.state.user.token);
......@@ -92,11 +113,17 @@ export default {
await this.$router.push("/login");
}
},
/**
* Method for routing to messages.
*/
loadMessages() {
this.newMessages = 0;
this.$router.push("/messages");
},
},
/**
* On creation of this component the websocket service checks if the user has new messages. To display by the chat button.
*/
created() {
ws.on(
"NEW_MESSAGE",
......@@ -111,24 +138,8 @@ export default {
</script>
<style scoped>
.notification-container {
position: relative;
}
.notification {
position: absolute;
background-color: #ff5a5f;
top: 0;
min-width: 20px;
min-height: 20px;
padding: 0.25rem;
transform: translate(-290%, 50%);
color: white;
font-size: 10px;
border-radius: 50%;
font-weight: bold;
text-align: center;
right: 0;
cursor: pointer;
}
@media (max-width: 768px) {
......
......@@ -14,22 +14,12 @@
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
{{ title }}
</h3>
<!-- Close button -->
<button
@click="close()"
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
>
<svg
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
<XIcon class="w-5 h-5" />
</button>
</div>
<!-- Modal body -->
......@@ -44,14 +34,26 @@
</template>
<script>
import { XIcon } from "@heroicons/vue/outline";
/**
* Modal for displaying notifications.
*/
export default {
name: "NotificationModal",
emits: ["close"],
components: {
XIcon,
},
props: {
visible: Boolean,
title: String,
message: String,
},
methods: {
/**
* Method that emits close to parent.
*/
close() {
this.$emit("close");
},
......
<template>
<!-- Pagination -->
<div v-if="totalPages() > 0">
<!-- Prev button -->
<span
v-if="showPreviousLink()"
class="cursor-pointer inline-flex items-center p-2 text-sm font-medium text-primary-light bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-gray-700"
......@@ -7,9 +9,11 @@
>
Forrige
</span>
<!-- Current page -->
<label class="mx-2 text-primary-light"
>{{ currentPage + 1 }} av {{ totalPages() }}</label
>
<!-- Next button -->
<span
v-if="showNextLink()"
class="cursor-pointer inline-flex items-center p-2 text-sm font-medium text-primary-light bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-gray-700"
......@@ -21,19 +25,41 @@
</template>
<script>
/**
* Pagination component
*/
export default {
name: "paginationTemplate",
props: ["items", "currentPage", "pageSize"],
emits: ["page:update"],
props: {
items: Array,
currentPage: Number,
pageSize: Number,
},
methods: {
updatePage(pageNumber) {
this.$emit("page:update", pageNumber);
},
/**
* Calculates the number of pages to display whole list.
*/
totalPages() {
return Math.ceil(this.items.length / this.pageSize);
},
/**
* Emits page update to parent.
*/
updatePage(pageNumber) {
this.$emit("page:update", pageNumber);
},
/**
* Checks if previous button is needed.
* Returns true if not on first page.
*/
showPreviousLink() {
return this.currentPage == 0 ? false : true;
},
/**
* Checks if next button is needed.
* Returns true if not on last page.
*/
showNextLink() {
return this.currentPage == this.totalPages() - 1 ? false : true;
},
......
......@@ -5,20 +5,19 @@
class="w-10 h-10 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg float-right text-sm p-1.5"
type="button"
>
<svg
class="w-6 h-6"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"
></path>
</svg>
<DotsVerticalIcon class="w-6 h-6" />
</button>
</template>
<script>
import { DotsVerticalIcon } from "@heroicons/vue/outline";
/**
* Triple dot button
*/
export default {
name: "TripleDotButton",
components: {
DotsVerticalIcon,
},
};
</script>
......@@ -49,7 +49,7 @@
<script>
import axios from "axios";
import { tokenHeader, parseCurrentUser } from "@/utils/token-utils";
import { getItemPictures, } from "@/utils/apiutil";
import { getItemPictures } from "@/utils/apiutil";
export default {
props: {
......@@ -61,7 +61,7 @@ export default {
data() {
return {
image: null,
}
};
},
computed: {
userID() {
......@@ -113,8 +113,8 @@ export default {
if (images.length > 0) {
this.image = images[0].picture;
} else {
this.image = "https://images.unsplash.com/photo-1453728013993-6d66e9c9123a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8dmlld3xlbnwwfHwwfHw%3D&w=1000&q=80";
this.image =
"https://images.unsplash.com/photo-1453728013993-6d66e9c9123a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8dmlld3xlbnwwfHwwfHw%3D&w=1000&q=80";
}
},
},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment