diff --git a/src/components/d3/D3HorizontalChart.tsx b/src/components/d3/D3HorizontalChart.tsx
deleted file mode 100644
index 421108b..0000000
--- a/src/components/d3/D3HorizontalChart.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import React, { memo, useEffect } from 'react'
-import * as d3 from 'd3'
-
-import LoaderPortal from '@components/LoaderPortal'
-import { useElementSize } from 'usehooks-ts'
-
-
-import '@styles/d3.less'
-
-type DataType = {
- name: string
- percent: number
-}
-
-type D3HorizontalChartProps = {
- width?: string
- height?: string
- data: DataType[]
- colors?: string[]
-}
-
-const D3HorizontalChart = memo((
- {
- width: givenWidth = '100%',
- height: givenHeight = '100%',
- data,
- colors
- }: D3HorizontalChartProps) => {
-
- const [rootRef, { width, height }] = useElementSize()
-
- const margin = { top: 50, right: 100, bottom: 50, left: 100 }
-
- useEffect(() => {
- if (width < 100 || height < 100) return
- const _width = width - margin.left - margin.right
- const _height = height - margin.top - margin.bottom
-
- const svg = d3.select('#d3-horizontal-chart')
- .attr('width', '100%')
- .attr('height', '100%')
- .append('g')
- .attr('transform', `translate(${margin.left},${margin.top})`)
-
- const percents = ['percents']
- const names = data.map(d => d.name)
-
- const stackedData = d3.stack()
- //@ts-ignore
- .keys(percents)(data)
-
- const xMax = 100
-
- // scales
-
- const x = d3.scaleLinear()
- .domain([0, xMax])
- .range([0, _width])
-
- const y = d3.scaleBand()
- .domain(names)
- .range([0, _height])
- .padding(0.25)
-
- // axes
-
- const xAxisTop = d3.axisTop(x)
- .tickValues([0, 25, 50, 75, 100])
- .tickFormat(d => d + '%')
-
- const xAxisBottom = d3.axisBottom(x)
- .tickValues([0, 25, 50, 75, 100])
- .tickFormat(d => d + '%')
-
- const yAxisLeft = d3.axisLeft(y)
-
- const gridlines = d3.axisBottom(x)
- .tickValues([0, 25, 50, 75, 100])
- .tickFormat(d => '')
- .tickSize(_height)
-
- const yAxisRight = d3.axisRight(y)
- .ticks(0)
- .tickValues([])
- .tickFormat(d => '')
-
- svg.append('g')
- .attr('transform', `translate(0,0)`)
- .attr("class", "grid-line")
- .call(g => g.select('.domain').remove())
- .call(gridlines)
-
- svg.append('g')
- .attr('transform', `translate(0,0)`)
- .call(xAxisTop)
-
- svg.append("g")
- .call(yAxisLeft)
-
- svg.append('g')
- .attr('transform', `translate(0,${_height})`)
- .call(xAxisBottom)
-
- svg.append('g')
- .attr('transform', `translate(${_width},0)`)
- .call(yAxisRight)
-
- const layers = svg.append('g')
- .selectAll('g')
- .data(stackedData)
- .join('g')
-
- // transition for bars
- const duration = 1000
- const t = d3.transition()
- .duration(duration)
- .ease(d3.easeLinear)
-
- layers.each(function() {
- d3.select(this)
- .selectAll('rect')
- //@ts-ignore
- .data(d => d)
- .join('rect')
- .attr('fill', (d, i) => colors ? colors[i] : 'black')
- //@ts-ignore
- .attr('y', d => y(d.data.name))
- .attr('height', y.bandwidth())
- //@ts-ignore
- .transition(t)
- //@ts-ignore
- .attr('width', d => x(d.data.percent))
- })
-
- return () => {
- svg.selectAll("g").selectAll("*").remove()
- }
- }, [width, height, data])
-
- return (
-
-
-
-
-
- )
-})
-
-export default D3HorizontalChart
\ No newline at end of file
diff --git a/src/components/d3/D3HorizontalPercentChart.tsx b/src/components/d3/D3HorizontalPercentChart.tsx
new file mode 100644
index 0000000..631edf0
--- /dev/null
+++ b/src/components/d3/D3HorizontalPercentChart.tsx
@@ -0,0 +1,95 @@
+import { memo, useEffect, useMemo, useRef } from 'react'
+import { useElementSize } from 'usehooks-ts'
+import { Property } from 'csstype'
+import * as d3 from 'd3'
+
+import LoaderPortal from '@components/LoaderPortal'
+import { ChartOffset } from './types'
+
+import '@styles/d3.less'
+import { usePartialProps } from '@asb/utils'
+
+export type PercentChartDataType = {
+ name: string
+ percent: number
+ color?: Property.Color
+}
+
+export type D3HorizontalChartProps = {
+ width?: Property.Width
+ height?: Property.Height
+ data: PercentChartDataType[]
+ offset?: Partial
+}
+
+const defaultOffset = { top: 50, right: 100, bottom: 50, left: 100 }
+
+export const D3HorizontalPercentChart = memo(({
+ width: givenWidth = '100%',
+ height: givenHeight = '100%',
+ offset: givenOffset,
+ data,
+}) => {
+ const offset = usePartialProps(givenOffset, defaultOffset)
+
+ const [divRef, { width, height }] = useElementSize()
+ const rootRef = useRef(null)
+
+ const root = useMemo(() => rootRef.current ? d3.select(rootRef.current) : null, [rootRef.current])
+
+ const inlineWidth = useMemo(() => width - offset.left - offset.right, [width])
+ const inlineHeight = useMemo(() => height - offset.top - offset.bottom, [height])
+
+ const xScale = useMemo(() => d3.scaleLinear().domain([0, 100]).range([0, inlineWidth]), [inlineWidth])
+ const yScale = useMemo(() => d3.scaleBand().domain(data.map((d) => d.name)).range([0, inlineHeight]).padding(0.25), [data, inlineHeight])
+
+ useEffect(() => { /// Отрисовываем оси X сверху и снизу
+ if (width < 100 || height < 100 || !root) return
+ const xAxisTop = d3.axisTop(xScale).tickFormat((d) => `${d}%`).ticks(4).tickSize(-inlineHeight)
+ const xAxisBottom = d3.axisBottom(xScale).tickFormat((d) => `${d}%`).ticks(4)
+
+ root.selectChild('.axis.x.bottom').call(xAxisBottom)
+ root.selectChild('.axis.x.top').call(xAxisTop)
+ .selectAll('.tick')
+ .attr('class', 'tick grid-line')
+ }, [root, width, height, xScale, inlineHeight])
+
+ useEffect(() => { /// Отрисовываем ось Y слева
+ if (width < 100 || height < 100 || !root) return
+ root.selectChild('.axis.y.left').call(d3.axisLeft(yScale))
+ }, [root, width, height, yScale])
+
+ useEffect(() => {
+ if (width < 100 || height < 100 || !root) return
+
+ const delay = d3.transition().duration(500).ease(d3.easeLinear)
+
+ const rects = root.selectChild('.data').selectAll('rect').data(data)
+ rects.enter().append('rect')
+ rects.exit().remove()
+ root.selectChild('.data')
+ .selectAll('rect')
+ .attr('fill', (d) => d.color || 'black')
+ .attr('y', (d) => yScale(d.name) ?? null)
+ .attr('height', yScale.bandwidth())
+ .transition(delay)
+ .attr('width', (d) => xScale(d.percent))
+ }, [data, width, height, root, yScale, xScale])
+
+ return (
+
+
+
+
+
+ )
+})
+
+export default D3HorizontalPercentChart