@design-core/core 52.0.0 → 52.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,8 @@ export class DimStyleManager extends StyleManagerBase {
13
13
 
14
14
  this.indelibleItems.push('STANDARD');
15
15
  this.itemProperty = 'dimensionStyle';
16
+
17
+ this.setCstyle('ISO-25');
16
18
  }
17
19
 
18
20
  /** Get the item class */
@@ -23,12 +23,17 @@ export class StyleManagerBase extends TableManagerBase {
23
23
  * @param {boolean} showWarning
24
24
  */
25
25
  deleteStyle(itemIndex, showWarning = true) {
26
- // call the super class delete
27
- const deletedItem = this.deleteItem(itemIndex, showWarning);
26
+ // deleteItem returns the spliced array, or undefined when nothing was deleted
27
+ const deletedItem = this.deleteItem(itemIndex, showWarning)?.[0];
28
28
 
29
29
  if (deletedItem) {
30
30
  // delete all item using the selected style
31
31
  this.deleteStyleFromScene(deletedItem.name);
32
+
33
+ // the current style no longer exists, fall back to the first available style
34
+ if (this.currentstyle?.toUpperCase() === deletedItem.name.toUpperCase()) {
35
+ this.currentstyle = this.items[0]?.name;
36
+ }
32
37
  }
33
38
  }
34
39
 
@@ -37,10 +42,19 @@ export class StyleManagerBase extends TableManagerBase {
37
42
  * @param {string} style
38
43
  */
39
44
  deleteStyleFromScene(style) {
45
+ if (typeof style !== 'string') {
46
+ return;
47
+ }
48
+
49
+ const target = style.toUpperCase();
40
50
  const selectionSet = [];
41
51
 
42
52
  for (let i = 0; i < DesignCore.Scene.entities.count(); i++) {
43
- if ((DesignCore.Scene.entities.get(i).getProperty?.(this.itemProperty) ?? DesignCore.Scene.entities.get(i)[this.itemProperty]) === style) {
53
+ const entity = DesignCore.Scene.entities.get(i);
54
+ // entities without the property (e.g. a line has no dimensionStyle) are not dependent
55
+ const value = entity.getProperty?.(this.itemProperty) ?? entity[this.itemProperty];
56
+
57
+ if (typeof value === 'string' && value.toUpperCase() === target) {
44
58
  selectionSet.push(i);
45
59
  }
46
60
  }
@@ -307,9 +307,24 @@ export class TableManagerBase {
307
307
  * @param {string} newItemName
308
308
  */
309
309
  updateSceneItem(oldItemName, newItemName) {
310
+ const target = typeof oldItemName === 'string' ? oldItemName.toUpperCase() : undefined;
311
+ if (target === undefined) {
312
+ return;
313
+ }
314
+
310
315
  for (let i = 0; i < DesignCore.Scene.entities.count(); i++) {
311
- if (DesignCore.Scene.entities.get(i)[this.itemProperty] === oldItemName) {
312
- DesignCore.Scene.entities.get(i)[this.itemProperty] = newItemName;
316
+ const entity = DesignCore.Scene.entities.get(i);
317
+ const isProperty = entity.properties?.has(this.itemProperty) ?? false;
318
+ const value = isProperty ? entity.getProperty(this.itemProperty) : entity[this.itemProperty];
319
+
320
+ if (typeof value !== 'string' || value.toUpperCase() !== target) {
321
+ continue;
322
+ }
323
+
324
+ if (isProperty) {
325
+ entity.setProperty(this.itemProperty, newItemName);
326
+ } else {
327
+ entity[this.itemProperty] = newItemName;
313
328
  }
314
329
  }
315
330
  }
@@ -344,7 +359,14 @@ export class TableManagerBase {
344
359
  /** Purge unused items */
345
360
  purge() {
346
361
  const itemsToPurge = [];
362
+ // only style managers have a current style
363
+ const currentStyle = this.getCstyle?.()?.toUpperCase();
364
+
347
365
  this.items.forEach((item, index) => {
366
+ if (item.name.toUpperCase() === currentStyle) {
367
+ return;
368
+ }
369
+
348
370
  const searchTerm = this.itemProperty === 'block' ? item : item.name;
349
371
  const items = DesignCore.Scene.entities.find('ANY', this.itemProperty, searchTerm);
350
372
  if (items.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design-core/core",
3
- "version": "52.0.0",
3
+ "version": "52.2.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
@@ -12,6 +12,7 @@ import { Utils } from '../../core/lib/utils.js';
12
12
 
13
13
  // initialise core
14
14
  new Core();
15
+ DesignCore.DimStyleManager.setCstyle('STANDARD');
15
16
 
16
17
  // Test cases for user input
17
18
  const scenarios = [
@@ -11,6 +11,7 @@ import { Polyline } from '../../core/entities/polyline.js';
11
11
 
12
12
  // initialise core
13
13
  new Core();
14
+ DesignCore.DimStyleManager.setCstyle('STANDARD');
14
15
 
15
16
  // Test cases for user input
16
17
  const scenarios = [
@@ -24,6 +24,7 @@ let core;
24
24
 
25
25
  beforeAll(() => {
26
26
  core = new Core();
27
+ core.dimStyleManager.setCstyle('STANDARD');
27
28
 
28
29
  // Entities are arranged in a 3x3 grid of 100x100 cells:
29
30
  //
@@ -95,6 +95,7 @@ async function executeToolWithInputs(tool, selectionIndices, inputs) {
95
95
 
96
96
  beforeAll(async () => {
97
97
  core = new Core();
98
+ core.dimStyleManager.setCstyle('STANDARD');
98
99
 
99
100
  // =====================================================================
100
101
  // Phase 1: Showcase entities (idx 0–7)
@@ -0,0 +1,90 @@
1
+ import { Core } from '../../core/core/core.js';
2
+ import { Point } from '../../core/entities/point.js';
3
+
4
+ const core = new Core();
5
+ const dimStyleManager = core.dimStyleManager;
6
+
7
+ const dimensionData = (dimensionStyle) => ({
8
+ points: [new Point(), new Point(10, 0), new Point(5, 5)],
9
+ dimensionStyle: dimensionStyle,
10
+ });
11
+
12
+ beforeEach(() => {
13
+ while (core.scene.entities.count() > 0) {
14
+ core.scene.entities.remove(0);
15
+ }
16
+ });
17
+
18
+ test('Test DimStyleManager.deleteStyle removes only dimensions using the style', () => {
19
+ dimStyleManager.addItem({ name: 'CUSTOM' });
20
+
21
+ core.scene.addEntity('Line', { points: [new Point(), new Point(10, 10)] });
22
+ core.scene.addEntity('Circle', { points: [new Point()], radius: 5 });
23
+ core.scene.addEntity('RotatedDimension', dimensionData('CUSTOM'));
24
+ core.scene.addEntity('RotatedDimension', dimensionData('STANDARD'));
25
+ expect(core.scene.entities.count()).toBe(4);
26
+
27
+ dimStyleManager.deleteStyle(dimStyleManager.getItemIndex('CUSTOM'));
28
+
29
+ expect(dimStyleManager.itemExists('CUSTOM')).toBe(false);
30
+ const types = core.scene.entities.get(0).type + ',' + core.scene.entities.get(1).type + ',' + core.scene.entities.get(2).type;
31
+ expect(core.scene.entities.count()).toBe(3);
32
+ expect(types).toBe('Line,Circle,RotatedDimension');
33
+ expect(core.scene.entities.get(2).getProperty('dimensionStyle')).toBe('STANDARD');
34
+ });
35
+
36
+ test('Test DimStyleManager.deleteStyle matches style names case-insensitively', () => {
37
+ dimStyleManager.addItem({ name: 'MixedCase' });
38
+ core.scene.addEntity('RotatedDimension', dimensionData('MIXEDCASE'));
39
+
40
+ dimStyleManager.deleteStyle(dimStyleManager.getItemIndex('MixedCase'));
41
+
42
+ expect(core.scene.entities.count()).toBe(0);
43
+ });
44
+
45
+ test('Test DimStyleManager.deleteStyle resets the current style when it is deleted', () => {
46
+ dimStyleManager.addItem({ name: 'CURRENT' });
47
+ dimStyleManager.setCstyle('CURRENT');
48
+
49
+ dimStyleManager.deleteStyle(dimStyleManager.getItemIndex('CURRENT'));
50
+
51
+ expect(dimStyleManager.getCstyle()).toBe(dimStyleManager.getItems()[0].name);
52
+ });
53
+
54
+ test('Test DimStyleManager.deleteStyle keeps indelible styles and their dimensions', () => {
55
+ core.scene.addEntity('RotatedDimension', dimensionData('STANDARD'));
56
+
57
+ dimStyleManager.deleteStyle(dimStyleManager.getItemIndex('STANDARD'), false);
58
+
59
+ expect(dimStyleManager.itemExists('STANDARD')).toBe(true);
60
+ expect(core.scene.entities.count()).toBe(1);
61
+ });
62
+
63
+ test('Test DimStyleManager.renameStyle updates dimensions through the property API', () => {
64
+ dimStyleManager.addItem({ name: 'OLDNAME' });
65
+ core.scene.addEntity('Line', { points: [new Point(), new Point(10, 10)] });
66
+ core.scene.addEntity('RotatedDimension', dimensionData('OLDNAME'));
67
+ core.scene.addEntity('RotatedDimension', dimensionData('STANDARD'));
68
+
69
+ dimStyleManager.renameStyle(dimStyleManager.getItemIndex('OLDNAME'), 'NEWNAME');
70
+
71
+ const dimension = core.scene.entities.get(1);
72
+ expect(dimension.getProperty('dimensionStyle')).toBe('NEWNAME');
73
+ expect(Object.hasOwn(dimension, 'dimensionStyle')).toBe(false);
74
+ expect(core.scene.entities.get(2).getProperty('dimensionStyle')).toBe('STANDARD');
75
+ expect(dimension.getDimensionStyle()?.name).toBe('NEWNAME');
76
+ });
77
+
78
+ test('Test DimStyleManager defaults to the ISO-25 current style', () => {
79
+ expect(new Core().dimStyleManager.getCstyle()).toBe('ISO-25');
80
+ });
81
+
82
+ test('Test DimStyleManager.purge keeps the current style', () => {
83
+ dimStyleManager.setCstyle('ISO-25');
84
+
85
+ dimStyleManager.purge();
86
+
87
+ expect(dimStyleManager.itemExists('ISO-25')).toBe(true);
88
+ expect(dimStyleManager.getCstyle()).toBe('ISO-25');
89
+ expect(dimStyleManager.itemExists('ANNOTATIVE')).toBe(false);
90
+ });
@@ -1,5 +1,7 @@
1
1
  import { Core } from '../../core/core/core.js';
2
2
  import { TableManagerBase } from '../../core/tables/tableManagerBase.js';
3
+ import { DesignCore } from '../../core/designCore.js';
4
+ import { Point } from '../../core/entities/point.js';
3
5
 
4
6
  // initialise core
5
7
  new Core();
@@ -192,3 +194,168 @@ test('Test tableManagerBase.updateItem', () => {
192
194
  tableManagerBase.updateItem(0, 'textHeight', 20);
193
195
  expect(tableManagerBase.getItemByIndex(0).textHeight).toBe(20);
194
196
  });
197
+
198
+ describe('tableManagerBase deleteItem return contract', () => {
199
+ beforeEach(() => {
200
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'Standard' }];
201
+ tableManagerBase.indelibleItems = ['Standard'];
202
+ });
203
+
204
+ test('returns the spliced array containing the deleted item', () => {
205
+ const deleted = tableManagerBase.deleteItem(0);
206
+
207
+ expect(Array.isArray(deleted)).toBe(true);
208
+ expect(deleted).toEqual([{ name: 'itemOne' }]);
209
+ expect(tableManagerBase.itemExists('itemOne')).toBe(false);
210
+ });
211
+
212
+ test('returns undefined for an invalid index', () => {
213
+ expect(tableManagerBase.deleteItem(10)).toBeUndefined();
214
+ expect(tableManagerBase.itemCount()).toBe(2);
215
+ });
216
+
217
+ test('returns undefined and keeps indelible items, ignoring case', () => {
218
+ tableManagerBase.indelibleItems = ['STANDARD'];
219
+
220
+ expect(tableManagerBase.deleteItem(1, false)).toBeUndefined();
221
+ expect(tableManagerBase.itemExists('Standard')).toBe(true);
222
+ });
223
+
224
+ test('invalidates the name lookup cache', () => {
225
+ expect(tableManagerBase.getItemByName('itemOne')).toBeDefined();
226
+
227
+ tableManagerBase.deleteItem(0);
228
+
229
+ expect(tableManagerBase.getItemByName('itemOne')).toBeUndefined();
230
+ });
231
+ });
232
+
233
+ describe('tableManagerBase lookups', () => {
234
+ beforeEach(() => {
235
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'itemTwo' }];
236
+ });
237
+
238
+ test('getItemIndex is case-insensitive and returns -1 when missing', () => {
239
+ expect(tableManagerBase.getItemIndex('ITEMTWO')).toBe(1);
240
+ expect(tableManagerBase.getItemIndex('missing')).toBe(-1);
241
+ });
242
+
243
+ test('itemExists and getItemByName are case-insensitive', () => {
244
+ expect(tableManagerBase.itemExists('ITEMONE')).toBe(true);
245
+ expect(tableManagerBase.getItemByName('itemone').name).toBe('itemOne');
246
+ });
247
+
248
+ test('getItemByName cache is refreshed after addItem and rename', () => {
249
+ expect(tableManagerBase.getItemByName('itemOne')).toBeDefined();
250
+
251
+ tableManagerBase.addItem({ name: 'itemThree' });
252
+ expect(tableManagerBase.getItemByName('itemThree')).toBeDefined();
253
+
254
+ tableManagerBase.renameItem(0, 'itemRenamed');
255
+ expect(tableManagerBase.getItemByName('itemOne')).toBeUndefined();
256
+ expect(tableManagerBase.getItemByName('itemRenamed')).toBeDefined();
257
+ });
258
+
259
+ test('clearItems removes all items', () => {
260
+ tableManagerBase.clearItems();
261
+
262
+ expect(tableManagerBase.itemCount()).toBe(0);
263
+ expect(tableManagerBase.getItemByName('itemOne')).toBeUndefined();
264
+ });
265
+
266
+ test('addItem returns the existing item when not overwriting', () => {
267
+ const existing = tableManagerBase.getItemByName('itemOne');
268
+
269
+ expect(tableManagerBase.addItem({ name: 'ITEMONE' })).toBe(existing);
270
+ });
271
+ });
272
+
273
+ describe('tableManagerBase renameItem', () => {
274
+ beforeEach(() => {
275
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'itemTwo' }, { name: 'Standard' }];
276
+ tableManagerBase.indelibleItems = ['Standard'];
277
+ });
278
+
279
+ test('returns the new name', () => {
280
+ expect(tableManagerBase.renameItem(0, 'renamed')).toBe('renamed');
281
+ });
282
+
283
+ test('returns undefined when the name is unchanged apart from case', () => {
284
+ expect(tableManagerBase.renameItem(0, 'ITEMONE')).toBeUndefined();
285
+ expect(tableManagerBase.getItemByIndex(0).name).toBe('itemOne');
286
+ });
287
+
288
+ test('cannot rename an indelible item', () => {
289
+ expect(tableManagerBase.renameItem(2, 'other')).toBeUndefined();
290
+ expect(tableManagerBase.getItemByIndex(2).name).toBe('Standard');
291
+ });
292
+
293
+ test('makes a clashing name unique', () => {
294
+ expect(tableManagerBase.renameItem(0, 'itemTwo')).toBe('itemTwo_1');
295
+ });
296
+ });
297
+
298
+ describe('tableManagerBase scene references', () => {
299
+ const originalProperty = tableManagerBase.itemProperty;
300
+
301
+ const addLine = (layer) => {
302
+ const line = DesignCore.Scene.addEntity('Line', { points: [new Point(), new Point(10, 10)], layer: layer });
303
+ return line;
304
+ };
305
+
306
+ beforeEach(() => {
307
+ tableManagerBase.itemProperty = 'layer';
308
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'itemTwo' }];
309
+ tableManagerBase.indelibleItems = [];
310
+ while (DesignCore.Scene.entities.count() > 0) {
311
+ DesignCore.Scene.entities.remove(0);
312
+ }
313
+ });
314
+
315
+ afterAll(() => {
316
+ tableManagerBase.itemProperty = originalProperty;
317
+ });
318
+
319
+ test('updateSceneItem renames references through the property API', () => {
320
+ addLine('itemOne');
321
+ addLine('itemTwo');
322
+
323
+ tableManagerBase.updateSceneItem('itemOne', 'renamed');
324
+
325
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('renamed');
326
+ expect(DesignCore.Scene.entities.get(1).getProperty('layer')).toBe('itemTwo');
327
+ });
328
+
329
+ test('updateSceneItem matches names case-insensitively', () => {
330
+ addLine('ITEMONE');
331
+
332
+ tableManagerBase.updateSceneItem('itemOne', 'renamed');
333
+
334
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('renamed');
335
+ });
336
+
337
+ test('updateSceneItem ignores an undefined name', () => {
338
+ addLine('itemOne');
339
+
340
+ tableManagerBase.updateSceneItem(undefined, 'renamed');
341
+
342
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('itemOne');
343
+ });
344
+
345
+ test('renameItem updates dependent entities', () => {
346
+ addLine('itemOne');
347
+
348
+ tableManagerBase.renameItem(0, 'renamed');
349
+
350
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('renamed');
351
+ });
352
+
353
+ test('purge removes only unused items', () => {
354
+ addLine('itemOne');
355
+
356
+ tableManagerBase.purge();
357
+
358
+ expect(tableManagerBase.itemExists('itemOne')).toBe(true);
359
+ expect(tableManagerBase.itemExists('itemTwo')).toBe(false);
360
+ });
361
+ });
@@ -46,7 +46,9 @@ test('Test purge.action', () => {
46
46
  expect(dimStyleManager.itemCount()).toBe(4);
47
47
  purge.option = 'Dimstyles';
48
48
  purge.action();
49
- expect(dimStyleManager.itemCount()).toBe(1);
49
+ // STANDARD is indelible and ISO-25 is the current style
50
+ expect(dimStyleManager.itemCount()).toBe(2);
51
+ expect(dimStyleManager.itemExists('ISO-25')).toBe(true);
50
52
 
51
53
  expect(layerManager.itemCount()).toBe(4);
52
54
  purge.option = 'Layers';