티스토리 뷰
[echarts.js] legend selector: Add function to deselect all legends
돔돔이 2023. 2. 3. 13:32echarts에서는 모든 라벨(legends)의 전체선택, 선택된 라벨 반전의 기능이 존재합니다.
하지만 전체선택 해제 기능은 없더라구요?!
echarts.js 소스코드에 legend 전체선택 해제 기능을 추가하는 방법을 작성해보려고 합니다!!
1. 전체선택, 반전선택
먼저 기존에 존재하는 전체선택("all")과 반전("inverse")는 이미 존재하는 기능이니 차트를 만들 때 옵션에 추가만 해주면 되게끔 만들어져있습니다.
아래와 같이 legend 안에 selector를 추가해주면 차트를 만들 때, 전체선택, 반전 버튼이 생성됩니다.
options = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left',
selector: [{
type: 'all',
title: '전체선택'
}, {
type: 'inverse',
title: '반전'
}]
},
...
}
전체선택, 반전 예제를 위해서 echarts 사이트의 Examples 페이지에서 코드를 수정하여 실행해보았습니다.
반전선택은 선택된 라벨은 선택해제가되고, 선택되지 않은 라벨들이 선택되는 기능입니다.
echarts examples URL 입니다.
https://echarts.apache.org/examples/en/editor.html?c=line-stack
2. 전체해제
전체선택과 반전선택이 있으면 전체해제도 있겠지?했는데 아무리 구글링해도 원하는 내용이 나오지 않더라구요.
그래서 본격 echarts.js 소스코드를 뜯어보았습니다.
echarts.js cdn을 찾아왔습니다.
https://cdnjs.cloudflare.com/ajax/libs/echarts/4.2.1-rc1/echarts.min.js
하지만 min.js 라서 소스코드들이 압축되어 있어 보기에 불편하니, 먼저 압축을 풀어봅시다.
min.js를 unminify해주는 온라인 사이트를 찾아서 소스코드를 해체(?)해주었습니다.
제가 사용한 사이트는 https://beautifier.io/ 입니다.
위 cdn js 소스코드들을 전체선택한 후 복붙(ctrl+a, ctrl+c)한 다음 Beautify Code 버튼을 클릭하면
자동으로 압축이 해제됩니다.
압축해된 소스코드를 열어 Ctrl+f로 legend selector의 "inverse" 키워드로 검색을 해보니,
54줄의 매칭되는 라인들이 있었습니다.
이제부터 전체해제 기능을 추가해봅니다. legend selector가 정의되어 있는 곳에 전체해제 기능을 추가해줄 것입니다.
Line.40720
Line 40720쯤에 있는 updateSelector에 "all", "inverse"만 존재하는 리스트가 있습니다.
여기에 "deSelect" (전체해제)을 추가해줍니다.
// Line 40720
e.prototype._updateSelector = function(t) {
var e = t.selector,
n = this.ecModel;
////// "all","inverse"만 있는 리스트에 "deSelect" 추가
!0 === e && (e = t.selector = ["all", "inverse", "deSelect"]), Y(e) && E(e, (function(t, i) {
X(t) && (t = {
type: t
}), e[i] = C(t, function(t, e) {
return "all" === e ? {
type: "all",
title: t.getLocaleModel().get(["legend", "selector", "all"])
} : "inverse" === e ? {
type: "inverse",
title: t.getLocaleModel().get(["legend", "selector", "inverse"])
} : "deSelect" === e ? { /// deSelect 추가
type: "deSelect",
title: t.getLocaleModel().get(["legend", "selector", "deSelect"])
} : void 0
}(n, t.type))
}))
}
Line.40780
Line 40780에 allSelect과 inverseSelect 버튼이 눌렀을 때 어떤 기능이 작동되도록 할것인지에 대해 적혀있는 코드들이 있습니다.
allSelect은 누가봐도 전체선택에 대한 내용이고 ,inverseSelect은 누가봐도 반전선택에 대한 내용입니다...!!
allSelect 내용을 살펴보니 모든 라벨에 대해서 "!0"이 되도록 설정하고 있고,
unSelect 내용을 보면 "!1"을 설정하도록 되어있는 것으로 보아 !0이 선택, !1이 선택해제인듯 합니다.
unSelect은 라벨 하나를 선택했을 때, 선택되어있으면 선택해제하는 기능입니다.
select: 라벨 한개에 대해 수행, allSelect: 라벨 전체에 대해 수행
unSelect: 라벨 한개에 대해 수행, deSelect: 라벨 전체애 대해 수행(우리가 추가해줄 기능)
unSelect과 이름이 중복되면 둘다 기능이 정상적으로 실행되지 않으니 중복되지 않게 deSelect으로 만들어주세요.
따라서 allSelect 내용을 복사해서 붙여넣은뒤 allSelect을 deSelect으로 바꿔주고, !0을 1!로 바꿔주면 됩니다.
// Line 40780
e.prototype.select = function(t) {
var e = this.option.selected;
"single" === this.get("selectedMode") && E(this._data, (function(t) {
e[t.get("name")] = !1
}));
e[t] = !0
}, e.prototype.unSelect = function(t) {
"single" !== this.get("selectedMode") && (this.option.selected[t] = !1)
}, e.prototype.toggleSelected = function(t) {
var e = this.option.selected;
e.hasOwnProperty(t) || (e[t] = !0), this[e[t] ? "unSelect" : "select"](t)
}, e.prototype.allSelect = function() {
var t = this._data,
e = this.option.selected;
E(t, (function(t) {
e[t.get("name", !0)] = !0
}))
}, e.prototype.inverseSelect = function() {
var t = this._data,
e = this.option.selected;
E(t, (function(t) {
var n = t.get("name", !0);
e.hasOwnProperty(n) || (e[n] = !0), e[n] = !e[n]
}))
// deSelect 추가
}, e.prototype.deSelect = function() {
var t = this._data,
e = this.option.selected;
E(t, (function(t) {
e[t.get("name", !1)] = !1
}))
}
Line.40947
사용자가 legend selector 버튼을 클릭했을 때 어떤 버튼을 클릭했느냐에 따라서 어떤 기능을 실행하게 할지에 대한 내용이 있습니다. 여기에 deSelect을 선택하면 legendDeSelect 타입의 액션이 실행되도록 추가해줄 것입니다.
기존에 작성되어있던 n.dispatchAction은 all 또는 inverse로만 이루어져있으니 주석처리 해주고,
클릭한 버튼의 타입에 따라 실행되도록 아래와 같이 수정합니다.
// Line 40957
e.prototype._createSelector = function(t, e, n, i, r) {
var o = this.getSelectorGroup();
dB(t, (function(t) {
var i = t.type,
r = new As({
style: {
x: 0,
y: 0,
align: "center",
verticalAlign: "middle"
},
onclick: function() { // 클릭했을 때의 내용 수정
var typeName = "legendAllSelect"
if (i == "inverse"){
typeName = "legendInverseSelect"
}else if(i == "deSelect"){
typeName = "legendDeSelect"
}
n.dispatchAction({
type: typeName
})
// n.dispatchAction({
// type: "all" === i ? "legendAllSelect" : "legendInverseSelect"
// })
}
});
o.add(r), Wh(r, {
normal: e.getModel("selectorLabel"),
emphasis: e.getModel(["emphasis", "selectorLabel"])
}, {
defaultText: t.title
}), Pl(r)
}))
}
Line. 41128
마찬가지로 allSelect, inverseSelect가 있는 곳에 deSelect를 똑같이 추가해줍니다.
위에서 deSelect을 선택하면 legendDeSelect타입의 액션이 실행되도록 하였으니
function wB에 legendDeSelect 액션을 등록해주었습니다.
function bB(t, e, n) {
var i, r = {},
o = "toggleSelected" === t;
return n.eachComponent("legend", (function(n) {
// deSelect 추가
o && null != i ? n[i ? "select" : "unSelect"](e.name) : "allSelect" === t || "inverseSelect" === t || "deSelect" === t ? n[t]() : (n[t](e.name), i = n.isSelected(e.name)), E(n.getData(), (function(t) {
var e = t.get("name");
if ("\n" !== e && "" !== e) {
var i = n.isSelected(e);
r.hasOwnProperty(e) ? r[e] = r[e] && i : r[e] = i
}
}))
})), "allSelect" === t || "inverseSelect" === t || "deSelect" === t ? {
selected: r
} : {
name: e.name,
selected: r
}
}
function wB(t) {
t.registerComponentModel(cB), t.registerComponentView(gB), t.registerProcessor(t.PRIORITY.PROCESSOR.SERIES_FILTER, _B), t.registerSubTypeDefaulter("legend", (function() {
return "plain"
})),
function(t) {
// deSelect 추가
t.registerAction("legendToggleSelect", "legendselectchanged", H(bB, "toggleSelected")),
t.registerAction("legendAllSelect", "legendselectall", H(bB, "allSelect")),
t.registerAction("legendInverseSelect", "legendinverseselect", H(bB, "inverseSelect")),
t.registerAction("legendSelect", "legendselected", H(bB, "select")),
t.registerAction("legendUnSelect", "legendunselected", H(bB, "unSelect")),
t.registerAction("legendDeSelect", "legenddeselected", H(bB, "deSelect"))
}(t)
}
이렇게 수정한 소스코드를 저장하고, 이제 전체해제 기능이 정상적으로 작동되는지 확인해봅니다.
정상적으로 모든 라벨이 전체해제 되고 있네요!!
테스트해본 소스입니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="echarts.js"></script>
<style>
#chart{
width: 1200px;
height: 600px;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
option = {
tooltip: {
trigger: 'axis'
},
legend: {
orient: 'vertical',
'left': 'left',
selector: [{
type: 'all',
title: '전체선택'
},{
type: 'inverse',
title: '반전'
},{
type: 'deSelect',
title: '전체해제'
},
],
data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine']
},
grid: {
top: '10%',
left: '18%',
right: '10%',
bottom: '5%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
name: 'Email',
type: 'line',
stack: 'Total',
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: 'Union Ads',
type: 'line',
stack: 'Total',
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: 'Video Ads',
type: 'line',
stack: 'Total',
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: 'Direct',
type: 'line',
stack: 'Total',
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: 'Search Engine',
type: 'line',
stack: 'Total',
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
};
var chart = document['getElementById']('chart');
var chart = echarts['init'](chart);
chart['setOption'](option, true);
</script>
</body>
</html>
'프로그래밍 > Frontend' 카테고리의 다른 글
[MapboxGL] 지도에 국가공간정보포털 건물데이터 벡터형식으로 올리기 (0) | 2023.03.13 |
---|---|
화면분할 코드(leaflet side-by-side.js)를 공공기관 지도에 활용하기 (feat. 스마트서울맵 SMAP, 통계청 SGIS) (0) | 2023.02.21 |
[javascript] 자바스크립트에서 속성 목록 확인 방법 (0) | 2023.01.03 |
https기반의 사이트에서 http video, iframe 사용할 때 나타나는 오류 (0) | 2022.09.03 |
[javascript] NodeList.forEach 메서드 사용법 및 활용 예시 (0) | 2022.09.02 |