Commit f231e41f authored by Дмитрий Малюгин's avatar Дмитрий Малюгин 🕓
Browse files

feat: make 'ProgressBar' editable

parent fad0839c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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'] },
@@ -93,6 +94,7 @@ export const Small: Story = {
    inactiveTheme: 'red',
    labelAfter: '/100',
    width: '500px',
    gradient: ['red', 'yellow', 'green'],
  },
};

+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>(), {
@@ -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);
});
@@ -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>
@@ -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);
@@ -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>