ScrollableLegendView.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { __extends } from "tslib";
  41. /**
  42. * Separate legend and scrollable legend to reduce package size.
  43. */
  44. import * as zrUtil from 'zrender/lib/core/util.js';
  45. import * as graphic from '../../util/graphic.js';
  46. import * as layoutUtil from '../../util/layout.js';
  47. import LegendView from './LegendView.js';
  48. var Group = graphic.Group;
  49. var WH = ['width', 'height'];
  50. var XY = ['x', 'y'];
  51. var ScrollableLegendView =
  52. /** @class */
  53. function (_super) {
  54. __extends(ScrollableLegendView, _super);
  55. function ScrollableLegendView() {
  56. var _this = _super !== null && _super.apply(this, arguments) || this;
  57. _this.type = ScrollableLegendView.type;
  58. _this.newlineDisabled = true;
  59. _this._currentIndex = 0;
  60. return _this;
  61. }
  62. ScrollableLegendView.prototype.init = function () {
  63. _super.prototype.init.call(this);
  64. this.group.add(this._containerGroup = new Group());
  65. this._containerGroup.add(this.getContentGroup());
  66. this.group.add(this._controllerGroup = new Group());
  67. };
  68. /**
  69. * @override
  70. */
  71. ScrollableLegendView.prototype.resetInner = function () {
  72. _super.prototype.resetInner.call(this);
  73. this._controllerGroup.removeAll();
  74. this._containerGroup.removeClipPath();
  75. this._containerGroup.__rectSize = null;
  76. };
  77. /**
  78. * @override
  79. */
  80. ScrollableLegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {
  81. var self = this; // Render content items.
  82. _super.prototype.renderInner.call(this, itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition);
  83. var controllerGroup = this._controllerGroup; // FIXME: support be 'auto' adapt to size number text length,
  84. // e.g., '3/12345' should not overlap with the control arrow button.
  85. var pageIconSize = legendModel.get('pageIconSize', true);
  86. var pageIconSizeArr = zrUtil.isArray(pageIconSize) ? pageIconSize : [pageIconSize, pageIconSize];
  87. createPageButton('pagePrev', 0);
  88. var pageTextStyleModel = legendModel.getModel('pageTextStyle');
  89. controllerGroup.add(new graphic.Text({
  90. name: 'pageText',
  91. style: {
  92. // Placeholder to calculate a proper layout.
  93. text: 'xx/xx',
  94. fill: pageTextStyleModel.getTextColor(),
  95. font: pageTextStyleModel.getFont(),
  96. verticalAlign: 'middle',
  97. align: 'center'
  98. },
  99. silent: true
  100. }));
  101. createPageButton('pageNext', 1);
  102. function createPageButton(name, iconIdx) {
  103. var pageDataIndexName = name + 'DataIndex';
  104. var icon = graphic.createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], {
  105. // Buttons will be created in each render, so we do not need
  106. // to worry about avoiding using legendModel kept in scope.
  107. onclick: zrUtil.bind(self._pageGo, self, pageDataIndexName, legendModel, api)
  108. }, {
  109. x: -pageIconSizeArr[0] / 2,
  110. y: -pageIconSizeArr[1] / 2,
  111. width: pageIconSizeArr[0],
  112. height: pageIconSizeArr[1]
  113. });
  114. icon.name = name;
  115. controllerGroup.add(icon);
  116. }
  117. };
  118. /**
  119. * @override
  120. */
  121. ScrollableLegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {
  122. var selectorGroup = this.getSelectorGroup();
  123. var orientIdx = legendModel.getOrient().index;
  124. var wh = WH[orientIdx];
  125. var xy = XY[orientIdx];
  126. var hw = WH[1 - orientIdx];
  127. var yx = XY[1 - orientIdx];
  128. selector && layoutUtil.box( // Buttons in selectorGroup always layout horizontally
  129. 'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));
  130. var selectorButtonGap = legendModel.get('selectorButtonGap', true);
  131. var selectorRect = selectorGroup.getBoundingRect();
  132. var selectorPos = [-selectorRect.x, -selectorRect.y];
  133. var processMaxSize = zrUtil.clone(maxSize);
  134. selector && (processMaxSize[wh] = maxSize[wh] - selectorRect[wh] - selectorButtonGap);
  135. var mainRect = this._layoutContentAndController(legendModel, isFirstRender, processMaxSize, orientIdx, wh, hw, yx, xy);
  136. if (selector) {
  137. if (selectorPosition === 'end') {
  138. selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap;
  139. } else {
  140. var offset = selectorRect[wh] + selectorButtonGap;
  141. selectorPos[orientIdx] -= offset;
  142. mainRect[xy] -= offset;
  143. }
  144. mainRect[wh] += selectorRect[wh] + selectorButtonGap;
  145. selectorPos[1 - orientIdx] += mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2;
  146. mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]);
  147. mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]);
  148. selectorGroup.x = selectorPos[0];
  149. selectorGroup.y = selectorPos[1];
  150. selectorGroup.markRedraw();
  151. }
  152. return mainRect;
  153. };
  154. ScrollableLegendView.prototype._layoutContentAndController = function (legendModel, isFirstRender, maxSize, orientIdx, wh, hw, yx, xy) {
  155. var contentGroup = this.getContentGroup();
  156. var containerGroup = this._containerGroup;
  157. var controllerGroup = this._controllerGroup; // Place items in contentGroup.
  158. layoutUtil.box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height);
  159. layoutUtil.box( // Buttons in controller are layout always horizontally.
  160. 'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true));
  161. var contentRect = contentGroup.getBoundingRect();
  162. var controllerRect = controllerGroup.getBoundingRect();
  163. var showController = this._showController = contentRect[wh] > maxSize[wh]; // In case that the inner elements of contentGroup layout do not based on [0, 0]
  164. var contentPos = [-contentRect.x, -contentRect.y]; // Remain contentPos when scroll animation perfroming.
  165. // If first rendering, `contentGroup.position` is [0, 0], which
  166. // does not make sense and may cause unexepcted animation if adopted.
  167. if (!isFirstRender) {
  168. contentPos[orientIdx] = contentGroup[xy];
  169. } // Layout container group based on 0.
  170. var containerPos = [0, 0];
  171. var controllerPos = [-controllerRect.x, -controllerRect.y];
  172. var pageButtonGap = zrUtil.retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); // Place containerGroup and controllerGroup and contentGroup.
  173. if (showController) {
  174. var pageButtonPosition = legendModel.get('pageButtonPosition', true); // controller is on the right / bottom.
  175. if (pageButtonPosition === 'end') {
  176. controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];
  177. } // controller is on the left / top.
  178. else {
  179. containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;
  180. }
  181. } // Always align controller to content as 'middle'.
  182. controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;
  183. contentGroup.setPosition(contentPos);
  184. containerGroup.setPosition(containerPos);
  185. controllerGroup.setPosition(controllerPos); // Calculate `mainRect` and set `clipPath`.
  186. // mainRect should not be calculated by `this.group.getBoundingRect()`
  187. // for sake of the overflow.
  188. var mainRect = {
  189. x: 0,
  190. y: 0
  191. }; // Consider content may be overflow (should be clipped).
  192. mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];
  193. mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.
  194. mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);
  195. containerGroup.__rectSize = maxSize[wh];
  196. if (showController) {
  197. var clipShape = {
  198. x: 0,
  199. y: 0
  200. };
  201. clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);
  202. clipShape[hw] = mainRect[hw];
  203. containerGroup.setClipPath(new graphic.Rect({
  204. shape: clipShape
  205. })); // Consider content may be larger than container, container rect
  206. // can not be obtained from `containerGroup.getBoundingRect()`.
  207. containerGroup.__rectSize = clipShape[wh];
  208. } else {
  209. // Do not remove or ignore controller. Keep them set as placeholders.
  210. controllerGroup.eachChild(function (child) {
  211. child.attr({
  212. invisible: true,
  213. silent: true
  214. });
  215. });
  216. } // Content translate animation.
  217. var pageInfo = this._getPageInfo(legendModel);
  218. pageInfo.pageIndex != null && graphic.updateProps(contentGroup, {
  219. x: pageInfo.contentPosition[0],
  220. y: pageInfo.contentPosition[1]
  221. }, // When switch from "show controller" to "not show controller", view should be
  222. // updated immediately without animation, otherwise causes weird effect.
  223. showController ? legendModel : null);
  224. this._updatePageInfoView(legendModel, pageInfo);
  225. return mainRect;
  226. };
  227. ScrollableLegendView.prototype._pageGo = function (to, legendModel, api) {
  228. var scrollDataIndex = this._getPageInfo(legendModel)[to];
  229. scrollDataIndex != null && api.dispatchAction({
  230. type: 'legendScroll',
  231. scrollDataIndex: scrollDataIndex,
  232. legendId: legendModel.id
  233. });
  234. };
  235. ScrollableLegendView.prototype._updatePageInfoView = function (legendModel, pageInfo) {
  236. var controllerGroup = this._controllerGroup;
  237. zrUtil.each(['pagePrev', 'pageNext'], function (name) {
  238. var key = name + 'DataIndex';
  239. var canJump = pageInfo[key] != null;
  240. var icon = controllerGroup.childOfName(name);
  241. if (icon) {
  242. icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true));
  243. icon.cursor = canJump ? 'pointer' : 'default';
  244. }
  245. });
  246. var pageText = controllerGroup.childOfName('pageText');
  247. var pageFormatter = legendModel.get('pageFormatter');
  248. var pageIndex = pageInfo.pageIndex;
  249. var current = pageIndex != null ? pageIndex + 1 : 0;
  250. var total = pageInfo.pageCount;
  251. pageText && pageFormatter && pageText.setStyle('text', zrUtil.isString(pageFormatter) ? pageFormatter.replace('{current}', current == null ? '' : current + '').replace('{total}', total == null ? '' : total + '') : pageFormatter({
  252. current: current,
  253. total: total
  254. }));
  255. };
  256. /**
  257. * contentPosition: Array.<number>, null when data item not found.
  258. * pageIndex: number, null when data item not found.
  259. * pageCount: number, always be a number, can be 0.
  260. * pagePrevDataIndex: number, null when no previous page.
  261. * pageNextDataIndex: number, null when no next page.
  262. * }
  263. */
  264. ScrollableLegendView.prototype._getPageInfo = function (legendModel) {
  265. var scrollDataIndex = legendModel.get('scrollDataIndex', true);
  266. var contentGroup = this.getContentGroup();
  267. var containerRectSize = this._containerGroup.__rectSize;
  268. var orientIdx = legendModel.getOrient().index;
  269. var wh = WH[orientIdx];
  270. var xy = XY[orientIdx];
  271. var targetItemIndex = this._findTargetItemIndex(scrollDataIndex);
  272. var children = contentGroup.children();
  273. var targetItem = children[targetItemIndex];
  274. var itemCount = children.length;
  275. var pCount = !itemCount ? 0 : 1;
  276. var result = {
  277. contentPosition: [contentGroup.x, contentGroup.y],
  278. pageCount: pCount,
  279. pageIndex: pCount - 1,
  280. pagePrevDataIndex: null,
  281. pageNextDataIndex: null
  282. };
  283. if (!targetItem) {
  284. return result;
  285. }
  286. var targetItemInfo = getItemInfo(targetItem);
  287. result.contentPosition[orientIdx] = -targetItemInfo.s; // Strategy:
  288. // (1) Always align based on the left/top most item.
  289. // (2) It is user-friendly that the last item shown in the
  290. // current window is shown at the begining of next window.
  291. // Otherwise if half of the last item is cut by the window,
  292. // it will have no chance to display entirely.
  293. // (3) Consider that item size probably be different, we
  294. // have calculate pageIndex by size rather than item index,
  295. // and we can not get page index directly by division.
  296. // (4) The window is to narrow to contain more than
  297. // one item, we should make sure that the page can be fliped.
  298. for (var i = targetItemIndex + 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i <= itemCount; ++i) {
  299. currItemInfo = getItemInfo(children[i]);
  300. if ( // Half of the last item is out of the window.
  301. !currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize || // If the current item does not intersect with the window, the new page
  302. // can be started at the current item or the last item.
  303. currItemInfo && !intersect(currItemInfo, winStartItemInfo.s)) {
  304. if (winEndItemInfo.i > winStartItemInfo.i) {
  305. winStartItemInfo = winEndItemInfo;
  306. } else {
  307. // e.g., when page size is smaller than item size.
  308. winStartItemInfo = currItemInfo;
  309. }
  310. if (winStartItemInfo) {
  311. if (result.pageNextDataIndex == null) {
  312. result.pageNextDataIndex = winStartItemInfo.i;
  313. }
  314. ++result.pageCount;
  315. }
  316. }
  317. winEndItemInfo = currItemInfo;
  318. }
  319. for (var i = targetItemIndex - 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i >= -1; --i) {
  320. currItemInfo = getItemInfo(children[i]);
  321. if ( // If the the end item does not intersect with the window started
  322. // from the current item, a page can be settled.
  323. (!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s)) && // e.g., when page size is smaller than item size.
  324. winStartItemInfo.i < winEndItemInfo.i) {
  325. winEndItemInfo = winStartItemInfo;
  326. if (result.pagePrevDataIndex == null) {
  327. result.pagePrevDataIndex = winStartItemInfo.i;
  328. }
  329. ++result.pageCount;
  330. ++result.pageIndex;
  331. }
  332. winStartItemInfo = currItemInfo;
  333. }
  334. return result;
  335. function getItemInfo(el) {
  336. if (el) {
  337. var itemRect = el.getBoundingRect();
  338. var start = itemRect[xy] + el[xy];
  339. return {
  340. s: start,
  341. e: start + itemRect[wh],
  342. i: el.__legendDataIndex
  343. };
  344. }
  345. }
  346. function intersect(itemInfo, winStart) {
  347. return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize;
  348. }
  349. };
  350. ScrollableLegendView.prototype._findTargetItemIndex = function (targetDataIndex) {
  351. if (!this._showController) {
  352. return 0;
  353. }
  354. var index;
  355. var contentGroup = this.getContentGroup();
  356. var defaultIndex;
  357. contentGroup.eachChild(function (child, idx) {
  358. var legendDataIdx = child.__legendDataIndex; // FIXME
  359. // If the given targetDataIndex (from model) is illegal,
  360. // we use defaultIndex. But the index on the legend model and
  361. // action payload is still illegal. That case will not be
  362. // changed until some scenario requires.
  363. if (defaultIndex == null && legendDataIdx != null) {
  364. defaultIndex = idx;
  365. }
  366. if (legendDataIdx === targetDataIndex) {
  367. index = idx;
  368. }
  369. });
  370. return index != null ? index : defaultIndex;
  371. };
  372. ScrollableLegendView.type = 'legend.scroll';
  373. return ScrollableLegendView;
  374. }(LegendView);
  375. export default ScrollableLegendView;