-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
79 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
226 changes: 78 additions & 148 deletions
226
...s/component-library/src/components/feedback-indicator/mt-progress-bar/mt-progress-bar.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,166 +1,96 @@ | ||
<template> | ||
<mt-base-field | ||
<div | ||
class="mt-progress-bar" | ||
role="progressbar" | ||
:aria-valuenow="modelValue" | ||
:aria-valuenow="model" | ||
:aria-valuemax="maxValue" | ||
aria-label="Current progress" | ||
:has-focus="false" | ||
> | ||
<template #label> | ||
{{ label }} | ||
|
||
<span class="mt-progress-bar__progress-label"> | ||
{{ progressLabel }} | ||
</span> | ||
</template> | ||
|
||
<template #element> | ||
<div class="mt-progress-bar__total"> | ||
<div | ||
class="mt-progress-bar__value" | ||
data-testid="progress-bar-value" | ||
:style="{ width: styleWidth }" | ||
:class="progressClasses" | ||
/> | ||
</div> | ||
</template> | ||
|
||
<template #error> | ||
<mt-field-error v-if="error" :error="error" /> | ||
</template> | ||
</mt-base-field> | ||
</template> | ||
<mt-field-label id="some-id" :style="{ gridArea: 'label' }">{{ label }}</mt-field-label> | ||
|
||
<script lang="ts"> | ||
import { defineComponent } from "vue"; | ||
import MtBaseField from "../../form/_internal/mt-base-field/mt-base-field.vue"; | ||
import MtFieldError from "../../form/_internal/mt-field-error/mt-field-error.vue"; | ||
<mt-text class="mt-progress-bar__progress-label" as="span" size="xs"> | ||
{{ progressLabel }} | ||
</mt-text> | ||
|
||
export default defineComponent({ | ||
name: "MtProgressBar", | ||
<div class="mt-progress-bar__track"> | ||
<div | ||
:class="['mt-progress-bar__fill', { 'mt-progress-bar__fill--with-error': !!error }]" | ||
:style="{ width: fillWidth }" | ||
></div> | ||
</div> | ||
|
||
components: { | ||
"mt-base-field": MtBaseField, | ||
"mt-field-error": MtFieldError, | ||
}, | ||
<mt-field-error v-if="error" :error="error" :style="{ marginTop: 0, gridArea: 'error' }" /> | ||
</div> | ||
</template> | ||
|
||
props: { | ||
/** | ||
* The current value which is used for showing the current progress. | ||
*/ | ||
modelValue: { | ||
type: Number, | ||
default: 0, | ||
}, | ||
/** | ||
* The max value sets the value where the progress will be finished. | ||
*/ | ||
maxValue: { | ||
type: Number, | ||
default: 100, | ||
required: false, | ||
}, | ||
/** | ||
* A label for the progress bar. Usually used to guide the user what value kind of activity is currently running. | ||
*/ | ||
label: { | ||
type: String, | ||
required: false, | ||
default: null, | ||
}, | ||
/** | ||
* Change how the progress label looks like. Examples are "kb", "mb", "items" or more. For percentage just use "percentage" | ||
* @example "kb" | ||
*/ | ||
progressLabelType: { | ||
type: String, | ||
required: false, | ||
default: "percent", | ||
}, | ||
/** | ||
* An error in your business logic related to this field. | ||
* | ||
* @example {"code": 500, "detail": "Error while loading"} | ||
*/ | ||
error: { | ||
type: Object, | ||
required: false, | ||
default: null, | ||
}, | ||
<script setup lang="ts"> | ||
import MtFieldLabel from "@/components/form/_internal/mt-field-label/mt-field-label.vue"; | ||
import MtFieldError from "@/components/form/_internal/mt-field-error/mt-field-error.vue"; | ||
import MtText from "@/components/content/mt-text/mt-text.vue"; | ||
import { computed } from "vue"; | ||
const model = defineModel<number>(); | ||
const props = withDefaults( | ||
defineProps<{ | ||
label: string; | ||
maxValue: number; | ||
error?: { detail: string; code: number } | null; | ||
progressLabelType?: string; | ||
}>(), | ||
{ | ||
progressLabelType: "percent", | ||
}, | ||
); | ||
computed: { | ||
progressLabel(): string { | ||
if (!this.progressLabelType || this.progressLabelType === "percent") { | ||
return this.styleWidth; | ||
} | ||
return `${this.modelValue} ${this.progressLabelType} / ${this.maxValue} ${this.progressLabelType}`; | ||
}, | ||
styleWidth(): string { | ||
// @ts-expect-error - vue can't detect value correctly | ||
let percentage = parseInt((this.modelValue / this.maxValue) * 100); | ||
if (percentage > 100) { | ||
percentage = 100; | ||
} | ||
if (percentage < 0) { | ||
percentage = 0; | ||
} | ||
return `${percentage}%`; | ||
}, | ||
progressClasses() { | ||
return { | ||
"mt-progress-bar__value--no-transition": | ||
this.modelValue < 1 || this.modelValue >= this.maxValue, | ||
"mt-progress-bar__value--has-error": !!this.error, | ||
}; | ||
}, | ||
}, | ||
const progressLabel = computed<string>(() => { | ||
if (props.progressLabelType === "percent") return fillWidth.value; | ||
return `${model.value} ${props.progressLabelType} / ${props.maxValue} ${props.progressLabelType}`; | ||
}); | ||
const fillWidth = computed<`${string}%`>(() => { | ||
if (!model.value) return "0%"; | ||
const percentage = Math.floor((model.value / props.maxValue) * 100); | ||
if (percentage > 100) return "100%"; | ||
if (percentage < 0) return "0%"; | ||
return `${percentage}%`; | ||
}); | ||
</script> | ||
|
||
<style lang="scss"> | ||
<style scoped> | ||
.mt-progress-bar { | ||
.mt-block-field__block { | ||
border: none; | ||
} | ||
label { | ||
display: flex; | ||
} | ||
&__progress-label { | ||
display: flex; | ||
margin-left: auto; | ||
} | ||
.mt-progress-bar__total { | ||
width: 100%; | ||
height: 8px; | ||
background-color: var(--color-background-primary-disabled); | ||
border-radius: var(--border-radius-round); | ||
} | ||
.mt-progress-bar__value { | ||
transition: 1s width linear; | ||
height: 100%; | ||
background-color: var(--color-interaction-primary-default); | ||
border-radius: var(--border-radius-round); | ||
&--no-transition { | ||
transition: 0s width linear; | ||
} | ||
&--has-error { | ||
transition: 0s width linear; | ||
background-color: var(--color-interaction-critical-default); | ||
} | ||
} | ||
display: grid; | ||
grid-template-areas: | ||
"label progress" | ||
"track track" | ||
"error error"; | ||
row-gap: 0.5rem; | ||
} | ||
.mt-progress-bar__progress-label { | ||
color: var(--color-text-secondary); | ||
grid-area: progress; | ||
justify-self: end; | ||
} | ||
.mt-progress-bar__track { | ||
border-radius: var(--border-radius-round); | ||
height: 0.5rem; | ||
width: 100%; | ||
background: var(--color-background-primary-disabled); | ||
grid-area: track; | ||
} | ||
.mt-progress-bar__fill { | ||
border-radius: var(--border-radius-round); | ||
height: 100%; | ||
background: var(--color-interaction-primary-default); | ||
} | ||
.mt-progress-bar__fill--with-error { | ||
background: var(--color-interaction-critical-default); | ||
} | ||
</style> |