Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • clickable-select-chevron
  • feat/new-image-formats
  • master
  • 1.2.0
  • 1.4.0
  • 1.4.1
  • 1.4.2
  • 1.5.0
  • 1.5.1
  • 1.5.2
  • 1.5.3
  • 1.5.4
  • 1.5.5
  • 1.6.0
  • 1.6.1
  • 1.6.2
  • 1.6.3
  • 1.6.4
  • 1.7.0
  • 1.8.0
  • 2.0.0
  • 2.0.0-alpha-1
  • 2.0.0-alpha-10
  • 2.0.0-alpha-11
  • 2.0.0-alpha-12
  • 2.0.0-alpha-13
  • 2.0.0-alpha-14
  • 2.0.0-alpha-15
  • 2.0.0-alpha-16
  • 2.0.0-alpha-17
  • 2.0.0-alpha-2
  • 2.0.0-alpha-3
  • 2.0.0-alpha-4
  • 2.0.0-alpha-5
  • 2.0.0-alpha-6
  • 2.0.0-alpha-7
  • 2.0.0-alpha-8
  • 2.0.0-alpha-9
  • 2.0.1
  • 2.0.2
  • 2.0.3
  • 2.0.4
  • 2.1.0
  • 2.1.1
  • 2.1.2
  • 2.10.0
  • 2.11.0
  • 2.12.1
  • 2.13.0
  • 2.14.0
  • 2.15.0
  • 2.16.0
  • 2.16.1
  • 2.17.0
  • 2.18.0
  • 2.19.0
  • 2.2.0
  • 2.2.1
  • 2.20.0
  • 2.3.0
  • 2.3.1
  • 2.3.2
  • 2.3.3
  • 2.3.4
  • 2.4.0
  • 2.5.0
  • 2.5.1
  • 2.5.2
  • 2.6.0
  • 2.7.0
  • 2.7.1
  • 2.8.0
  • 2.9.0
  • 2.9.1
74 results

Target

Select target project
  • to/weby/ui-styleguide
  • va-fighters/ui-styleguide
2 results
Select Git revision
  • master
  • 1.2.0
  • 1.4.0
  • 1.4.1
  • 1.4.2
  • 1.5.0
  • 1.5.1
  • 1.5.2
  • 1.5.3
  • 1.5.4
  • 1.5.5
  • 1.6.0
  • 1.6.1
  • 1.6.2
  • 1.6.3
  • 1.6.4
  • 1.7.0
  • 1.8.0
  • 2.0.0-alpha-1
  • 2.0.0-alpha-10
  • 2.0.0-alpha-11
  • 2.0.0-alpha-2
  • 2.0.0-alpha-3
  • 2.0.0-alpha-4
  • 2.0.0-alpha-5
  • 2.0.0-alpha-6
  • 2.0.0-alpha-7
  • 2.0.0-alpha-8
  • 2.0.0-alpha-9
