Categories
Vuetify

Vuetify — Cards

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Cards

A card is a container for content.

We can add one with the v-card component

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="mx-auto" max-width="344" outlined>
          <v-list-item three-line>
            <v-list-item-content>
              <div class="overline mb-4">Foo</div>
              <v-list-item-title class="headline mb-1">Headline</v-list-item-title>
              <v-list-item-subtitle>Lorem ipsum</v-list-item-subtitle>
            </v-list-item-content>

            <v-list-item-avatar tile size="80" color="grey"></v-list-item-avatar>
          </v-list-item>

          <v-card-actions>
            <v-btn text>Button</v-btn>
            <v-btn text>Button</v-btn>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add the v-card with a v-list-item for the content.

three-line makes it display 3 lines.

v-list-item-avatar has the avatar.

v-card-action has the action buttons for the card.

We can use it for wrapping content.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="d-inline-block mx-auto">
          <v-container>
            <v-row justify="space-between">
              <v-col cols="auto">
                <v-img
                  height="200"
                  width="200"
                  src="https://placekitten.com/200/200"
                ></v-img>
              </v-col>

              <v-col cols="auto" class="text-center pl-0">
                <v-row class="flex-column ma-0 fill-height" justify="center">
                  <v-col class="px-0">
                    <v-btn icon>
                      <v-icon>mdi-heart</v-icon>
                    </v-btn>
                  </v-col>

                  <v-col class="px-0">
                    <v-btn icon>
                      <v-icon>mdi-bookmark</v-icon>
                    </v-btn>
                  </v-col>

                  <v-col class="px-0">
                    <v-btn icon>
                      <v-icon>mdi-share-variant</v-icon>
                    </v-btn>
                  </v-col>
                </v-row>
              </v-col>
            </v-row>
          </v-container>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the v-img to display an image on the left side.

The v-img is inside the v-col

We have the v-col component on to display some buttons on the right side.

Information Card

We can add various kinds of text in a card with the v-card-text component.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="mx-auto" max-width="344">
          <v-card-text>
            <div>Lorem ipsum</div>
            <p class="display-1 text--primary">Lorem ipsum</p>
            <p>Lorem ipsum</p>
            <div class="text--primary">Lorem ipsum</div>
          </v-card-text>
          <v-card-actions>
            <v-btn text color="deep-purple accent-4">Learn More</v-btn>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the v-card-text component to display some text that fits in the v-card .

Conclusion

We can add cards with various kinds of content.

Categories
Vuetify

Vuetify — Card Media

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Card Media with Text

We can add a card with images and text.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="mx-auto" max-width="400">
          <v-img
            class="white--text align-end"
            height="200px"
            src="https://cdn.vuetifyjs.com/images/cards/docks.jpg"
          >
            <v-card-title>Lorem ipsum</v-card-title>
          </v-img>

          <v-card-subtitle class="pb-0">Lorem ipsum</v-card-subtitle>

          <v-card-text class="text--primary">
            <div>Lorem ipsum</div>
            <div>Lorem ipsum</div>
          </v-card-text>

          <v-card-actions>
            <v-btn color="orange" text>Lorem ipsum</v-btn>
            <v-btn color="orange" text>Lorem ipsum</v-btn>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the v-card component with the v-img inside it.

This way, we display the image on top.

v-card-title has the title text.

v-card-subtitle has the subtitle text.

And we add the v-card-text to add the body text.

The v-card-actions to add the actions.

v-btn has the buttons at the bottom.

Grids

We can add a grid to the card.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="mx-auto" max-width="500">
          <v-system-bar color="indigo darken-2" dark>
            <v-spacer></v-spacer>
            <v-icon>mdi-window-minimize</v-icon>
            <v-icon>mdi-window-maximize</v-icon>
            <v-icon>mdi-close</v-icon>
          </v-system-bar>

          <v-toolbar color="indigo" dark>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>
            <v-toolbar-title>Discover</v-toolbar-title>
            <v-spacer></v-spacer>
            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>
          </v-toolbar>

          <v-container fluid>
            <v-row dense>
              <v-col v-for="card in cards" :key="card.title" :cols="card.flex">
                <v-card>
                  <v-img
                    :src="card.src"
                    class="white--text align-end"
                    gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)"
                    height="200px"
                  >
                    <v-card-title v-text="card.title"></v-card-title>
                  </v-img>

                  <v-card-actions>
                    <v-spacer></v-spacer>
                    <v-btn icon>
                      <v-icon>mdi-heart</v-icon>
                    </v-btn>
                    <v-btn icon>
                      <v-icon>mdi-bookmark</v-icon>
                    </v-btn>
                    <v-btn icon>
                      <v-icon>mdi-share-variant</v-icon>
                    </v-btn>
                  </v-card-actions>
                </v-card>
              </v-col>
            </v-row>
          </v-container>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    cards: [
      {
        title: "house",
        src: "https://cdn.vuetifyjs.com/images/cards/house.jpg",
        flex: 12,
      },
      {
        title: "road",
        src: "https://cdn.vuetifyjs.com/images/cards/road.jpg",
        flex: 6,
      },
      {
        title: "plane",
        src: "https://cdn.vuetifyjs.com/images/cards/plane.jpg",
        flex: 6,
      },
    ],
  }),
};
</script>

