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
Branches
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"],
methods: {
updatePage(pageNumber) {
this.$emit("page:update", pageNumber);
emits: ["page:update"],
props: {
items: Array,
currentPage: Number,
pageSize: Number,
},
methods: {
/**
* 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.
Please to comment