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

feat: component 'ProgressBar'

parent 62c425c0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ export interface IMDItemProps {
  onClick?: () => void;
}

export interface IKnobColorGap {
export interface IColorGap {
  start: number;
  end: number;
  color: TThemeColor;
+21 −3
Original line number Diff line number Diff line
@@ -11,14 +11,13 @@ import type {
  TThemeColorNoWhite,
} from '@interfaces/common';
import type {
  IKnobColorGap,
  IColorGap,
  IMDItemProps,
  ISBOption,
  ISelectGroup,
  ISelectOption,
  ISliderOptions,
  ITableColumn,
  ITableItem,
  ITreeItem,
} from '@interfaces/componentsProp';

@@ -74,7 +73,7 @@ export interface IKnobProps {
  step?: number;
  size?: TSize;
  theme?: TThemeColor;
  colorGaps?: IKnobColorGap[];
  colorGaps?: IColorGap[];
  negativeTheme?: TThemeColor;
  color?: TThemeColor;
  background?: string;
@@ -192,6 +191,25 @@ export interface IButtonProps {
  darknessTextColor?: TDarkness;
}

export interface IProgressBarProps {
  value: number;
  max?: number;
  width?: string;
  height?: string;
  size?: TSize;
  fontSize?: string;
  colorGaps?: IColorGap[];
  colorInactiveGaps?: IColorGap[];
  theme?: TThemeColor;
  inactiveTheme?: TThemeColor;
  darknessTheme?: TDarkness;
  darknessInactiveTheme?: TDarkness;
  showLabel?: boolean;
  labelBefore?: string;
  labelAfter?: string;
  noBorder?: boolean;
}

export interface IRatingProps {
  count?: number;
  size?: TSize;
+2 −2
Original line number Diff line number Diff line
import { convertThemeToColor } from '@helpers/common';
import { EThemeColor, type TDarkness, type TSize, type TThemeColor } from '@interfaces/common';
import type { IKnobColorGap } from '@interfaces/componentsProp';
import type { IColorGap } from '@interfaces/componentsProp';

export const calcNewValue = (
  event: MouseEvent,
@@ -40,7 +40,7 @@ export const calcStart = (container: Element) => {
};

export const calcThemeColor = (
  colorGaps: IKnobColorGap[] | undefined,
  colorGaps: IColorGap[] | undefined,
  theme: TThemeColor,
  darknessTheme: TDarkness,
  value: number,
+140 −0
Original line number Diff line number Diff line
import type { Meta, StoryObj } from '@storybook/vue3';

import ProgressBar from './ProgressBar.vue';

const meta: Meta = {
  title: 'Components/ProgressBar',
  component: ProgressBar,
  tags: ['autodocs'],
  parameters: {
    docs: {
      description: {
        component: 'A component that is used as a ProgressBar.',
      },
    },
  },
  argTypes: {
    value: { control: 'number' },
    max: { control: 'number' },
    width: { control: 'text' },
    height: { control: 'text' },
    labelBefore: { control: 'text' },
    labelAfter: { control: 'text' },
    showLabel: { control: 'boolean' },
    noBorder: { control: 'boolean' },
    size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] },
    fontSize: { control: 'text' },
    colorGaps: { control: 'object' },
    colorInactiveGaps: { control: 'object' },
    darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] },
    darknessInactiveTheme: {
      control: 'select',
      options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
    },
    theme: {
      control: 'select',
      options: [
        'white',
        'blue',
        'sky',
        'cyan',
        'teal',
        'green',
        'yellow',
        'orange',
        'pink',
        'fuchsia',
        'purple',
        'indigo',
        'rose',
        'red',
        'black',
      ],
    },
    inactiveTheme: {
      control: 'select',
      options: [
        'white',
        'blue',
        'sky',
        'cyan',
        'teal',
        'green',
        'yellow',
        'orange',
        'pink',
        'fuchsia',
        'purple',
        'indigo',
        'rose',
        'red',
        'black',
      ],
    },
  },
  args: {},
} satisfies Meta<typeof ProgressBar>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Simple: Story = {
  args: {
    value: 40,
  },
};

export const Small: Story = {
  args: {
    size: 'small',
    value: 40,
    theme: 'red',
    inactiveTheme: 'red',
    labelAfter: '/100',
    width: '500px',
  },
};

export const Full: Story = {
  args: {
    colorGaps: [
      {
        start: 0,
        end: 10,
        color: 'purple',
        darknessColor: '700',
      },
      {
        start: 10,
        end: 20,
        color: 'red',
      },
      {
        start: 20,
        end: 30,
        color: 'orange',
      },
      {
        start: 30,
        end: 40,
        color: 'yellow',
      },
      {
        start: 40,
        end: 50,
        color: 'green',
      },
    ],

    size: 'huge',
    value: 35,
    max: 50,
    labelAfter: '',
    showLabel: true,
    labelBefore: '$',
    inactiveTheme: 'white',
    darknessInactiveTheme: '400',
    noBorder: true,
  },
};
+84 −0
Original line number Diff line number Diff line
<script setup lang="ts">
import type { IProgressBarProps } from '@interfaces/componentsProps';
import { computed } from 'vue';
import { convertThemeToColor, convertThemeToTextColor } from '@helpers/common';

const props = withDefaults(defineProps<IProgressBarProps>(), {
  value: 0,
  max: 100,
  size: 'normal',
  theme: 'black',
  inactiveTheme: 'white',
  darknessTheme: '500',
  darknessInactiveTheme: '300',
  showLabel: true,
  labelAfter: '%',
});
const active = computed(() => `${(props.value / props.max) * 100}%`);
const activeColor = computed(() => {
  if (!props.colorGaps) return convertThemeToColor(props.theme, props.darknessTheme);
  const current = props.colorGaps.find((item) => item.start <= props.value && props.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);
  if (!current) return convertThemeToColor(props.inactiveTheme, props.darknessInactiveTheme);
  return convertThemeToColor(current.color, current.darknessColor);
});
const textColor = computed(() => convertThemeToTextColor(props.theme, props.darknessTheme));
const fontSize = computed(() => {
  if (props.fontSize) return props.fontSize;
  const size = props.size;
  if (size === 'normal') return '16px';
  if (size === 'large') return '20px';
  if (size === 'huge') return '24px';
  return '12px';
});
const defaultHeight = computed(() => {
  const size = props.size;
  if (size === 'normal') return '30px';
  if (size === 'large') return '45px';
  if (size === 'huge') return '60px';
  return '15px';
});
</script>

<template>
  <section
    class="container"
    :style="`width: ${width ?? '300px'}; height: ${height ?? defaultHeight}; border: ${noBorder ? '' : '2px solid black'}`"
  >
    <div class="active">
      <span v-show="showLabel" class="value">{{ labelBefore }}{{ value }}{{ labelAfter }}</span>
    </div>
  </section>
</template>

<style scoped>
.container {
  position: relative;
  overflow: hidden;
  border-radius: calc(v-bind(fontSize) / 2.5);
  background-color: v-bind(inactiveColor);
}
.active {
  width: v-bind(active);
  height: 100%;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 0;
  left: 0;
  transition: width 0.4s ease-in-out;
  background-color: v-bind(activeColor);
}
.value {
  font-weight: bold;
  font-size: v-bind(fontSize);
  color: v-bind(textColor);
}
</style>