selection.js 17 KB

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