We have the v-container component which has the v-row and v-col components to create the layout.

The layout is created according to the cards array.

We also set the class and gradient to style the boxes.

Also, we add the v-card-actions component to display the buttons at the bottom.

Conclusion

We can add media to cards.

Also, we can add text and buttons to the position we want.

Categories
Vuetify

Vuetify — Calendar Categories and Drag and Drop

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Calendar Categories

We can categorize calendar events with various categories.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-sheet height="64">
          <v-toolbar flat color="white">
            <v-btn outlined class="mr-4" color="grey darken-2" @click="setToday">Today</v-btn>
            <v-btn fab text small color="grey darken-2" @click="prev">
              <v-icon small>mdi-chevron-left</v-icon>
            </v-btn>
            <v-btn fab text small color="grey darken-2" @click="next">
              <v-icon small>mdi-chevron-right</v-icon>
            </v-btn>
            <v-toolbar-title v-if="$refs.calendar">{{ $refs.calendar.title }}</v-toolbar-title>
            <v-spacer></v-spacer>
          </v-toolbar>
        </v-sheet>
        <v-sheet height="600">
          <v-calendar
            ref="calendar"
            v-model="focus"
            color="primary"
            type="category"
            category-show-all
            :categories="categories"
            :events="events"
            :event-color="getEventColor"
            [@change](http://twitter.com/change "Twitter profile for @change")="fetchEvents"
          ></v-calendar>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    focus: "",
    events: [],
    colors: [
      "blue",
      "indigo",
      "deep-purple"
    ],
    names: [
      "Meeting",
      "Holiday",
      "Leave"
    ],
    categories: ["John Smith", "Mary Walker"],
  }),
  mounted() {
    this.$refs.calendar.checkChange();
  },
  methods: {
    getEventColor(event) {
      return event.color;
    },
    setToday() {
      this.focus = "";
    },
    prev() {
      this.$refs.calendar.prev();
    },
    next() {
      this.$refs.calendar.next();
    },
    fetchEvents({ start, end }) {
      const events = [];

      const min = new Date(`${start.date}T00:00:00`);
      const max = new Date(`${end.date}T23:59:59`);
      const days = (max.getTime() - min.getTime()) / 86400000;
      const eventCount = this.rnd(days, days + 20);

      for (let i = 0; i < eventCount; i++) {
        const allDay = this.rnd(0, 3) === 0;
        const firstTimestamp = this.rnd(min.getTime(), max.getTime());
        const first = new Date(firstTimestamp - (firstTimestamp % 900000));
        const secondTimestamp = this.rnd(2, allDay ? 288 : 8) * 900000;
        const second = new Date(first.getTime() + secondTimestamp);

        events.push({
          name: this.names[this.rnd(0, this.names.length - 1)],
          start: first,
          end: second,
          color: this.colors[this.rnd(0, this.colors.length - 1)],
          timed: !allDay,
          category: this.categories[this.rnd(0, this.categories.length - 1)],
        });
      }

      this.events = events;
    },
    rnd(a, b) {
      return Math.floor((b - a + 1) * Math.random()) + a;
    },
  },
};
</script>

We set the categories to the categories we want to display.

category-show-all makes all category show.

Also, we set the type prop to category so that we display event categories.

Drag and Drop

