This commit is contained in:
2023-02-27 19:40:03 +10:00
parent 1ee2a1189d
commit 955c06f5aa
30 changed files with 767 additions and 586 deletions

View File

@@ -5,6 +5,7 @@
:class="[
'sm-button',
classType,
{ 'sm-button-small': small },
{ 'sm-button-block': block },
{ 'sm-dropdown-button': dropdown },
]"
@@ -37,7 +38,12 @@
v-else-if="!isEmpty(to) && typeof to == 'string'"
:href="to"
:disabled="disabled"
:class="['sm-button', classType, { 'sm-button-block': block }]"
:class="[
'sm-button',
classType,
{ 'sm-button-small': small },
{ 'sm-button-block': block },
]"
:type="buttonType">
{{ label }}
<ion-icon v-if="icon" :icon="icon" />
@@ -46,7 +52,12 @@
v-else-if="!isEmpty(to) && typeof to == 'object'"
:to="to"
:disabled="disabled"
:class="['sm-button', classType, { 'sm-button-block': block }]">
:class="[
'sm-button',
classType,
{ 'sm-button-small': small },
{ 'sm-button-block': block },
]">
<ion-icon v-if="icon && iconLocation == 'before'" :icon="icon" />
{{ label }}
<ion-icon v-if="icon && iconLocation == 'after'" :icon="icon" />
@@ -67,7 +78,7 @@ const props = defineProps({
},
iconLocation: {
type: String,
default: "before",
default: "after",
required: false,
validator: (value: string) => {
return ["before", "after"].includes(value);
@@ -89,6 +100,11 @@ const props = defineProps({
default: false,
required: false,
},
small: {
type: Boolean,
default: false,
required: false,
},
dropdown: {
type: Object,
default: null,

View File

@@ -27,8 +27,8 @@ const emits = defineEmits(["submit"]);
/**
* Handle the user submitting the form.
*/
const handleSubmit = function () {
if (props.modelValue.validate()) {
const handleSubmit = async function () {
if (await props.modelValue.validate()) {
emits("submit");
}
};

View File

@@ -5,7 +5,7 @@
<script setup lang="ts">
import DOMPurify from "dompurify";
import { computed } from "vue";
import "../../../import-meta";
import { ImportMetaExtras } from "../../../import-meta";
const props = defineProps({
html: {
@@ -22,7 +22,9 @@ const computedContent = computed(() => {
let html = "";
const regex = new RegExp(
`<a ([^>]*?)href="${import.meta.env.APP_URL}(.*?>.*?)</a>`,
`<a ([^>]*?)href="${
(import.meta as ImportMetaExtras).env.APP_URL
}(.*?>.*?)</a>`,
"ig"
);

View File

@@ -286,6 +286,7 @@ const handleMediaSelect = async (event) => {
flex-direction: column;
margin-bottom: map-get($spacer, 4);
flex: 1;
width: 100%;
&.sm-input-active {
label {

View File

@@ -23,7 +23,7 @@
</template>
</ul>
<SMButton
:to="{ name: 'workshop-list' }"
:to="{ name: 'event-list' }"
class="sm-navbar-cta"
label="Find a workshop"
icon="arrow-forward-outline" />
@@ -70,13 +70,13 @@ const menuItems = [
{
name: "news",
label: "News",
to: { name: "news" },
to: { name: "post-list" },
icon: "newspaper-outline",
},
{
name: "workshops",
label: "Workshops",
to: { name: "workshop-list" },
to: { name: "event-list" },
icon: "library-outline",
},
{

View File

@@ -0,0 +1,135 @@
<template>
<div class="sm-pagination">
<ion-icon
name="chevron-back-outline"
:class="[{ disabled: computedDisablePrevButton }]"
@click="handleClickPrev" />
<span class="sm-pagination-info">{{ computedPaginationInfo }}</span>
<ion-icon
name="chevron-forward-outline"
:class="[{ disabled: computedDisableNextButton }]"
@click="handleClickNext" />
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
const props = defineProps({
modelValue: {
type: Number,
required: true,
},
total: {
type: Number,
required: true,
},
perPage: {
type: Number,
required: true,
},
});
const emits = defineEmits(["update:modelValue"]);
/**
* Returns the pagination info
*/
const computedPaginationInfo = computed(() => {
if (props.total == 0) {
return "0 - 0 of 0";
}
const start = (props.modelValue - 1) * props.perPage + 1;
const end = start + props.perPage - 1;
return `${start} - ${end} of ${props.total}`;
});
/**
* Return the total number of pages.
*/
const computedTotalPages = computed(() => {
return Math.ceil(props.total / props.perPage);
});
/**
* Return if the previous button should be disabled.
*/
const computedDisablePrevButton = computed(() => {
return props.modelValue <= 1;
});
/**
* Return if the next button should be disabled.
*/
const computedDisableNextButton = computed(() => {
return props.modelValue >= computedTotalPages.value;
});
/**
* Handle click on previous button
*
* @param {MouseEvent} $event The mouse event.
*/
const handleClickPrev = ($event: MouseEvent): void => {
if (
$event.target &&
($event.target as HTMLElement).classList.contains("disabled") ==
false &&
props.modelValue > 1
) {
emits("update:modelValue", props.modelValue - 1);
}
};
/**
* Handle click on next button
*
* @param {MouseEvent} $event The mouse event.
*/
const handleClickNext = ($event: MouseEvent): void => {
if (
$event.target &&
($event.target as HTMLElement).classList.contains("disabled") ==
false &&
props.modelValue < computedTotalPages.value
) {
emits("update:modelValue", props.modelValue + 1);
}
};
</script>
<style lang="scss">
.sm-pagination {
display: flex;
justify-content: center;
align-items: center;
ion-icon {
border: 1px solid $secondary-color;
border-radius: 4px;
padding: 0.25rem;
cursor: pointer;
transition: color 0.1s ease-in-out, background-color 0.1s ease-in-out;
color: $font-color;
&.disabled {
cursor: not-allowed;
color: $secondary-color;
}
&:not(.disabled) {
&:hover {
background-color: $secondary-color;
color: #eee;
}
}
}
.sm-pagination-info {
margin: 0 map-get($spacer, 3);
}
}
</style>

View File

@@ -31,7 +31,11 @@
{{ computedContent }}
</div>
<div v-if="button.length > 0" class="sm-panel-button">
<SMButton :to="to" :type="buttonType" :label="button" />
<SMButton
:to="to"
:type="buttonType"
:block="true"
:label="button" />
</div>
</div>
</router-link>
@@ -288,5 +292,9 @@ watch(
line-height: 130%;
flex: 1;
}
.sm-panel-button {
margin-top: map-get($spacer, 4);
}
}
</style>

View File

@@ -1,14 +1,25 @@
<template>
<div class="sm-toolbar">
<div class="sm-toolbar-column sm-toolbar-column-left">
<div v-if="slots.left" class="sm-toolbar-column sm-toolbar-column-left">
<slot name="left"></slot>
</div>
<div class="sm-toolbar-column sm-toolbar-column-right">
<div v-if="slots.default" class="sm-toolbar-column">
<slot></slot>
</div>
<div
v-if="slots.right"
class="sm-toolbar-column sm-toolbar-column-right">
<slot name="right"></slot>
</div>
</div>
</template>
<script setup lang="ts">
import { useSlots } from "vue";
const slots = useSlots();
</script>
<style lang="scss">
.sm-toolbar {
display: flex;
@@ -17,68 +28,41 @@
.sm-toolbar-column {
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
align-items: flex-start;
&.sm-toolbar-column-left {
justify-content: flex-start;
}
input {
margin-bottom: 0;
& > * {
margin: 0 map-get($spacer, 1);
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
// &.form-footer-column-left, &.form-footer-column-right {
// a, button {
// margin-left: map-get($spacer, 1);
// margin-right: map-get($spacer, 1);
// &:first-of-type {
// margin-left: 0;
// }
// &:last-of-type {
// margin-right: 0;
// }
// }
// }
&.sm-toolbar-column-right {
justify-content: flex-end;
}
// }
// }
}
}
// @media screen and (max-width: 768px) {
// .form-footer {
// flex-direction: column-reverse;
@media screen and (max-width: 768px) {
.sm-toolbar {
.sm-toolbar-column {
flex-direction: column;
// .form-footer-column {
// &.form-footer-column-left, &.form-footer-column-right {
// display: flex;
// flex-direction: column-reverse;
// justify-content: center;
// & > * {
// display: block;
// width: 100%;
// text-align: center;
// margin-top: map-get($spacer, 1);
// margin-bottom: map-get($spacer, 1);
// margin-left: 0 !important;
// margin-right: 0 !important;
// }
// }
// &.form-footer-column-left {
// margin-bottom: -#{map-get($spacer, 1) / 2};
// }
// &.form-footer-column-right {
// margin-top: -#{map-get($spacer, 1) / 2};
// }
// }
& > * {
margin: 0;
}
}
}
}
</style>