Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
students
survey
Commits
928486a6
Commit
928486a6
authored
Jan 02, 2023
by
Yoon, Daeki
😅
Browse files
chart 시작
parent
bff0b7f5
Changes
33
Show whitespace changes
Inline
Side-by-side
frontend/src/charts/axes/Axis.tsx
0 → 100644
View file @
928486a6
import
React
from
"
react
"
;
import
type
{
AxisScale
,
AxisDomain
}
from
"
d3-axis
"
;
function
identity
(
x
:
any
)
{
return
x
;
}
/**
* Instead of a component for each orientation (like AxisLeft, AxisRight),
* we provide a value from this Orient object. Provide a value, like
* Orient.left, to the `orient` prop of the Axis component
* to place the axis on the left.
*/
export
enum
Orient
{
top
=
1
,
right
=
2
,
bottom
=
3
,
left
=
4
,
}
function
translateX
(
x
:
number
)
{
return
"
translate(
"
+
x
+
"
,0)
"
;
}
function
translateY
(
y
:
number
)
{
return
"
translate(0,
"
+
y
+
"
)
"
;
}
/**
* The axis component. This renders an axis, within a
* `g` element, for use in a chart.
*/
export
const
Axis
=
<
Domain
extends
AxisDomain
>
(
{
scale
,
ticks
,
tickArguments
=
[],
tickValues
=
null
,
tickFormat
=
null
,
tickSize
,
tickSizeInner
=
6
,
tickSizeOuter
=
6
,
tickPadding
=
3
,
tickTextProps
=
{},
tickLineProps
=
{},
domainPathProps
=
{},
orient
=
Orient
.
bottom
,
offset
=
typeof
window
!==
"
undefined
"
&&
window
.
devicePixelRatio
>
1
?
0
:
0.5
,
}
:
{
/** An initialized d3 scale object, like a d3.linearScale */
scale
:
AxisScale
<
Domain
>
;
ticks
?:
any
[];
tickArguments
?:
any
[];
tickValues
?:
any
[]
|
null
;
tickFormat
?:
any
;
tickSize
?:
number
;
tickSizeInner
?:
number
;
tickSizeOuter
?:
number
;
tickPadding
?:
number
;
/** Additional attributes to add to tick text elements, or null to omit */
tickTextProps
?:
React
.
SVGProps
<
SVGTextElement
>
|
null
;
/** Additional attributes to add to tick line elements, or null to omit */
tickLineProps
?:
React
.
SVGProps
<
SVGLineElement
>
|
null
;
/** Additional attributes to the domain path, or null to omit */
domainPathProps
?:
React
.
SVGProps
<
SVGPathElement
>
|
null
;
offset
?:
number
;
orient
?:
Orient
;
}
) =>
{
if
(
tickSize
)
{
tickSizeInner
=
tickSize
;
tickSizeOuter
=
tickSize
;
}
if
(
ticks
)
{
tickArguments
=
ticks
;
}
function
number
(
scale
:
AxisScale
<
Domain
>
)
{
return
(
d
:
any
)
=>
{
const
value
=
scale
(
d
);
return
value
===
undefined
?
0
:
+
value
;
};
}
function
center
(
scale
:
AxisScale
<
Domain
>
,
offset
:
number
)
{
if
(
scale
.
bandwidth
)
{
offset
=
Math
.
max
(
0
,
scale
.
bandwidth
()
-
offset
*
2
)
/
2
;
}
if
((
scale
as
any
).
round
())
offset
=
Math
.
round
(
offset
);
return
(
d
:
Domain
)
=>
{
const
value
=
scale
(
d
);
return
value
===
undefined
?
0
:
value
+
offset
;
};
}
const k = orient === Orient.top || orient === Orient.left ? -1 : 1,
x = orient === Orient.left || orient === Orient.right ? "x" : "y",
transform =
orient === Orient.top || orient === Orient.bottom
? translateX
: translateY;
// Rendering
const values =
tickValues == null
? (scale as any).ticks
? (scale as any).ticks.apply(scale, tickArguments)
: scale.domain()
: tickValues,
format =
tickFormat == null
? "tickFormat" in scale
? (scale as any).tickFormat.apply(scale, tickArguments)
: identity
: tickFormat,
spacing = Math.max(tickSizeInner, 0) + tickPadding,
range = scale.range(),
range0 = +range[0] + offset,
range1 = +range[range.length - 1] + offset,
position = (scale.bandwidth ? center : number)(scale.copy(), offset);
const domainPath =
orient === Orient.left || orient === Orient.right
? tickSizeOuter
? "M" +
k * tickSizeOuter +
"," +
range0 +
"H" +
offset +
"V" +
range1 +
"H" +
k * tickSizeOuter
: "M" + offset + "," + range0 + "V" + range1
: tickSizeOuter
? "M" +
range0 +
"," +
k * tickSizeOuter +
"V" +
offset +
"H" +
range1 +
"V" +
k * tickSizeOuter
: "M" + range0 + "," + offset + "H" + range1;
const lineProps =
{
[
x
+
"
2
"
]:
k
*
tickSizeInner
,
}
;
const textProps =
{
[
x
]:
k
*
spacing
,
}
;
return (
<
g
>
{
values
.
map
((
tick
:
any
,
i
:
number
)
=>
(
<
g
className
=
"tick"
key
=
{
i
}
transform
=
{
transform
(
position
(
tick
)
+
offset
)
}
>
{
tickLineProps
&&
(
<
line
stroke
=
"currentColor"
{
...
lineProps
}
{
...
tickLineProps
}
/>
)
}
{
tickTextProps
&&
(
<
text
fill
=
"currentColor"
dy
=
{
orient
===
Orient
.
top
?
"
0em
"
:
orient
===
Orient
.
bottom
?
"
0.71em
"
:
"
0.32em
"
}
fontSize
=
"10"
fontFamily
=
"sans-serif"
textAnchor
=
{
orient
===
Orient
.
right
?
"
start
"
:
orient
===
Orient
.
left
?
"
end
"
:
"
middle
"
}
{
...
textProps
}
{
...
tickTextProps
}
>
{
format
(
tick
)
}
</
text
>
)
}
</
g
>
))
}
{
domainPathProps
&&
(
<
path
className
=
"domain"
stroke
=
"currentColor"
fill
=
"transparent"
d
=
{
domainPath
}
{
...
domainPathProps
}
/>
)
}
</
g
>
);
};
frontend/src/charts/axes/AxisCirclesD3.tsx
0 → 100644
View file @
928486a6
import
React
,
{
useEffect
,
useRef
,
useState
}
from
"
react
"
;
import
{
scaleLinear
}
from
"
d3-scale
"
;
import
{
max
,
min
,
axisBottom
,
select
}
from
"
d3
"
;
import
{
generateRandomDataset
}
from
"
../helpers
"
;
export
const
AxisCirclesD3
=
()
=>
{
const
[
dataset
,
setDataset
]
=
useState
(
generateRandomDataset
(
30
,
1
));
const
ref
=
useRef
(
null
);
const
width
=
100
,
height
=
50
,
r
=
1
;
const
xPad
=
3
,
yPad
=
3
;
const
xMin
=
min
(
dataset
,
(
d
)
=>
d
[
0
])
||
0
;
const
xMax
=
max
(
dataset
,
(
d
)
=>
d
[
0
])
||
100
;
const
yMin
=
min
(
dataset
,
(
d
)
=>
d
[
1
])
||
0
;
const
yMax
=
max
(
dataset
,
(
d
)
=>
d
[
1
])
||
50
;
useEffect
(()
=>
{
const
xScale
=
scaleLinear
()
.
domain
([
xMin
,
xMax
])
.
range
([
0
+
xMin
,
width
-
xPad
]);
const
yScale
=
scaleLinear
()
.
domain
([
yMin
,
yMax
])
.
range
([
yMin
,
height
-
yPad
]);
const
rScale
=
scaleLinear
()
.
domain
([
xMin
+
yMin
,
xMax
+
yMax
])
.
range
([
0
,
height
/
10
]);
const
svgElmt
=
select
(
ref
.
current
);
const
xAxis
=
axisBottom
(
xScale
);
svgElmt
.
append
(
"
g
"
).
call
(
xAxis
);
},
[]);
return
(
<
div
style
=
{
{
backgroundColor
:
"
gray
"
}
}
>
<
svg
ref
=
{
ref
}
viewBox
=
{
`0 0
${
width
+
xMin
}
${
height
+
3
}
`
}
></
svg
>
</
div
>
);
};
frontend/src/charts/axes/AxisExamples.tsx
0 → 100644
View file @
928486a6
import
{
Axis
,
Orient
}
from
"
./Axis
"
;
import
{
BottomAxis
}
from
"
./BottomAxis
"
;
import
rawData
from
"
../data/data.json
"
;
import
{
scaleUtc
,
scaleLinear
}
from
"
d3-scale
"
;
import
{
line
}
from
"
d3-shape
"
;
import
{
max
,
extent
}
from
"
d3
"
;
import
useResizeObserver
from
"
../hooks/useResizeObserver
"
;
import
{
useSize
}
from
"
../hooks/useSize
"
;
import
{
useRef
}
from
"
react
"
;
import
{
useChartDimensions
}
from
"
../hooks/useChartDimensions
"
;
import
{
Comment
}
from
"
../commons
"
;
type
Record
=
{
date
:
Date
;
value
:
number
;
};
const
data
=
rawData
.
map
((
d
:
any
)
=>
{
return
{
date
:
new
Date
(
d
.
date
),
value
:
+
d
.
value
,
};
})
as
Record
[];
export
const
AxisExamples
=
()
=>
{
const
[
target
,
dimensions
]
=
useChartDimensions
({
marginTop
:
20
,
marginBottom
:
30
,
marginLeft
:
30
,
marginRight
:
20
,
});
// console.log("dimensions height=", dimensions.height);
// const target = useRef(null);
// const size = useSize(target);
// console.log("size height=", size?.height);
// const width = 600;
// const height = 400;
const
width
=
dimensions
.
width
||
600
;
const
height
=
dimensions
.
height
||
400
;
const
margin
=
{
top
:
dimensions
.
marginTop
,
right
:
dimensions
.
marginRight
,
bottom
:
dimensions
.
marginBottom
,
left
:
dimensions
.
marginLeft
,
};
// const width = size?.width || 600;
// const height = size?.height || 400;
// const margin = {
// top: 20,
// right: 20,
// bottom: 30,
// left: 30,
// };
const
x
=
scaleUtc
()
.
domain
(
extent
(
data
,
(
d
)
=>
d
.
date
)
as
[
Date
,
Date
])
.
range
([
margin
.
left
,
width
-
margin
.
right
]);
const
y
=
scaleLinear
<
number
>
()
.
domain
([
0
,
max
(
data
,
(
d
)
=>
d
.
value
)]
as
[
number
,
number
])
.
nice
()
.
range
([
height
-
margin
.
bottom
,
margin
.
top
]);
const
lineXY
=
line
<
Record
>
()
.
defined
((
d
)
=>
!
isNaN
(
d
.
value
))
.
x
((
d
)
=>
x
(
d
.
date
))
.
y
((
d
)
=>
y
(
d
.
value
));
return
(
<
div
>
<
h1
className
=
"text-3xl"
>
Axis Examples
</
h1
>
<
Comment
>
Resizable element 적용
</
Comment
>
<
div
ref
=
{
target
}
style
=
{
{
height
:
"
80vh
"
,
border
:
"
1px solid black
"
}
}
>
<
svg
width
=
{
width
}
height
=
{
height
}
>
<
path
fill
=
"none"
stroke
=
"steelblue"
strokeWidth
=
"1.5"
strokeLinejoin
=
"round"
strokeLinecap
=
"round"
d
=
{
lineXY
(
data
)
as
string
}
/>
<
g
transform
=
{
`translate(
${
margin
.
left
}
,0)`
}
>
<
Axis
scale
=
{
y
}
orient
=
{
Orient
.
left
}
/>
</
g
>
<
g
transform
=
{
`translate(0,
${
height
-
margin
.
bottom
}
)`
}
>
<
Axis
scale
=
{
x
}
orient
=
{
Orient
.
bottom
}
/>
</
g
>
</
svg
>
</
div
>
</
div
>
);
};
frontend/src/charts/axes/AxisScaleLinear.tsx
0 → 100644
View file @
928486a6
import
React
,
{
useMemo
}
from
"
react
"
;
import
{
ScaleLinear
,
scaleLinear
}
from
"
d3-scale
"
;
type
Props
=
{
x
:
number
;
y
:
number
;
scale
:
ScaleLinear
<
number
,
number
>
;
};
export
const
AxisScaleLinear
=
({
x
=
0
,
y
=
0
,
scale
}:
Props
)
=>
{
const
orient
=
"
bottom
"
;
const
ticks
=
useMemo
(
()
=>
scale
.
ticks
().
map
((
value
)
=>
({
value
,
xOffset
:
scale
(
value
),
})),
[]
);
// console.log("ticks:", ticks);
const
range
=
scale
.
range
();
return
(
<
g
fontSize
=
"10px"
textAnchor
=
"middle"
transform
=
{
`translate(
${
x
}
,
${
y
}
)`
}
>
<
path
d
=
{
`M
${
range
[
0
]}
0.5 H
${
range
[
1
]}
`
}
stroke
=
"currentColor"
/>
{
ticks
.
map
(({
value
,
xOffset
})
=>
(
<
g
key
=
{
value
}
transform
=
{
`translate(
${
xOffset
}
, 0)`
}
>
<
line
y2
=
{
6
}
stroke
=
"currentColor"
/>
<
text
key
=
{
value
}
dy
=
"1.5em"
>
{
value
}
</
text
>
</
g
>
))
}
</
g
>
);
};
frontend/src/charts/axes/AxisV0.tsx
0 → 100644
View file @
928486a6
import
React
,
{
useMemo
}
from
"
react
"
;
import
{
scaleLinear
}
from
"
d3-scale
"
;
export
const
AxisV0
=
()
=>
{
const
ticks
=
useMemo
(()
=>
{
const
xScale
=
scaleLinear
().
domain
([
0
,
100
]).
range
([
10
,
290
]);
return
xScale
.
ticks
().
map
((
value
)
=>
({
value
,
xOffset
:
xScale
(
value
),
}));
},
[]);
return
(
<
svg
>
<
path
d
=
"M 9.5 0.5 H 290.5"
stroke
=
"currentColor"
/>
{
ticks
.
map
(({
value
,
xOffset
})
=>
(
<
g
key
=
{
value
}
transform
=
{
`translate(
${
xOffset
}
, 0)`
}
>
<
line
y2
=
{
6
}
stroke
=
"currentColor"
/>
<
text
key
=
{
value
}
style
=
{
{
fontSize
:
"
10px
"
,
textAnchor
:
"
middle
"
,
transform
:
"
translateY(20px)
"
,
}
}
>
{
value
}
</
text
>
</
g
>
))
}
</
svg
>
);
};
frontend/src/charts/axes/AxisVerticalScaleLinear.tsx
0 → 100644
View file @
928486a6
import
React
,
{
useMemo
}
from
"
react
"
;
import
{
ScaleLinear
,
scaleLinear
}
from
"
d3-scale
"
;
type
Props
=
{
x
:
number
;
y
:
number
;
scale
:
ScaleLinear
<
number
,
number
>
;
};
export
const
AxisVerticalScaleLinear
=
({
x
=
0
,
y
=
0
,
scale
}:
Props
)
=>
{
const
orient
=
"
left
"
;
const
ticks
=
useMemo
(
()
=>
scale
.
ticks
().
map
((
value
)
=>
({
value
,
yOffset
:
scale
(
value
),
})),
[]
);
// console.log("ticks:", ticks);
const
range
=
scale
.
range
();
return
(
<
g
fontSize
=
"10px"
textAnchor
=
"middle"
transform
=
{
`translate(
${
x
}
,
${
y
}
)`
}
>
<
path
d
=
{
`M 0.5
${
range
[
0
]}
V
${
range
[
1
]}
`
}
stroke
=
"currentColor"
/>
{
ticks
.
map
(({
value
,
yOffset
})
=>
(
<
g
key
=
{
value
}
transform
=
{
`translate(0,
${
yOffset
}
)`
}
>
<
line
x2
=
{
-
6
}
stroke
=
"currentColor"
/>
<
text
key
=
{
value
}
dx
=
"-1.5em"
dy
=
{
"
0.3em
"
}
>
{
value
}
</
text
>
</
g
>
))
}
</
g
>
);
};
frontend/src/charts/axes/BottomAxis.tsx
0 → 100644
View file @
928486a6
import
React
from
"
react
"
;
import
{
axisBottom
}
from
"
d3-axis
"
;
import
{
scaleLinear
}
from
"
d3-scale
"
;
export
const
BottomAxis
=
()
=>
{
let
scale
=
scaleLinear
().
domain
([
0
,
100
]).
range
([
0
,
500
]);
let
axis
=
axisBottom
(
scale
);
console
.
log
(
"
axis:
"
,
axis
);
return
(
<
div
>
<
h1
>
BottomAxis
</
h1
>
<
svg
width
=
"600"
height
=
"100"
>
<
g
transform
=
"translate(20, 50)"
></
g
>
</
svg
>
</
div
>
);
};
frontend/src/charts/axes/index.tsx
0 → 100644
View file @
928486a6
export
{
AxisExamples
}
from
"
./AxisExamples
"
;
export
{
AxisScaleLinear
}
from
"
./AxisScaleLinear
"
;
export
{
AxisVerticalScaleLinear
}
from
"
./AxisVerticalScaleLinear
"
;
export
{
Axis
,
Orient
}
from
"
./Axis
"
;
frontend/src/charts/bars/Bar.tsx
0 → 100644
View file @
928486a6
import
{
ScaleBand
,
ScaleLinear
}
from
"
d3-scale
"
;
import
React
from
"
react
"
;
type
Props
=
{
dataset
:
number
[];
height
:
number
;
xScale
:
ScaleBand
<
number
>
;
yScale
:
ScaleLinear
<
number
,
number
>
;
};
export
const
Bar
=
({
dataset
,
height
,
xScale
,
yScale
}:
Props
)
=>
{
// console.log("dataset:", dataset);
return
(
<
g
>
{
dataset
.
map
((
d
,
i
)
=>
{
console
.
log
(
"
d
"
,
d
,
"
i
"
,
i
,
"
x:
"
,
xScale
(
i
),
"
y
"
,
yScale
(
d
));
return
(
<
rect
key
=
{
i
}
x
=
{
xScale
(
i
)
}
y
=
{
height
-
yScale
(
d
)
}
width
=
{
xScale
.
bandwidth
()
}
height
=
{
yScale
(
d
)
}
fill
=
{
`rgb(0, 0,
${
Math
.
round
(
d
*
10
)}
)`
}
></
rect
>
);
})
}
</
g
>
);
};
frontend/src/charts/bars/BarChart.tsx
0 → 100644
View file @
928486a6
import
{
max
,
min
,
range
}
from
"
d3
"
;
import
{
scaleLinear
,
scaleBand
}
from
"
d3-scale
"
;
import
{
BarText
}
from
"
../texts
"
;
import
{
Bar
}
from
"
./Bar
"
;
type
Props
=
{
dataset
:
number
[];
dimensions
:
DOMRect
|
undefined
;
};
export
const
BarChart
=
({
dataset
,
dimensions
}:
Props
)
=>
{
const
margin
=
{
top
:
0
,
right
:
0
,
bottom
:
0
,
left
:
0
};
const
width
=
dimensions
?.
width
||
600
;
const
height
=
dimensions
?.
height
||
300
;
const
innerWidth
=
width
-
margin
.
left
-
margin
.
right
;
const
innerHeight
=
height
-
margin
.
bottom
-
margin
.
top
;
console
.
log
(
"
width:
"
,
width
,
"
height:
"
,
height
);
const
xScale
=
scaleBand
<
number
>
()
.
domain
(
range
(
dataset
.
length
))
.
rangeRound
([
0
,
innerWidth
])
.
paddingInner
(
0.05
);
const
yScale
=
scaleLinear
()
.
domain
([
0
,
max
(
dataset
)
||
0
])
.
rangeRound
([
0
,
innerHeight
]);
return
(
<
div
>
<
svg
width
=
{
width
}
height
=
{
height
}
// style={{ borderWidth: "2px", borderColor: "black" }}
>
<
g
transform
=
{
`translate(
${
margin
.
left
}
,
${
margin
.
top
}
)`
}
>
<
Bar
dataset
=
{
dataset
}
xScale
=
{
xScale
}
yScale
=
{
yScale
}
height
=
{
innerHeight
}
/>
<
BarText
dataset
=
{
dataset
}
xScale
=
{
xScale
}
yScale
=
{
yScale
}
height
=
{
innerHeight
}
/>
</
g
>
</
svg
>
</
div
>
);
};
frontend/src/charts/bars/BarChartWithAnimation.tsx
0 → 100644
View file @
928486a6
import
{
max
,
min
,
range
}
from
"
d3
"
;
import
{
scaleLinear
,
scaleBand
}
from
"
d3-scale
"
;
import
{
BarText
}
from
"
../texts
"
;
import
{
BarWithAnimation
}
from
"
./BarWithAnimation
"
;
type
Props
=
{
dataset
:
number
[];
};
export
const
BarChartWithAnimation
=
({
dataset
}:
Props
)
=>
{
const
margin
=
{
top
:
20
,
right
:
30
,
bottom
:
20
,
left
:
30
};
const
width
=
window
.
innerWidth
-
margin
.
left
-
margin
.
right
;
const
height
=
300
-
margin
.
bottom
-
margin
.
top
;
const
xScale
=
scaleBand
<
number
>
()
.
domain
(
range
(
dataset
.
length
))
.
rangeRound
([
0
,
width
])
.
paddingInner
(
0.05
);
const
yScale
=
scaleLinear
()
.
domain
([
0
,
max
(
dataset
)
||
0
])
.
rangeRound
([
0
,
height
]);
return
(
<
svg
width
=
{
width
+
margin
.
left
+
margin
.
right
}
height
=
{
height
+
margin
.
bottom
+
margin
.
top
}
style
=
{
{
borderWidth
:
"
2px
"
,
borderColor
:
"
black
"
}
}
>
<
BarWithAnimation
dataset
=
{
dataset
}
xScale
=
{
xScale
}
yScale
=
{
yScale
}
height
=
{
height
}
/>
<
BarText
dataset
=
{
dataset
}
xScale
=
{
xScale
}
yScale
=
{
yScale
}
height
=
{
height
}
/>
</
svg
>
);
};
frontend/src/charts/bars/BarExample.tsx
0 → 100644
View file @
928486a6
import
{
useRef
,
useState
}
from
"
react
"
;
import
{
BarChart
}
from
"
./BarChart
"
;
import
{
generateDataset1D
}
from
"
../helpers
"
;
import
{
BarChartWithAnimation
}
from
"
./BarChartWithAnimation
"
;
import
{
Button
,
Comment
}
from
"
../commons
"
;
import
{
useSize
}
from
"
../hooks
"
;
const
initDataset
=
[
5
,
10
,
13
,
19
,
21
,
25
,
22
,
18
,
15
,
13
,
11
,
12
,
15
,
20
,
18
,
17
,
16
,
18
,
23
,
25
,
];
const
nextDataset
=
[
21
,
16
,
6
,
18
,
10
,
18
,
20
,
9
,
17
,
1
,
5
,
11
,
18
,
5
,
2
,
22
,
7
,
23
,
3
,
3
,
];
export
const
BarExample
=
()
=>
{
const
[
dataset
,
setDataset
]
=
useState
(
initDataset
);
const
target
=
useRef
(
null
);
const
size
=
useSize
(
target
);
const
toggle
=
useRef
(
true
);
const
handleClick
=
()
=>
{
setDataset
(
generateDataset1D
());
};
const
handleToggle
=
()
=>
{
toggle
.
current
?
setDataset
(
nextDataset
)
:
setDataset
(
initDataset
);
toggle
.
current
=
!
toggle
.
current
;
};
return
(
<
div
>
<
div
className
=
"my-4"
>
<
Button
className
=
"mr-4"
onClick
=
{
handleClick
}
>
새로운 무작위 데이터로 차트를 갱신하려면 클릭하세요.
</
Button
>
<
Button
onClick
=
{
handleToggle
}
>
데이터 토글
</
Button
>
</
div
>
<
Comment
>
애니메이션이 들어간 바차트. 문제는 텍스트가 같이 애니메이션이 안된다는
것입니다.
</
Comment
>
<
BarChartWithAnimation
dataset
=
{
dataset
}
/>
<
Comment
>
바차트
</
Comment
>
<
div
ref
=
{
target
}
>
<
BarChart
dataset
=
{
dataset
}
dimensions
=
{
size
}
/>
</
div
>
</
div
>
);
};
frontend/src/charts/bars/BarWithAnimation.tsx
0 → 100644
View file @
928486a6
import
{
ScaleBand
,
ScaleLinear
}
from
"
d3-scale
"
;
import
React
,
{
Fragment
,
useEffect
,
useRef
}
from
"
react
"
;
type
Props
=
{
dataset
:
number
[];
height
:
number
;
xScale
:
ScaleBand
<
number
>
;
yScale
:
ScaleLinear
<
number
,
number
>
;
};
export
const
BarWithAnimation
=
({
dataset
,
height
,
xScale
,
yScale
,
}:
Props
)
=>
{
// console.log("dataset:", dataset);
const
preDataset
=
useRef
(
dataset
);
useEffect
(()
=>
{
preDataset
.
current
=
dataset
;
},
[
dataset
]);
const
getStyle
=
(
i
:
number
)
=>
{
const
style
=
{
// fill: "red",
animationName
:
`inmoveleftright-
${
i
}
`
,
animationDuration
:
"
1s
"
,
// animationDirection: "alternate",
animationIterationCount
:
"
1
"
,
// transformOrigin: "bottom",
};
return
style
;
};
return
(
<
g
transform
=
{
`scale(1, -1) translate(0,
${
-
height
}
)`
}
>
{
dataset
.
map
((
d
,
i
)
=>
{
// console.log("d", d, "i", i, "x:", xScale(i), "y", yScale(d));
return
(
<
Fragment
key
=
{
Math
.
random
()
}
>
<
style
>
{
`
@keyframes inmoveleftright-
${
i
}
{
0% {
height:
${
yScale
(
preDataset
.
current
[
i
])}
px;
}
100% {
height:
${
yScale
(
d
)}
px;
}
}
`
}
</
style
>
<
rect
x
=
{
xScale
(
i
)
}
y
=
{
0
}
width
=
{
xScale
.
bandwidth
()
}
height
=
{
yScale
(
d
)
}
fill
=
{
`rgb(0, 0,
${
Math
.
round
(
d
*
10
)}
)`
}
style
=
{
getStyle
(
i
)
}
></
rect
>
</
Fragment
>
);
})
}
</
g
>
);
};
frontend/src/charts/bars/index.tsx
0 → 100644
View file @
928486a6
export
{
Bar
}
from
"
./Bar
"
;
export
{
BarChart
}
from
"
./BarChart
"
;
export
{
BarExample
}
from
"
./BarExample
"
;
frontend/src/charts/hooks/index.ts
0 → 100644
View file @
928486a6
export
{
useSize
}
from
"
./useSize
"
;
frontend/src/charts/hooks/useChartDimensions.tsx
0 → 100644
View file @
928486a6
/**
* 출처: https://github.com/73nko/advanced-d3/blob/master/13-using-d3-with-react-js/src/completed/Chart/utils.js
*/
import
{
useEffect
,
useState
,
useRef
}
from
"
react
"
;
export
const
combineChartDimensions
=
(
dimensions
:
any
)
=>
{
let
parsedDimensions
=
{
marginTop
:
40
,
marginRight
:
30
,
marginBottom
:
40
,
marginLeft
:
75
,
...
dimensions
,
};
return
{
...
parsedDimensions
,
boundedHeight
:
Math
.
max
(
parsedDimensions
.
height
-
parsedDimensions
.
marginTop
-
parsedDimensions
.
marginBottom
,
0
),
boundedWidth
:
Math
.
max
(
parsedDimensions
.
width
-
parsedDimensions
.
marginLeft
-
parsedDimensions
.
marginRight
,
0
),
};
};
export
const
useChartDimensions
=
(
passedSettings
:
any
)
=>
{
const
ref
=
useRef
<
HTMLElement
>
();
const
dimensions
=
combineChartDimensions
(
passedSettings
);
const
[
width
,
changeWidth
]
=
useState
(
0
);
const
[
height
,
changeHeight
]
=
useState
(
0
);
useEffect
(()
=>
{
if
(
dimensions
.
width
&&
dimensions
.
height
)
return
;
const
element
=
ref
.
current
;
const
resizeObserver
=
new
ResizeObserver
((
entries
)
=>
{
if
(
!
Array
.
isArray
(
entries
))
return
;
if
(
!
entries
.
length
)
return
;
const
entry
=
entries
[
0
];
if
(
width
!==
entry
.
contentRect
.
width
)
changeWidth
(
entry
.
contentRect
.
width
);
if
(
height
!==
entry
.
contentRect
.
height
)
changeHeight
(
entry
.
contentRect
.
height
);
});
element
&&
resizeObserver
.
observe
(
element
);
return
()
=>
element
&&
resizeObserver
.
unobserve
(
element
);
},
[
passedSettings
,
height
,
width
,
dimensions
]);
const
newSettings
=
combineChartDimensions
({
...
dimensions
,
width
:
dimensions
.
width
||
width
,
height
:
dimensions
.
height
||
height
,
});
return
[
ref
,
newSettings
];
};
frontend/src/charts/hooks/useLatest.tsx
0 → 100644
View file @
928486a6
/**
* 출처: https://github.com/jaredLunde/react-hook
*/
import
{
useRef
,
useEffect
}
from
"
react
"
;
const
useLatest
=
<
T
extends
any
>
(current: T) =>
{
const
storedValue
=
useRef
(
current
);
useEffect
(()
=>
{
storedValue
.
current
=
current
;
});
return
storedValue
;
}
;
export default useLatest;
frontend/src/charts/hooks/usePassiveLayoutEffect.tsx
0 → 100644
View file @
928486a6
/**
* 출처: https://github.com/jaredLunde/react-hook
*/
import
React
from
"
react
"
;
const
usePassiveLayoutEffect
=
React
[
typeof
document
!==
"
undefined
"
&&
document
.
createElement
!==
void
0
?
"
useLayoutEffect
"
:
"
useEffect
"
];
export
default
usePassiveLayoutEffect
;
frontend/src/charts/hooks/useResizeObserver.tsx
0 → 100644
View file @
928486a6
/**
* 출처: https://github.com/jaredLunde/react-hook
*/
import
{
ResizeObserver
as
Polyfill
,
ResizeObserverEntry
,
}
from
"
@juggle/resize-observer
"
;
import
useLayoutEffect
from
"
./usePassiveLayoutEffect
"
;
import
useLatest
from
"
./useLatest
"
;
// 브라우저 ResizeObserver를 사용할 건지 폴리필을 사용할 건지 결정
const
ResizeObserver
=
typeof
window
!==
"
undefined
"
&&
"
ResizeObserver
"
in
window
?
// @ts-ignore
window
.
ResizeObserver
:
Polyfill
;
/**
* A React hook that fires a callback whenever ResizeObserver detects a change to its size
*
* @param target A React ref created by `useRef()` or an HTML element
* @param callback Invoked with a single `ResizeObserverEntry` any time
* the `target` resizes
*/
function
useResizeObserver
<
T
extends
HTMLElement
>
(
target
:
React
.
RefObject
<
T
>
|
T
|
null
,
callback
:
UseResizeObserverCallback
):
Polyfill
{
const
resizeObserver
=
getResizeObserver
();
const
storedCallback
=
useLatest
(
callback
);
useLayoutEffect
(()
=>
{
let
didUnsubscribe
=
false
;
const
targetEl
=
target
&&
"
current
"
in
target
?
target
.
current
:
target
;
if
(
!
targetEl
)
return
()
=>
{};
function
cb
(
entry
:
ResizeObserverEntry
,
observer
:
Polyfill
)
{
if
(
didUnsubscribe
)
return
;
storedCallback
.
current
(
entry
,
observer
);
}
resizeObserver
.
subscribe
(
targetEl
as
HTMLElement
,
cb
);
return
()
=>
{
didUnsubscribe
=
true
;
resizeObserver
.
unsubscribe
(
targetEl
as
HTMLElement
,
cb
);
};
},
[
target
,
resizeObserver
,
storedCallback
]);
return
resizeObserver
.
observer
;
}
function
createResizeObserver
()
{
let
ticking
=
false
;
let
allEntries
:
ResizeObserverEntry
[]
=
[];
const
callbacks
:
Map
<
any
,
Array
<
UseResizeObserverCallback
>>
=
new
Map
();
const
observer
=
new
ResizeObserver
(
(
entries
:
ResizeObserverEntry
[],
obs
:
Polyfill
)
=>
{
allEntries
=
allEntries
.
concat
(
entries
);
if
(
!
ticking
)
{
window
.
requestAnimationFrame
(()
=>
{
const
triggered
=
new
Set
<
Element
>
();
for
(
let
i
=
0
;
i
<
allEntries
.
length
;
i
++
)
{
if
(
triggered
.
has
(
allEntries
[
i
].
target
))
continue
;
triggered
.
add
(
allEntries
[
i
].
target
);
const
cbs
=
callbacks
.
get
(
allEntries
[
i
].
target
);
cbs
?.
forEach
((
cb
)
=>
cb
(
allEntries
[
i
],
obs
));
}
allEntries
=
[];
ticking
=
false
;
});
}
ticking
=
true
;
}
);
return
{
observer
,
subscribe
(
target
:
HTMLElement
,
callback
:
UseResizeObserverCallback
)
{
observer
.
observe
(
target
);
const
cbs
=
callbacks
.
get
(
target
)
??
[];
cbs
.
push
(
callback
);
callbacks
.
set
(
target
,
cbs
);
},
unsubscribe
(
target
:
HTMLElement
,
callback
:
UseResizeObserverCallback
)
{
const
cbs
=
callbacks
.
get
(
target
)
??
[];
if
(
cbs
.
length
===
1
)
{
observer
.
unobserve
(
target
);
callbacks
.
delete
(
target
);
return
;
}
const
cbIndex
=
cbs
.
indexOf
(
callback
);
if
(
cbIndex
!==
-
1
)
cbs
.
splice
(
cbIndex
,
1
);
callbacks
.
set
(
target
,
cbs
);
},
};
}
let
_resizeObserver
:
ReturnType
<
typeof
createResizeObserver
>
;
const
getResizeObserver
=
()
=>
!
_resizeObserver
?
(
_resizeObserver
=
createResizeObserver
())
:
_resizeObserver
;
export
type
UseResizeObserverCallback
=
(
entry
:
ResizeObserverEntry
,
observer
:
Polyfill
)
=>
any
;
export
default
useResizeObserver
;
frontend/src/charts/hooks/useSize.tsx
0 → 100644
View file @
928486a6
import
React
from
"
react
"
;
import
useResizeObserver
from
"
./useResizeObserver
"
;
export
const
useSize
=
(
target
:
React
.
RefObject
<
HTMLElement
>
)
=>
{
const
[
size
,
setSize
]
=
React
.
useState
<
DOMRectReadOnly
>
();
React
.
useLayoutEffect
(()
=>
{
setSize
(
target
.
current
?.
getBoundingClientRect
());
},
[
target
]);
// Where the magic happens
useResizeObserver
(
target
,
(
entry
)
=>
setSize
(
entry
.
contentRect
));
return
size
;
};
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment