Skip to content

Commit 2648fe7

Browse files
authored
Merge pull request #2 from yWorks/dev
Fix concurrent layouts applying obsolete node sizes.
2 parents 309799b + 48c9951 commit 2648fe7

File tree

4 files changed

+68
-33
lines changed

4 files changed

+68
-33
lines changed

examples/src/examples/custom-item-style/index.css

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.item-name {
22
margin-top: 10px;
33
margin-left: 20px;
4+
margin-right: 20px;
45
font-family: Arial, Helvetica, sans-serif;
56
font-size: 20px;
67
font-weight: bold;
@@ -9,17 +10,19 @@
910
.item-price {
1011
margin-top: 10px;
1112
margin-left: 20px;
13+
margin-right: 20px;
1214
font-family: Arial, Helvetica, sans-serif;
1315
font-size: 16px;
1416
}
1517

1618
.item-summary {
17-
display: grid;
19+
display: flex;
20+
justify-content: center;
1821
align-items: center;
22+
text-align: center;
1923
height: 100%;
20-
margin-left: 20px;
2124
font-family: Arial, Helvetica, sans-serif;
22-
font-size: 42px;
25+
font-size: 30px;
2326
font-weight: bold;
2427
}
2528

examples/src/examples/custom-item-style/index.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,26 @@ import {
88

99
import './index.css'
1010

11-
type MetalItem = UserSupplyChainItem & { price: string }
11+
type MetalItem = UserSupplyChainItem & { name: string; price: string }
1212

1313
const data = {
1414
items: [
15-
{ name: 'Copper', id: 1, price: 'USD 8500/t', className: 'copper', width: 180, height: 70 },
16-
{ name: 'Zinc', id: 2, price: 'USD 2500/t', className: 'zinc', width: 180, height: 70 },
17-
{ name: 'Brass', id: 3, price: 'USD 6500/t', className: 'brass', width: 180, height: 70 }
15+
{ name: 'Copper', id: 1, price: 'USD 8500/t', className: 'copper' },
16+
{ name: 'Zinc', id: 2, price: 'USD 2500/t', className: 'zinc' },
17+
{ name: 'Brass', id: 3, price: 'USD 6500/t', className: 'brass' }
1818
],
1919
connections: [
2020
{ sourceId: 1, targetId: 3 },
2121
{ sourceId: 2, targetId: 3 }
2222
]
2323
} satisfies SupplyChainData<MetalItem, UserSupplyChainConnection>
2424

25+
const abbreviations = new Map<string, string>([
26+
['Copper', 'Cu'],
27+
['Zinc', 'Zn'],
28+
['Brass', 'Brass']
29+
])
30+
2531
export function CustomSupplyChainItem({
2632
dataItem,
2733
detail,
@@ -53,7 +59,7 @@ export function CustomSupplyChainItem({
5359
<div className="item-price">{customSupplyChainItem.price}</div>
5460
</div>
5561
) : (
56-
<div className="item-summary">{customSupplyChainItem.name}</div>
62+
<div className="item-summary">{abbreviations.get(customSupplyChainItem.name)}</div>
5763
)}
5864
</div>
5965
)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@yworks/react-yfiles-supply-chain",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"author": {
55
"name": "yFiles for HTML team @ yWorks GmbH",
66
"email": "yfileshtml@yworks.com"

src/core/LayoutSupport.ts

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
PartitionCellId,
1717
PartitionGrid,
1818
PartitionGridData,
19-
PolylineEdgeStyle
19+
PolylineEdgeStyle,
20+
Rect
2021
} from 'yfiles'
2122
import {
2223
GridPositioningFunction,
@@ -37,7 +38,8 @@ export class LayoutSupport<TSupplyChainItem extends SupplyChainItem> {
3738
this.workerPromise = null
3839
}
3940
}
40-
private executor: LayoutExecutorAsync | null = null
41+
private executorAsync: LayoutExecutorAsync | null = null
42+
private executor: LayoutExecutor | null = null
4143

4244
private hiddenEdgeStyle = new PolylineEdgeStyle({ stroke: 'transparent' })
4345
private hiddenLabelStyle = new DefaultLabelStyle({
@@ -182,28 +184,53 @@ export class LayoutSupport<TSupplyChainItem extends SupplyChainItem> {
182184
}
183185
}
184186

185-
private createLayoutExecutor(
187+
/**
188+
* When a layout animation is already running, it might have started
189+
* with now obsolete node sizes - stop the running animation and restore
190+
* the latest measured node sizes.
191+
*/
192+
private async maybeCancel() {
193+
const syncRunning = this.executor && this.executor.running
194+
const asyncRunning = this.executorAsync && this.executorAsync.running
195+
if (syncRunning || asyncRunning) {
196+
const layouts = new Map<INode, Rect>()
197+
for (const node of this.graphComponent.graph.nodes) {
198+
layouts.set(node, node.layout.toRect())
199+
}
200+
await this.executor?.stop()
201+
await this.executorAsync?.cancel()
202+
for (const node of this.graphComponent.graph.nodes) {
203+
if (layouts.has(node)) {
204+
this.graphComponent.graph.setNodeLayout(node, layouts.get(node)!)
205+
}
206+
}
207+
}
208+
}
209+
210+
private async createLayoutExecutor(
186211
incremental: boolean,
187212
incrementalNodes: INode[],
188213
fixedNode: INode | null = null,
189214
fitViewport: boolean
190215
): Promise<LayoutExecutor> {
191-
return Promise.resolve(
192-
new LayoutExecutor({
193-
graphComponent: this.graphComponent,
194-
layout: createLayout(incremental, this.layoutOptions),
195-
layoutData: this.createLayoutData(
196-
this.graphComponent.graph,
197-
incremental,
198-
incrementalNodes,
199-
fixedNode
200-
),
201-
duration: '300ms',
202-
animateViewport: fitViewport,
203-
updateContentRect: true,
204-
targetBoundsInsets: defaultGraphFitInsets
205-
})
206-
)
216+
await this.maybeCancel()
217+
218+
this.executor = new LayoutExecutor({
219+
graphComponent: this.graphComponent,
220+
layout: createLayout(incremental, this.layoutOptions),
221+
layoutData: this.createLayoutData(
222+
this.graphComponent.graph,
223+
incremental,
224+
incrementalNodes,
225+
fixedNode
226+
),
227+
duration: '300ms',
228+
animateViewport: fitViewport,
229+
updateContentRect: true,
230+
targetBoundsInsets: defaultGraphFitInsets
231+
})
232+
233+
return Promise.resolve(this.executor)
207234
}
208235

209236
private async createLayoutExecutorAsync(
@@ -212,9 +239,8 @@ export class LayoutSupport<TSupplyChainItem extends SupplyChainItem> {
212239
fixedNode: INode | null = null,
213240
fitViewport: boolean
214241
): Promise<LayoutExecutorAsync> {
215-
if (this.executor) {
216-
await this.executor.cancel()
217-
}
242+
await this.maybeCancel()
243+
218244
const worker = await this.workerPromise!
219245

220246
// helper function that performs the actual message passing to the web worker
@@ -239,7 +265,7 @@ export class LayoutSupport<TSupplyChainItem extends SupplyChainItem> {
239265
})
240266
}
241267

242-
this.executor = new LayoutExecutorAsync({
268+
this.executorAsync = new LayoutExecutorAsync({
243269
messageHandler: webWorkerMessageHandler,
244270
graphComponent: this.graphComponent,
245271
layoutData: this.createLayoutData(
@@ -253,6 +279,6 @@ export class LayoutSupport<TSupplyChainItem extends SupplyChainItem> {
253279
updateContentRect: true,
254280
targetBoundsInsets: defaultGraphFitInsets
255281
})
256-
return this.executor
282+
return this.executorAsync
257283
}
258284
}

0 commit comments

Comments
 (0)