29 results
Show changes
Showing
with 980 additions and 109 deletions
import Vue from 'vue';
import UiSubnav from './Subnav.vue';
const appFactory = (el, attrs) => {
// Bootstrap Vue.js.
new Vue({el, components: { UiSubnav }});
};
export default appFactory;
......@@ -2,13 +2,13 @@
<div class="flip-clock">
<template v-for="data in timeData" v-show="show">
<span v-bind:key="data.label" class="flip-clock__piece" :id="data.elementId" v-show="data.show">
<span :class="['flip-clock__card', 'flip-card', clockclasses ]">
<span :class="['flip-clock__card', 'flip-card', clockClasses ]">
<b class="flip-card__top">{{ data.current | twoDigits }}</b>
<b class="flip-card__bottom" v-bind:data-value="data.current | twoDigits"></b>
<b class="flip-card__back" v-bind:data-value="data.previous | twoDigits"></b>
<b class="flip-card__back-bottom" v-bind:data-value="data.previous | twoDigits"></b>
</span>
<span :class="['flip-clock__slot', 'font-alt', slotclasses]">{{ data.label }}</span>
<span :class="['flip-clock__slot', 'font-alt', slotClasses]">{{ data.label }}</span>
</span>
</template>
</div>
......@@ -16,7 +16,7 @@
<script>
import Vue from "vue";
import { forEachNode } from "../../utils";
import { forEachNode } from "../utils";
export default {
name: 'flipCountdown',
......@@ -31,11 +31,11 @@ export default {
type: String,
default: 'days,hours,minutes,seconds'
},
clockclasses: {
clockClasses: {
type: String,
default: 'text-6xl'
},
slotclasses: {
slotClasses: {
type: String,
default: 'text-3xl'
}
......
......@@ -4,7 +4,7 @@
<h1 class="head-alt-sm mb-2">Vyberte kraj</h1>
<ul class="region-map__list leading-loose whitespace-no-wrap text-sm">
<li v-for="region in regions" :key="region.id">
<a href="#" @mouseover="current = region" @mouseout="current = null">{{ region.name }}</a>
<a href="#" @click="selectRegion(region)" @mouseover="current = region" @mouseout="current = null">{{ region.name }}</a>
</li>
</ul>
</div>
......@@ -17,7 +17,7 @@
viewBox="0 75 800 450"
>
<g>
<a xlink:href="#" v-for="region in regions" :key="region.id" @mouseover="current = region" @mouseout="current = null">
<a xlink:href="#" v-for="region in regions" :key="region.id" @mouseover="current = region" @mouseout="current = null" @click="selectRegion(region)">
<path
:class="{'region-map__region': true, 'region-map__region--current': current === region}"
:d="region.polygon"
......@@ -34,7 +34,33 @@ import Vue from "vue";
export default {
props: {
links: {
type: Object,
default: function () {
return {
"praha": "https://praha.pirati.cz",
"stredocesky": "https://stredocesky.pirati.cz",
"jihocesky": "https://jihocesky.pirati.cz",
"plzensky": "https://plzensky.pirati.cz",
"karlovarsky": "https://karlovarsky.pirati.cz",
"ustecky": "https://ustecky.pirati.cz",
"liberecky": "https://liberecky.pirati.cz",
"kralovehradecky": "https://kralovehradecky.pirati.cz",
"moravskoslezsky": "https://moravskoslezsky.pirati.cz",
"pardubicky": "https://pardubicky.pirati.cz",
"vysocina": "https://vysocina.pirati.cz",
"jihomoravsky": "https://jihomoravsky.pirati.cz",
"olomoucky": "https://olomoucky.pirati.cz",
"zlinsky": "https://zlinsky.pirati.cz"
};
}
}
},
methods: {
selectRegion(region) {
const href = this.$props.links[region.id];
window.location.href = href;
}
},
data() {
return {
......
<script>
import UiRegionMap from "../region-map/RegionMap";
import UiSubnavViewProvider from "./SubnavViewProvider";
import UiSubnavViewProvider from "../../components/ViewProvider";
export default {
components: {
......
<script>
export default {
mounted() {
console.log(`Mounted generic Vue app in ` , this.$el);
}
}
</script>
<template>
<div>
<slot v-bind:views="views" v-bind:isCurrentView="isCurrentView" v-bind:toggleView="toggleView"></slot>
<slot
v-bind:views="views"
v-bind:isCurrentView="isCurrentView"
v-bind:toggleView="toggleView"
v-bind:showView="showView"
v-bind:setView="setView"
></slot>
</div>
</template>
......@@ -9,11 +15,20 @@ export default {
props: {
initial: {
default: () => {}
},
syncLocation: {
type: Boolean,
default: false,
},
locationParam: {
type: String,
default: "view",
}
},
data() {
return {
views: this.$props.initial,
queryParams: null,
keyListener: e => {
// Esc
if (e.keyCode === 27) {
......@@ -22,18 +37,38 @@ export default {
}
};
},
methods: {
setView(viewId, show) {
this.$data.views[viewId] = show;
watch: {
routeView() {
const queryParams = new URLSearchParams(window.location.search);
}
},
toggleView(viewId) {
methods: {
setView(viewId, show, hideOthers = false) {
if (hideOthers) {
Object.keys(this.$data.views).forEach(key => {
if (key !== viewId) {
this.setView(key, false);
}
});
}
this.$data.views[viewId] = show;
this.setView(viewId, !this.isCurrentView(viewId));
if (show && this.$props.syncLocation) {
const queryParams = new URLSearchParams(window.location.search);
queryParams.set(this.$props.locationParam, viewId);
history.pushState(null, null, "?" + queryParams.toString());
}
},
setViews(updates) {
this.$data.views = Object.assign({}, this.data.views, updates);
},
toggleView(viewId) {
this.setView(viewId, !this.isCurrentView(viewId), true);
},
showView(viewId) {
this.setView(viewId, true, true);
},
isCurrentView(viewId) {
return this.$data.views[viewId];
......@@ -45,7 +80,16 @@ export default {
}
},
mounted() {
window.addEventListener('keydown', this.$data.keyListener);;
window.addEventListener('keydown', this.$data.keyListener);
if (this.$props.syncLocation) {
const queryParams = new URLSearchParams(window.location.search);
const locationView = queryParams.get(this.$props.locationParam);
if (locationView && Object.keys(this.$data.views).indexOf(locationView) !== -1) {
this.showView(locationView);
}
}
},
destroyed() {
window.removeEventListener('keydown', this.$data.keyListener);
......
<script>
const initialEvents = [
{
id: 2,
start: "2020-07-08T10:00:00.000Z",
startTimestamp: new Date("2020-07-08T10:00:00.000Z").getTime(),
startDateVerbose: "středa 8. července 2020",
startTimeVerbose: "12:00",
allDay: false,
end: "2020-07-08T11:00:00.000Z",
title: "Pirátský oběd - Chrudim",
description:
"Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
link:
"https://www.google.com/calendar/event?eid=Mmw1Y2RwMTByYm80Y204cWxsaW1maWJmcTJfMjAyMDA3MDhUMTAwMDAwWiA3cjY3M3JsaDI1NW9mb3JodjNvZWIybDBnMEBn"
},
{
id: 15,
start: "2020-07-13T19:00:00.000Z",
startTimestamp: new Date("2020-07-13T19:00:00.000Z").getTime(),
startDateVerbose: "pondělí 13. července 2020",
startTimeVerbose: "21:00",
allDay: false,
end: "2020-07-13T19:30:00.000Z",
title: "Mumble - předsednictvo",
link:
"https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDcxM1QxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
},
{
id: 3,
start: "2020-07-15T10:00:00.000Z",
startTimestamp: new Date("2020-07-15T10:00:00.000Z").getTime(),
startDateVerbose: "středa 15. července 2020",
startTimeVerbose: "12:00",
allDay: false,
end: "2020-07-15T11:00:00.000Z",
title: "Pirátský oběd - Chrudim",
description:
"Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
link:
"https://www.google.com/calendar/event?eid=Mmw1Y2RwMTByYm80Y204cWxsaW1maWJmcTJfMjAyMDA3MTVUMTAwMDAwWiA3cjY3M3JsaDI1NW9mb3JodjNvZWIybDBnMEBn",
mapLink: "https://maps.google.com"
},
{
id: 16,
start: "2020-07-20T19:00:00.000Z",
startTimestamp: new Date("2020-07-20T19:00:00.000Z").getTime(),
startDateVerbose: "pondělí 20. července 2020",
startTimeVerbose: "21:00",
allDay: false,
end: "2020-07-20T19:30:00.000Z",
title: "Mumble - předsednictvo",
link:
"https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDcyMFQxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
},
{
id: 4,
start: "2020-07-22T10:00:00.000Z",
startTimestamp: new Date("2020-07-22T10:00:00.000Z").getTime(),
startDateVerbose: "středa 22. července 2020",
startTimeVerbose: "12:00",
allDay: false,
end: "2020-07-22T11:00:00.000Z",
title: "Pirátský oběd - Chrudim",
description:
"Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
link:
"https://www.google.com/calendar/event?eid=Mmw1Y2RwMTByYm80Y204cWxsaW1maWJmcTJfMjAyMDA3MjJUMTAwMDAwWiA3cjY3M3JsaDI1NW9mb3JodjNvZWIybDBnMEBn"
},
{
id: 17,
start: "2020-07-27T19:00:00.000Z",
startTimestamp: new Date("2020-07-27T19:00:00.000Z").getTime(),
startDateVerbose: "pondělí 27. července 2020",
startTimeVerbose: "21:00",
allDay: false,
end: "2020-07-27T19:30:00.000Z",
title: "Mumble - předsednictvo",
link:
"https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDcyN1QxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
},
{
id: 5,
start: "2020-07-29T10:00:00.000Z",
startTimestamp: new Date("2020-07-29T10:00:00.000Z").getTime(),
startDateVerbose: "středa 29. července 2020",
startTimeVerbose: "12:00",
allDay: false,
end: "2020-07-29T11:00:00.000Z",
title: "Pirátský oběd - Chrudim",
description:
"Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
link:
"https://www.google.com/calendar/event?eid=Mmw1Y2RwMTByYm80Y204cWxsaW1maWJmcTJfMjAyMDA3MjlUMTAwMDAwWiA3cjY3M3JsaDI1NW9mb3JodjNvZWIybDBnMEBn"
},
{
id: 18,
start: "2020-08-03T19:00:00.000Z",
startTimestamp: new Date("2020-08-03T19:00:00.000Z").getTime(),
startDateVerbose: "pondělí 3. srpna 2020",
startTimeVerbose: "21:00",
allDay: false,
end: "2020-08-03T19:30:00.000Z",
title: "Mumble - předsednictvo",
link:
"https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDgwM1QxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
},
{
id: 6,
start: "2020-08-05T10:00:00.000Z",
startTimestamp: new Date("2020-08-05T10:00:00.000Z").getTime(),
startDateVerbose: "středa 5. srpna 2020",
startTimeVerbose: "12:00",
allDay: false,
end: "2020-08-05T11:00:00.000Z",
title: "Pirátský oběd - Chrudim",
description:
"Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
link:
"https://www.google.com/calendar/event?eid=Mmw1Y2RwMTByYm80Y204cWxsaW1maWJmcTJfMjAyMDA4MDVUMTAwMDAwWiA3cjY3M3JsaDI1NW9mb3JodjNvZWIybDBnMEBn"
}
];
const moreEvents = [
{
id: 19,
start: "2020-08-10T19:00:00.000Z",
startTimestamp: new Date("2020-08-10T19:00:00.000Z").getTime(),
startDateVerbose: "pondělí 10. srpna 2020",
startTimeVerbose: "21:00",
allDay: false,
end: "2020-08-10T19:30:00.000Z",
title: "Mumble - předsednictvo",
link:
"https://www.google.com/calendar/event?eid=YzVpM2FvaGc2MHAzY2I5aGM1aW1jYjlrNjBvbThiYjE2dGk2NGI5ajY4cjY0ZGhrNzVnamdjOWdjb18yMDIwMDgxMFQxOTAwMDBaIDdyNjczcmxoMjU1b2Zvcmh2M29lYjJsMGcwQGc"
},
{
id: 7,
start: "2020-08-12T10:00:00.000Z",
startTimestamp: new Date("2020-08-12T10:00:00.000Z").getTime(),
startDateVerbose: "středa 12. srpna 2020",
startTimeVerbose: "12:00",
allDay: false,
end: "2020-08-12T11:00:00.000Z",
title: "Pirátský oběd - Chrudim",
description:
"Pravidelné setkání pirátů při středečním obědě. Nejen o politice a s chutí.",
link:
"https://www.google.com/calendar/event?eid=Mmw1Y2RwMTByYm80Y204cWxsaW1maWJmcTJfMjAyMDA4MTJUMTAwMDAwWiA3cjY3M3JsaDI1NW9mb3JodjNvZWIybDBnMEBn"
}
];
export default {
data: () => ({
events: initialEvents,
hasMore: true,
}),
methods: {
onShowMore() {
this.$data.events = [...initialEvents, ...moreEvents];
this.$data.hasMore = false;
}
},
render() {
return this.$scopedSlots.default({
events: this.events,
hasMore: this.hasMore,
onShowMore: this.onShowMore,
});
}
};
</script>
<script>
const pageSize = 10;
export default {
props: {
calendarId: {
type: String,
required: true,
},
apiKey: {
type: String,
required: true,
}
},
data() {
return {
events: [],
toShow: 7,
};
},
computed: {
displayedEvents() {
return this.events.slice(0, this.toShow);
},
hasMore() {
return this.toShow < this.events.length;
},
},
methods: {
onShowMore() {
this.toShow += pageSize;
},
loadEventsFromStorage() {
if (window.sessionStorage && window.sessionStorage['__pircal_' + this.calendarId]) {
return JSON.parse(window.sessionStorage['__pircal_' + this.calendarId]);
}
},
// Store events to sessionStorage if possible to save requests.
storeEventsToStorage() {
if (window.sessionStorage) {
window.sessionStorage['__pircal_' + this.calendarId] = JSON.stringify(this.events);
}
}
},
mounted() {
const ev = this.loadEventsFromStorage();
if (! ev) {
const toIso = now => now.toISOString().split('.')[0]+"Z";
const now = new Date();
const timeMin = now.toISOString();
const timeMax = new Date(+now + (1000 * 60 * 60 * 24 * 90)).toISOString(); // 90 days ahead
const reqUrl = `https://www.googleapis.com/calendar/v3/calendars/${this.calendarId}/events?key=${encodeURIComponent(this.apiKey)}&maxResults=150&timeMin=${encodeURIComponent(timeMin)}&timeMax=${encodeURIComponent(timeMax)}&sanitizeHtml=true&singleEvents=true&maxAtendees=1`;
let counter = 0;
fetch(reqUrl)
.then(response => {
if (!response.ok) {
throw new Error("Problem loading events from google");
}
return response.json()
})
.then(resp => {
this.events = resp.items
.map(e => {
const start = new Date(e.start.dateTime || e.start.date);
const end = new Date(e.end.dateTime || e.end.date);
const startDateVerbose = start.toLocaleDateString('cs-CZ', {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'});
const startTimeVerbose = start.getHours() + ':' + start.getMinutes().toString().padStart(2, '0');
const allDay = ! e.start.dateTime;
return {
id: counter++,
start: start,
startTimestamp: start.getTime(),
startDateVerbose,
startTimeVerbose,
allDay,
end: end,
title: e.summary,
description: e.description,
link: e.htmlLink
};
})
.sort((e1, e2) => e1.start < e2.start ? -1 : 1);
this.storeEventsToStorage();
});
} else {
this.events = ev;
}
},
render() {
return this.$scopedSlots.default({
events: this.displayedEvents,
hasMore: this.hasMore,
onShowMore: this.onShowMore,
});
}
};
</script>
<template>
<div class="calendar grid grid-cols-4">
<div v-if="showBanner" class="col-span-4 xl:col-span-1">
<aside class="banner bg-orange-300 text-white h-full">
<i class="ico--calendar banner__icon"></i>
<div class="banner__body">
<h1 class="head-alt-md banner__cta">{{ name }}</h1>
<button class="btn btn--white btn--fullwidth sm:btn--autowidth mt-8" v-if="onShowMore && hasMore" @click="onShowMore()">
<div class="btn__body">Zobrazit další</div>
</button>
</div>
</aside>
</div>
<div :class="{'col-span-4 xl:col-span-3': showBanner, 'col-span-4': !showBanner}">
<div class="grid grid-cols-12 items-center calendar-table-row" v-for="event in events" v-bind:key="event.id" :class="{'calendar-table-row--standalone': !showBanner}">
<div class="col-span-2 text-orange-300 head-alt-md calendar-table-row__col"><span>{{ event.startTimestamp | dateDay }}</span></div>
<div class="col-span-8 grid grid-cols-3 col-gap-4 calendar-table-row__col" :class="{'calendar-table-row__col--norborder': !event.mapLink}">
<div class="col-span-3 md:col-span-1">
<strong class="block">{{ event.startDateVerbose }}</strong>
<p class="font-light text-sm mt-1">{{ event.allDay ? "Celý den" : event.startTimeVerbose }}</p>
</div>
<div class="col-span-3 md:col-span-2 mt-4 md:mt-0">
<a v-if="event.link" v-bind:href="event.link" class="font-bold block" target="_blank" rel="noreferrer noopener">{{ event.title }}</a>
<strong v-if="!event.link" class="block">{{ event.title }}</strong>
<p class="font-light text-sm mt-1" v-if="event.description">{{ event.description }}</p>
</div>
</div>
<div class="col-span-2 text-center font-light calendar-table-row__col">
<a :href="event.mapLink" v-if="event.mapLink" class="icon-link">
<i class="ico--location text-violet-300 mr-1" aria-hidden="true"></i>
<span>Mapa</span>
</a>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
default: "Kalendář"
},
events: {
type: Array,
required: true,
},
onShowMore: {
type: Function,
required: false,
},
hasMore: {
type: Boolean,
default: true,
},
showBanner: {
type: Boolean,
default: true,
},
},
filters: {
dateDay: (val) => {
return `${new Date(val).getDate()}.`;
}
}
};
</script>
......@@ -9,17 +9,15 @@
</template>
<script>
import screens from "../../../../screens";
const lgScreenSize = parseInt(screens.lg.replace("px", ""), 10);
import { isLgScreenSize } from "../../utils";
export default {
data() {
return {
isLgScreenSize: this.getWindowWidth() >= lgScreenSize,
isLgScreenSize: isLgScreenSize(),
show: false,
resizeHandler: () => {
this.$data.isLgScreenSize = this.getWindowWidth() >= lgScreenSize;
this.$data.isLgScreenSize = isLgScreenSize();
},
};
},
......@@ -43,9 +41,6 @@ export default {
}
},
methods: {
getWindowWidth() {
return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
},
handleClick() {
if (this.$props.href) {
window.location = this.$props.href;
......
<script>
import FullCalendar from "@fullcalendar/vue"
import dayGridPlugin from "@fullcalendar/daygrid"
import { showEventData } from './utils'
export default {
components: {
FullCalendar // make the <FullCalendar> tag available
},
props: ["events"],
data() {
return {
calendarOptions: {
plugins: [ dayGridPlugin ],
initialView: "dayGridMonth",
contentHeight: "auto",
locale: "cs",
buttonText: {
today: "dnes"
},
eventTimeFormat: {
hour: 'numeric',
minute: '2-digit',
meridiem: false
},
moreLinkContent: (arg) => { return `+${arg.num} další` },
eventClick (info) {
showEventData(info)
},
events: JSON.parse(this.events)
}
}
}
}
</script>
<template>
<FullCalendar :options="calendarOptions" />
</template>
<script>
import FullCalendar from "@fullcalendar/vue"
import dayGridPlugin from "@fullcalendar/daygrid"
import multiMonthPlugin from "@fullcalendar/multimonth"
import { showEventData } from './utils'
export default {
components: {
FullCalendar // make the <FullCalendar> tag available
},
props: ["events"],
data() {
return {
calendarOptions: {
plugins: [ dayGridPlugin, multiMonthPlugin ],
initialView: "multiMonthYear",
contentHeight: "auto",
locale: "cs",
buttonText: {
today: "dnes"
},
moreLinkContent: (arg) => { return `+${arg.num} další` },
eventClick (info) {
showEventData(info)
},
events: JSON.parse(this.events)
}
}
}
}
</script>
<template>
<FullCalendar :options="calendarOptions" />
</template>
import tippy from 'tippy.js';
const showEventData = (info) => {
if (
info.event.url === ''
&& info.event.extendedProps.location === undefined
&& info.event.extendedProps.url === undefined
) {
return
}
info.jsEvent.preventDefault();
const popup = tippy(
info.el,
{
content: `
<div class="p-2 flex flex-col gap-3 text-white whitespace-nowrap">
${
(info.event.title !== undefined) ?
`
<div class="flex gap-2 items-baseline">
<div>${info.event.title}</div>
</div>
` : ''
}
${
(info.event.extendedProps.location !== undefined) ?
`
<div class="flex gap-2 items-baseline">
<i class="ico--location" aria-label="Místo konání"></i>
<div>${info.event.extendedProps.location}</div>
</div>
` : ''
}
${
(info.event.extendedProps.description !== undefined) ?
`
<div class="flex gap-2 items-baseline">
<i class="ico--info" aria-label="Popis"></i>
<div>${info.event.extendedProps.description}</div>
</div>
` : ''
}
${
(info.event.url !== '') ?
`
<div class="flex gap-2 items-baseline">
<i class="ico--link" aria-label="Adresa"></i>
<a class="underline cursor-pointer" href="${info.event.url}">${info.event.url}</a>
</div>
` : ''
}
</div>
`,
trigger: (
(
info.event.url !== ''
&& info.event.extendedProps.location === undefined
&& info.event.extendedProps.url === undefined
) ?
'hover' : 'click'
),
allowHTML: true,
interactive: true
}
);
popup.show();
}
export { showEventData }
<script>
import UiNavbarSubitem from "./NavbarSubitem";
import screens from "../../../../screens";
const lgScreenSize = parseInt(screens.lg.replace("px", ""), 10);
import UiNavbarSubitemReplacing from "./NavbarSubitemReplacing";
import { isLgScreenSize } from "../../utils";
export default {
components: {
UiNavbarSubitem
UiNavbarSubitem,
UiNavbarSubitemReplacing,
},
data() {
return {
isLgScreenSize: this.getWindowWidth() >= lgScreenSize,
isLgScreenSize: isLgScreenSize(),
show: false,
resizeHandler: () => {
this.$data.isLgScreenSize = this.getWindowWidth() >= lgScreenSize;
this.$data.isLgScreenSize = isLgScreenSize();
},
};
},
methods: {
getWindowWidth() {
return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
}
},
mounted() {
this.$nextTick(() => {
window.addEventListener("resize", this.$data.resizeHandler);
......
<template>
<div @mouseenter="show = true" @mouseleave="show = false">
<span class="navbar-menu__link navbar-menu__submenu-toggle" :class="{'navbar-menu__submenu-toggle--open': show}" @click="handleClick">{{ label }}</span>
<div @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<span v-if="!href" class="navbar-menu__link navbar-menu__submenu-toggle" :class="{'navbar-menu__submenu-toggle--open': show}" @click="handleClick">{{ label }}</span>
<a v-if="href" :href="href" class="navbar-menu__link navbar-menu__submenu-toggle" :class="{'navbar-menu__submenu-toggle--open': show}" @click.prevent="handleClick">{{ label }}</a>
<div :class="{'navbar-menu__submenu-wrap--show': show}" class="navbar-menu__submenu-wrap">
<slot>
</slot>
......@@ -10,6 +10,8 @@
</template>
<script>
import { isLgScreenSize } from "../../utils";
export default {
data() {
return {
......@@ -25,10 +27,23 @@ export default {
}
},
methods: {
handleClick() {
onMouseEnter() {
if (isLgScreenSize()) {
this.$data.show = true;
}
},
onMouseLeave() {
if (isLgScreenSize()) {
this.$data.show = false;
}
},
handleClick(evt) {
// On mobile screens, first click should just toggle and redir on second one
if (isLgScreenSize() || this.$data.show) {
if (this.$props.href) {
window.location = this.$props.href;
}
}
this.$data.show = !this.$data.show;
}
......
<template>
<div @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<span v-if="!href" class="navbar-menu__link navbar-menu__submenu-toggle" :class="{'navbar-menu__submenu-toggle--open': show}" @click="handleClick">{{ label }}</span>
<a v-if="href" :href="href" class="navbar-menu__link navbar-menu__submenu-toggle" :class="{'navbar-menu__submenu-toggle--open': show}" @click.prevent="handleClick">{{ label }}</a>
<div :class="{'navbar-menu__submenu-wrap--show': show}" class="navbar-menu__submenu-wrap">
<ul class="navbar-menu__submenu">
<li v-for="(item, index) in parsedItems" :key="index">
<a :href="item[1]" class="navbar-menu__link">{{ item[0] }}</a>
</li>
</ul>
</div>
</div>
</template>
<script>
import { isLgScreenSize } from "../../utils";
export default {
data() {
return {
show: false,
parsedItems: JSON.parse(this.items),
};
},
props: {
href: {
type: String,
},
label: {
type: String,
},
items: {
type: String,
}
},
methods: {
onMouseEnter() {
if (isLgScreenSize()) {
this.$data.show = true;
}
},
onMouseLeave() {
if (isLgScreenSize()) {
this.$data.show = false;
}
},
handleClick(evt) {
// On mobile screens, first click should just toggle and redir on second one
if (isLgScreenSize() || this.$data.show) {
if (this.$props.href) {
window.location = this.$props.href;
}
}
this.$data.show = !this.$data.show;
}
}
}
</script>
import Vue from "vue";
import { forEachNode } from "./utils";
import Apps from "./apps";
import Renderer from "./components/calendar/Renderer";
import DummyProvider from "./components/calendar/DummyProvider";
import GoogleProvider from "./components/calendar/GoogleProvider"
import MonthCalendar from "./components/full-calendar/MonthCalendar";
import YearCalendar from "./components/full-calendar/YearCalendar";
import RegionMap from "./components/RegionMap";
import ViewProvider from "./components/ViewProvider";
import Navbar from "./components/navbar/Navbar";
import FooterCollapsible from "./components/footer/FooterCollapsible";
import FlipClock from "./components/FlipClock";
import Chart from 'chart.js/auto';
Vue.component("ui-calendar-renderer", Renderer);
Vue.component("ui-calendar-dummy-provider", DummyProvider);
Vue.component("ui-calendar-google-provider", GoogleProvider);
Vue.component("ui-month-calendar", MonthCalendar);
Vue.component("ui-year-calendar", YearCalendar);
Vue.component("ui-region-map", RegionMap);
Vue.component("ui-view-provider", ViewProvider);
Vue.component("ui-navbar", Navbar);
Vue.component("ui-footer-collapsible", FooterCollapsible);
Vue.component("ui-flip-clock", FlipClock);
import UiApp from "./components/UiApp.vue";
const appFactory = (el, attrs) => {
// Bootstrap Vue.js.
new Vue({
el,
components: {
UiApp
}
});
};
/**
* Bootstrap Vue.js application at given Element instance.
......@@ -14,26 +53,45 @@ import Apps from "./apps";
*/
function renderVueAppElement(el) {
const attrs = Object.assign({}, el.dataset);
if (! attrs.app) {
console.warn(el, 'Cannot bootstrap: missing data-app');
return;
return appFactory(el, attrs);
}
const app = Apps[attrs.app];
if (! app) {
console.warn(el, `Cannot bootstrap: unknown app ${attrs.app}`);
return;
}
function renderChart(element) {
const data = [
{ year: 2010, count: 10 },
{ year: 2011, count: 20 },
{ year: 2012, count: 15 },
{ year: 2013, count: 25 },
{ year: 2014, count: 22 },
{ year: 2015, count: 30 },
{ year: 2016, count: 28 },
];
return app(el, attrs);
new Chart(
element,
{
type: 'bar',
data: {
labels: data.map(row => row.year),
datasets: [
{
label: 'Firemní akvizice každý rok',
data: data.map(row => row.count)
}
]
}
}
);
}
function init(event) {
// Initialize Vue.js apps.
forEachNode(document.querySelectorAll('.__js-root'), renderVueAppElement)
// Show example charts.
forEachNode(document.querySelectorAll('.__chart-example'), renderChart);
}
document.addEventListener('DOMContentLoaded', init);
import screens from "../../screens";
const lgScreenSize = parseInt(screens.lg.replace("px", ""), 10);
export const forEachNode = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) {
callback.call(scope, array[i]); // passes back stuff we need
}
};
export function getWindowWidth() {
return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
}
export function isLgScreenSize() {
return getWindowWidth() >= lgScreenSize;
}
......@@ -11,56 +11,7 @@ function defaultOptions() {
display: 'inline-block',
fontWeight: '400',
maxWidth: '20rem',
colors: {
// white: {
// background: defaultConfig.colors['white'],
// text: defaultConfig.colors['black'],
// },
// black: {
// background: defaultConfig.colors['black'],
// text: defaultConfig.colors['white'],
// },
// grey: {
// background: defaultConfig.colors['grey'],
// text: defaultConfig.colors['black'],
// },
// red: {
// background: defaultConfig.colors['red'],
// text: defaultConfig.colors['white'],
// },
// orange: {
// background: defaultConfig.colors['orange'],
// text: defaultConfig.colors['white'],
// },
// yellow: {
// background: defaultConfig.colors['yellow'],
// text: defaultConfig.colors['yellow-darkest'],
// },
// green: {
// background: defaultConfig.colors['green'],
// text: defaultConfig.colors['white'],
// },
// teal: {
// background: defaultConfig.colors['teal'],
// text: defaultConfig.colors['white'],
// },
// blue: {
// background: defaultConfig.colors['blue'],
// text: defaultConfig.colors['white'],
// },
// indigo: {
// background: defaultConfig.colors['indigo'],
// text: defaultConfig.colors['white'],
// },
// purple: {
// background: defaultConfig.colors['purple'],
// text: defaultConfig.colors['white'],
// },
// pink: {
// background: defaultConfig.colors['pink'],
// text: defaultConfig.colors['white'],
// },
},
colors: {},
}
}
......@@ -108,6 +59,10 @@ module.exports = function (options) {
maxWidth: theme('maxWidth.xs'),
textDecoration: 'none',
},
'.btn[disabled]': {
opacity: '0.7',
cursor: 'not-allowed',
},
'.btn:hover': {
textDecoration: 'none',
},
......@@ -155,6 +110,46 @@ module.exports = function (options) {
'.btn--condensed .btn__body': {
padding: '.75em 1em',
},
'@keyframes btn-loading-spinner': {
'to': {
transform: 'rotate(360deg)',
}
},
'.btn--loading': {
position: 'relative',
},
'.btn--loading::before': {
pointerEvents: 'none',
content: "''",
position: 'absolute',
left: '0',
right: '0',
top: '0',
bottom: '0',
zIndex: '2',
backgroundColor: theme('colors.black'),
opacity: '0.4',
},
'.btn--loading::after': {
pointerEvents: 'none',
content: "''",
position: 'absolute',
left: '0',
right: '0',
top: '0',
bottom: '0',
zIndex: '3',
top: '50%',
left: '50%',
width: '1.5rem',
height: '1.5rem',
marginTop: '-0.75rem',
marginLeft: '-0.75rem',
borderRadius: '50%',
border: `3px solid ${theme('colors.cyan.200')}`,
borderLeftColor: 'transparent',
animation: 'btn-loading-spinner 1s linear infinite',
}
},
..._.map(options.colors, (colorOptions, name) => {
const bg = getThemeColor(colorOptions.background, '#000');
......
......@@ -6,13 +6,65 @@ const lighen = (clr, val) => Color(clr).lighten(val).rgb().string();
const darken = (clr, val) => Color(clr).darken(val).rgb().string();
module.exports = {
content: [
"./source/**/*.mustache",
"./source/**/*.json",
"./source/**/*.js",
"./source/**/*.vue",
"./public/**/*.html",
],
options: {
whitelistPatterns: [
/^((sm|md|lg|xl)\:)?(block|inline|fixed|absolute|inline-block|flex|inline-flex|grid)/,
/^((sm|md|lg|xl)\:)?w-\d\d?/,
/^((sm|md|lg|xl)\:)?w-(auto|full|screen)/,
/^((sm|md|lg|xl)\:)?h-\d\d?/,
/^((sm|md|lg|xl)\:)?h-screen/,
/^((sm|md|lg|xl)\:)?-?m.?-\d\d?/,
/^((sm|md|lg|xl)\:)?-?m.?-auto?/,
/^((sm|md|lg|xl)\:)?p.?-\d\d?/,
/^((sm|md|lg|xl)\:)?space-.?-\d\d?/,
/^((sm|md|lg|xl)\:)?hidden/,
/^((sm|md|lg|xl)\:)?block/,
/^((sm|md|lg|xl)\:)?inline-block/,
/^((sm|md|lg|xl)\:)?flex/,
/^((sm|md|lg|xl)\:)?inline-flex/,
/^((sm|md|lg|xl)\:)?grid/,
/^((sm|md|lg|xl)\:)?gap-\d\d?/,
/^((sm|md|lg|xl)\:)?static/,
/^((sm|md|lg|xl)\:)?flex-row-*/,
/^((sm|md|lg|xl)\:)?flex-col-*/,
/^((sm|md|lg|xl)\:)?grid-cols-*/,
/^((sm|md|lg|xl)\:)?col-span-*/,
/^((sm|md|lg|xl)\:)?row-span-*/,
/^((sm|md|lg|xl)\:)?order-*/,
/^((sm|md|lg|xl)\:)?items-*/,
/^((sm|md|lg|xl)\:)?float-*/,
/^((sm|md|lg|xl|hover)\:)?text-*/,
/^((sm|md|lg|xl)\:)?grid-flow-*/,
/^((sm|md|lg|xl)\:)?head-*/,
/^((sm|md|lg|xl)\:)?clearfix/,
/^((sm|md|lg|xl|hover)\:)?elevation-*/,
/^((sm|md|lg|xl|hover)\:)?border(-.*)?/,
/^((sm|md|lg|xl|hover)\:)?bg-opacity-\d\d?/,
/whitespace-*/,
/opacity-*/,
/^truncate/,
/^break-*/,
/^overflow-*/,
/^duration-*/,
/^max-w-*/,
/^max-h-*/,
/ico--*/,
],
},
theme: {
extend: {
maxWidth: {
'xxs': '16rem',
},
gridTemplateColumns: {
'candidate-table-row': 'auto auto auto auto 1fr'
height: {
'120': '30rem',
},
opacity: {
'85': '0.85',
......@@ -22,12 +74,17 @@ module.exports = {
},
spacing: {
'0/5': '0.125rem',
'14': '3.5rem',
'28': '7rem',
'36': '9rem',
'44': '11rem',
'52': '13rem',
'80': '20rem'
}
'80': '20rem',
},
// flip-x
scale: {
'-1': '-1',
},
},
// Breakpoints
screens,
......@@ -63,6 +120,7 @@ module.exports = {
'black': '#000000',
'white': '#ffffff',
'grey': {
'25': '#fafafa',
'50': '#f7f7f7',
'100': '#f3f3f3',
'125': '#f0f0f0',
......@@ -74,10 +132,22 @@ module.exports = {
'700': '#202020',
'800': '#1f1f1f',
},
'olive': {
'100': '#d6e8b3'
},
'green': {
'100': '#d6e8b3',
'200': '#92c6ab',
'300': darken('#92c6ab', 0.1),
'300': '#76cc9f',
'400': '#4ca971',
},
'yellow': {
'100': '#fff7bf',
'200': '#f7f38a',
'300': '#ffea5a',
'400': '#fde119',
},
'red': {
'600': '#d60d53'
},
'brands': {
'facebook': '#067ceb',
......@@ -109,6 +179,9 @@ module.exports = {
'400': '#840048',
'500': '#670047',
},
'pirati': {
'yellow': '#fec934'
},
},
container: {
center: true,
......@@ -125,6 +198,11 @@ module.exports = {
},
variants: {
textDecorationColor: ['responsive', 'hover'],
background: ['responsive'],
appearance: ['responsive'],
space: ['responsive'],
text: ['responsive', 'hover'],
zIndex: ['responsive'],
},
plugins: [
require('tailwind-css-variables')(
......@@ -135,7 +213,7 @@ module.exports = {
// options
}
),
require('tailwindcss-typography')({
require('@tailwindcss/typography')({
// all these options default to the values specified here
ellipsis: false, // whether to generate ellipsis utilities
hyphens: false, // whether to generate hyphenation utilities
......@@ -177,11 +255,13 @@ module.exports = {
'grey-125': {
text: 'black',
background: 'grey.125',
hoverText: 'black',
},
'white': {
text: 'black',
background: 'white',
iconBorder: 'grey.100',
hoverText: 'black',
},
'blue-300': {
text: 'white',
......@@ -191,9 +271,13 @@ module.exports = {
text: 'white',
background: 'cyan.200',
},
'green-200': {
'green-300': {
text: 'white',
background: 'green.300',
},
'green-400': {
text: 'white',
background: 'green.200',
background: 'green.400',
},
'orange-300': {
text: 'white',
......@@ -207,6 +291,10 @@ module.exports = {
text: 'white',
background: 'violet.500',
},
'red-600': {
text: 'white',
background: 'red.600',
},
'brands-facebook': {
text: 'white',
background: 'brands.facebook',
......