diff --git a/src/common/interfaces/componentsProp.ts b/src/common/interfaces/componentsProp.ts index 0b549ea70b274680b69feb16707b92d7e3183a46..ae44f557bff7cae5428a33904cd4a2d4453afec2 100644 --- a/src/common/interfaces/componentsProp.ts +++ b/src/common/interfaces/componentsProp.ts @@ -55,7 +55,7 @@ export interface IMDItemProps { onClick?: () => void; } -export interface IKnobColorGap { +export interface IColorGap { start: number; end: number; color: TThemeColor; diff --git a/src/common/interfaces/componentsProps.ts b/src/common/interfaces/componentsProps.ts index ec87cbdb8ac7e646334b5cb938594af7f76f6ece..07b4d81b249e82ae90efbba4640002a115db7897 100644 --- a/src/common/interfaces/componentsProps.ts +++ b/src/common/interfaces/componentsProps.ts @@ -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; diff --git a/src/components/Knob/helpers.ts b/src/components/Knob/helpers.ts index 2a53cd0e573c8d96b940eb92d5c33344fa39e2c9..56cb24e280ebb755686755c77e9260ac9e872ed6 100644 --- a/src/components/Knob/helpers.ts +++ b/src/components/Knob/helpers.ts @@ -1,6 +1,6 @@ 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, diff --git a/src/components/ProgressBar/ProgressBar.stories.ts b/src/components/ProgressBar/ProgressBar.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..27e2b24c90a930a7c22e05f61de4861ac8d31ace --- /dev/null +++ b/src/components/ProgressBar/ProgressBar.stories.ts @@ -0,0 +1,140 @@ +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, + }, +}; diff --git a/src/components/ProgressBar/ProgressBar.vue b/src/components/ProgressBar/ProgressBar.vue new file mode 100644 index 0000000000000000000000000000000000000000..61cd3836a05c0f9a0a7ee1a1f62db9dedeb2a6e2 --- /dev/null +++ b/src/components/ProgressBar/ProgressBar.vue @@ -0,0 +1,84 @@ +<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>