selection.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. import { __classPrivateFieldGet, __read, __spreadArray, __values } from "tslib";
  2. import { group } from 'd3-array';
  3. import { Circle, Ellipse, Group, HTML, Image, Line, Path, Polygon, Polyline, Rect, Text } from '../shapes';
  4. function error(msg) {
  5. throw new Error(msg);
  6. }
  7. /**
  8. * A simple implementation of d3-selection for @antv/g.
  9. * It has the core features of d3-selection and extended ability.
  10. * Every methods of selection returns new selection if elements
  11. * are mutated(e.g. append, remove), otherwise return the selection itself(e.g. attr, style).
  12. * @see https://github.com/d3/d3-selection
  13. * @see https://github.com/antvis/g
  14. * @todo Nested selections.
  15. * @todo More useful functor.
  16. */
  17. var Selection = /** @class */ (function () {
  18. function Selection(elements, data, parent, document, selections, transitions, updateElements) {
  19. if (elements === void 0) { elements = null; }
  20. if (data === void 0) { data = null; }
  21. if (parent === void 0) { parent = null; }
  22. if (document === void 0) { document = null; }
  23. if (selections === void 0) { selections = [null, null, null, null, null]; }
  24. if (transitions === void 0) { transitions = []; }
  25. if (updateElements === void 0) { updateElements = []; }
  26. _Selection_instances.add(this);
  27. this._elements = Array.from(elements);
  28. this._data = data;
  29. this._parent = parent;
  30. this._document = document;
  31. this._enter = selections[0];
  32. this._update = selections[1];
  33. this._exit = selections[2];
  34. this._merge = selections[3];
  35. this._split = selections[4];
  36. this._transitions = transitions;
  37. this._facetElements = updateElements;
  38. }
  39. Selection.prototype.selectAll = function (selector) {
  40. var elements = typeof selector === 'string' ? this._parent.querySelectorAll(selector) : selector;
  41. return new Selection(elements, null, this._elements[0], this._document);
  42. };
  43. Selection.prototype.selectFacetAll = function (selector) {
  44. var elements = typeof selector === 'string' ? this._parent.querySelectorAll(selector) : selector;
  45. return new Selection(this._elements, null, this._parent, this._document, undefined, undefined, elements);
  46. };
  47. /**
  48. * @todo Replace with querySelector which has bug now.
  49. */
  50. Selection.prototype.select = function (selector) {
  51. var element = typeof selector === 'string' ? this._parent.querySelectorAll(selector)[0] || null : selector;
  52. return new Selection([element], null, element, this._document);
  53. };
  54. Selection.prototype.append = function (node) {
  55. var _this = this;
  56. var callback = typeof node === 'function' ? node : function () { return _this.createElement(node); };
  57. var elements = [];
  58. if (this._data !== null) {
  59. // For empty selection, append new element to parent.
  60. // Each element is bind with datum.
  61. for (var i = 0; i < this._data.length; i++) {
  62. var d = this._data[i];
  63. var _a = __read(Array.isArray(d) ? d : [d, null], 2), datum = _a[0], from = _a[1];
  64. var newElement = callback(datum, i);
  65. newElement.__data__ = datum;
  66. if (from !== null)
  67. newElement.__fromElements__ = from;
  68. this._parent.appendChild(newElement);
  69. elements.push(newElement);
  70. }
  71. return new Selection(elements, null, this._parent, this._document);
  72. }
  73. // For non-empty selection, append new element to
  74. // selected element and return new selection.
  75. for (var i = 0; i < this._elements.length; i++) {
  76. var element = this._elements[i];
  77. var datum = element.__data__;
  78. var newElement = callback(datum, i);
  79. element.appendChild(newElement);
  80. elements.push(newElement);
  81. }
  82. return new Selection(elements, null, elements[0], this._document);
  83. };
  84. Selection.prototype.maybeAppend = function (id, node) {
  85. var element = __classPrivateFieldGet(this, _Selection_instances, "m", _Selection_maybeAppend).call(this, id[0] === '#' ? id : "#".concat(id), node);
  86. element.attr('id', id);
  87. return element;
  88. };
  89. Selection.prototype.maybeAppendByClassName = function (className, node) {
  90. var cls = className.toString();
  91. var element = __classPrivateFieldGet(this, _Selection_instances, "m", _Selection_maybeAppend).call(this, cls[0] === '.' ? cls : ".".concat(cls), node);
  92. element.attr('className', cls);
  93. return element;
  94. };
  95. Selection.prototype.maybeAppendByName = function (name, node) {
  96. var element = __classPrivateFieldGet(this, _Selection_instances, "m", _Selection_maybeAppend).call(this, "[name=\"".concat(name, "\"]"), node);
  97. element.attr('name', name);
  98. return element;
  99. };
  100. /**
  101. * Bind data to elements, and produce three selection:
  102. * Enter: Selection with empty elements and data to be bind to elements.
  103. * Update: Selection with elements to be updated.
  104. * Exit: Selection with elements to be removed.
  105. */
  106. Selection.prototype.data = function (data, id, groupId) {
  107. var e_1, _a;
  108. if (id === void 0) { id = function (d) { return d; }; }
  109. if (groupId === void 0) { groupId = function () { return null; }; }
  110. // An Array of new data.
  111. var enter = [];
  112. // An Array of elements to be updated.
  113. var update = [];
  114. // A Set of elements to be removed.
  115. var exit = new Set(this._elements);
  116. // An Array of data to be merged into one element.
  117. var merge = [];
  118. // A Set of elements to be split into multiple datum.
  119. var split = new Set();
  120. // A Map from key to each element.
  121. var keyElement = new Map(this._elements.map(function (d, i) { return [id(d.__data__, i), d]; }));
  122. // A Map from key to exist element. The Update Selection
  123. // can get element from this map, this is for diff among
  124. // facets.
  125. var keyUpdateElement = new Map(this._facetElements.map(function (d, i) { return [id(d.__data__, i), d]; }));
  126. // A Map from groupKey to a group of elements.
  127. var groupKeyElements = group(this._elements, function (d) { return groupId(d.__data__); });
  128. // Diff data with selection(elements with data).
  129. // !!! Note
  130. // The switch is strictly ordered, not not change the order of them.
  131. for (var i = 0; i < data.length; i++) {
  132. var datum = data[i];
  133. var key = id(datum, i);
  134. var groupKey = groupId(datum, i);
  135. // Append element to update selection if incoming data has
  136. // exactly the same key with elements.
  137. if (keyElement.has(key)) {
  138. var element = keyElement.get(key);
  139. element.__data__ = datum;
  140. element.__facet__ = false;
  141. update.push(element);
  142. exit.delete(element);
  143. keyElement.delete(key);
  144. // Append element to update selection if incoming data has
  145. // exactly the same key with updateElements.
  146. }
  147. else if (keyUpdateElement.has(key)) {
  148. var element = keyUpdateElement.get(key);
  149. element.__data__ = datum;
  150. // Flag this element should update its parentNode.
  151. element.__facet__ = true;
  152. update.push(element);
  153. keyUpdateElement.delete(key);
  154. // Append datum to merge selection if existed elements has
  155. // its key as groupKey.
  156. }
  157. else if (groupKeyElements.has(key)) {
  158. var group_2 = groupKeyElements.get(key);
  159. merge.push([datum, group_2]);
  160. try {
  161. for (var group_1 = (e_1 = void 0, __values(group_2)), group_1_1 = group_1.next(); !group_1_1.done; group_1_1 = group_1.next()) {
  162. var element = group_1_1.value;
  163. exit.delete(element);
  164. }
  165. }
  166. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  167. finally {
  168. try {
  169. if (group_1_1 && !group_1_1.done && (_a = group_1.return)) _a.call(group_1);
  170. }
  171. finally { if (e_1) throw e_1.error; }
  172. }
  173. groupKeyElements.delete(key);
  174. // Append element to split selection if incoming data has
  175. // groupKey as its key, and bind to datum for it.
  176. }
  177. else if (keyElement.has(groupKey)) {
  178. var element = keyElement.get(groupKey);
  179. if (element.__toData__)
  180. element.__toData__.push(datum);
  181. else
  182. element.__toData__ = [datum];
  183. split.add(element);
  184. exit.delete(element);
  185. }
  186. else {
  187. enter.push(datum);
  188. }
  189. }
  190. // Create new selection with enter, update and exit.
  191. var S = [
  192. new Selection([], enter, this._parent, this._document),
  193. new Selection(update, null, this._parent, this._document),
  194. new Selection(exit, null, this._parent, this._document),
  195. new Selection([], merge, this._parent, this._document),
  196. new Selection(split, null, this._parent, this._document),
  197. ];
  198. return new Selection(this._elements, null, this._parent, this._document, S);
  199. };
  200. Selection.prototype.merge = function (other) {
  201. var elements = __spreadArray(__spreadArray([], __read(this._elements), false), __read(other._elements), false);
  202. var transitions = __spreadArray(__spreadArray([], __read(this._transitions), false), __read(other._transitions), false);
  203. return new Selection(elements, null, this._parent, this._document, undefined, transitions);
  204. };
  205. Selection.prototype.createElement = function (type) {
  206. if (this._document) {
  207. return this._document.createElement(type, {});
  208. }
  209. var Ctor = Selection.registry[type];
  210. if (Ctor)
  211. return new Ctor();
  212. return error("Unknown node type: ".concat(type));
  213. };
  214. /**
  215. * Apply callback for each selection(enter, update, exit)
  216. * and merge them into one selection.
  217. */
  218. Selection.prototype.join = function (enter, update, exit, merge, split) {
  219. if (enter === void 0) { enter = function (d) { return d; }; }
  220. if (update === void 0) { update = function (d) { return d; }; }
  221. if (exit === void 0) { exit = function (d) { return d.remove(); }; }
  222. if (merge === void 0) { merge = function (d) { return d; }; }
  223. if (split === void 0) { split = function (d) { return d.remove(); }; }
  224. var newEnter = enter(this._enter);
  225. var newUpdate = update(this._update);
  226. var newExit = exit(this._exit);
  227. var newMerge = merge(this._merge);
  228. var newSplit = split(this._split);
  229. return newUpdate.merge(newEnter).merge(newExit).merge(newMerge).merge(newSplit);
  230. };
  231. Selection.prototype.remove = function () {
  232. var _loop_1 = function (i) {
  233. var element = this_1._elements[i];
  234. var transition = this_1._transitions[i];
  235. if (transition) {
  236. transition.then(function () { return element.remove(); });
  237. }
  238. else {
  239. element.remove();
  240. }
  241. };
  242. var this_1 = this;
  243. // Remove node immediately if there is no transition,
  244. // otherwise wait until transition finished.
  245. for (var i = 0; i < this._elements.length; i++) {
  246. _loop_1(i);
  247. }
  248. return new Selection([], null, this._parent, this._document, undefined, this._transitions);
  249. };
  250. Selection.prototype.each = function (callback) {
  251. for (var i = 0; i < this._elements.length; i++) {
  252. var element = this._elements[i];
  253. var datum = element.__data__;
  254. callback.call(element, datum, i);
  255. }
  256. return this;
  257. };
  258. Selection.prototype.attr = function (key, value) {
  259. var callback = typeof value !== 'function' ? function () { return value; } : value;
  260. return this.each(function (d, i) {
  261. if (value !== undefined)
  262. this[key] = callback.call(this, d, i);
  263. });
  264. };
  265. Selection.prototype.style = function (key, value, callbackable) {
  266. if (callbackable === void 0) { callbackable = true; }
  267. var callback = typeof value !== 'function' || !callbackable ? function () { return value; } : value;
  268. return this.each(function (d, i) {
  269. if (value !== undefined)
  270. this.style[key] = callback.call(this, d, i);
  271. });
  272. };
  273. Selection.prototype.styles = function (style, callbackable) {
  274. if (style === void 0) { style = {}; }
  275. if (callbackable === void 0) { callbackable = true; }
  276. return this.each(function (d, i) {
  277. var _this = this;
  278. Object.entries(style).forEach(function (_a) {
  279. var _b = __read(_a, 2), key = _b[0], value = _b[1];
  280. var callback = typeof value !== 'function' || !callbackable ? function () { return value; } : value;
  281. if (value !== undefined)
  282. _this.attr(key, callback.call(_this, d, i));
  283. });
  284. });
  285. };
  286. Selection.prototype.update = function (option, callbackable) {
  287. if (callbackable === void 0) { callbackable = true; }
  288. var callback = typeof option !== 'function' || !callbackable ? function () { return option; } : option;
  289. return this.each(function (d, i) {
  290. if (option && this.update)
  291. this.update(callback.call(this, d, i));
  292. });
  293. };
  294. /** if current stage is maybeAppend, skip update stage */
  295. Selection.prototype.maybeUpdate = function (option, callbackable) {
  296. if (callbackable === void 0) { callbackable = true; }
  297. var callback = typeof option !== 'function' || !callbackable ? function () { return option; } : option;
  298. return this.each(function (d, i) {
  299. if (option && this.update)
  300. this.update(callback.call(this, d, i));
  301. });
  302. };
  303. Selection.prototype.transition = function (callback) {
  304. var T = this._transitions;
  305. return this.each(function (d, i) {
  306. T[i] = callback.call(this, d, i);
  307. });
  308. };
  309. Selection.prototype.on = function (event, handler) {
  310. this.each(function () {
  311. this.addEventListener(event, handler);
  312. });
  313. return this;
  314. };
  315. Selection.prototype.call = function (callback) {
  316. var args = [];
  317. for (var _i = 1; _i < arguments.length; _i++) {
  318. args[_i - 1] = arguments[_i];
  319. }
  320. callback.call.apply(callback, __spreadArray([this._parent, this], __read(args), false));
  321. return this;
  322. };
  323. Selection.prototype.node = function () {
  324. return this._elements[0];
  325. };
  326. Selection.prototype.nodes = function () {
  327. return this._elements;
  328. };
  329. Selection.prototype.transitions = function () {
  330. return this._transitions.filter(function (t) { return !!t; });
  331. };
  332. Selection.prototype.parent = function () {
  333. return this._parent;
  334. };
  335. var _Selection_instances, _Selection_maybeAppend;
  336. _Selection_instances = new WeakSet(), _Selection_maybeAppend = function _Selection_maybeAppend(selector, node) {
  337. var element = this._elements[0];
  338. var child = element.querySelector(selector);
  339. if (child)
  340. return new Selection([child], null, this._parent, this._document);
  341. var newChild = typeof node === 'string' ? this.createElement(node) : node();
  342. element.appendChild(newChild);
  343. return new Selection([newChild], null, this._parent, this._document);
  344. };
  345. Selection.registry = {
  346. g: Group,
  347. rect: Rect,
  348. circle: Circle,
  349. path: Path,
  350. text: Text,
  351. ellipse: Ellipse,
  352. image: Image,
  353. line: Line,
  354. polygon: Polygon,
  355. polyline: Polyline,
  356. html: HTML,
  357. };
  358. return Selection;
  359. }());
  360. export { Selection };
  361. export function select(node) {
  362. return new Selection([node], null, node, node.ownerDocument);
  363. }
  364. export function maybeAppend(parent, selector, node) {
  365. if (!parent.querySelector(selector)) {
  366. return select(parent).append(node);
  367. }
  368. return select(parent).select(selector);
  369. }
  370. //# sourceMappingURL=selection.js.map