-
Notifications
You must be signed in to change notification settings - Fork 276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(web-core): circle progress bar component #8402
base: master
Are you sure you want to change the base?
Changes from all commits
20a5726
a761fa2
483ea4f
56af897
9ea3d34
f64c459
a3ecf09
0f0982d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<template> | ||
<ComponentStory | ||
v-slot="{ properties }" | ||
:params="[ | ||
prop('value').num().preset(75).required().widget(), | ||
prop('max-value').num().default(100).preset(100).widget(), | ||
prop('accent').required().enum('info', 'success', 'warning', 'danger').preset('info').widget(), | ||
prop('size').required().enum('extra-small', 'small', 'medium', 'large').preset('large').widget(), | ||
]" | ||
:presets | ||
> | ||
<UiCircleProgressBar v-bind="properties" /> | ||
</ComponentStory> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import ComponentStory from '@/components/component-story/ComponentStory.vue' | ||
import { prop } from '@/libs/story/story-param' | ||
import UiCircleProgressBar from '@core/components/ui/circle-progress-bar/UiCircleProgressBar.vue' | ||
|
||
const presets = { | ||
'Half of 500': { | ||
props: { | ||
'max-value': 500, | ||
value: 250, | ||
}, | ||
}, | ||
'75% of 300': { | ||
props: { | ||
'max-value': 300, | ||
value: 225, | ||
}, | ||
}, | ||
} | ||
Comment on lines
+21
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be careful to use all the required props ( |
||
</script> |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,152 @@ | ||||||||||||||||||||||||||||||||||||||||||||
<!-- v3 --> | ||||||||||||||||||||||||||||||||||||||||||||
<template> | ||||||||||||||||||||||||||||||||||||||||||||
<div class="progress-circle-container"> | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The CSS class should match the component's name and the guidelines |
||||||||||||||||||||||||||||||||||||||||||||
<svg | ||||||||||||||||||||||||||||||||||||||||||||
:width="circleSize" | ||||||||||||||||||||||||||||||||||||||||||||
:height="circleSize" | ||||||||||||||||||||||||||||||||||||||||||||
:viewBox="`0 0 ${circleSize} ${circleSize}`" | ||||||||||||||||||||||||||||||||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||||||||||||||||||||||||||||||||
class="progress-circle" | ||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||
<circle | ||||||||||||||||||||||||||||||||||||||||||||
:r="radius" | ||||||||||||||||||||||||||||||||||||||||||||
:cx="circleSize / 2" | ||||||||||||||||||||||||||||||||||||||||||||
:cy="circleSize / 2" | ||||||||||||||||||||||||||||||||||||||||||||
fill="transparent" | ||||||||||||||||||||||||||||||||||||||||||||
class="progress-circle-background" | ||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||
<circle | ||||||||||||||||||||||||||||||||||||||||||||
:r="radius" | ||||||||||||||||||||||||||||||||||||||||||||
:cx="circleSize / 2" | ||||||||||||||||||||||||||||||||||||||||||||
:cy="circleSize / 2" | ||||||||||||||||||||||||||||||||||||||||||||
fill="transparent" | ||||||||||||||||||||||||||||||||||||||||||||
class="progress-circle-foreground progress-circle-fill" | ||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||
</svg> | ||||||||||||||||||||||||||||||||||||||||||||
<div v-if="size !== 'extra-small'" class="progress-circle-overlay"> | ||||||||||||||||||||||||||||||||||||||||||||
<VtsIcon v-if="isComplete" :icon="icon" class="progress-circle-icon" :accent="iconAccent" /> | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
<span v-else class="progress-circle-text" :class="fontClass">{{ percentValue }}</span> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
</template> | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
<script setup lang="ts"> | ||||||||||||||||||||||||||||||||||||||||||||
import VtsIcon from '@core/components/icon/VtsIcon.vue' | ||||||||||||||||||||||||||||||||||||||||||||
import { faCheck, faExclamation } from '@fortawesome/free-solid-svg-icons' | ||||||||||||||||||||||||||||||||||||||||||||
import { computed } from 'vue' | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const { accent, size, value, maxValue } = defineProps<{ | ||||||||||||||||||||||||||||||||||||||||||||
accent: ProgressCircleAccent | ||||||||||||||||||||||||||||||||||||||||||||
size: ProgressCircleSize | ||||||||||||||||||||||||||||||||||||||||||||
value: number | ||||||||||||||||||||||||||||||||||||||||||||
maxValue: number | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if |
||||||||||||||||||||||||||||||||||||||||||||
}>() | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
type ProgressCircleAccent = 'info' | 'success' | 'warning' | 'danger' | ||||||||||||||||||||||||||||||||||||||||||||
type ProgressCircleSize = 'extra-small' | 'small' | 'medium' | 'large' | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const circleSizeMap = { | ||||||||||||||||||||||||||||||||||||||||||||
'extra-small': 16, | ||||||||||||||||||||||||||||||||||||||||||||
small: 40, | ||||||||||||||||||||||||||||||||||||||||||||
medium: 64, | ||||||||||||||||||||||||||||||||||||||||||||
large: 164, | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const iconSizeMap = { | ||||||||||||||||||||||||||||||||||||||||||||
'extra-small': '', | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can use |
||||||||||||||||||||||||||||||||||||||||||||
small: '1.6rem', | ||||||||||||||||||||||||||||||||||||||||||||
medium: '3.2rem', | ||||||||||||||||||||||||||||||||||||||||||||
large: '4.8rem', | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const strokeWidthMap = { | ||||||||||||||||||||||||||||||||||||||||||||
'extra-small': 2, | ||||||||||||||||||||||||||||||||||||||||||||
small: 4, | ||||||||||||||||||||||||||||||||||||||||||||
medium: 6, | ||||||||||||||||||||||||||||||||||||||||||||
large: 16, | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const fontClasses = { | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To keep consistency?
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
'extra-small': '', | ||||||||||||||||||||||||||||||||||||||||||||
small: 'typo-body-bold-small', | ||||||||||||||||||||||||||||||||||||||||||||
medium: 'typo-h5', | ||||||||||||||||||||||||||||||||||||||||||||
large: 'typo-h3', | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const circleSize = computed(() => circleSizeMap[size]) | ||||||||||||||||||||||||||||||||||||||||||||
const fontClass = computed(() => fontClasses[size]) | ||||||||||||||||||||||||||||||||||||||||||||
const iconSize = computed(() => iconSizeMap[size]) | ||||||||||||||||||||||||||||||||||||||||||||
const strokeWidth = computed(() => strokeWidthMap[size]) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const radius = computed(() => (circleSize.value - strokeWidth.value) / 2) | ||||||||||||||||||||||||||||||||||||||||||||
const circumference = computed(() => 2 * Math.PI * radius.value) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const isComplete = computed(() => value >= maxValue) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const strokeColor = computed(() => | ||||||||||||||||||||||||||||||||||||||||||||
size === 'extra-small' | ||||||||||||||||||||||||||||||||||||||||||||
? `var(--color-${accent}-item-base)` | ||||||||||||||||||||||||||||||||||||||||||||
: isComplete.value && (accent === 'info' || accent === 'success') | ||||||||||||||||||||||||||||||||||||||||||||
? 'var(--color-success-item-base)' | ||||||||||||||||||||||||||||||||||||||||||||
: `var(--color-${accent}-item-base)` | ||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+86
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could have better readability if we extract a config object, like above:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const backgroundStrokeColor = computed(() => `var(--color-${accent}-background-selected)`) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const iconAccent = computed(() => | ||||||||||||||||||||||||||||||||||||||||||||
isComplete.value && (accent === 'info' || accent === 'success') ? 'success' : accent | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the condition const isCompleteWithSuccess = computed(() => isComplete.value && ['info', 'success'].includes(accent)) |
||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||
const valuePercent = computed(() => Math.round((value / maxValue) * 100)) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const dashOffset = computed(() => { | ||||||||||||||||||||||||||||||||||||||||||||
if (valuePercent.value > 100) return | ||||||||||||||||||||||||||||||||||||||||||||
return circumference.value * (1 - valuePercent.value / 100) | ||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const percentValue = computed(() => `${valuePercent.value}%`) | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const icon = computed(() => (accent === 'warning' || accent === 'danger' ? faExclamation : faCheck)) | ||||||||||||||||||||||||||||||||||||||||||||
</script> | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
<style lang="postcss" scoped> | ||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-container { | ||||||||||||||||||||||||||||||||||||||||||||
position: relative; | ||||||||||||||||||||||||||||||||||||||||||||
display: inline-flex; | ||||||||||||||||||||||||||||||||||||||||||||
align-items: center; | ||||||||||||||||||||||||||||||||||||||||||||
justify-content: center; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-background { | ||||||||||||||||||||||||||||||||||||||||||||
stroke: v-bind(backgroundStrokeColor); | ||||||||||||||||||||||||||||||||||||||||||||
stroke-width: v-bind(strokeWidth); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-foreground { | ||||||||||||||||||||||||||||||||||||||||||||
stroke-width: v-bind(strokeWidth); | ||||||||||||||||||||||||||||||||||||||||||||
stroke-linecap: butt; | ||||||||||||||||||||||||||||||||||||||||||||
transition: stroke-dashoffset 0.3s ease; | ||||||||||||||||||||||||||||||||||||||||||||
transform: rotate(-90deg); | ||||||||||||||||||||||||||||||||||||||||||||
transform-origin: center; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-fill { | ||||||||||||||||||||||||||||||||||||||||||||
stroke: v-bind(strokeColor); | ||||||||||||||||||||||||||||||||||||||||||||
stroke-dasharray: v-bind(circumference); | ||||||||||||||||||||||||||||||||||||||||||||
stroke-dashoffset: v-bind(dashOffset); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-overlay { | ||||||||||||||||||||||||||||||||||||||||||||
position: absolute; | ||||||||||||||||||||||||||||||||||||||||||||
top: 50%; | ||||||||||||||||||||||||||||||||||||||||||||
left: 50%; | ||||||||||||||||||||||||||||||||||||||||||||
transform: translate(-50%, -50%); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-text { | ||||||||||||||||||||||||||||||||||||||||||||
color: v-bind(strokeColor); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.progress-circle-icon { | ||||||||||||||||||||||||||||||||||||||||||||
font-size: v-bind(iconSize); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
</style> | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+111
to
+152
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remember to use our guidelines for CSS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The prop is required, plus there is no default value (only
preset
is enough):