We can drag and drop events in the calendar.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-sheet height="600">
          <v-calendar
            ref="calendar"
            v-model="value"
            color="primary"
            type="4day"
            :events="events"
            :event-color="getEventColor"
            :event-ripple="false"
            @change="getEvents"
            @mousedown:event="startDrag"
            @mousedown:time="startTime"
            @mousemove:time="mouseMove"
            @mouseup:time="endDrag"
            @mouseleave.native="cancelDrag"
          >
            <template #event="{ event, timed, eventSummary }">
              <div class="v-event-draggable" v-html="eventSummary()"></div>
              <div v-if="timed" class="v-event-drag-bottom" @mousedown.stop="extendBottom(event)"></div>
            </template>
          </v-calendar>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    value: "",
    events: [],
    colors: [
      "red",
      "green",
      "blue",
    ],
    names: [
      "Birthday",
      "Conference",
      "Party",
    ],
    dragEvent: null,
    dragStart: null,
    createEvent: null,
    createStart: null,
    extendOriginal: null,
  }),
  methods: {
    startDrag({ event, timed }) {
      if (event && timed) {
        this.dragEvent = event;
        this.dragTime = null;
        this.extendOriginal = null;
      }
    },
    startTime(tms) {
      const mouse = this.toTime(tms);

      if (this.dragEvent && this.dragTime === null) {
        const start = this.dragEvent.start;

        this.dragTime = mouse - start;
      } else {
        this.createStart = this.roundTime(mouse);
        this.createEvent = {
          name: `Event #${this.events.length}`,
          color: this.rndElement(this.colors),
          start: this.createStart,
          end: this.createStart,
          timed: true,
        };

        this.events.push(this.createEvent);
      }
    },
    extendBottom(event) {
      this.createEvent = event;
      this.createStart = event.start;
      this.extendOriginal = event.end;
    },
    mouseMove(tms) {
      const mouse = this.toTime(tms);

      if (this.dragEvent && this.dragTime !== null) {
        const start = this.dragEvent.start;
        const end = this.dragEvent.end;
        const duration = end - start;
        const newStartTime = mouse - this.dragTime;
        const newStart = this.roundTime(newStartTime);
        const newEnd = newStart + duration;

        this.dragEvent.start = newStart;
        this.dragEvent.end = newEnd;
      } else if (this.createEvent && this.createStart !== null) {
        const mouseRounded = this.roundTime(mouse, false);
        const min = Math.min(mouseRounded, this.createStart);
        const max = Math.max(mouseRounded, this.createStart);

        this.createEvent.start = min;
        this.createEvent.end = max;
      }
    },
    endDrag() {
      this.dragTime = null;
      this.dragEvent = null;
      this.createEvent = null;
      this.createStart = null;
      this.extendOriginal = null;
    },
    cancelDrag() {
      if (this.createEvent) {
        if (this.extendOriginal) {
          this.createEvent.end = this.extendOriginal;
        } else {
          const i = this.events.indexOf(this.createEvent);
          if (i !== -1) {
            this.events.splice(i, 1);
          }
        }
      }

      this.createEvent = null;
      this.createStart = null;
      this.dragTime = null;
      this.dragEvent = null;
    },
    roundTime(time, down = true) {
      const roundTo = 15;
      const roundDownTime = roundTo * 60 * 1000;

      return down
        ? time - (time % roundDownTime)
        : time + (roundDownTime - (time % roundDownTime));
    },
    toTime(tms) {
      return new Date(
        tms.year,
        tms.month - 1,
        tms.day,
        tms.hour,
        tms.minute
      ).getTime();
    },
    getEventColor(event) {
      const rgb = parseInt(event.color.substring(1), 16);
      const r = (rgb >> 16) & 0xff;
      const g = (rgb >> 8) & 0xff;
      const b = (rgb >> 0) & 0xff;

      return event === this.dragEvent
        ? `rgba(${r}, ${g}, ${b}, 0.7)`
        : event === this.createEvent
        ? `rgba(${r}, ${g}, ${b}, 0.7)`
        : event.color;
    },
    getEvents({ start, end }) {
      const events = [];

      const min = new Date(`${start.date}T00:00:00`).getTime();
      const max = new Date(`${end.date}T23:59:59`).getTime();
      const days = (max - min) / 86400000;
      const eventCount = this.rnd(days, days + 20);

      for (let i = 0; i < eventCount; i++) {
        const timed = this.rnd(0, 3) !== 0;
        const firstTimestamp = this.rnd(min, max);
        const secondTimestamp = this.rnd(2, timed ? 8 : 288) * 900000;
        const start = firstTimestamp - (firstTimestamp % 900000);
        const end = start + secondTimestamp;

        events.push({
          name: this.rndElement(this.names),
          color: this.rndElement(this.colors),
          start,
          end,
          timed,
        });
      }

      this.events = events;
    },
    rnd(a, b) {
      return Math.floor((b - a + 1) * Math.random()) + a;
    },
    rndElement(arr) {
      return arr[this.rnd(0, arr.length - 1)];
    },
  },
};
</script>

We have the mousedown and mouseup events to let us handle the dragging and dropping of the events.

The event start and end time of the event as we move the event.

The startTime event sets the this.createEvent when we move the calendar item.

Then in the mouseMove method, we set the start and end date.

Now we’ll see the event update when we drag and drop it.

Conclusion

We can create calendars that move events and set categories.

Categories
Vuetify

Vuetify — Calendar

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Calendars

The v-calendar component is used to display information in different kinds of calendar views.

It has slots for all-day or timed elements.

