Loading src/components/ProgressBar/ProgressBar.stories.ts +2 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ const meta: Meta = { noBorder: { control: 'boolean' }, size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] }, fontSize: { control: 'text' }, gradient: { control: 'object' }, colorGaps: { control: 'object' }, colorInactiveGaps: { control: 'object' }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, Loading Loading @@ -93,6 +94,7 @@ export const Small: Story = { inactiveTheme: 'red', labelAfter: '/100', width: '500px', gradient: ['red', 'yellow', 'green'], }, }; Loading src/components/ProgressBar/ProgressBar.vue +28 −6 Original line number Diff line number Diff line <script setup lang="ts"> import type { IProgressBarProps } from '@interfaces/componentsProps'; import { computed } from 'vue'; import { computed, ref, type Ref } from 'vue'; import { convertThemeToColor, convertThemeToTextColor } from '@helpers/common'; const props = withDefaults(defineProps<IProgressBarProps>(), { Loading @@ -14,16 +14,22 @@ const props = withDefaults(defineProps<IProgressBarProps>(), { showLabel: true, labelAfter: '%', }); const active = computed(() => `${(props.value / props.max) * 100}%`); const value = defineModel() as Ref<number>; if (props.value) { value.value = props.value; } const active = computed(() => `${(value.value / props.max) * 100}%`); const activeColor = computed(() => { if (props.gradient) return `linear-gradient(to right, ${props.gradient.join(',')})`; if (!props.colorGaps) return convertThemeToColor(props.theme, props.darknessTheme); const current = props.colorGaps.find((item) => item.start <= props.value && props.value <= item.end); const current = props.colorGaps.find((item) => item.start <= value.value && value.value <= item.end); if (!current) return convertThemeToColor(props.theme, props.darknessTheme); return convertThemeToColor(current.color, current.darknessColor); }); const inactiveColor = computed(() => { if (!props.colorInactiveGaps) return convertThemeToColor(props.inactiveTheme, props.darknessInactiveTheme); const current = props.colorInactiveGaps.find((item) => item.start <= props.value && props.value <= item.end); const current = props.colorInactiveGaps.find((item) => item.start <= value.value && value.value <= item.end); if (!current) return convertThemeToColor(props.inactiveTheme, props.darknessInactiveTheme); return convertThemeToColor(current.color, current.darknessColor); }); Loading @@ -43,12 +49,27 @@ const defaultHeight = computed(() => { if (size === 'huge') return '60px'; return '15px'; }); const isClickHold = ref<boolean>(false); const setNewValue = (event: MouseEvent) => { const layerX = event.layerX; value.value = Math.round((layerX / (props.width ? parseInt(props.width) : 300)) * props.max); }; const onPointerDown = (event: MouseEvent) => { isClickHold.value = true; setNewValue(event); }; </script> <template> <section class="container" id="progressBar" :style="`width: ${width ?? '300px'}; height: ${height ?? defaultHeight}; border: ${noBorder ? '' : '2px solid black'}`" @pointerdown.prevent="onPointerDown($event)" @pointermove="isClickHold ? setNewValue($event) : ''" @pointerup="isClickHold = false" > <div class="active"> <span v-show="showLabel" class="value">{{ labelBefore }}{{ value }}{{ labelAfter }}</span> Loading @@ -62,6 +83,7 @@ const defaultHeight = computed(() => { overflow: hidden; border-radius: calc(v-bind(fontSize) / 2.5); background-color: v-bind(inactiveColor); cursor: pointer; } .active { width: v-bind(active); Loading @@ -73,12 +95,12 @@ const defaultHeight = computed(() => { position: absolute; top: 0; left: 0; transition: width 0.4s ease-in-out; background-color: v-bind(activeColor); background: v-bind(activeColor); } .value { font-weight: bold; font-size: v-bind(fontSize); color: v-bind(textColor); user-select: none; } </style> Loading
src/components/ProgressBar/ProgressBar.stories.ts +2 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ const meta: Meta = { noBorder: { control: 'boolean' }, size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] }, fontSize: { control: 'text' }, gradient: { control: 'object' }, colorGaps: { control: 'object' }, colorInactiveGaps: { control: 'object' }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, Loading Loading @@ -93,6 +94,7 @@ export const Small: Story = { inactiveTheme: 'red', labelAfter: '/100', width: '500px', gradient: ['red', 'yellow', 'green'], }, }; Loading
src/components/ProgressBar/ProgressBar.vue +28 −6 Original line number Diff line number Diff line <script setup lang="ts"> import type { IProgressBarProps } from '@interfaces/componentsProps'; import { computed } from 'vue'; import { computed, ref, type Ref } from 'vue'; import { convertThemeToColor, convertThemeToTextColor } from '@helpers/common'; const props = withDefaults(defineProps<IProgressBarProps>(), { Loading @@ -14,16 +14,22 @@ const props = withDefaults(defineProps<IProgressBarProps>(), { showLabel: true, labelAfter: '%', }); const active = computed(() => `${(props.value / props.max) * 100}%`); const value = defineModel() as Ref<number>; if (props.value) { value.value = props.value; } const active = computed(() => `${(value.value / props.max) * 100}%`); const activeColor = computed(() => { if (props.gradient) return `linear-gradient(to right, ${props.gradient.join(',')})`; if (!props.colorGaps) return convertThemeToColor(props.theme, props.darknessTheme); const current = props.colorGaps.find((item) => item.start <= props.value && props.value <= item.end); const current = props.colorGaps.find((item) => item.start <= value.value && value.value <= item.end); if (!current) return convertThemeToColor(props.theme, props.darknessTheme); return convertThemeToColor(current.color, current.darknessColor); }); const inactiveColor = computed(() => { if (!props.colorInactiveGaps) return convertThemeToColor(props.inactiveTheme, props.darknessInactiveTheme); const current = props.colorInactiveGaps.find((item) => item.start <= props.value && props.value <= item.end); const current = props.colorInactiveGaps.find((item) => item.start <= value.value && value.value <= item.end); if (!current) return convertThemeToColor(props.inactiveTheme, props.darknessInactiveTheme); return convertThemeToColor(current.color, current.darknessColor); }); Loading @@ -43,12 +49,27 @@ const defaultHeight = computed(() => { if (size === 'huge') return '60px'; return '15px'; }); const isClickHold = ref<boolean>(false); const setNewValue = (event: MouseEvent) => { const layerX = event.layerX; value.value = Math.round((layerX / (props.width ? parseInt(props.width) : 300)) * props.max); }; const onPointerDown = (event: MouseEvent) => { isClickHold.value = true; setNewValue(event); }; </script> <template> <section class="container" id="progressBar" :style="`width: ${width ?? '300px'}; height: ${height ?? defaultHeight}; border: ${noBorder ? '' : '2px solid black'}`" @pointerdown.prevent="onPointerDown($event)" @pointermove="isClickHold ? setNewValue($event) : ''" @pointerup="isClickHold = false" > <div class="active"> <span v-show="showLabel" class="value">{{ labelBefore }}{{ value }}{{ labelAfter }}</span> Loading @@ -62,6 +83,7 @@ const defaultHeight = computed(() => { overflow: hidden; border-radius: calc(v-bind(fontSize) / 2.5); background-color: v-bind(inactiveColor); cursor: pointer; } .active { width: v-bind(active); Loading @@ -73,12 +95,12 @@ const defaultHeight = computed(() => { position: absolute; top: 0; left: 0; transition: width 0.4s ease-in-out; background-color: v-bind(activeColor); background: v-bind(activeColor); } .value { font-weight: bold; font-size: v-bind(fontSize); color: v-bind(textColor); user-select: none; } </style>