Skip to content

Commit

Permalink
✨ 41 sparkles adding business hours component (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
langehm authored May 28, 2024
1 parent d35418a commit 97ceb3c
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/components/BuisnessHours/BusinessHourType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Shorthand notation of all seven days in german.
*
* @typedef {"Mo" | "Di" | "Mi" | "Do" | "Fr" | "Sa" | "So"} WeekDays
*/
type WeekDays = "Mo" | "Di" | "Mi" | "Do" | "Fr" | "Sa" | "So";

/**
* @typedef {Object} OpeningHour
* @property {string} from - The start time of the opening period (in 'HH:mm' format).
* @property {string} to - The end time of the opening period (in 'HH:mm' format).
*/
type OpeningHour = {
from: string;
to: string;
};

/**
* @typedef {Object} BusinessHourType
* @property {WeekDays} weekDay - The day of the week for which the opening hours apply.
* @property {OpeningHour[]} openingHours - A list of opening hours for the specified day of the week.
*/
type BusinessHourType = {
weekDay: WeekDays;
openingHours: OpeningHour[];
};

export type { BusinessHourType, OpeningHour, WeekDays };
108 changes: 108 additions & 0 deletions src/components/BuisnessHours/BusinessHours.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import MucBusinessHours from "./MucBusinessHours.vue";

export default {
component: MucBusinessHours,
title: "MucBusinessHours",
tags: ["autodocs"],
// 👇 Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked
parameters: {
docs: {
description: {
component: `
The businessHours component is used to display the business hours for each day of the week.
The current day is highlighted as well as the days that are closed.
In the toggleable variant, the current day and the opening hours are also displayed on the toggle button.
[🔗 Patternlab-Docs](https://patternlab.muenchen.space/?p=viewall-components-business-hours)
`,
},
},
},
};

const businessHours = [
{
weekDay: "Mo",
openingHours: [
{
from: "08:00",
to: "12:00",
},
{
from: "14:00",
to: "18:00",
},
],
},
{
weekDay: "Di",
openingHours: [
{
from: "09:00",
to: "13:00",
},
],
},
{
weekDay: "Mi",
openingHours: [
{
from: "10:00",
to: "14:00",
},
],
},
{
weekDay: "Do",
openingHours: [
{
from: "08:00",
to: "12:00",
},
{
from: "15:00",
to: "19:00",
},
],
},
{
weekDay: "Fr",
openingHours: [
{
from: "08:00",
to: "12:00",
},
{
from: "13:00",
to: "17:00",
},
],
},
{
weekDay: "Sa",
openingHours: [
{
from: "10:00",
to: "13:00",
},
],
},
{
weekDay: "So",
openingHours: [],
},
];

export const Default = {
args: {
businessHours: businessHours,
toggleable: true,
},
};

export const Fixed = {
args: {
...Default.args,
toggleable: false,
},
};
166 changes: 166 additions & 0 deletions src/components/BuisnessHours/MucBusinessHours.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<template>
<div class="m-business-hours-toggle">
<h3 class="m-business-hours-toggle__title visually-hidden">
Öffnungszeiten
</h3>
<button
v-if="toggleable"
@click="toggleCollapsable"
class="m-business-hours-toggle__trigger is-open"
:class="collapsedClass"
type="button"
data-bs-toggle="collapse"
:aria-expanded="!collapsed"
>
<svg
aria-hidden="true"
class="icon icon--before"
>
<use :xlink:href="'#icon-' + icon"></use>
</svg>
<div v-if="todaysBusinessHours">
<span> {{ todaysBusinessHours.weekDay }} geöffnet </span>
<span
v-for="(time, index) in todaysBusinessHours.openingHours"
:key="index"
>
{{ time.from }} bis {{ time.to }}
<span v-if="index < todaysBusinessHours.openingHours.length - 1">
und
</span>
</span>
</div>

<svg
aria-hidden="true"
class="icon icon--after"
>
<use xlink:href="#icon-chevron-down"></use>
</svg>
</button>
<div
class="m-business-hours-toggle__content"
:class="collapseClass"
>
<ul class="ml-0">
<li
v-for="day in businessHours"
:key="day.weekDay"
:class="highlightBusinessDay(day)"
>
<span class="weekday">{{ day.weekDay }}</span>
<span class="hours">
<div v-if="day.openingHours.length === 0">geschlossen</div>
<div
v-else
v-for="time in day.openingHours"
:key="time.from"
>
{{ time.from }} - {{ time.to }}
</div>
</span>
</li>
</ul>
<div
v-if="slots['hint']"
class="hint"
>
<slot name="hint" />
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import { BusinessHourType } from "./BusinessHourType";
const LOCALES = "de-DE";
const props = withDefaults(
defineProps<{
/**
* This array includes all the opening hours for all days of the week.
*/
businessHours: BusinessHourType[];
/**
* Lets you choose between the toggleable and fixed state of the component.
* In the fixed state, no toggle button will be shown.
*/
toggleable?: boolean;
/**
* Choose an icon for the toggle button. The default if none is given is the time icon.
*/
icon?: string;
}>(),
{
icon: "time",
toggleable: false,
}
);
let collapsed = ref(props.toggleable);
const slots = defineSlots<{
/**
* Display a hint beneath all the opening hours.
*/
hint(): any;
}>();
/**
* Toggles the collapsed state of the business hours section.
*/
const toggleCollapsable = () => {
collapsed.value = !collapsed.value;
};
/**
* Computes the CSS class for the collapse state.
*
* @returns {string} The CSS class for the collapse state.
*/
const collapseClass = computed(() => (collapsed.value ? "collapse" : ""));
/**
* Computes the CSS class for the collapsed state.
*
* @returns {string} The CSS class for the collapsed state.
*/
const collapsedClass = computed(() => (collapsed.value ? "collapsed" : ""));
/**
* Computes the short name of today's day.
*
* @returns {string} The short name of today's day (e.g., "Mo", "Di").
*/
const todaysDayShortName = computed(() => {
const today = new Date();
return today.toLocaleDateString(LOCALES, { weekday: "short" });
});
/**
* Highlights the business day based on whether it has opening hours and if it is today's day.
*
* @param {BusinessHourType} businessHour - The business hour object to check.
* @returns {string} The CSS class indicating if the business is open or closed.
*/
const highlightBusinessDay = (businessHour: BusinessHourType) => {
if (businessHour.openingHours.length === 0) return "is-closed";
if (businessHour.weekDay === todaysDayShortName.value) return "is-open";
};
/**
* Computes today's business hours from the provided business hours.
*
* @returns {BusinessHourType | undefined} The business hours for today, if available.
*/
const todaysBusinessHours = computed(() =>
props.businessHours.find((curr) => curr.weekDay === todaysDayShortName.value)
);
</script>

<style scoped></style>

0 comments on commit 97ceb3c

Please sign in to comment.