The weekly and monthly view has a slot for each day.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-sheet height="400">
          <v-calendar
            ref="calendar"
            :now="today"
            :value="today"
            :events="events"
            color="primary"
            type="week"
          ></v-calendar>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    today: "2020-01-08",
    events: [
      {
        name: "eat",
        start: "2020-01-07 09:00",
        end: "2020-01-07 10:00",
      },
      {
        name: "drink",
        start: "2020-01-10",
      },
      {
        name: "sleep",
        start: "2020-01-09 12:30",
        end: "2020-01-09 15:30",
      },
    ],
    mounted() {
      this.$refs.calendar.scrollToTime("08:00");
    },
  }),
};
</script>

We added the v-calendar component to display the items the way we want.

We set the now prop to the string of the date we want to display.

The value is set to the same thing and it’s the selected date.

events prop has a list of events to display.

The type has the type of calendar view we want to show.

color sets the color for the calendar items.

The events array has the objects with the name property for the event name.

start and end has the calendar start and end dates.

The mounted hook has the scrollToTime method call to let us scroll to the time we want.

Daily View

We can change the calendar view to a daily view.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-sheet height="400">
          <v-calendar color="primary" type="day">
            <template v-slot:day-header="{ present }">
              <template v-if="present" class="text-center">Today</template>
            </template>

            <template v-slot:interval="{ hour }">
              <div class="text-center">{{ hour }} o'clock</div>
            </template>
          </v-calendar>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the v-sheet component with the v-calendar component.

We popular the header slot to change the date display.

And we populate the interval slot to display the time the way we want.

Slots

We can populate various slots to format the code to way we want.

For instance, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-sheet height="500">
          <v-calendar :now="today" :value="today" color="primary">
            <template v-slot:day="{ present, past, date }">
              <v-row class="fill-height">
                <template v-if="past && tracked[date]">
                  <v-sheet
                    v-for="(percent, i) in tracked[date]"
                    :key="i"
                    :title="category[i]"
                    :color="colors[i]"
                    :width="`${percent}%`"
                    height="100%"
                    tile
                  ></v-sheet>
                </template>
              </v-row>
            </template>
          </v-calendar>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    today: "2020-01-10",
    tracked: {
      "2020-01-09": [23, 45, 10],
      "2020-01-08": [10],
      "2020-01-07": [0, 78, 5],
      "2020-01-06": [0, 0, 50],
      "2020-01-05": [0, 10, 23],
      "2020-01-04": [2, 90],
      "2020-01-03": [10, 32],
      "2020-01-02": [80, 10, 10],
      "2020-01-01": [20, 25, 10],
    },
    colors: ["red", "green", "blue"],
    category: ["Development", "Meetings", "Slacking"],
  }),
};
</script>

The tracked object has arrays of percentages of the bar to display.

We can change the width of what we want with it.

The color prop sets the color.

title sets the title of the bar.

tile make the bars display side by side.

Conclusion

We can display the calendar with the v-calendar component.

It has a daily, weekly, and monthly view.

Categories
Vuetify

Vuetify — Buttons

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Buttons

We can add buttons with the v-btn component.

For instance, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <div class="my-2">
          <v-btn text small color="primary">Primary</v-btn>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

to add a button.

The small prop makes it small.

And the color prop changes the color.

The text prop removes the raised style.

Raised Button

A raised button is the default style.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <div class="my-2">
          <v-btn small color="primary">Primary</v-btn>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

to add a raised button.

A raised button has a shadow.

Depressed Button

The depressed prop lets us make a depressed button.

A depressed button has no shadow.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <div class="my-2">
          <v-btn depressed color="primary">Primary</v-btn>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

to create a depressed button.

Button Dropdown Variants

Vuetify lets us add a button dropdown.

To do that, we write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <div id="dropdown">
          <v-overflow-btn class="my-2" :items="fruits" label="Choose Fruit" target="#dropdown"></v-overflow-btn>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    fruits: ["apple", "orange", "pear"],
  }),
};
</script>

We have the items prop that takes an array of strings with the choices.

label has the label that’s displayed.

The target is the selector of the container.

We can also add the segmented prop to make a segmented dropdown button.

And the editable prop makes the dropdown editable.

Icon Button

We can add a icon prop with the v-icon component to add an icon button.

For instance, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-btn icon color="pink">
          <v-icon>mdi-heart</v-icon>
        </v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We also set the color to the color we want.

The disabled prop will disable the button.

Floating Button

We can also add a floating button with the fab prop.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-btn class="mx-2" fab dark large color="purple">
          <v-icon dark>mdi-android</v-icon>
        </v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add the fab and large props to make the button a floating button.

Conclusion

We can add a button with the v-btn component.