This commit is contained in:
2023-03-28 10:34:44 +02:00
parent f803effbbd
commit f7e0f37528
428 changed files with 9982 additions and 100 deletions
@@ -0,0 +1,96 @@
<template>
<md-card>
<md-card-header
class="card-chart"
:data-background-color="dataBackgroundColor"
>
<div :id="chartId" class="ct-chart"></div>
</md-card-header>
<md-card-content>
<slot name="content"></slot>
</md-card-content>
<md-card-actions md-alignment="left">
<slot name="footer"></slot>
</md-card-actions>
</md-card>
</template>
<script>
export default {
name: "chart-card",
props: {
footerText: {
type: String,
default: "",
},
headerTitle: {
type: String,
default: "Chart title",
},
chartType: {
type: String,
default: "Line", // Line | Pie | Bar
},
chartOptions: {
type: Object,
default: () => {
return {};
},
},
chartResponsiveOptions: {
type: Array,
default: () => {
return [];
},
},
chartData: {
type: Object,
default: () => {
return {
labels: [],
series: [],
};
},
},
dataBackgroundColor: {
type: String,
default: "",
},
},
data() {
return {
chartId: "no-id",
};
},
methods: {
/***
* Initializes the chart by merging the chart options sent via props and the default chart options
*/
initChart(Chartist) {
var chartIdQuery = `#${this.chartId}`;
Chartist[this.chartType](chartIdQuery, this.chartData, this.chartOptions);
},
/***
* Assigns a random id to the chart
*/
updateChartId() {
var currentTime = new Date().getTime().toString();
var randomInt = this.getRandomInt(0, currentTime);
this.chartId = `div_${randomInt}`;
},
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
},
mounted() {
this.updateChartId();
import("chartist").then((Chartist) => {
let ChartistLib = Chartist.default || Chartist;
this.$nextTick(() => {
this.initChart(ChartistLib);
});
});
},
};
</script>
@@ -0,0 +1,13 @@
<template>
<md-card class="md-card-nav-tabs">
<md-card-content>
<slot name="content"></slot>
</md-card-content>
</md-card>
</template>
<script>
export default {
name: "nav-tabs-card",
};
</script>
@@ -0,0 +1,26 @@
<template>
<md-card class="md-card-stats">
<md-card-header :data-background-color="dataBackgroundColor">
<slot name="header"></slot>
</md-card-header>
<md-card-content>
<slot name="content"></slot>
</md-card-content>
<md-card-actions md-alignment="left">
<slot name="footer"></slot>
</md-card-actions>
</md-card>
</template>
<script>
export default {
name: "stats-card",
props: {
dataBackgroundColor: {
type: String,
default: "",
},
},
};
</script>
+45
View File
@@ -0,0 +1,45 @@
<template>
<div
class="dropdown"
:class="{ open: isOpen }"
@click="toggleDropDown"
v-click-outside="closeDropDown"
>
<slot name="title">
<a
class="dropdown-toggle"
data-toggle="dropdown"
href="javascript:void(0)"
>
<i :class="icon"></i>
<p class="notification">
{{ title }}
<b class="caret"></b>
</p>
</a>
</slot>
<slot></slot>
</div>
</template>
<script>
export default {
name: "drop-down",
props: {
title: String,
icon: String,
},
data() {
return {
isOpen: false,
};
},
methods: {
toggleDropDown() {
this.isOpen = !this.isOpen;
},
closeDropDown() {
this.isOpen = false;
},
},
};
</script>
@@ -0,0 +1,134 @@
<template>
<div
@click="close()"
data-notify="container"
class="alert open alert-with-icon"
role="alert"
:class="[verticalAlign, horizontalAlign, alertType]"
:style="customPosition"
data-notify-position="top-center"
>
<button
type="button"
aria-hidden="true"
class="close"
data-notify="dismiss"
@click="close"
>
×
</button>
<i data-notify="icon" class="material-icons">{{ icon }}</i>
<span data-notify="message" v-html="message"></span>
</div>
</template>
<script>
export default {
name: "notification",
props: {
message: String,
icon: String,
verticalAlign: {
type: String,
default: "top",
},
horizontalAlign: {
type: String,
default: "center",
},
type: {
type: String,
default: "info",
},
timeout: {
type: Number,
default: 2500,
},
timestamp: {
type: Date,
default: () => new Date(),
},
},
data() {
return {
elmHeight: 0,
};
},
computed: {
hasIcon() {
return this.icon && this.icon.length > 0;
},
alertType() {
return `alert-${this.type}`;
},
customPosition() {
let initialMargin = 20;
let alertHeight = this.elmHeight + 10;
let sameAlertsCount = this.$notifications.state.filter((alert) => {
return (
alert.horizontalAlign === this.horizontalAlign &&
alert.verticalAlign === this.verticalAlign &&
alert.timestamp <= this.timestamp
);
}).length;
let pixels = (sameAlertsCount - 1) * alertHeight + initialMargin;
let styles = {};
if (this.verticalAlign === "top") {
styles.top = `${pixels}px`;
} else {
styles.bottom = `${pixels}px`;
}
return styles;
},
},
methods: {
close() {
this.$emit("on-close", this.timestamp);
},
},
mounted() {
this.elmHeight = this.$el.clientHeight;
if (this.timeout) {
setTimeout(this.close, this.timeout);
}
},
};
</script>
<style lang="scss" scoped>
@media screen and (max-width: 991px) {
.alert {
width: auto !important;
margin: 0 10px;
&.left {
left: 0 !important;
}
&.right {
right: 0 !important;
}
&.center {
margin: 0 10px !important;
}
}
}
.alert {
z-index: 100;
cursor: pointer;
position: absolute;
width: 41%;
&.center {
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
margin: 0 auto;
}
&.left {
left: 20px;
}
&.right {
right: 20px;
}
}
</style>
@@ -0,0 +1,52 @@
<template>
<div class="notifications">
<transition-group name="list">
<notification
v-for="notification in notifications"
:key="notification.timestamp.getTime()"
:message="notification.message"
:icon="notification.icon"
:type="notification.type"
:timestamp="notification.timestamp"
:vertical-align="notification.verticalAlign"
:horizontal-align="notification.horizontalAlign"
@on-close="removeNotification"
>
</notification>
</transition-group>
</div>
</template>
<script>
import Notification from "./Notification.vue";
export default {
components: {
Notification,
},
data() {
return {
notifications: this.$notifications.state,
};
},
methods: {
removeNotification(timestamp) {
this.$notifications.removeNotification(timestamp);
},
},
};
</script>
<style lang="scss">
.list-move {
transition: transform 0.3s, opacity 0.4s;
}
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
transition: opacity 0.4s;
}
.list-enter, .list-leave-to /* .list-leave-active for <2.1.8 */ {
opacity: 0;
}
</style>
@@ -0,0 +1,60 @@
import Notifications from "./Notifications.vue";
const NotificationStore = {
state: [], // here the notifications will be added
removeNotification(timestamp) {
const indexToDelete = this.state.findIndex(
(n) => n.timestamp === timestamp
);
if (indexToDelete !== -1) {
this.state.splice(indexToDelete, 1);
}
},
addNotification(notification) {
notification.timestamp = new Date();
notification.timestamp.setMilliseconds(
notification.timestamp.getMilliseconds() + this.state.length
);
this.state.push(notification);
},
notify(notification) {
if (Array.isArray(notification)) {
notification.forEach((notificationInstance) => {
this.addNotification(notificationInstance);
});
} else {
this.addNotification(notification);
}
},
};
var NotificationsPlugin = {
install(Vue) {
Vue.mixin({
data() {
return {
notificationStore: NotificationStore,
};
},
methods: {
notify(notification) {
this.notificationStore.notify(notification);
},
},
});
Object.defineProperty(Vue.prototype, "$notify", {
get() {
return this.$root.notify;
},
});
Object.defineProperty(Vue.prototype, "$notifications", {
get() {
return this.$root.notificationStore;
},
});
Vue.component("Notifications", Notifications);
},
};
export default NotificationsPlugin;
@@ -0,0 +1,97 @@
<template>
<div
class="sidebar"
:data-color="sidebarItemColor"
:data-image="sidebarBackgroundImage"
:style="sidebarStyle"
>
<div class="logo">
<a href="#" class="simple-text logo-mini">
<div class="logo-img">
<img :src="imgLogo" alt="" />
</div>
</a>
<a
href="https://www.creative-tim.com/product/vue-material-dashboard"
target="_blank"
class="simple-text logo-normal"
>
{{ title }}
</a>
</div>
<div class="sidebar-wrapper">
<slot name="content"></slot>
<md-list class="nav">
<!--By default vue-router adds an active class to each route link. This way the links are colored when clicked-->
<slot>
<sidebar-link
v-for="(link, index) in sidebarLinks"
:key="link.name + index"
:to="link.path"
:link="link"
>
</sidebar-link>
</slot>
</md-list>
</div>
</div>
</template>
<script>
import SidebarLink from "./SidebarLink.vue";
export default {
components: {
SidebarLink,
},
props: {
title: {
type: String,
default: "Vue MD",
},
sidebarBackgroundImage: {
type: String,
default: require("@/assets/img/sidebar-2.jpg"),
},
imgLogo: {
type: String,
default: require("@/assets/img/vue-logo.png"),
},
sidebarItemColor: {
type: String,
default: "green",
validator: (value) => {
let acceptedValues = ["", "purple", "blue", "green", "orange", "red"];
return acceptedValues.indexOf(value) !== -1;
},
},
sidebarLinks: {
type: Array,
default: () => [],
},
autoClose: {
type: Boolean,
default: true,
},
},
provide() {
return {
autoClose: this.autoClose,
};
},
computed: {
sidebarStyle() {
return {
backgroundImage: `url(${this.sidebarBackgroundImage})`,
};
},
},
};
</script>
<style>
@media screen and (min-width: 991px) {
.nav-mobile-menu {
display: none;
}
}
</style>
@@ -0,0 +1,53 @@
<template>
<li class="md-list-item">
<router-link
class="md-list-item-router md-list-item-container md-button-clean"
@click="hideSidebar"
v-bind="$attrs"
>
<div class="md-list-item-content md-ripple">
<slot>
<md-icon>{{ link.icon }}</md-icon>
<p>{{ link.name }}</p>
</slot>
</div>
</router-link>
</li>
</template>
<script>
export default {
inject: {
autoClose: {
default: true,
},
},
props: {
link: {
type: [String, Object],
default: () => {
return {
name: "",
path: "",
icon: "",
};
},
},
tag: {
type: String,
default: "router-link",
},
},
methods: {
hideSidebar() {
if (
this.autoClose &&
this.$sidebar &&
this.$sidebar.showSidebar === true
) {
this.$sidebar.displaySidebar(false);
}
},
},
};
</script>
<style></style>
@@ -0,0 +1,31 @@
import Sidebar from "./SideBar.vue";
import SidebarLink from "./SidebarLink.vue";
const SidebarStore = {
showSidebar: false,
displaySidebar(value) {
this.showSidebar = value;
},
};
const SidebarPlugin = {
install(Vue) {
Vue.mixin({
data() {
return {
sidebarStore: SidebarStore,
};
},
});
Object.defineProperty(Vue.prototype, "$sidebar", {
get() {
return this.$root.sidebarStore;
},
});
Vue.component("side-bar", Sidebar);
Vue.component("sidebar-link", SidebarLink);
},
};
export default SidebarPlugin;
@@ -0,0 +1,51 @@
<template>
<div>
<md-table v-model="users" @md-selected="onSelect">
<md-table-row
slot="md-table-row"
slot-scope="{ item }"
md-selectable="multiple"
md-auto-select
>
<md-table-cell>{{ item.name }}</md-table-cell>
<md-table-cell>
<md-button class="md-just-icon md-simple md-primary">
<md-icon>edit</md-icon>
<md-tooltip md-direction="top">Edit</md-tooltip>
</md-button>
<md-button class="md-just-icon md-simple md-danger">
<md-icon>close</md-icon>
<md-tooltip md-direction="top">Close</md-tooltip>
</md-button>
</md-table-cell>
</md-table-row>
</md-table>
</div>
</template>
<script>
export default {
name: "nav-tabs-table",
data() {
return {
selected: [],
users: [
{
name: 'Sign contract for "What are conference organizers afraid of?"',
},
{
name: "Lines From Great Russian Literature? Or E-mails From My Boss?",
},
{
name: "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit",
},
],
};
},
methods: {
onSelect: function (items) {
this.selected = items;
},
},
};
</script>
@@ -0,0 +1,60 @@
<template>
<div>
<md-table v-model="users" :table-header-color="tableHeaderColor">
<md-table-row slot="md-table-row" slot-scope="{ item }">
<md-table-cell md-label="ID">{{ item.id }}</md-table-cell>
<md-table-cell md-label="Name">{{ item.name }}</md-table-cell>
<md-table-cell md-label="Salary">{{ item.salary }}</md-table-cell>
<md-table-cell md-label="Country">{{ item.country }}</md-table-cell>
<md-table-cell md-label="City">{{ item.city }}</md-table-cell>
</md-table-row>
</md-table>
</div>
</template>
<script>
export default {
name: "ordered-table",
props: {
tableHeaderColor: {
type: String,
default: "",
},
},
data() {
return {
selected: [],
users: [
{
id: 1,
name: "Dakota Rice",
salary: "$36,738",
country: "Niger",
city: "Oud-Turnhout",
},
{
id: 2,
name: "Minerva Hooper",
salary: "$23,738",
country: "Curaçao",
city: "Sinaai-Waas",
},
{
id: 3,
name: "Sage Rodriguez",
salary: "$56,142",
country: "Netherlands",
city: "Overland Park",
},
{
id: 4,
name: "Philip Chaney",
salary: "$38,735",
country: "Korea, South",
city: "Gloucester",
},
],
};
},
};
</script>
@@ -0,0 +1,67 @@
<template>
<div>
<md-table v-model="users" :table-header-color="tableHeaderColor">
<md-table-row slot="md-table-row" slot-scope="{ item }">
<md-table-cell md-label="Name">{{ item.name }}</md-table-cell>
<md-table-cell md-label="Country">{{ item.country }}</md-table-cell>
<md-table-cell md-label="City">{{ item.city }}</md-table-cell>
<md-table-cell md-label="Salary">{{ item.salary }}</md-table-cell>
</md-table-row>
</md-table>
</div>
</template>
<script>
export default {
name: "simple-table",
props: {
tableHeaderColor: {
type: String,
default: "",
},
},
data() {
return {
selected: [],
users: [
{
name: "Dakota Rice",
salary: "$36,738",
country: "Niger",
city: "Oud-Turnhout",
},
{
name: "Minerva Hooper",
salary: "$23,738",
country: "Curaçao",
city: "Sinaai-Waas",
},
{
name: "Sage Rodriguez",
salary: "$56,142",
country: "Netherlands",
city: "Overland Park",
},
{
name: "Philip Chaney",
salary: "$38,735",
country: "Korea, South",
city: "Gloucester",
},
{
name: "Doris Greene",
salary: "$63,542",
country: "Malawi",
city: "Feldkirchen in Kārnten",
},
{
name: "Mason Porter",
salary: "$78,615",
country: "Chile",
city: "Gloucester",
},
],
};
},
};
</script>
+18
View File
@@ -0,0 +1,18 @@
// Cards
import ChartCard from "./Cards/ChartCard.vue";
import NavTabsCard from "./Cards/NavTabsCard.vue";
import StatsCard from "./Cards/StatsCard.vue";
// Tables
import NavTabsTable from "./Tables/NavTabsTable.vue";
import OrderedTable from "./Tables/OrderedTable.vue";
import SimpleTable from "./Tables/SimpleTable.vue";
export {
ChartCard,
NavTabsCard,
StatsCard,
NavTabsTable,
OrderedTable,
SimpleTable,
};