diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dc9b35db38..a833d7e1620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # ChangeLog +### 4.7.3 + +- feat: beforechangedata and afterchagnedata for graph changeData; +- feat: Annotation supports icon events callbacks; +- feat: Annotation supports defaultBegin position configuration for new annotation cards; +- perf: Annotation updated automatically when graph data changed and graph item visiblity changed; +- fix: Destroy legend canvas when the plugin is destroyed, closes: #3931; + ### 4.7.2 - feat: Annotation plugin supports configuring behaviors for collapse and close icon; diff --git a/packages/core/package.json b/packages/core/package.json index aad25416fcb..fe7e2520ca1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-core", - "version": "0.7.2", + "version": "0.7.3", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", diff --git a/packages/core/src/global.ts b/packages/core/src/global.ts index 2dac1a4d402..68ce212a061 100644 --- a/packages/core/src/global.ts +++ b/packages/core/src/global.ts @@ -64,7 +64,7 @@ const colorSet = { }; export default { - version: '0.7.2', + version: '0.7.3', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/packages/core/src/graph/graph.ts b/packages/core/src/graph/graph.ts index 59d5a9e4db7..47a81731e28 100644 --- a/packages/core/src/graph/graph.ts +++ b/packages/core/src/graph/graph.ts @@ -1611,6 +1611,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs if (!dataValidation(data)) { return this; } + this.emit('beforechangedata'); if (stack && this.get('enabledStack')) { this.pushStack('changedata', { before: self.save(), @@ -1729,6 +1730,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs setTimeout(() => { canvas.set('localRefresh', localRefresh); }, 16); + this.emit('afterchangedata'); return this; } diff --git a/packages/element/package.json b/packages/element/package.json index 33b861be487..4ae37c7c08c 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-element", - "version": "0.7.2", + "version": "0.7.3", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -61,7 +61,7 @@ }, "dependencies": { "@antv/g-base": "^0.5.1", - "@antv/g6-core": "0.7.2", + "@antv/g6-core": "0.7.3", "@antv/util": "~2.0.5" }, "devDependencies": { diff --git a/packages/g6/package.json b/packages/g6/package.json index ddf971c1b29..ad5daabf9e3 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6", - "version": "4.7.2", + "version": "4.7.3", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -66,7 +66,7 @@ ] }, "dependencies": { - "@antv/g6-pc": "0.7.2" + "@antv/g6-pc": "0.7.3" }, "devDependencies": { "@babel/core": "^7.7.7", diff --git a/packages/g6/src/index.ts b/packages/g6/src/index.ts index 13aa793ce33..9a5b46d5a71 100644 --- a/packages/g6/src/index.ts +++ b/packages/g6/src/index.ts @@ -1,7 +1,7 @@ import G6 from '@antv/g6-pc'; -G6.version = '4.7.2'; +G6.version = '4.7.3'; export * from '@antv/g6-pc'; export default G6; -export const version = '4.7.2'; +export const version = '4.7.3'; diff --git a/packages/pc/package.json b/packages/pc/package.json index 38b0e247b8b..f5546b29d6f 100644 --- a/packages/pc/package.json +++ b/packages/pc/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-pc", - "version": "0.7.2", + "version": "0.7.3", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -75,9 +75,9 @@ "@antv/g-canvas": "^0.5.2", "@antv/g-math": "^0.1.1", "@antv/g-svg": "^0.5.1", - "@antv/g6-core": "0.7.2", - "@antv/g6-element": "0.7.2", - "@antv/g6-plugin": "0.7.2", + "@antv/g6-core": "0.7.3", + "@antv/g6-element": "0.7.3", + "@antv/g6-plugin": "0.7.3", "@antv/hierarchy": "^0.6.7", "@antv/layout": "^0.3.0", "@antv/matrix-util": "^3.1.0-beta.3", diff --git a/packages/pc/src/global.ts b/packages/pc/src/global.ts index c9348b47f58..c6e0bb69d4d 100644 --- a/packages/pc/src/global.ts +++ b/packages/pc/src/global.ts @@ -7,7 +7,7 @@ const textColor = 'rgb(0, 0, 0)'; const colorSet = getColorsWithSubjectColor(subjectColor, backColor); export default { - version: '0.7.2', + version: '0.7.3', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 7984634811d..5a432afe3c0 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-plugin", - "version": "0.7.2", + "version": "0.7.3", "description": "G6 Plugin", "main": "lib/index.js", "module": "es/index.js", @@ -22,8 +22,8 @@ "@antv/g-base": "^0.5.1", "@antv/g-canvas": "^0.5.2", "@antv/g-svg": "^0.5.2", - "@antv/g6-core": "0.7.2", - "@antv/g6-element": "0.7.2", + "@antv/g6-core": "0.7.3", + "@antv/g6-element": "0.7.3", "@antv/matrix-util": "^3.1.0-beta.3", "@antv/scale": "^0.3.4", "@antv/util": "^2.0.9", diff --git a/packages/plugin/src/annotation/index.ts b/packages/plugin/src/annotation/index.ts index 05725793327..bfa8aa1fc40 100644 --- a/packages/plugin/src/annotation/index.ts +++ b/packages/plugin/src/annotation/index.ts @@ -75,7 +75,8 @@ interface AnnotationConfig { linkStyle?: ShapeStyle, linkHighlightStyle?: ShapeStyle, getTitle?: (item) => string | HTMLDivElement, - getContent?: (item) => string | HTMLDivElement + getContent?: (item) => string | HTMLDivElement, + onAnnotationChange?: (info: any, action: string) => void; } @@ -96,14 +97,17 @@ interface CardCfg { maxWidth?: number; collapseType?: 'minimize' | 'hide'; // 点击收起按钮(-)的响应,最小化、隐藏 closeType?: 'hide' | 'remove'; // 点击关闭按钮(x)的相应,隐藏、移除 + defaultBegin?: { left?: number, top?: number, right?: number, bottom?: number }, // 一个个卡片出生的起始位置,后续卡片会往后排列 onMouseEnterIcon?: (evt: any, id: string, type: 'expand' | 'collapse' | 'close') => void; onMouseLeaveIcon?: (evt: any, id: string, type: 'expand' | 'collapse' | 'close') => void; + onClickIcon?: (evt: any, id: string, type: 'expand' | 'collapse' | 'close') => void; } interface CardInfoMap { [id: string]: CardCfg & { card: HTMLDivElement, link: IShape, + isCanvas?: boolean, cardBBox?: { left: number, right: number, @@ -146,6 +150,9 @@ export default class Annotation extends Base { 'viewportchange': 'updateLinks', 'afterlayout': 'updateLinks', 'aftergraphrefreshposition': 'updateLinks', + 'afterupdateitem': 'updateLink', + 'afterchangedata': 'onGraphDataChange', + 'afteritemvisibilitychange': 'onGraphItemVisibilityChange' } switch (this.get('trigger')) { case 'click': @@ -161,7 +168,6 @@ export default class Annotation extends Base { private getDOMContent(cfg) { if (this.destroyed) return; const { - itemId, collapsed, maxWidth, title = '', @@ -392,6 +398,7 @@ export default class Annotation extends Base { title: propsTitle, content: propsContent, maxTitleLength, + defaultBegin, ...otherCardCfg } = Object.assign({}, self.get('cardCfg') || {}, cfg); const linkGroup = self.get('linkGroup'); @@ -418,7 +425,8 @@ export default class Annotation extends Base { height, width, }); - if (card) { + let exist = !!card; + if (exist) { // 移除相应连线 link?.remove(true); // 替换原来的卡片 @@ -434,14 +442,23 @@ export default class Annotation extends Base { // 使用配置的位置 x = propsX; y = propsY; - } else if (!card) { + } else if (!exist && !isCanvas) { // 第一次创建,且无 conatiner,初始化位置 const { top: containerTop } = containerBBox; + const { left: beginLeft, right: propsBeginRight = 16, top: propsBeginTop = 8, bottom: beginBottom } = defaultBegin || {}; + let beginRight = propsBeginRight; + let beginTop = propsBeginTop; + if (!isNaN(beginLeft)) { + beginRight = container.scrollWidth - beginLeft; + } + if (!isNaN(beginBottom)) { + beginTop = container.scrollHeight - beginBottom; + } const cardWidth = isNumber(minWidth) ? minWidth : 100; - x = container.scrollWidth - newCard.scrollWidth - 16 - (rows.length - 1) * cardWidth; + x = container.scrollWidth - newCard.scrollWidth - (rows.length - 1) * cardWidth - beginRight; const currentRow = rows[rows.length - 1]; - const lastCardBBox = currentRow[currentRow.length - 1]; - y = ((lastCardBBox?.bottom - containerTop) || 0) + 8; + const { bbox: lastCardBBox } = currentRow[currentRow.length - 1] || {}; + y = (lastCardBBox?.bottom - containerTop) || beginTop } modifyCSS(newCard, { position: 'absolute', @@ -476,21 +493,32 @@ export default class Annotation extends Base { x, y, cardBBox, + content: content || propsContent, + title: title || propsTitle, + isCanvas }; + self.set('cardInfoMap', cardInfoMap); if (containerCfg) { this.updateCardPositionsInConatainer(); this.updateLinks(); - } else if (!card) { - // 没有 container、新增 card 时,记录当前列中最下方位置,方便换行 - const { bottom: containerBottom = 0 } = containerBBox; - rows[rows.length - 1].push(cardBBox); - if (cardBBox.bottom > containerBottom - 16) { - rows.push([]); + } else { + const hasPropsPosition = !isNaN(propsX) && !isNaN(propsY); + if (!exist && !isCanvas && !hasPropsPosition) { + // 没有 container、新增 card 时,记录当前列中最下方位置,方便换行 + const { bottom: containerBottom = 0, top: containerTop } = containerBBox; + rows[rows.length - 1].push({ + id: itemId, + bbox: cardBBox + }); + if (cardBBox.top > containerBottom - containerTop - cardBBox.height - 16) rows.push([]); + this.set('rows', rows) } - this.set('rows', rows) } this.updateCardSize(itemId); + + const onAnnotationChange = this.get('onAnnotationChange'); + onAnnotationChange?.(cardInfoMap[itemId], exist ? 'update' : 'create'); } public updateCardPositionsInConatainer() { @@ -557,6 +585,8 @@ export default class Annotation extends Base { const { card, link } = cardInfoMap[id]; modifyCSS(card, { display: 'none' }); link?.hide(); + const onAnnotationChange = this.get('onAnnotationChange'); + onAnnotationChange(cardInfoMap[id], 'hide'); } /** @@ -568,11 +598,14 @@ export default class Annotation extends Base { if (this.destroyed) return; const cardInfoMap = this.get('cardInfoMap'); if (!cardInfoMap) return; - const { card, link } = cardInfoMap[id]; + const cardInfo = cardInfoMap[id] + const { card, link } = cardInfo; const container = this.get('container'); container.removeChild(card); link?.remove(true); delete cardInfoMap[id]; + const onAnnotationChange = this.get('onAnnotationChange'); + onAnnotationChange(cardInfo, 'remove'); } private bindListener(card, itemId) { @@ -645,6 +678,7 @@ export default class Annotation extends Base { } }) card.addEventListener('click', e => { + const { onClickIcon } = this.get('cardCfg') || {}; if (e.target.className === 'g6-annotation-collapse' || e.target.className === 'g6-annotation-expand') { // collapse & expand const { collapseType } = this.get('cardCfg'); @@ -653,6 +687,7 @@ export default class Annotation extends Base { } else { this.handleExpandCollapseCard(itemId); } + onClickIcon?.(e, itemId, e.target.className === 'g6-annotation-collapse' ? 'collapse' : 'expand') } else if (e.target.className === 'g6-annotation-close') { // close const { closeType } = this.get('cardCfg'); @@ -661,6 +696,7 @@ export default class Annotation extends Base { } else { this.hideCard(itemId); } + onClickIcon?.(e, itemId, 'close') } }); // dblclick to edit the title and content text @@ -697,6 +733,9 @@ export default class Annotation extends Base { } inputWrapper.parentNode.replaceChild(target, inputWrapper); this.updateCardSize(itemId); + + const onAnnotationChange = this.get('onAnnotationChange'); + onAnnotationChange?.(cardInfo, 'update'); }); }); } @@ -766,6 +805,17 @@ export default class Annotation extends Base { visibility: 'visible' }); this.set('dragging', false); + + // 移动过的卡片从 rows 中移除,避免影响后续卡片出生位置 + const rows = this.get("rows"); + rows?.forEach(rowItems => { + for (let i = rowItems.length - 1; i >= 0; i--) { + if (rowItems[i].id === itemId) rowItems.splice(i, 1); + } + }) + + const onAnnotationChange = this.get('onAnnotationChange'); + onAnnotationChange?.(cardInfoMap[itemId], 'update'); } } card.addEventListener('dragend', dragendListener); @@ -787,25 +837,60 @@ export default class Annotation extends Base { } } + public updateLink({ item }) { + if (!item) return; + const cardInfoMap: CardInfoMap = this.get('cardInfoMap'); + if (!cardInfoMap) return; + const canvas = this.get('canvas'); + const graph = this.get('graph'); + const id = item.getID(); + const { link, card } = cardInfoMap[id] || {}; + if (link) { + const path = getPathItem2Card(item, card.getBoundingClientRect(), graph, canvas); + link.attr('path', path) + } + } + public updateLinks() { if (this.destroyed) return; const cardInfoMap: CardInfoMap = this.get('cardInfoMap'); if (!cardInfoMap) return; const graph = this.get('graph'); - const canvas = this.get('canvas'); Object.values(cardInfoMap).forEach(cardInfo => { - const { link, id, card } = cardInfo; + const { id } = cardInfo; + const item = graph.findById(id); + this.updateLink({ item }); + }) + } + + public onGraphDataChange() { + const cardInfoMap: CardInfoMap = this.get('cardInfoMap'); + if (!cardInfoMap) return; + const graph = this.get('graph'); + Object.values(cardInfoMap).forEach(info => { + const { id, card, isCanvas } = info; + if (isCanvas || card.style.display === 'none') return; const item = graph.findById(id); - if (link && item) { - const path = getPathItem2Card(item, card.getBoundingClientRect(), graph, canvas); - link.attr('path', path) + if (item && item.isVisible()) { + this.toggleAnnotation(item); + } else { + this.hideCard(id); } }) } + public onGraphItemVisibilityChange({ item, visible }) { + if (!item || item.destroyed) return; + const cardInfoMap: CardInfoMap = this.get('cardInfoMap'); + if (!cardInfoMap) return; + const id = item.getID(); + if (!cardInfoMap[id]) return; + if (!visible) this.hideCard(id); + } + public saveData(saveClosed = false) { const cardInfoMap: CardInfoMap = this.get('cardInfoMap'); - if (!cardInfoMap) []; + if (!cardInfoMap) return; const graph = this.get('graph'); const getTitle = this.get('getTitle'); const getContent = this.get('getContent'); @@ -813,7 +898,7 @@ export default class Annotation extends Base { Object.values(cardInfoMap).forEach(info => { const { title, content, x, y, id, collapsed, card } = info; if (card.style.display === 'none' && !saveClosed) return; - const item = graph.findById(id); + const item = graph.findById(id) || graph.get('canvas'); data.push({ id, x, @@ -831,7 +916,7 @@ export default class Annotation extends Base { const graph = this.get('graph'); data.forEach(info => { const { id, x, y, title, content, collapsed, visible } = info; - const item = graph.findById(id); + const item = graph.findById(id) || graph.get('canvas'); this.toggleAnnotation(item, { x, y, title, content, collapsed }); if (!visible) this.hideCard(id); }) diff --git a/packages/plugin/src/base.ts b/packages/plugin/src/base.ts index 3fddc99afa7..d2c2ac7f026 100644 --- a/packages/plugin/src/base.ts +++ b/packages/plugin/src/base.ts @@ -25,7 +25,6 @@ export default abstract class PluginBase { */ constructor(cfgs?: IPluginBaseConfig) { this._cfgs = deepMix(this.getDefaultCfgs(), cfgs); - console.log('_cfgs', this._cfgs) this._events = {}; this.destroyed = false; } diff --git a/packages/plugin/src/legend/index.ts b/packages/plugin/src/legend/index.ts index 6e5a55370d7..baf478f5286 100644 --- a/packages/plugin/src/legend/index.ts +++ b/packages/plugin/src/legend/index.ts @@ -766,6 +766,8 @@ export default class Legend extends Base { } public destroy() { + const lc = this.get('legendCanvas'); + lc?.destroy(); const graph: IGraph = this.get('graph'); const graphContainer = graph.get('container'); const container: HTMLDivElement = this.get('container'); diff --git a/packages/site/docs/api/Plugins.en.md b/packages/site/docs/api/Plugins.en.md index 69a93a7ac59..0b037c86b12 100644 --- a/packages/site/docs/api/Plugins.en.md +++ b/packages/site/docs/api/Plugins.en.md @@ -992,10 +992,5 @@ type ControllerCfg = Partial<{ | speedControllerStyle | { offsetX?: number, offsetY?: number, scale?: number, pointer?: ShapeStyle, text?: ShapeStyle, scroller?: ShapeStyle} | null | The style of the 'speed controller'. `scale`, `offsetX`, `offsetY` are also can be assigned to it and each sub-styles to controll the size and position of the speed controller and sub-shapes | | timeTypeControllerStyle | { offsetX?: number, offsetY?: number, scale?: number, box?: ShapeStyle, check?: ShapeStyle, text?: ShapeStyle } | null | The style of the 'time type controller'. `scale`, `offsetX`, `offsetY` are also can be assigned to it and each sub-styles to controll the size and position of the speed controller and sub-shapes | | containerStyle | ShapeStyle | {} | [Supported from v4.5.1] The style of the background rect of the controller | -<<<<<<< Updated upstream -| timePointControllerText | string | "单一时间" | The text for the right-botton switch controlling play with single time point or time range | -| timeRangeControllerText | string | "时间范围" | The text for the right-botton switch controlling play with single time point or time range | -======= | timePointControllerText | string | "单一时间" | The text for the right-bottom switch controlling play with single time point or time range | | timeRangeControllerText | string | "时间范围" | The text for the right-bottom switch controlling play with single time point or time range | ->>>>>>> Stashed changes