@rocketshow/designer 1.42.0 → 1.46.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.
@@ -26,15 +26,16 @@ import * as i3$2 from '@angular/forms';
26
26
  import { FormsModule } from '@angular/forms';
27
27
  import * as i11 from 'ngx-bootstrap/popover';
28
28
  import { PopoverModule } from 'ngx-bootstrap/popover';
29
- import * as i7 from 'ngx-sortablejs-plus';
30
- import { SortablejsModule } from 'ngx-sortablejs-plus';
31
29
  import * as i10 from 'ngx-bootstrap/typeahead';
32
30
  import { TypeaheadModule } from 'ngx-bootstrap/typeahead';
33
- import * as i4$1 from 'ngx-bootstrap-slider';
31
+ import * as i3$3 from 'ngx-bootstrap-slider';
34
32
  import { NgxBootstrapSliderModule } from 'ngx-bootstrap-slider';
35
- import * as i8 from '@angular/common';
33
+ import * as i9 from '@angular/common';
34
+ import * as i7 from 'ngx-sortablejs-plus';
35
+ import { SortablejsModule } from 'ngx-sortablejs-plus';
36
36
  import * as i5$1 from 'ngx-bootstrap/accordion';
37
37
  import { AccordionModule } from 'ngx-bootstrap/accordion';
38
+ import { KEYS, TREE_ACTIONS, TreeModule } from '@ali-hm/angular-tree-component';
38
39
  import { BrowserModule } from '@angular/platform-browser';
39
40
  import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
40
41
 
@@ -1053,13 +1054,107 @@ class UuidService {
1053
1054
  }]
1054
1055
  }], () => [], null); })();
1055
1056
 
1057
+ class CachedFixturePixel {
1058
+ }
1059
+
1060
+ class FixturePixelGroupResolveService {
1061
+ matchNumberRule(value, ruleRaw) {
1062
+ const rule = ruleRaw.trim().toLowerCase();
1063
+ // comparisons: <=5, >=5, =5
1064
+ const m = rule.match(/^(<=|>=|=)\s*(-?\d+)$/);
1065
+ if (m) {
1066
+ const op = m[1];
1067
+ const n = Number(m[2]);
1068
+ if (op === '<=')
1069
+ return value <= n;
1070
+ if (op === '>=')
1071
+ return value >= n;
1072
+ return value === n;
1073
+ }
1074
+ // even / odd
1075
+ if (rule === 'even')
1076
+ return value % 2 === 0;
1077
+ if (rule === 'odd')
1078
+ return Math.abs(value % 2) === 1;
1079
+ // 3n, 3n+1, 3n+2, etc.
1080
+ const mn = rule.match(/^(\d+)n(?:\+(\d+))?$/);
1081
+ if (mn) {
1082
+ const base = Number(mn[1]); // e.g. 3
1083
+ const rem = Number(mn[2] ?? '0'); // e.g. 1
1084
+ // handle negative values reasonably too
1085
+ const mod = ((value % base) + base) % base;
1086
+ return mod === ((rem % base) + base) % base;
1087
+ }
1088
+ // If you want, you can treat a plain number as "=number"
1089
+ const asNum = Number(rule);
1090
+ if (!Number.isNaN(asNum))
1091
+ return value === asNum;
1092
+ throw new Error(`Unknown numeric rule: "${ruleRaw}"`);
1093
+ }
1094
+ matchesAllConstraints(p, def) {
1095
+ // AND across present fields
1096
+ if (def.x && !def.x.every((rule) => this.matchNumberRule(p.x, rule)))
1097
+ return false;
1098
+ if (def.y && !def.y.every((rule) => this.matchNumberRule(p.y, rule)))
1099
+ return false;
1100
+ if (def.z && !def.z.every((rule) => this.matchNumberRule(p.z, rule)))
1101
+ return false;
1102
+ if (def.name) {
1103
+ // Interpret each entry as a regex; AND across them (change to "some" if you want OR)
1104
+ const ok = def.name.every((pattern) => {
1105
+ const re = new RegExp(pattern);
1106
+ return re.test(p.key);
1107
+ });
1108
+ if (!ok)
1109
+ return false;
1110
+ }
1111
+ return true;
1112
+ }
1113
+ resolveGroupPixels(group, pixelGroups, allPixels) {
1114
+ const def = pixelGroups[group];
1115
+ if (!def)
1116
+ return []; // unknown group
1117
+ // 1) "all"
1118
+ if (def === 'all')
1119
+ return allPixels.slice();
1120
+ // 2) explicit keys
1121
+ if (Array.isArray(def)) {
1122
+ const wanted = new Set(def);
1123
+ return allPixels.filter((p) => wanted.has(p.key));
1124
+ }
1125
+ // 3) constraints object
1126
+ return allPixels.filter((p) => this.matchesAllConstraints(p, def));
1127
+ }
1128
+ getPixelsForKeyOrGroup(profile, allPixelKeys, keyOrGroup) {
1129
+ let result = [];
1130
+ // search for a key first
1131
+ const pixel = allPixelKeys.find((p) => p.key === keyOrGroup);
1132
+ if (pixel)
1133
+ return [pixel];
1134
+ // search for a group
1135
+ if (profile.matrix.pixelGroups[keyOrGroup]) {
1136
+ result.push(...this.resolveGroupPixels(keyOrGroup, profile.matrix.pixelGroups, allPixelKeys));
1137
+ }
1138
+ return result;
1139
+ }
1140
+ static { this.ɵfac = function FixturePixelGroupResolveService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixturePixelGroupResolveService)(); }; }
1141
+ static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: FixturePixelGroupResolveService, factory: FixturePixelGroupResolveService.ɵfac, providedIn: 'root' }); }
1142
+ }
1143
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixturePixelGroupResolveService, [{
1144
+ type: Injectable,
1145
+ args: [{
1146
+ providedIn: 'root',
1147
+ }]
1148
+ }], null, null); })();
1149
+
1056
1150
  class FixtureService {
1057
- constructor(http, projectService, translateService, toastrService, uuidService) {
1151
+ constructor(http, projectService, translateService, toastrService, uuidService, fixturePixelGroupResolveService) {
1058
1152
  this.http = http;
1059
1153
  this.projectService = projectService;
1060
1154
  this.translateService = translateService;
1061
1155
  this.toastrService = toastrService;
1062
1156
  this.uuidService = uuidService;
1157
+ this.fixturePixelGroupResolveService = fixturePixelGroupResolveService;
1063
1158
  this.cachedFixtures = [];
1064
1159
  // is the side-tab settings currently selected?
1065
1160
  this.settingsSelection = false;
@@ -1077,7 +1172,7 @@ class FixtureService {
1077
1172
  }
1078
1173
  getCachedFixtureByUuid(uuid, pixelKey) {
1079
1174
  for (const fixture of this.cachedFixtures) {
1080
- if (this.fixtureUuidAndPixelKeyEquals(fixture.fixture.uuid, uuid, fixture.pixelKey, pixelKey)) {
1175
+ if (this.fixtureUuidAndPixelKeyEquals(fixture.fixture.uuid, uuid, fixture.pixel?.key, pixelKey)) {
1081
1176
  return fixture;
1082
1177
  }
1083
1178
  }
@@ -1086,7 +1181,7 @@ class FixtureService {
1086
1181
  return undefined;
1087
1182
  }
1088
1183
  for (const fixture of this.cachedFixtures) {
1089
- if (!fixture.pixelKey && fixture.fixture.uuid === uuid) {
1184
+ if (!fixture.pixel && fixture.fixture.uuid === uuid) {
1090
1185
  return fixture;
1091
1186
  }
1092
1187
  }
@@ -1323,131 +1418,250 @@ class FixtureService {
1323
1418
  channels.push(cachedFixtureChannel);
1324
1419
  }
1325
1420
  alphanumericSort(a, b) {
1326
- return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
1421
+ return a.key.localeCompare(b.key, undefined, { numeric: true, sensitivity: 'base' });
1422
+ }
1423
+ evenlySpacedPosCm(index, count, size) {
1424
+ if (count <= 0)
1425
+ return 0;
1426
+ if (index < 0 || index >= count)
1427
+ return 0;
1428
+ if (count === 1)
1429
+ return size / 2;
1430
+ const step = size / (count + 1); // equal edge margins
1431
+ const pos = (index + 1) * step; // center position
1432
+ return pos;
1433
+ }
1434
+ createPixel(key, x, y, z, profile) {
1435
+ let pixel = new CachedFixturePixel();
1436
+ if (key) {
1437
+ pixel.key = key;
1438
+ }
1439
+ else {
1440
+ // no key available. generate a unique one based on the dimensions
1441
+ pixel.key = 'Pixel ' + (x + 1) + '-' + (y + 1) + '-' + (z + 1);
1442
+ }
1443
+ pixel.x = x;
1444
+ pixel.y = y;
1445
+ pixel.z = z;
1446
+ // calculate the position of the beam, based on the physical dimensions
1447
+ // dimensions in mm
1448
+ const widthCm = profile.physical.dimensions[0] / 10;
1449
+ const heightCm = profile.physical.dimensions[1] / 10;
1450
+ const depthCm = profile.physical.dimensions[2] / 10;
1451
+ let maxX = 0;
1452
+ let maxY = 0;
1453
+ let maxZ = 0;
1454
+ if (profile.matrix.pixelCount) {
1455
+ maxX = profile.matrix.pixelCount[0];
1456
+ maxY = profile.matrix.pixelCount[1];
1457
+ maxZ = profile.matrix.pixelCount[2];
1458
+ }
1459
+ else {
1460
+ maxX = profile.matrix.pixelKeys.length;
1461
+ maxY = profile.matrix.pixelKeys[0].length;
1462
+ maxZ = profile.matrix.pixelKeys[0][0].length;
1463
+ }
1464
+ // distribute the pixels with even spacing
1465
+ pixel.positionX = this.evenlySpacedPosCm(x, maxX, widthCm) - widthCm / 2;
1466
+ pixel.positionY = this.evenlySpacedPosCm(y, maxY, heightCm) - heightCm / 2;
1467
+ pixel.positionZ = this.evenlySpacedPosCm(z, maxZ, depthCm) - depthCm / 2;
1468
+ return pixel;
1327
1469
  }
1328
1470
  getAllPixelKeys(profile) {
1329
1471
  let result = [];
1330
- if (!profile.matrix.pixelKeys) {
1472
+ if (!profile.matrix || !profile.matrix.pixelKeys) {
1331
1473
  return result;
1332
1474
  }
1333
- for (let pixelKeyX of profile.matrix.pixelKeys) {
1334
- for (let pixelKeyY of pixelKeyX) {
1335
- for (let pixelKeyZ of pixelKeyY) {
1336
- if (pixelKeyZ) {
1337
- result.push(pixelKeyZ);
1475
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1476
+ for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1477
+ for (let z = 0; z < profile.matrix.pixelKeys[x][y].length; z++) {
1478
+ const pixelKey = profile.matrix.pixelKeys[x][y][z];
1479
+ if (pixelKey) {
1480
+ result.push(this.createPixel(pixelKey, x, y, z, profile));
1338
1481
  }
1339
1482
  }
1340
1483
  }
1341
1484
  }
1342
1485
  return result;
1343
1486
  }
1344
- getAllPixelGroups(profile) {
1487
+ getAllPixelsInGroups(profile, allPixelKeys) {
1345
1488
  let result = [];
1346
- if (!profile.matrix.pixelGroups) {
1489
+ if (!profile.matrix || !profile.matrix.pixelGroups) {
1347
1490
  return result;
1348
1491
  }
1349
1492
  for (const property of Object.keys(profile.matrix.pixelGroups)) {
1350
- result.push(property);
1493
+ result.concat(this.fixturePixelGroupResolveService.resolveGroupPixels(property, profile.matrix.pixelGroups, allPixelKeys));
1351
1494
  }
1352
1495
  return result;
1353
1496
  }
1354
- getPixelKeysInOrder(profile, repeatFor) {
1497
+ // get pixels in order for a specific channel repeatFor
1498
+ getPixelsInOrder(profile, repeatFor) {
1499
+ let allPixelKeys = this.getAllPixelKeys(profile);
1355
1500
  let result = [];
1356
- // could contain just a single string (e.g. 'eachPixelABC') or
1501
+ // could contain different things
1357
1502
  if (repeatFor.length > 1) {
1358
- // an array of pixel (groups)
1359
- result = repeatFor;
1503
+ // an array of pixel keys or groups
1504
+ for (let entry of repeatFor) {
1505
+ result = result.concat(this.fixturePixelGroupResolveService.getPixelsForKeyOrGroup(profile, allPixelKeys, entry));
1506
+ }
1360
1507
  }
1361
1508
  else if (repeatFor.length === 0) {
1362
1509
  return result;
1363
1510
  }
1364
1511
  else if (repeatFor[0] === 'eachPixelABC') {
1365
1512
  // Gets computed into an alphanumerically sorted list of all pixelKeys
1366
- if (profile.matrix.pixelCount.length === 3) {
1513
+ if (profile.matrix.pixelKeys) {
1514
+ result = result.concat(allPixelKeys);
1515
+ result.sort(this.alphanumericSort);
1516
+ result.sort((a, b) => a.key.localeCompare(b.key, undefined, { numeric: true, sensitivity: 'base' }));
1517
+ }
1518
+ else if (profile.matrix.pixelCount.length === 3) {
1367
1519
  for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1368
1520
  for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1369
1521
  for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1370
- result.push('Pixel ' + (x + 1) + '-' + (y + 1) + '-' + (z + 1));
1522
+ result.push(this.createPixel(null, x, y, z, profile));
1371
1523
  }
1372
1524
  }
1373
1525
  }
1374
1526
  }
1375
- else {
1376
- result = result.concat(this.getAllPixelKeys(profile));
1377
- }
1378
- result.sort(this.alphanumericSort);
1379
1527
  }
1380
1528
  else if (repeatFor[0] === 'eachPixelGroup') {
1381
1529
  // Gets computed into an array of all pixel group keys, ordered by appearance in the JSON file
1382
- result = result.concat(this.getAllPixelGroups(profile));
1530
+ result = result.concat(this.getAllPixelsInGroups(profile, allPixelKeys));
1383
1531
  }
1384
1532
  else if (repeatFor[0] === 'eachPixelXYZ') {
1385
- for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1386
- for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1387
- for (let z = 0; z < profile.matrix.pixelKeys[x][y].length; z++) {
1388
- const pixel = profile.matrix.pixelKeys[x][y][z];
1389
- if (pixel) {
1390
- result.push(pixel);
1533
+ if (profile.matrix.pixelKeys) {
1534
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1535
+ for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1536
+ for (let z = 0; z < profile.matrix.pixelKeys[x][y].length; z++) {
1537
+ const pixel = profile.matrix.pixelKeys[x][y][z];
1538
+ if (pixel) {
1539
+ result.push(this.createPixel(pixel, x, y, z, profile));
1540
+ }
1541
+ }
1542
+ }
1543
+ }
1544
+ }
1545
+ else if (profile.matrix.pixelCount.length === 3) {
1546
+ for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1547
+ for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1548
+ for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1549
+ result.push(this.createPixel(null, x, y, z, profile));
1391
1550
  }
1392
1551
  }
1393
1552
  }
1394
1553
  }
1395
1554
  }
1396
1555
  else if (repeatFor[0] === 'eachPixelXZY') {
1397
- for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1398
- for (let z = 0; z < profile.matrix.pixelKeys[x][0].length; z++) {
1399
- for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1400
- const pixel = profile.matrix.pixelKeys[x][y][z];
1401
- if (pixel) {
1402
- result.push(pixel);
1556
+ if (profile.matrix.pixelKeys) {
1557
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1558
+ for (let z = 0; z < profile.matrix.pixelKeys[x][0].length; z++) {
1559
+ for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1560
+ const pixel = profile.matrix.pixelKeys[x][y][z];
1561
+ if (pixel) {
1562
+ result.push(this.createPixel(pixel, x, y, z, profile));
1563
+ }
1564
+ }
1565
+ }
1566
+ }
1567
+ }
1568
+ else if (profile.matrix.pixelCount.length === 3) {
1569
+ for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1570
+ for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1571
+ for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1572
+ result.push(this.createPixel(null, x, y, z, profile));
1403
1573
  }
1404
1574
  }
1405
1575
  }
1406
1576
  }
1407
1577
  }
1408
1578
  else if (repeatFor[0] === 'eachPixelYXZ') {
1409
- for (let y = 0; y < profile.matrix.pixelKeys[0].length; y++) {
1410
- for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1411
- for (let z = 0; z < profile.matrix.pixelKeys[x][y].length; z++) {
1412
- const pixel = profile.matrix.pixelKeys[x][y][z];
1413
- if (pixel) {
1414
- result.push(pixel);
1579
+ if (profile.matrix.pixelKeys) {
1580
+ for (let y = 0; y < profile.matrix.pixelKeys[0].length; y++) {
1581
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1582
+ for (let z = 0; z < profile.matrix.pixelKeys[x][y].length; z++) {
1583
+ const pixel = profile.matrix.pixelKeys[x][y][z];
1584
+ if (pixel) {
1585
+ result.push(this.createPixel(pixel, x, y, z, profile));
1586
+ }
1587
+ }
1588
+ }
1589
+ }
1590
+ }
1591
+ else if (profile.matrix.pixelCount.length === 3) {
1592
+ for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1593
+ for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1594
+ for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1595
+ result.push(this.createPixel(null, x, y, z, profile));
1415
1596
  }
1416
1597
  }
1417
1598
  }
1418
1599
  }
1419
1600
  }
1420
1601
  else if (repeatFor[0] === 'eachPixelYZX') {
1421
- for (let y = 0; y < profile.matrix.pixelKeys[0].length; y++) {
1422
- for (let z = 0; z < profile.matrix.pixelKeys[0][y].length; z++) {
1423
- for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1424
- const pixel = profile.matrix.pixelKeys[x][y][z];
1425
- if (pixel) {
1426
- result.push(pixel);
1602
+ if (profile.matrix.pixelKeys) {
1603
+ for (let y = 0; y < profile.matrix.pixelKeys[0].length; y++) {
1604
+ for (let z = 0; z < profile.matrix.pixelKeys[0][y].length; z++) {
1605
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1606
+ const pixel = profile.matrix.pixelKeys[x][y][z];
1607
+ if (pixel) {
1608
+ result.push(this.createPixel(pixel, x, y, z, profile));
1609
+ }
1610
+ }
1611
+ }
1612
+ }
1613
+ }
1614
+ else if (profile.matrix.pixelCount.length === 3) {
1615
+ for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1616
+ for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1617
+ for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1618
+ result.push(this.createPixel(null, x, y, z, profile));
1427
1619
  }
1428
1620
  }
1429
1621
  }
1430
1622
  }
1431
1623
  }
1432
1624
  else if (repeatFor[0] === 'eachPixelZXY') {
1433
- for (let z = 0; z < profile.matrix.pixelKeys[0][0].length; z++) {
1434
- for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1435
- for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1436
- const pixel = profile.matrix.pixelKeys[x][y][z];
1437
- if (pixel) {
1438
- result.push(pixel);
1625
+ if (profile.matrix.pixelKeys) {
1626
+ for (let z = 0; z < profile.matrix.pixelKeys[0][0].length; z++) {
1627
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1628
+ for (let y = 0; y < profile.matrix.pixelKeys[x].length; y++) {
1629
+ const pixel = profile.matrix.pixelKeys[x][y][z];
1630
+ if (pixel) {
1631
+ result.push(this.createPixel(pixel, x, y, z, profile));
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1636
+ }
1637
+ else if (profile.matrix.pixelCount.length === 3) {
1638
+ for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1639
+ for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1640
+ for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1641
+ result.push(this.createPixel(null, x, y, z, profile));
1439
1642
  }
1440
1643
  }
1441
1644
  }
1442
1645
  }
1443
1646
  }
1444
1647
  else if (repeatFor[0] === 'eachPixelZYX') {
1445
- for (let z = 0; z < profile.matrix.pixelKeys[0][0].length; z++) {
1446
- for (let y = 0; y < profile.matrix.pixelKeys[0].length; y++) {
1447
- for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1448
- const pixel = profile.matrix.pixelKeys[x][y][z];
1449
- if (pixel) {
1450
- result.push(pixel);
1648
+ if (profile.matrix.pixelKeys) {
1649
+ for (let z = 0; z < profile.matrix.pixelKeys[0][0].length; z++) {
1650
+ for (let y = 0; y < profile.matrix.pixelKeys[0].length; y++) {
1651
+ for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
1652
+ const pixel = profile.matrix.pixelKeys[x][y][z];
1653
+ if (pixel) {
1654
+ result.push(this.createPixel(pixel, x, y, z, profile));
1655
+ }
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+ else if (profile.matrix.pixelCount.length === 3) {
1661
+ for (let z = 0; z < profile.matrix.pixelCount[2]; z++) {
1662
+ for (let y = 0; y < profile.matrix.pixelCount[1]; y++) {
1663
+ for (let x = 0; x < profile.matrix.pixelCount[0]; x++) {
1664
+ result.push(this.createPixel(null, x, y, z, profile));
1451
1665
  }
1452
1666
  }
1453
1667
  }
@@ -1497,6 +1711,8 @@ class FixtureService {
1497
1711
  }
1498
1712
  getCachedChannels(profile, mode, pixelKey) {
1499
1713
  const channels = [];
1714
+ const allPixelKeys = this.getAllPixelKeys(profile);
1715
+ const allPixelsInGroups = this.getAllPixelsInGroups(profile, allPixelKeys);
1500
1716
  if (!mode) {
1501
1717
  return channels;
1502
1718
  }
@@ -1508,18 +1724,19 @@ class FixtureService {
1508
1724
  for (const availableChannelName of Object.keys(profile.availableChannels)) {
1509
1725
  if (channel.name === availableChannelName) {
1510
1726
  const availableChannel = profile.availableChannels[availableChannelName];
1727
+ channelFound = true;
1511
1728
  this.addCachedChannel(channels, availableChannel, availableChannelName, null, profile);
1512
1729
  }
1513
1730
  }
1514
1731
  // check the template channels, if not found in the available channels
1515
1732
  if (!channelFound) {
1516
1733
  for (const templateChannelName of Object.keys(profile.templateChannels)) {
1517
- let existingPixelKeys = this.getAllPixelKeys(profile).concat(this.getAllPixelGroups(profile));
1734
+ let existingPixelKeys = allPixelKeys.concat(allPixelsInGroups);
1518
1735
  for (const existingPixelKey of existingPixelKeys) {
1519
- const channelName = this.getChannelNameWithPixelKey(templateChannelName, existingPixelKey);
1736
+ const channelName = this.getChannelNameWithPixelKey(templateChannelName, existingPixelKey.key);
1520
1737
  if (channel.name === channelName) {
1521
1738
  const templateChannel = profile.templateChannels[templateChannelName];
1522
- this.addCachedChannel(channels, templateChannel, channelName, existingPixelKey, profile);
1739
+ this.addCachedChannel(channels, templateChannel, channelName, existingPixelKey.key, profile);
1523
1740
  }
1524
1741
  }
1525
1742
  }
@@ -1527,17 +1744,17 @@ class FixtureService {
1527
1744
  }
1528
1745
  else {
1529
1746
  // reference a channel through a pixel matrix
1530
- const availablePixelKeys = this.getPixelKeysInOrder(profile, channel.repeatFor);
1747
+ const availablePixels = this.getPixelsInOrder(profile, channel.repeatFor);
1531
1748
  if (channel.channelOrder === 'perPixel') {
1532
1749
  // each channel for each pixel
1533
- for (const availablePixelKey of availablePixelKeys) {
1534
- if (availablePixelKey === pixelKey) {
1750
+ for (const availablePixel of availablePixels) {
1751
+ if (availablePixel.key === pixelKey) {
1535
1752
  for (const modeTemplateChannelName of channel.templateChannels) {
1536
1753
  for (const templateChannelName of Object.keys(profile.templateChannels)) {
1537
1754
  // don't check the fine channels. only add the coarse channel.
1538
1755
  if (modeTemplateChannelName === templateChannelName) {
1539
1756
  const templateChannel = profile.templateChannels[templateChannelName];
1540
- this.addCachedChannel(channels, templateChannel, templateChannelName, availablePixelKey, profile);
1757
+ this.addCachedChannel(channels, templateChannel, templateChannelName, availablePixel.key, profile);
1541
1758
  }
1542
1759
  }
1543
1760
  }
@@ -1547,13 +1764,13 @@ class FixtureService {
1547
1764
  else if (channel.channelOrder === 'perChannel') {
1548
1765
  // each pixel for each channel
1549
1766
  for (const modeTemplateChannelName of channel.templateChannels) {
1550
- for (const availablePixelKey of availablePixelKeys) {
1551
- if (availablePixelKey === pixelKey) {
1767
+ for (const availablePixel of availablePixels) {
1768
+ if (availablePixel.key === pixelKey) {
1552
1769
  for (const templateChannelName of Object.keys(profile.templateChannels)) {
1553
1770
  // don't check the fine channels. only add the coarse channel.
1554
1771
  if (modeTemplateChannelName === templateChannelName) {
1555
1772
  const templateChannel = profile.templateChannels[templateChannelName];
1556
- this.addCachedChannel(channels, templateChannel, templateChannelName, availablePixelKey, profile);
1773
+ this.addCachedChannel(channels, templateChannel, templateChannelName, availablePixel.key, profile);
1557
1774
  }
1558
1775
  }
1559
1776
  }
@@ -1564,17 +1781,20 @@ class FixtureService {
1564
1781
  }
1565
1782
  return channels;
1566
1783
  }
1567
- fixtureGetUniquePixelKeys(fixture) {
1568
- let pixelKeys = new Set();
1784
+ // get all used pixels for all channels in the currently used fixture mode
1785
+ fixtureGetUniquePixels(fixture) {
1786
+ let pixels = [];
1569
1787
  let profile = this.getProfileByUuid(fixture.profileUuid);
1570
1788
  let mode = this.getModeByFixture(profile, fixture);
1571
1789
  // add the unique pixel keys
1572
1790
  for (let channel of mode.channels) {
1573
- this.getPixelKeysInOrder(profile, channel.repeatFor).forEach((str) => {
1574
- pixelKeys.add(str);
1791
+ this.getPixelsInOrder(profile, channel.repeatFor).forEach((pixel) => {
1792
+ if (pixels.filter((p) => p.key === pixel.key).length === 0) {
1793
+ pixels.push(pixel);
1794
+ }
1575
1795
  });
1576
1796
  }
1577
- return Array.from(pixelKeys);
1797
+ return pixels;
1578
1798
  }
1579
1799
  // does the fixture have a general channel without reference to specific pixel keys in the current mode?
1580
1800
  fixtureHasGeneralChannel(fixture) {
@@ -1582,7 +1802,7 @@ class FixtureService {
1582
1802
  let mode = this.getModeByFixture(profile, fixture);
1583
1803
  // add the unique pixel keys
1584
1804
  for (let channel of mode.channels) {
1585
- if (this.getPixelKeysInOrder(profile, channel.repeatFor).length === 0) {
1805
+ if (this.getPixelsInOrder(profile, channel.repeatFor).length === 0) {
1586
1806
  return true;
1587
1807
  }
1588
1808
  }
@@ -1601,14 +1821,14 @@ class FixtureService {
1601
1821
  cachedFixture.channels = this.getCachedChannels(cachedFixture.profile, cachedFixture.mode, null);
1602
1822
  this.cachedFixtures.push(cachedFixture);
1603
1823
  }
1604
- const pixelKeys = this.fixtureGetUniquePixelKeys(fixture);
1605
- for (let pixelKey of pixelKeys) {
1824
+ const pixels = this.fixtureGetUniquePixels(fixture);
1825
+ for (let pixel of pixels) {
1606
1826
  const cachedFixture = new CachedFixture();
1607
1827
  cachedFixture.fixture = fixture;
1608
- cachedFixture.pixelKey = pixelKey;
1828
+ cachedFixture.pixel = pixel;
1609
1829
  cachedFixture.profile = this.getProfileByUuid(fixture.profileUuid);
1610
1830
  cachedFixture.mode = this.getModeByFixture(cachedFixture.profile, fixture);
1611
- cachedFixture.channels = this.getCachedChannels(cachedFixture.profile, cachedFixture.mode, pixelKey);
1831
+ cachedFixture.channels = this.getCachedChannels(cachedFixture.profile, cachedFixture.mode, pixel.key);
1612
1832
  this.cachedFixtures.push(cachedFixture);
1613
1833
  }
1614
1834
  }
@@ -1721,7 +1941,7 @@ class FixtureService {
1721
1941
  });
1722
1942
  }
1723
1943
  }
1724
- static { this.ɵfac = function FixtureService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixtureService)(i0.ɵɵinject(i1.HttpClient), i0.ɵɵinject(ProjectService), i0.ɵɵinject(i4.TranslateService), i0.ɵɵinject(i3$1.ToastrService), i0.ɵɵinject(UuidService)); }; }
1944
+ static { this.ɵfac = function FixtureService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixtureService)(i0.ɵɵinject(i1.HttpClient), i0.ɵɵinject(ProjectService), i0.ɵɵinject(i4.TranslateService), i0.ɵɵinject(i3$1.ToastrService), i0.ɵɵinject(UuidService), i0.ɵɵinject(FixturePixelGroupResolveService)); }; }
1725
1945
  static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: FixtureService, factory: FixtureService.ɵfac, providedIn: 'root' }); }
1726
1946
  }
1727
1947
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureService, [{
@@ -1729,7 +1949,7 @@ class FixtureService {
1729
1949
  args: [{
1730
1950
  providedIn: 'root',
1731
1951
  }]
1732
- }], () => [{ type: i1.HttpClient }, { type: ProjectService }, { type: i4.TranslateService }, { type: i3$1.ToastrService }, { type: UuidService }], null); })();
1952
+ }], () => [{ type: i1.HttpClient }, { type: ProjectService }, { type: i4.TranslateService }, { type: i3$1.ToastrService }, { type: UuidService }, { type: FixturePixelGroupResolveService }], null); })();
1733
1953
 
1734
1954
  class PreviewMeshService {
1735
1955
  constructor() {
@@ -1831,7 +2051,11 @@ class ConfigService {
1831
2051
  }], () => [], null); })();
1832
2052
 
1833
2053
  class EffectService {
1834
- constructor() { }
2054
+ constructor() {
2055
+ // The effects are currently being edited
2056
+ this.effectsOpen = false;
2057
+ this.effectsOpenChanged = new Subject();
2058
+ }
1835
2059
  static { this.ɵfac = function EffectService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EffectService)(); }; }
1836
2060
  static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: EffectService, factory: EffectService.ɵfac, providedIn: 'root' }); }
1837
2061
  }
@@ -1937,12 +2161,12 @@ class PresetService {
1937
2161
  this.selectedPreset.fixtures.push(presetFixture);
1938
2162
  }
1939
2163
  }
1940
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
1941
- for (const pixelKey of pixelKeys) {
1942
- if (!this.fixtureIsSelected(fixture, pixelKey)) {
2164
+ const pixels = this.fixtureService.fixtureGetUniquePixels(fixture);
2165
+ for (const pixel of pixels) {
2166
+ if (!this.fixtureIsSelected(fixture, pixel.key)) {
1943
2167
  const presetFixture = new PresetFixture();
1944
2168
  presetFixture.fixtureUuid = fixture.uuid;
1945
- presetFixture.pixelKey = pixelKey;
2169
+ presetFixture.pixelKey = pixel.key;
1946
2170
  this.selectedPreset.fixtures.push(presetFixture);
1947
2171
  }
1948
2172
  }
@@ -1954,13 +2178,24 @@ class PresetService {
1954
2178
  }
1955
2179
  this.selectedPreset.fixtures = [];
1956
2180
  }
2181
+ presetFixtueEquals(fixture1, fixture2) {
2182
+ return (fixture1.fixtureUuid === fixture2.fixtureUuid &&
2183
+ ((!fixture2.pixelKey && !fixture1.pixelKey) || fixture2.pixelKey === fixture1.pixelKey));
2184
+ }
1957
2185
  removeDeletedFixtures() {
1958
2186
  // after changing the configuration in the fixture pool, we might need to
1959
2187
  // delete some fixtures
1960
2188
  for (const preset of this.projectService.project.presets) {
1961
2189
  for (let i = preset.fixtures.length - 1; i >= 0; i--) {
1962
- const presetFixture = this.fixtureService.getFixtureByUuid(preset.fixtures[i].fixtureUuid);
1963
- if (!presetFixture) {
2190
+ const presetFixture = preset.fixtures[i];
2191
+ let found = false;
2192
+ for (let projectFixture of this.projectService.project.presetFixtures) {
2193
+ if (this.presetFixtueEquals(presetFixture, projectFixture)) {
2194
+ found = true;
2195
+ break;
2196
+ }
2197
+ }
2198
+ if (!found) {
1964
2199
  preset.fixtures.splice(i, 1);
1965
2200
  }
1966
2201
  }
@@ -2191,11 +2426,10 @@ class PresetService {
2191
2426
  // loop over the project fixtures to keep the order
2192
2427
  for (let projectFixture of this.projectService.project.presetFixtures) {
2193
2428
  for (const presetFixture of this.selectedPreset.fixtures) {
2194
- if (projectFixture.fixtureUuid === presetFixture.fixtureUuid &&
2195
- ((!projectFixture.pixelKey && !presetFixture.pixelKey) || projectFixture.pixelKey === presetFixture.pixelKey)) {
2429
+ if (this.presetFixtueEquals(presetFixture, projectFixture)) {
2196
2430
  const fixture = this.fixtureService.getCachedFixtureByUuid(presetFixture.fixtureUuid, presetFixture.pixelKey);
2197
2431
  if (fixture.profile.uuid === profile.uuid) {
2198
- const exists = modeAndPixelKeys.some((item) => (item.mode === fixture.mode && !item.pixelKey && !fixture.pixelKey) || item.pixelKey === fixture.pixelKey);
2432
+ const exists = modeAndPixelKeys.some((item) => (item.mode === fixture.mode && !item.pixelKey && !fixture.pixel?.key) || item.pixelKey === fixture.pixel?.key);
2199
2433
  if (!exists) {
2200
2434
  modeAndPixelKeys.push({
2201
2435
  mode: fixture.mode,
@@ -2423,12 +2657,13 @@ class SceneService {
2423
2657
  }], () => [{ type: UuidService }, { type: EffectService }, { type: PresetService }, { type: ProjectService }], null); })();
2424
2658
 
2425
2659
  class TimelineService {
2426
- constructor(sceneService, presetService, projectService, http, configService) {
2660
+ constructor(sceneService, presetService, projectService, http, configService, ngZone) {
2427
2661
  this.sceneService = sceneService;
2428
2662
  this.presetService = presetService;
2429
2663
  this.projectService = projectService;
2430
2664
  this.http = http;
2431
2665
  this.configService = configService;
2666
+ this.ngZone = ngZone;
2432
2667
  this.playState = 'paused';
2433
2668
  this.zoom = 0;
2434
2669
  this.timelineClicking = false;
@@ -2519,9 +2754,12 @@ class TimelineService {
2519
2754
  }
2520
2755
  startTimeUpdater() {
2521
2756
  this.stopTimeUpdater();
2522
- const timeUpdater = timer(0, 40);
2523
- this.timeUpdateSubscription = timeUpdater.subscribe(() => {
2524
- this.updateCurrentTime();
2757
+ // Avoid triggering change detection with each animation frame -> run outside zone
2758
+ this.ngZone.runOutsideAngular(() => {
2759
+ const timeUpdater = timer(0, 40);
2760
+ this.timeUpdateSubscription = timeUpdater.subscribe(() => {
2761
+ this.updateCurrentTime();
2762
+ });
2525
2763
  });
2526
2764
  }
2527
2765
  stopTimeUpdater() {
@@ -3116,7 +3354,7 @@ class TimelineService {
3116
3354
  this.waveSurferReady.next();
3117
3355
  this.applyZoom(0);
3118
3356
  }
3119
- static { this.ɵfac = function TimelineService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TimelineService)(i0.ɵɵinject(SceneService), i0.ɵɵinject(PresetService), i0.ɵɵinject(ProjectService), i0.ɵɵinject(i1.HttpClient), i0.ɵɵinject(ConfigService)); }; }
3357
+ static { this.ɵfac = function TimelineService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TimelineService)(i0.ɵɵinject(SceneService), i0.ɵɵinject(PresetService), i0.ɵɵinject(ProjectService), i0.ɵɵinject(i1.HttpClient), i0.ɵɵinject(ConfigService), i0.ɵɵinject(i0.NgZone)); }; }
3120
3358
  static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: TimelineService, factory: TimelineService.ɵfac, providedIn: 'root' }); }
3121
3359
  }
3122
3360
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TimelineService, [{
@@ -3124,7 +3362,7 @@ class TimelineService {
3124
3362
  args: [{
3125
3363
  providedIn: 'root',
3126
3364
  }]
3127
- }], () => [{ type: SceneService }, { type: PresetService }, { type: ProjectService }, { type: i1.HttpClient }, { type: ConfigService }], null); })();
3365
+ }], () => [{ type: SceneService }, { type: PresetService }, { type: ProjectService }, { type: i1.HttpClient }, { type: ConfigService }, { type: i0.NgZone }], null); })();
3128
3366
 
3129
3367
  class PreviewService {
3130
3368
  constructor(presetService, fixtureService, sceneService, timelineService, projectService) {
@@ -3134,6 +3372,8 @@ class PreviewService {
3134
3372
  this.timelineService = timelineService;
3135
3373
  this.projectService = projectService;
3136
3374
  this.doUpdateFixtureSetup = new Subject();
3375
+ this.doUpdateStageAndPositions = new Subject();
3376
+ this.stageAndPositionsDirty = true;
3137
3377
  this.stageMeshes = [];
3138
3378
  this.stageMaterial = new THREE.MeshStandardMaterial({
3139
3379
  color: 0x0d0d0d,
@@ -3145,8 +3385,12 @@ class PreviewService {
3145
3385
  emissive: 0x0d0d0d,
3146
3386
  });
3147
3387
  this.fixtureSelectedMaterial = new THREE.MeshLambertMaterial({
3148
- color: 0xff00ff,
3149
- emissive: 0xff00ff,
3388
+ color: 0x660066,
3389
+ emissive: 0xaa00aa,
3390
+ emissiveIntensity: 0.000000000000000000001,
3391
+ });
3392
+ this.updateStageAndPositionsSubscription = this.doUpdateStageAndPositions.subscribe(() => {
3393
+ this.stageAndPositionsDirty = true;
3150
3394
  });
3151
3395
  }
3152
3396
  getAlreadyCalculatedFixture(fixtures, fixtureIndex) {
@@ -3155,7 +3399,9 @@ class PreviewService {
3155
3399
  for (let i = 0; i < fixtureIndex; i++) {
3156
3400
  const calculatedFixture = fixtures[i];
3157
3401
  if (calculatedFixture.fixture.dmxUniverseUuid === fixtures[fixtureIndex].fixture.dmxUniverseUuid &&
3158
- calculatedFixture.fixture.dmxFirstChannel === fixtures[fixtureIndex].fixture.dmxFirstChannel) {
3402
+ calculatedFixture.fixture.dmxFirstChannel === fixtures[fixtureIndex].fixture.dmxFirstChannel &&
3403
+ ((!calculatedFixture.pixel?.key && !fixtures[fixtureIndex].pixel?.key) ||
3404
+ calculatedFixture.pixel?.key === fixtures[fixtureIndex].pixel?.key)) {
3159
3405
  return calculatedFixture;
3160
3406
  }
3161
3407
  }
@@ -3479,7 +3725,7 @@ class PreviewService {
3479
3725
  }
3480
3726
  for (const preset of presets) {
3481
3727
  // search for this fixture in the preset and get it's preset-specific index (for chasing effects)
3482
- const fixtureIndex = this.getFixtureIndex(preset.preset, cachedFixture.fixture.uuid, cachedFixture.pixelKey);
3728
+ const fixtureIndex = this.getFixtureIndex(preset.preset, cachedFixture.fixture.uuid, cachedFixture.pixel?.key);
3483
3729
  if (fixtureIndex >= 0) {
3484
3730
  // this fixture is also in the preset -> mix the required values (overwrite existing values,
3485
3731
  // if set multiple times)
@@ -3590,7 +3836,7 @@ class PreviewService {
3590
3836
  }], () => [{ type: PresetService }, { type: FixtureService }, { type: SceneService }, { type: TimelineService }, { type: ProjectService }], null); })();
3591
3837
 
3592
3838
  class Fixture3d {
3593
- constructor(fixtureService, previewService, fixture, scene, hasSpotLight = false) {
3839
+ constructor(fixtureService, previewService, fixture, scene, fixtureGroup, hasSpotLight = false, hasBulb = false) {
3594
3840
  this.fixtureService = fixtureService;
3595
3841
  this.previewService = previewService;
3596
3842
  this.pointLightMaxIntensity = 2;
@@ -3601,10 +3847,17 @@ class Fixture3d {
3601
3847
  this.dimmer = 0;
3602
3848
  this.isSelected = false;
3603
3849
  this.isLoaded = false;
3850
+ // spotlight
3604
3851
  this.hasSpotLight = false;
3852
+ // glowing bulb
3853
+ this.hasBulb = false;
3854
+ this.bulbSphereRadius = 3;
3855
+ this.blackColor = new THREE.Color(0x000000);
3605
3856
  this.fixture = fixture;
3606
3857
  this.scene = scene;
3858
+ this.fixtureGroup = fixtureGroup;
3607
3859
  this.hasSpotLight = hasSpotLight;
3860
+ this.hasBulb = hasBulb;
3608
3861
  // evaluate, various capabilities of this fixture
3609
3862
  for (const cachedChannel of this.fixture.channels) {
3610
3863
  if (cachedChannel.channel) {
@@ -3706,6 +3959,15 @@ class Fixture3d {
3706
3959
  spotLightTarget.position.set(0, -10, 0);
3707
3960
  this.createSpotLightBeam();
3708
3961
  }
3962
+ if (this.hasBulb) {
3963
+ const bulbGeo = new THREE.SphereGeometry(this.bulbSphereRadius * 1.12, 64, 64);
3964
+ this.bulbMaterial = new THREE.MeshLambertMaterial({
3965
+ color: 0xff00ff,
3966
+ emissive: 0xff00ff,
3967
+ });
3968
+ this.bulbSphere = new THREE.Mesh(bulbGeo, this.bulbMaterial);
3969
+ this.fixtureGroup.add(this.bulbSphere);
3970
+ }
3709
3971
  }
3710
3972
  getCapabilityInValue(channel, value) {
3711
3973
  for (const capability of channel.capabilities) {
@@ -3716,6 +3978,44 @@ class Fixture3d {
3716
3978
  }
3717
3979
  return undefined;
3718
3980
  }
3981
+ updatePosition(object) {
3982
+ // the offset position (e.g. for single beams inside a fixture)
3983
+ if (object) {
3984
+ object.position.set(this.fixture.pixel ? this.fixture.pixel.positionX : 0, this.fixture.pixel ? this.fixture.pixel.positionY : 0, this.fixture.pixel ? this.fixture.pixel.positionZ : 0);
3985
+ }
3986
+ if (this.hasBulb) {
3987
+ this.bulbSphere.position.set(this.fixture.pixel ? this.fixture.pixel.positionX : 0, (this.fixture.pixel ? this.fixture.pixel.positionY : 0) - 15 + this.bulbSphereRadius / 2, this.fixture.pixel ? this.fixture.pixel.positionZ : 0);
3988
+ }
3989
+ switch (this.fixture.fixture.positioning) {
3990
+ case Positioning.topFront: {
3991
+ this.fixtureGroup.rotation.x = THREE.MathUtils.degToRad(0);
3992
+ this.fixtureGroup.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY - 13, this.fixture.fixture.positionZ);
3993
+ break;
3994
+ }
3995
+ case Positioning.bottomFront: {
3996
+ this.fixtureGroup.rotation.x = THREE.MathUtils.degToRad(180);
3997
+ this.fixtureGroup.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY + 13, this.fixture.fixture.positionZ);
3998
+ break;
3999
+ }
4000
+ case Positioning.topBack: {
4001
+ this.fixtureGroup.rotation.x = THREE.MathUtils.degToRad(0);
4002
+ this.fixtureGroup.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY - 13, this.fixture.fixture.positionZ);
4003
+ break;
4004
+ }
4005
+ case Positioning.bottomBack: {
4006
+ this.fixtureGroup.rotation.x = THREE.MathUtils.degToRad(180);
4007
+ this.fixtureGroup.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY + 13, this.fixture.fixture.positionZ);
4008
+ break;
4009
+ }
4010
+ case Positioning.manual: {
4011
+ this.fixtureGroup.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY, this.fixture.fixture.positionZ);
4012
+ this.fixtureGroup.rotation.x = THREE.MathUtils.degToRad(this.fixture.fixture.rotationX);
4013
+ this.fixtureGroup.rotation.y = THREE.MathUtils.degToRad(this.fixture.fixture.rotationY);
4014
+ this.fixtureGroup.rotation.z = THREE.MathUtils.degToRad(this.fixture.fixture.rotationZ);
4015
+ break;
4016
+ }
4017
+ }
4018
+ }
3719
4019
  // Apply the properties of the base fixture to the preview
3720
4020
  updatePreview(channelValues, masterDimmerValue) {
3721
4021
  // Apply default settings
@@ -3793,37 +4093,11 @@ class Fixture3d {
3793
4093
  this.spotLight.intensity = this.spotLightLightMaxIntensity * this.dimmer;
3794
4094
  this.spotLightBeam.material.uniforms.spotPosition.value = this.spotlightGroup.position;
3795
4095
  }
3796
- }
3797
- updatePosition(object) {
3798
- // Update the position
3799
- switch (this.fixture.fixture.positioning) {
3800
- case Positioning.topFront: {
3801
- object.rotation.x = THREE.MathUtils.degToRad(0);
3802
- object.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY - 13, this.fixture.fixture.positionZ);
3803
- break;
3804
- }
3805
- case Positioning.bottomFront: {
3806
- object.rotation.x = THREE.MathUtils.degToRad(180);
3807
- object.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY + 13, this.fixture.fixture.positionZ);
3808
- break;
3809
- }
3810
- case Positioning.topBack: {
3811
- object.rotation.x = THREE.MathUtils.degToRad(0);
3812
- object.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY - 13, this.fixture.fixture.positionZ);
3813
- break;
3814
- }
3815
- case Positioning.bottomBack: {
3816
- object.rotation.x = THREE.MathUtils.degToRad(180);
3817
- object.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY + 13, this.fixture.fixture.positionZ);
3818
- break;
3819
- }
3820
- case Positioning.manual: {
3821
- object.position.set(this.fixture.fixture.positionX, this.fixture.fixture.positionY, this.fixture.fixture.positionZ);
3822
- object.rotation.x = THREE.MathUtils.degToRad(this.fixture.fixture.rotationX);
3823
- object.rotation.y = THREE.MathUtils.degToRad(this.fixture.fixture.rotationY);
3824
- object.rotation.z = THREE.MathUtils.degToRad(this.fixture.fixture.rotationZ);
3825
- break;
3826
- }
4096
+ if (this.hasBulb) {
4097
+ // Normalize 0–255 → 0–1
4098
+ const intensity = Math.max(this.colorRed, this.colorGreen, this.colorBlue) / 255;
4099
+ this.bulbMaterial.color.copy(color.clone().lerp(this.blackColor, intensity));
4100
+ this.bulbMaterial.emissive.copy(color.clone().lerp(this.blackColor, intensity));
3827
4101
  }
3828
4102
  }
3829
4103
  createSpotLightBeam() {
@@ -3843,12 +4117,15 @@ class Fixture3d {
3843
4117
  if (this.hasSpotLight) {
3844
4118
  this.spotlightMaterial.dispose();
3845
4119
  }
4120
+ if (this.hasBulb) {
4121
+ this.bulbMaterial.dispose();
4122
+ }
3846
4123
  }
3847
4124
  }
3848
4125
 
3849
4126
  class ColorChanger3d extends Fixture3d {
3850
- constructor(fixtureService, previewService, previewMeshService, fixture, scene) {
3851
- super(fixtureService, previewService, fixture, scene, true);
4127
+ constructor(fixtureService, previewService, previewMeshService, fixture, scene, fixtureGroup, hasSpotLight = true, hasBulb = false) {
4128
+ super(fixtureService, previewService, fixture, scene, fixtureGroup, hasSpotLight, hasBulb);
3852
4129
  this.fixtureService = fixtureService;
3853
4130
  this.previewService = previewService;
3854
4131
  this.objectGroup = new THREE.Object3D();
@@ -3862,18 +4139,26 @@ class ColorChanger3d extends Fixture3d {
3862
4139
  createObjects() {
3863
4140
  super.createObjects();
3864
4141
  this.mesh.material = this.previewService.fixtureMaterial;
3865
- this.objectGroup.add(this.spotlightGroup);
4142
+ if (this.hasSpotLight) {
4143
+ this.objectGroup.add(this.spotlightGroup);
4144
+ }
3866
4145
  this.objectGroup.add(this.mesh);
3867
4146
  this.objectGroup.scale.multiplyScalar(9);
3868
- this.scene.add(this.objectGroup);
4147
+ this.fixtureGroup.add(this.objectGroup);
3869
4148
  this.isLoaded = true;
4149
+ this.updatePosition();
4150
+ }
4151
+ updatePosition() {
4152
+ if (!this.isLoaded) {
4153
+ return;
4154
+ }
4155
+ super.updatePosition(this.objectGroup);
3870
4156
  }
3871
4157
  updatePreview(channelValues, masterDimmerValue) {
3872
4158
  if (!this.isLoaded) {
3873
4159
  return;
3874
4160
  }
3875
4161
  super.updatePreview(channelValues, masterDimmerValue);
3876
- this.updatePosition(this.objectGroup);
3877
4162
  // Update the material
3878
4163
  if (this.lastSelected !== this.isSelected) {
3879
4164
  if (this.isSelected) {
@@ -3895,8 +4180,8 @@ class ColorChanger3d extends Fixture3d {
3895
4180
  }
3896
4181
 
3897
4182
  class MovingHead3d extends Fixture3d {
3898
- constructor(fixtureService, previewService, previewMeshService, fixture, scene) {
3899
- super(fixtureService, previewService, fixture, scene, true);
4183
+ constructor(fixtureService, previewService, previewMeshService, fixture, scene, fixtureGroup) {
4184
+ super(fixtureService, previewService, fixture, scene, fixtureGroup, true, true);
3900
4185
  this.fixtureService = fixtureService;
3901
4186
  this.previewService = previewService;
3902
4187
  this.headGroup = new THREE.Object3D();
@@ -3937,15 +4222,21 @@ class MovingHead3d extends Fixture3d {
3937
4222
  this.socket.position.set(0, 1.2, 0);
3938
4223
  // A moving head has about 32 cm in width
3939
4224
  this.objectGroup.scale.multiplyScalar(9);
3940
- this.scene.add(this.objectGroup);
4225
+ this.fixtureGroup.add(this.objectGroup);
3941
4226
  this.isLoaded = true;
4227
+ this.updatePosition();
4228
+ }
4229
+ updatePosition() {
4230
+ if (!this.isLoaded) {
4231
+ return;
4232
+ }
4233
+ super.updatePosition(this.objectGroup);
3942
4234
  }
3943
4235
  updatePreview(channelValues, masterDimmerValue) {
3944
4236
  if (!this.isLoaded) {
3945
4237
  return;
3946
4238
  }
3947
4239
  super.updatePreview(channelValues, masterDimmerValue);
3948
- this.updatePosition(this.objectGroup);
3949
4240
  // Apply default settings
3950
4241
  let panStart = 0;
3951
4242
  let panEnd = 255;
@@ -4004,17 +4295,20 @@ class MovingHead3d extends Fixture3d {
4004
4295
  }
4005
4296
  }
4006
4297
 
4007
- const _c0$6 = ["canvas"];
4298
+ const _c0$7 = ["canvas"];
4008
4299
  class PreviewComponent {
4009
- constructor(fixtureService, previewMeshService, animationService, previewService, timelineService, projectService) {
4300
+ constructor(fixtureService, previewMeshService, animationService, previewService, timelineService, projectService, ngZone) {
4010
4301
  this.fixtureService = fixtureService;
4011
4302
  this.previewMeshService = previewMeshService;
4012
4303
  this.animationService = animationService;
4013
4304
  this.previewService = previewService;
4014
4305
  this.timelineService = timelineService;
4015
4306
  this.projectService = projectService;
4307
+ this.ngZone = ngZone;
4016
4308
  this.scene = new THREE.Scene();
4017
4309
  this.fixtures3d = [];
4310
+ // grouping fixture (pixels) into the same fixture to being able to properly rotate around the center
4311
+ this.fixtureGroups = new Map();
4018
4312
  this.previewService.doUpdateFixtureSetup.subscribe(() => {
4019
4313
  this.syncFixtures();
4020
4314
  });
@@ -4030,18 +4324,40 @@ class PreviewComponent {
4030
4324
  fixture3d.destroy();
4031
4325
  }
4032
4326
  this.fixtures3d = [];
4327
+ // destroy all groups
4328
+ this.fixtureGroups.clear();
4033
4329
  // add all fixtures from the project
4330
+ const fixture3dMap = {
4331
+ [FixtureCategory['Moving Head']]: MovingHead3d,
4332
+ [FixtureCategory['Color Changer']]: ColorChanger3d,
4333
+ [FixtureCategory['Blinder']]: ColorChanger3d,
4334
+ };
4335
+ // TODO group fixtures of the same UUID but with differen pixels into the same fixture to ensure
4336
+ // rotation is aligned
4034
4337
  for (const fixture of this.fixtureService.cachedFixtures) {
4035
- switch (fixture.profile.categories[0]) {
4036
- case FixtureCategory['Moving Head']:
4037
- this.fixtures3d.push(new MovingHead3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene));
4038
- break;
4039
- case FixtureCategory['Color Changer']:
4040
- this.fixtures3d.push(new ColorChanger3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene));
4041
- break;
4042
- case FixtureCategory['Blinder']:
4043
- this.fixtures3d.push(new ColorChanger3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene));
4044
- break;
4338
+ const category = fixture.profile.categories.find((c) => fixture3dMap[c]);
4339
+ const uuid = fixture.fixture.uuid;
4340
+ let fixtureGroup = this.fixtureGroups.get(uuid);
4341
+ if (!fixtureGroup) {
4342
+ fixtureGroup = new THREE.Group();
4343
+ fixtureGroup.name = `fixture-${uuid}`;
4344
+ this.scene.add(fixtureGroup);
4345
+ this.fixtureGroups.set(uuid, fixtureGroup);
4346
+ }
4347
+ if (category) {
4348
+ const Fixture3dClass = fixture3dMap[category];
4349
+ if (Fixture3dClass === ColorChanger3d) {
4350
+ const isPixelBar = fixture.profile.categories?.[0] === FixtureCategory['Pixel Bar'];
4351
+ const fixture3d = new ColorChanger3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene, fixtureGroup, !isPixelBar, isPixelBar);
4352
+ this.fixtures3d.push(fixture3d);
4353
+ }
4354
+ else {
4355
+ const fixture3d = new Fixture3dClass(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene, fixtureGroup);
4356
+ this.fixtures3d.push(fixture3d);
4357
+ }
4358
+ }
4359
+ else {
4360
+ console.warn(`No supported category found for fixture`);
4045
4361
  }
4046
4362
  }
4047
4363
  }
@@ -4052,22 +4368,28 @@ class PreviewComponent {
4052
4368
  this.renderer.render(this.scene, this.camera);
4053
4369
  }
4054
4370
  updateStagePosition(positioning, xMin, xMax, yMin, yMax, zMin, zMax) {
4055
- let positionCount = 0; // Number of fixtures in the same position
4056
- let positionIndex = 1;
4371
+ let positionCount = 0; // number of fixtures in the same position (only counting one pixel)
4372
+ let positionIndex = 0;
4373
+ let lastFixtureUuid = undefined; // skip new positioning if it's just a new pixel inside the same fixture
4057
4374
  this.projectService.project.presetFixtures.forEach((element, index) => {
4058
4375
  const fixture = this.fixtureService.getFixtureByUuid(element.fixtureUuid);
4059
- if (fixture.positioning === positioning) {
4376
+ if (fixture.positioning === positioning && element.fixtureUuid != lastFixtureUuid) {
4060
4377
  positionCount++;
4061
4378
  }
4379
+ lastFixtureUuid = element.fixtureUuid;
4062
4380
  });
4381
+ lastFixtureUuid = undefined;
4063
4382
  this.projectService.project.presetFixtures.forEach((element, index) => {
4064
4383
  const fixture = this.fixtureService.getFixtureByUuid(element.fixtureUuid);
4065
4384
  if (fixture.positioning === positioning) {
4385
+ if (element.fixtureUuid != lastFixtureUuid) {
4386
+ positionIndex++;
4387
+ }
4066
4388
  fixture.positionX = xMin + ((xMax - xMin) / (positionCount + 1)) * positionIndex;
4067
4389
  fixture.positionY = yMin + ((yMax - yMin) / (positionCount + 1)) * positionIndex;
4068
4390
  fixture.positionZ = zMin + ((zMax - zMin) / (positionCount + 1)) * positionIndex;
4069
- positionIndex++;
4070
4391
  }
4392
+ lastFixtureUuid = element.fixtureUuid;
4071
4393
  });
4072
4394
  }
4073
4395
  animate(timeMillis) {
@@ -4090,6 +4412,10 @@ class PreviewComponent {
4090
4412
  const presets = this.previewService.getPresets(timeMillis);
4091
4413
  const calculatedFixtures = this.previewService.getChannelValues(timeMillis, presets);
4092
4414
  for (const fixture3d of this.fixtures3d) {
4415
+ // Update the fixture positions
4416
+ if (this.previewService.stageAndPositionsDirty) {
4417
+ fixture3d.updatePosition();
4418
+ }
4093
4419
  // Update the fixture properties
4094
4420
  fixture3d.updatePreview(calculatedFixtures.get(fixture3d.fixture) || [], this.projectService.project.masterDimmerValue);
4095
4421
  // Select the fixture, if required
@@ -4097,11 +4423,12 @@ class PreviewComponent {
4097
4423
  fixture3d.isSelected = this.fixtureService.settingsFixtureIsSelected(fixture3d.fixture.fixture);
4098
4424
  }
4099
4425
  else {
4100
- fixture3d.isSelected = this.previewService.fixtureIsSelected(fixture3d.fixture.fixture.uuid, fixture3d.fixture.pixelKey, presets);
4426
+ fixture3d.isSelected = this.previewService.fixtureIsSelected(fixture3d.fixture.fixture.uuid, fixture3d.fixture.pixel?.key, presets);
4101
4427
  }
4102
4428
  }
4103
4429
  // Render the scene
4104
4430
  this.render();
4431
+ this.previewService.stageAndPositionsDirty = false;
4105
4432
  requestAnimationFrame(this.animate.bind(this));
4106
4433
  }
4107
4434
  getAspectRatio() {
@@ -4174,11 +4501,14 @@ class PreviewComponent {
4174
4501
  this.setupControls();
4175
4502
  this.setupScene();
4176
4503
  this.onResize();
4177
- this.animate(null);
4504
+ // Avoid triggering change detection with each animation frame -> run outside zone
4505
+ this.ngZone.runOutsideAngular(() => {
4506
+ this.animate(null);
4507
+ });
4178
4508
  }
4179
- static { this.ɵfac = function PreviewComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PreviewComponent)(i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(PreviewMeshService), i0.ɵɵdirectiveInject(AnimationService), i0.ɵɵdirectiveInject(PreviewService), i0.ɵɵdirectiveInject(TimelineService), i0.ɵɵdirectiveInject(ProjectService)); }; }
4509
+ static { this.ɵfac = function PreviewComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PreviewComponent)(i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(PreviewMeshService), i0.ɵɵdirectiveInject(AnimationService), i0.ɵɵdirectiveInject(PreviewService), i0.ɵɵdirectiveInject(TimelineService), i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(i0.NgZone)); }; }
4180
4510
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PreviewComponent, selectors: [["lib-app-preview"]], viewQuery: function PreviewComponent_Query(rf, ctx) { if (rf & 1) {
4181
- i0.ɵɵviewQuery(_c0$6, 5);
4511
+ i0.ɵɵviewQuery(_c0$7, 5);
4182
4512
  } if (rf & 2) {
4183
4513
  let _t;
4184
4514
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.canvasRef = _t.first);
@@ -4191,7 +4521,7 @@ class PreviewComponent {
4191
4521
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PreviewComponent, [{
4192
4522
  type: Component,
4193
4523
  args: [{ selector: 'lib-app-preview', standalone: false, template: "<div class=\"h-100\" #canvas style=\"overflow: hidden\"></div>\n" }]
4194
- }], () => [{ type: FixtureService }, { type: PreviewMeshService }, { type: AnimationService }, { type: PreviewService }, { type: TimelineService }, { type: ProjectService }], { canvasRef: [{
4524
+ }], () => [{ type: FixtureService }, { type: PreviewMeshService }, { type: AnimationService }, { type: PreviewService }, { type: TimelineService }, { type: ProjectService }, { type: i0.NgZone }], { canvasRef: [{
4195
4525
  type: ViewChild,
4196
4526
  args: ['canvas']
4197
4527
  }], onResize: [{
@@ -4336,11 +4666,11 @@ class ProjectLoadService {
4336
4666
  projectFixture.fixtureUuid = fixture.uuid;
4337
4667
  this.projectService.project.presetFixtures.push(projectFixture);
4338
4668
  }
4339
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
4340
- for (let pixelKey of pixelKeys) {
4669
+ const pixels = this.fixtureService.fixtureGetUniquePixels(fixture);
4670
+ for (let pixelKey of pixels) {
4341
4671
  const projectFixture = new PresetFixture();
4342
4672
  projectFixture.fixtureUuid = fixture.uuid;
4343
- projectFixture.pixelKey = pixelKey;
4673
+ projectFixture.pixelKey = pixelKey.key;
4344
4674
  this.projectService.project.presetFixtures.push(projectFixture);
4345
4675
  }
4346
4676
  }
@@ -4352,11 +4682,11 @@ class ProjectLoadService {
4352
4682
  presetFixture.fixtureUuid = fixtureUuid;
4353
4683
  preset.fixtures.push(presetFixture);
4354
4684
  }
4355
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
4356
- for (let pixelKey of pixelKeys) {
4685
+ const pixels = this.fixtureService.fixtureGetUniquePixels(fixture);
4686
+ for (let pixel of pixels) {
4357
4687
  const presetFixture = new PresetFixture();
4358
4688
  presetFixture.fixtureUuid = fixtureUuid;
4359
- presetFixture.pixelKey = pixelKey;
4689
+ presetFixture.pixelKey = pixel.key;
4360
4690
  preset.fixtures.push(presetFixture);
4361
4691
  }
4362
4692
  }
@@ -5252,49 +5582,46 @@ function FixturePoolComponent_For_49_Template(rf, ctx) { if (rf & 1) {
5252
5582
  const _r5 = i0.ɵɵgetCurrentView();
5253
5583
  i0.ɵɵelementStart(0, "div", 52);
5254
5584
  i0.ɵɵlistener("click", function FixturePoolComponent_For_49_Template_div_click_0_listener() { const fixture_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.selectFixture(fixture_r6)); });
5255
- i0.ɵɵelementStart(1, "div", 5)(2, "div", 53);
5256
- i0.ɵɵelement(3, "i", 54);
5257
- i0.ɵɵelementEnd();
5258
- i0.ɵɵelementStart(4, "div", 55)(5, "p", 46);
5259
- i0.ɵɵelement(6, "span");
5260
- i0.ɵɵtext(7);
5585
+ i0.ɵɵelementStart(1, "div", 5)(2, "div", 53)(3, "p", 46);
5586
+ i0.ɵɵelement(4, "span");
5587
+ i0.ɵɵtext(5);
5261
5588
  i0.ɵɵelementEnd()();
5262
- i0.ɵɵelementStart(8, "div", 56)(9, "a", 48);
5263
- i0.ɵɵlistener("click", function FixturePoolComponent_For_49_Template_a_click_9_listener() { const fixture_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(); ctx_r2.removeFixture(fixture_r6); return i0.ɵɵresetView(false); });
5264
- i0.ɵɵelement(10, "i", 57);
5589
+ i0.ɵɵelementStart(6, "div", 54)(7, "a", 48);
5590
+ i0.ɵɵlistener("click", function FixturePoolComponent_For_49_Template_a_click_7_listener() { const fixture_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(); ctx_r2.removeFixture(fixture_r6); return i0.ɵɵresetView(false); });
5591
+ i0.ɵɵelement(8, "i", 55);
5265
5592
  i0.ɵɵelementEnd()()()();
5266
5593
  } if (rf & 2) {
5267
5594
  const fixture_r6 = ctx.$implicit;
5268
5595
  const ctx_r2 = i0.ɵɵnextContext();
5269
5596
  i0.ɵɵclassProp("active", fixture_r6 == ctx_r2.selectedFixture);
5270
- i0.ɵɵadvance(6);
5597
+ i0.ɵɵadvance(4);
5271
5598
  i0.ɵɵclassMap(i0.ɵɵinterpolate1("icon-", ctx_r2.fixtureService.getFixtureIconClass(ctx_r2.fixtureService.getProfileByUuid(fixture_r6.profileUuid)), " mr-1"));
5272
5599
  i0.ɵɵadvance();
5273
5600
  i0.ɵɵtextInterpolate1("", fixture_r6.name, " ");
5274
5601
  } }
5275
5602
  function FixturePoolComponent_For_67_Template(rf, ctx) { if (rf & 1) {
5276
5603
  const _r7 = i0.ɵɵgetCurrentView();
5277
- i0.ɵɵelementStart(0, "div", 58);
5604
+ i0.ɵɵelementStart(0, "div", 56);
5278
5605
  i0.ɵɵlistener("mousedown", function FixturePoolComponent_For_67_Template_div_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.channelMouseDown($event)); })("mouseover", function FixturePoolComponent_For_67_Template_div_mouseover_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.channelMouseOver($event)); });
5279
- i0.ɵɵelementStart(1, "div", 59)(2, "div", 60)(3, "small");
5606
+ i0.ɵɵelementStart(1, "div", 57)(2, "div", 58)(3, "small");
5280
5607
  i0.ɵɵtext(4, "\u00A0");
5281
5608
  i0.ɵɵelementEnd()()()();
5282
5609
  } if (rf & 2) {
5283
- const ɵ$index_161_r8 = ctx.$index;
5610
+ const ɵ$index_157_r8 = ctx.$index;
5284
5611
  const ctx_r2 = i0.ɵɵnextContext();
5285
- i0.ɵɵclassProp("dmx-channel-occupied", ctx_r2.channelOccupied(ɵ$index_161_r8))("dmx-channel-occupied-start", ctx_r2.channelOccupiedStart(ɵ$index_161_r8))("dmx-channel-occupied-end", ctx_r2.channelOccupiedEnd(ɵ$index_161_r8))("dmx-channel-selected", ctx_r2.channelSelected(ɵ$index_161_r8))("dmx-channel-overlapped", ctx_r2.channelOverlapped(ɵ$index_161_r8));
5286
- i0.ɵɵproperty("popover", i0.ɵɵinterpolate(ɵ$index_161_r8 + 1));
5287
- i0.ɵɵattribute("data-index", ɵ$index_161_r8);
5612
+ i0.ɵɵclassProp("dmx-channel-occupied", ctx_r2.channelOccupied(ɵ$index_157_r8))("dmx-channel-occupied-start", ctx_r2.channelOccupiedStart(ɵ$index_157_r8))("dmx-channel-occupied-end", ctx_r2.channelOccupiedEnd(ɵ$index_157_r8))("dmx-channel-selected", ctx_r2.channelSelected(ɵ$index_157_r8))("dmx-channel-overlapped", ctx_r2.channelOverlapped(ɵ$index_157_r8));
5613
+ i0.ɵɵproperty("popover", i0.ɵɵinterpolate(ɵ$index_157_r8 + 1));
5614
+ i0.ɵɵattribute("data-index", ɵ$index_157_r8);
5288
5615
  i0.ɵɵadvance();
5289
5616
  i0.ɵɵclassProp("dmx-channel-text-visible", ctx_r2.showChannelNumbers);
5290
- i0.ɵɵattribute("data-index", ɵ$index_161_r8);
5617
+ i0.ɵɵattribute("data-index", ɵ$index_157_r8);
5291
5618
  i0.ɵɵadvance();
5292
- i0.ɵɵattribute("data-index", ɵ$index_161_r8);
5619
+ i0.ɵɵattribute("data-index", ɵ$index_157_r8);
5293
5620
  i0.ɵɵadvance();
5294
- i0.ɵɵattribute("data-index", ɵ$index_161_r8);
5621
+ i0.ɵɵattribute("data-index", ɵ$index_157_r8);
5295
5622
  } }
5296
5623
  function FixturePoolComponent_Conditional_69_For_14_Template(rf, ctx) { if (rf & 1) {
5297
- i0.ɵɵelementStart(0, "option", 66);
5624
+ i0.ɵɵelementStart(0, "option", 64);
5298
5625
  i0.ɵɵtext(1);
5299
5626
  i0.ɵɵpipe(2, "translate");
5300
5627
  i0.ɵɵelementEnd();
@@ -5306,43 +5633,43 @@ function FixturePoolComponent_Conditional_69_For_14_Template(rf, ctx) { if (rf &
5306
5633
  i0.ɵɵtextInterpolate3(" ", mode_r10.name, " - ", ctx_r2.fixtureService.getModeChannelCount(ctx_r2.selectedFixtureProfile, mode_r10), " ", i0.ɵɵpipeBind1(2, 4, "designer.fixture-pool.channels"), " ");
5307
5634
  } }
5308
5635
  function FixturePoolComponent_Conditional_69_For_22_Template(rf, ctx) { if (rf & 1) {
5309
- i0.ɵɵelementStart(0, "option", 66);
5636
+ i0.ɵɵelementStart(0, "option", 64);
5310
5637
  i0.ɵɵtext(1);
5311
5638
  i0.ɵɵelementEnd();
5312
5639
  } if (rf & 2) {
5313
- const ɵ$index_211_r11 = ctx.$index;
5314
- i0.ɵɵproperty("ngValue", ɵ$index_211_r11);
5640
+ const ɵ$index_207_r11 = ctx.$index;
5641
+ i0.ɵɵproperty("ngValue", ɵ$index_207_r11);
5315
5642
  i0.ɵɵadvance();
5316
- i0.ɵɵtextInterpolate1(" ", ɵ$index_211_r11 + 1, " ");
5643
+ i0.ɵɵtextInterpolate1(" ", ɵ$index_207_r11 + 1, " ");
5317
5644
  } }
5318
5645
  function FixturePoolComponent_Conditional_69_Template(rf, ctx) { if (rf & 1) {
5319
5646
  const _r9 = i0.ɵɵgetCurrentView();
5320
- i0.ɵɵelementStart(0, "div")(1, "div", 20)(2, "label", 61);
5647
+ i0.ɵɵelementStart(0, "div")(1, "div", 20)(2, "label", 59);
5321
5648
  i0.ɵɵtext(3);
5322
5649
  i0.ɵɵpipe(4, "translate");
5323
5650
  i0.ɵɵelementEnd();
5324
- i0.ɵɵelementStart(5, "div", 62)(6, "input", 63);
5651
+ i0.ɵɵelementStart(5, "div", 60)(6, "input", 61);
5325
5652
  i0.ɵɵtwoWayListener("ngModelChange", function FixturePoolComponent_Conditional_69_Template_input_ngModelChange_6_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.selectedFixture.name, $event) || (ctx_r2.selectedFixture.name = $event); return i0.ɵɵresetView($event); });
5326
5653
  i0.ɵɵelementEnd()()();
5327
- i0.ɵɵelementStart(7, "div", 20)(8, "label", 64);
5654
+ i0.ɵɵelementStart(7, "div", 20)(8, "label", 62);
5328
5655
  i0.ɵɵtext(9);
5329
5656
  i0.ɵɵpipe(10, "translate");
5330
5657
  i0.ɵɵelementEnd();
5331
- i0.ɵɵelementStart(11, "div", 62)(12, "select", 65);
5658
+ i0.ɵɵelementStart(11, "div", 60)(12, "select", 63);
5332
5659
  i0.ɵɵlistener("ngModelChange", function FixturePoolComponent_Conditional_69_Template_select_ngModelChange_12_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.selectedFixture.modeShortName = $event); });
5333
- i0.ɵɵrepeaterCreate(13, FixturePoolComponent_Conditional_69_For_14_Template, 3, 6, "option", 66, i0.ɵɵrepeaterTrackByIdentity);
5660
+ i0.ɵɵrepeaterCreate(13, FixturePoolComponent_Conditional_69_For_14_Template, 3, 6, "option", 64, i0.ɵɵrepeaterTrackByIdentity);
5334
5661
  i0.ɵɵelementEnd()()();
5335
- i0.ɵɵelementStart(15, "div", 20)(16, "label", 64);
5662
+ i0.ɵɵelementStart(15, "div", 20)(16, "label", 62);
5336
5663
  i0.ɵɵtext(17);
5337
5664
  i0.ɵɵpipe(18, "translate");
5338
5665
  i0.ɵɵelementEnd();
5339
- i0.ɵɵelementStart(19, "div", 62)(20, "select", 65);
5666
+ i0.ɵɵelementStart(19, "div", 60)(20, "select", 63);
5340
5667
  i0.ɵɵlistener("ngModelChange", function FixturePoolComponent_Conditional_69_Template_select_ngModelChange_20_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.selectedFixture.dmxFirstChannel = $event); });
5341
- i0.ɵɵrepeaterCreate(21, FixturePoolComponent_Conditional_69_For_22_Template, 2, 2, "option", 66, i0.ɵɵrepeaterTrackByIdentity);
5668
+ i0.ɵɵrepeaterCreate(21, FixturePoolComponent_Conditional_69_For_22_Template, 2, 2, "option", 64, i0.ɵɵrepeaterTrackByIndex);
5342
5669
  i0.ɵɵelementEnd()()();
5343
5670
  i0.ɵɵelementStart(23, "div", 20);
5344
- i0.ɵɵelement(24, "label", 64);
5345
- i0.ɵɵelementStart(25, "div", 62)(26, "button", 42);
5671
+ i0.ɵɵelement(24, "label", 62);
5672
+ i0.ɵɵelementStart(25, "div", 60)(26, "button", 42);
5346
5673
  i0.ɵɵlistener("click", function FixturePoolComponent_Conditional_69_Template_button_click_26_listener() { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.addCopy(ctx_r2.selectedFixture)); });
5347
5674
  i0.ɵɵtext(27);
5348
5675
  i0.ɵɵpipe(28, "translate");
@@ -5384,7 +5711,7 @@ class FixturePoolComponent {
5384
5711
  this.dmxChannels = [];
5385
5712
  this.showChannelNumbers = false;
5386
5713
  this.onClose = new Subject();
5387
- this.fixturePool = [...this.projectService.project.fixtures];
5714
+ this.fixturePool = structuredClone(this.projectService.project.fixtures);
5388
5715
  if (this.fixturePool.length > 0) {
5389
5716
  this.selectFixture(this.fixturePool[0]);
5390
5717
  }
@@ -5604,12 +5931,11 @@ class FixturePoolComponent {
5604
5931
  return;
5605
5932
  }
5606
5933
  }
5607
- this.projectService.project.fixtures = this.fixturePool;
5608
- // remove deleted fixtures from preset fixtures
5934
+ // remove deleted fixtures/pixel keys from preset fixtures
5609
5935
  for (let i = this.projectService.project.presetFixtures.length - 1; i >= 0; i--) {
5610
5936
  let found = false;
5611
5937
  const presetFixture = this.projectService.project.presetFixtures[i];
5612
- for (let fixture of this.projectService.project.fixtures) {
5938
+ for (let fixture of this.fixturePool) {
5613
5939
  if (presetFixture.fixtureUuid === fixture.uuid) {
5614
5940
  found = true;
5615
5941
  break;
@@ -5619,8 +5945,23 @@ class FixturePoolComponent {
5619
5945
  this.projectService.project.presetFixtures.splice(i, 1);
5620
5946
  }
5621
5947
  }
5622
- // add the new fixtures to the preset fixtures
5623
- for (let fixture of this.projectService.project.fixtures) {
5948
+ // If a mode is changed, we need to re-add the fixture to make sure, all pixel keys
5949
+ // are removed/added
5950
+ for (let projectFixture of this.projectService.project.fixtures) {
5951
+ for (let poolFixture of this.fixturePool) {
5952
+ if (poolFixture.profileUuid === projectFixture.profileUuid && poolFixture.modeShortName != projectFixture.modeShortName) {
5953
+ // the mode has changed -> remove it from the presetfixtures
5954
+ for (let i = this.projectService.project.presetFixtures.length - 1; i >= 0; i--) {
5955
+ const presetFixture = this.projectService.project.presetFixtures[i];
5956
+ if (presetFixture.fixtureUuid === poolFixture.uuid) {
5957
+ this.projectService.project.presetFixtures.splice(i, 1);
5958
+ }
5959
+ }
5960
+ }
5961
+ }
5962
+ }
5963
+ // add the new fixtures/pixel keys to the preset fixtures
5964
+ for (let fixture of this.fixturePool) {
5624
5965
  let found = false;
5625
5966
  for (let presetFixture of this.projectService.project.presetFixtures) {
5626
5967
  if (presetFixture.fixtureUuid === fixture.uuid) {
@@ -5634,15 +5975,16 @@ class FixturePoolComponent {
5634
5975
  presetFixture.fixtureUuid = fixture.uuid;
5635
5976
  this.projectService.project.presetFixtures.push(presetFixture);
5636
5977
  }
5637
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
5638
- for (let pixelKey of pixelKeys) {
5978
+ const pixels = this.fixtureService.fixtureGetUniquePixels(fixture);
5979
+ for (let pixel of pixels) {
5639
5980
  const presetFixture = new PresetFixture();
5640
5981
  presetFixture.fixtureUuid = fixture.uuid;
5641
- presetFixture.pixelKey = pixelKey;
5982
+ presetFixture.pixelKey = pixel.key;
5642
5983
  this.projectService.project.presetFixtures.push(presetFixture);
5643
5984
  }
5644
5985
  }
5645
5986
  }
5987
+ this.projectService.project.fixtures = this.fixturePool;
5646
5988
  this.fixtureService.updateCachedFixtures();
5647
5989
  this.presetService.removeDeletedFixtures();
5648
5990
  this.previewService.updateFixtureSetup();
@@ -5693,7 +6035,7 @@ class FixturePoolComponent {
5693
6035
  static { this.ɵfac = function FixturePoolComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixturePoolComponent)(i0.ɵɵdirectiveInject(i1$1.BsModalRef), i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(UuidService), i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(PreviewService), i0.ɵɵdirectiveInject(i4.TranslateService), i0.ɵɵdirectiveInject(i3$1.ToastrService), i0.ɵɵdirectiveInject(PresetService), i0.ɵɵdirectiveInject(ConfigService), i0.ɵɵdirectiveInject(i1$1.BsModalService)); }; }
5694
6036
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FixturePoolComponent, selectors: [["lib-app-fixture-pool"]], hostBindings: function FixturePoolComponent_HostBindings(rf, ctx) { if (rf & 1) {
5695
6037
  i0.ɵɵlistener("mouseup", function FixturePoolComponent_mouseup_HostBindingHandler($event) { return ctx.mouseUp($event); }, i0.ɵɵresolveWindow)("keydown.enter", function FixturePoolComponent_keydown_enter_HostBindingHandler($event) { return ctx.handleKeyboardEvent($event); }, i0.ɵɵresolveDocument);
5696
- } }, standalone: false, decls: 77, vars: 35, consts: [[1, "modal-header"], [1, "modal-title", "pull-left"], ["type", "button", "aria-label", "Close", 1, "close", "pull-right", 3, "click"], ["aria-hidden", "true"], [1, "modal-body"], [1, "row"], [1, "col-6"], ["type", "text", "id", "searchSet", 1, "form-control", "input-block", "mb-3", 3, "input", "placeholder"], [1, "card", "border-secondary", "w-100", 2, "height", "200px"], [1, "card-body", "p-0", "w-100"], [1, "list-group", "list-group-flush", "w-100"], [1, "list-group-item", "flex-column", "align-items-start", "d-flex"], [1, "list-group-item"], [1, "d-inline-block", "mb-0", "mt-2"], ["href", "https://open-fixture-library.org/", "target", "_blank"], ["href", "https://open-fixture-library.org/fixture-editor", "target", "_blank", "role", "button", 1, "mt-2", "btn", "btn-secondary", "btn-sm", "float-right"], ["aria-hidden", "true", 1, "fa", "fa-file-o"], ["type", "button", "role", "button", 1, "mr-2", "mt-2", "btn", "btn-secondary", "btn-sm", "float-right", 3, "disabled"], ["type", "button", "role", "button", 1, "btn", "btn-secondary", 3, "click"], ["aria-hidden", "true", 1, "fa", "fa-plus"], [1, "form-group", "row"], [1, "col-lg-4", "col-form-label"], [1, "col-lg-8"], [1, "custom-select"], [1, "card", "border-secondary", 2, "height", "235px"], [1, "card-body", "p-0"], [1, "list-group", "list-group-flush", 3, "sortablejs"], [1, "list-group-item", 2, "cursor", "pointer", 3, "active"], [1, "row", "mt-3"], [1, "col"], ["id", "myTab", "role", "tablist", 1, "nav", "nav-tabs", "mb-4"], [1, "nav-item"], ["id", "sets-tab", "data-toggle", "tab", "href", "#dmx", "role", "tab", 1, "nav-link", "active"], ["aria-hidden", "true", 1, "icon-ola"], ["id", "compositions-tab", "data-toggle", "tab", "href", "#settings", "role", "tab", 1, "nav-link"], ["aria-hidden", "true", 1, "fa", "fa-cog"], ["id", "myTabContent", 1, "tab-content"], ["id", "dmx", "role", "tabpanel", 1, "tab-pane", "show", "active"], ["placement", "top", "triggers", "mouseenter:mouseleave", 1, "dmx-channel", 3, "dmx-channel-occupied", "dmx-channel-occupied-start", "dmx-channel-occupied-end", "dmx-channel-selected", "dmx-channel-overlapped", "popover"], ["id", "settings", "role", "tabpanel", 1, "tab-pane"], [1, "modal-footer"], ["href", "#", "role", "button", 1, "mr-3", "my-auto", 3, "click"], ["type", "button", 1, "btn", "btn-primary", 3, "click"], [1, "mb-0", "mx-auto"], [1, "fa", "fa-spinner", "fa-pulse", "fa-fw"], [1, "col-10", "col-auto", "my-auto", "pl-2"], [1, "mb-0"], [1, "col-2", "my-auto"], ["href", "#", "role", "button", 1, "btn", "btn-primary", "btn-sm", "float-right", 3, "click"], ["type", "button", "role", "button", 1, "mr-2", "mt-2", "btn", "btn-secondary", "btn-sm", "float-right", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa", "fa-spinner", "fa-pulse", "fa-fw"], ["aria-hidden", "true", 1, "fa", "fa-download", "fa-fw"], [1, "list-group-item", 2, "cursor", "pointer", 3, "click"], [1, "col-auto", "list-sort-handle", "my-auto", 2, "cursor", "move", "cursor", "-webkit-grabbing"], ["aria-hidden", "true", 1, "fa", "fa-bars"], [1, "col-auto", "flex-grow", "pl-0", "my-auto"], [1, "col-auto", "my-auto"], ["aria-hidden", "true", 1, "fa", "fa-minus"], ["placement", "top", "triggers", "mouseenter:mouseleave", 1, "dmx-channel", 3, "mousedown", "mouseover", "popover"], [1, "d-flex", "w-100", "dmx-channel-text"], [1, "m-auto", 2, "font-size", "11px"], ["for", "fixtureName", 1, "col-lg-3", "col-form-label"], [1, "col-lg-9"], ["type", "text", "maxlength", "200", "id", "fixtureName", 1, "form-control", 3, "ngModelChange", "ngModel"], [1, "col-lg-3", "col-form-label"], [1, "custom-select", 3, "ngModelChange", "ngModel"], [3, "ngValue"]], template: function FixturePoolComponent_Template(rf, ctx) { if (rf & 1) {
6038
+ } }, standalone: false, decls: 77, vars: 34, consts: [[1, "modal-header"], [1, "modal-title", "pull-left"], ["type", "button", "aria-label", "Close", 1, "close", "pull-right", 3, "click"], ["aria-hidden", "true"], [1, "modal-body"], [1, "row"], [1, "col-6"], ["type", "text", "id", "searchSet", 1, "form-control", "input-block", "mb-3", 3, "input", "placeholder"], [1, "card", "border-secondary", "w-100", 2, "height", "200px"], [1, "card-body", "p-0", "w-100"], [1, "list-group", "list-group-flush", "w-100"], [1, "list-group-item", "flex-column", "align-items-start", "d-flex"], [1, "list-group-item"], [1, "d-inline-block", "mb-0", "mt-2"], ["href", "https://open-fixture-library.org/", "target", "_blank"], ["href", "https://open-fixture-library.org/fixture-editor", "target", "_blank", "role", "button", 1, "mt-2", "btn", "btn-secondary", "btn-sm", "float-right"], ["aria-hidden", "true", 1, "fa", "fa-file-o"], ["type", "button", "role", "button", 1, "mr-2", "mt-2", "btn", "btn-secondary", "btn-sm", "float-right", 3, "disabled"], ["type", "button", "role", "button", 1, "btn", "btn-secondary", 3, "click"], ["aria-hidden", "true", 1, "fa", "fa-plus"], [1, "form-group", "row"], [1, "col-lg-4", "col-form-label"], [1, "col-lg-8"], [1, "custom-select"], [1, "card", "border-secondary", 2, "height", "235px"], [1, "card-body", "p-0"], [1, "list-group", "list-group-flush"], [1, "list-group-item", 2, "cursor", "pointer", 3, "active"], [1, "row", "mt-3"], [1, "col"], ["id", "myTab", "role", "tablist", 1, "nav", "nav-tabs", "mb-4"], [1, "nav-item"], ["id", "sets-tab", "data-toggle", "tab", "href", "#dmx", "role", "tab", 1, "nav-link", "active"], ["aria-hidden", "true", 1, "icon-ola"], ["id", "compositions-tab", "data-toggle", "tab", "href", "#settings", "role", "tab", 1, "nav-link"], ["aria-hidden", "true", 1, "fa", "fa-cog"], ["id", "myTabContent", 1, "tab-content"], ["id", "dmx", "role", "tabpanel", 1, "tab-pane", "show", "active"], ["placement", "top", "triggers", "mouseenter:mouseleave", 1, "dmx-channel", 3, "dmx-channel-occupied", "dmx-channel-occupied-start", "dmx-channel-occupied-end", "dmx-channel-selected", "dmx-channel-overlapped", "popover"], ["id", "settings", "role", "tabpanel", 1, "tab-pane"], [1, "modal-footer"], ["href", "#", "role", "button", 1, "mr-3", "my-auto", 3, "click"], ["type", "button", 1, "btn", "btn-primary", 3, "click"], [1, "mb-0", "mx-auto"], [1, "fa", "fa-spinner", "fa-pulse", "fa-fw"], [1, "col-10", "col-auto", "my-auto", "pl-2"], [1, "mb-0"], [1, "col-2", "my-auto"], ["href", "#", "role", "button", 1, "btn", "btn-primary", "btn-sm", "float-right", 3, "click"], ["type", "button", "role", "button", 1, "mr-2", "mt-2", "btn", "btn-secondary", "btn-sm", "float-right", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa", "fa-spinner", "fa-pulse", "fa-fw"], ["aria-hidden", "true", 1, "fa", "fa-download", "fa-fw"], [1, "list-group-item", 2, "cursor", "pointer", 3, "click"], [1, "col-auto", "flex-grow", "pl-0", "my-auto"], [1, "col-auto", "my-auto"], ["aria-hidden", "true", 1, "fa", "fa-minus"], ["placement", "top", "triggers", "mouseenter:mouseleave", 1, "dmx-channel", 3, "mousedown", "mouseover", "popover"], [1, "d-flex", "w-100", "dmx-channel-text"], [1, "m-auto", 2, "font-size", "11px"], ["for", "fixtureName", 1, "col-lg-3", "col-form-label"], [1, "col-lg-9"], ["type", "text", "maxlength", "200", "id", "fixtureName", 1, "form-control", 3, "ngModelChange", "ngModel"], [1, "col-lg-3", "col-form-label"], [1, "custom-select", 3, "ngModelChange", "ngModel"], [3, "ngValue"]], template: function FixturePoolComponent_Template(rf, ctx) { if (rf & 1) {
5697
6039
  i0.ɵɵelementStart(0, "div", 0)(1, "h4", 1);
5698
6040
  i0.ɵɵtext(2);
5699
6041
  i0.ɵɵpipe(3, "translate");
@@ -5738,7 +6080,7 @@ class FixturePoolComponent {
5738
6080
  i0.ɵɵtext(44, "Universe 1");
5739
6081
  i0.ɵɵelementEnd()()()()()();
5740
6082
  i0.ɵɵelementStart(45, "div", 24)(46, "div", 25)(47, "div", 26);
5741
- i0.ɵɵrepeaterCreate(48, FixturePoolComponent_For_49_Template, 11, 6, "div", 27, i0.ɵɵrepeaterTrackByIdentity);
6083
+ i0.ɵɵrepeaterCreate(48, FixturePoolComponent_For_49_Template, 9, 6, "div", 27, i0.ɵɵrepeaterTrackByIdentity);
5742
6084
  i0.ɵɵelementEnd()()()()();
5743
6085
  i0.ɵɵelementStart(50, "div", 28)(51, "div", 29)(52, "ul", 30)(53, "li", 31)(54, "a", 32);
5744
6086
  i0.ɵɵelement(55, "i", 33);
@@ -5751,7 +6093,7 @@ class FixturePoolComponent {
5751
6093
  i0.ɵɵpipe(62, "translate");
5752
6094
  i0.ɵɵelementEnd()()();
5753
6095
  i0.ɵɵelementStart(63, "div", 36)(64, "div", 37)(65, "div");
5754
- i0.ɵɵrepeaterCreate(66, FixturePoolComponent_For_67_Template, 5, 18, "div", 38, i0.ɵɵrepeaterTrackByIdentity);
6096
+ i0.ɵɵrepeaterCreate(66, FixturePoolComponent_For_67_Template, 5, 18, "div", 38, i0.ɵɵrepeaterTrackByIndex);
5755
6097
  i0.ɵɵelementEnd()();
5756
6098
  i0.ɵɵelementStart(68, "div", 39);
5757
6099
  i0.ɵɵconditionalCreate(69, FixturePoolComponent_Conditional_69_Template, 29, 15, "div");
@@ -5768,42 +6110,40 @@ class FixturePoolComponent {
5768
6110
  i0.ɵɵelementEnd()();
5769
6111
  } if (rf & 2) {
5770
6112
  i0.ɵɵadvance(2);
5771
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(3, 14, "designer.fixture-pool.title"));
6113
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(3, 13, "designer.fixture-pool.title"));
5772
6114
  i0.ɵɵadvance(8);
5773
- i0.ɵɵproperty("placeholder", i0.ɵɵinterpolate(i0.ɵɵpipeBind1(11, 16, "designer.fixture-pool.search-profiles")));
6115
+ i0.ɵɵproperty("placeholder", i0.ɵɵinterpolate(i0.ɵɵpipeBind1(11, 15, "designer.fixture-pool.search-profiles")));
5774
6116
  i0.ɵɵadvance(5);
5775
6117
  i0.ɵɵconditional(ctx.loadingProfiles ? 15 : -1);
5776
6118
  i0.ɵɵadvance();
5777
- i0.ɵɵrepeater(i0.ɵɵpipeBind2(18, 18, ctx.filteredProfiles, "uuid"));
6119
+ i0.ɵɵrepeater(i0.ɵɵpipeBind2(18, 17, ctx.filteredProfiles, "uuid"));
5778
6120
  i0.ɵɵadvance(10);
5779
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(27, 21, "designer.fixture-pool.add-profile"), " ");
6121
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(27, 20, "designer.fixture-pool.add-profile"), " ");
5780
6122
  i0.ɵɵadvance(2);
5781
6123
  i0.ɵɵconditional(ctx.configService.localProfiles ? 28 : -1);
5782
6124
  i0.ɵɵadvance(6);
5783
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(35, 23, "designer.fixture-pool.create-fixture-from-profile-file"), " ");
6125
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(35, 22, "designer.fixture-pool.create-fixture-from-profile-file"), " ");
5784
6126
  i0.ɵɵadvance(5);
5785
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(40, 25, "designer.fixture-pool.universe"));
5786
- i0.ɵɵadvance(8);
5787
- i0.ɵɵproperty("sortablejs", ctx.fixturePool);
5788
- i0.ɵɵadvance();
6127
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(40, 24, "designer.fixture-pool.universe"));
6128
+ i0.ɵɵadvance(9);
5789
6129
  i0.ɵɵrepeater(ctx.fixturePool);
5790
6130
  i0.ɵɵadvance(8);
5791
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(57, 27, "designer.fixture-pool.dmx"), " ");
6131
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(57, 26, "designer.fixture-pool.dmx"), " ");
5792
6132
  i0.ɵɵadvance(5);
5793
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(62, 29, "designer.fixture-pool.settings"), " ");
6133
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(62, 28, "designer.fixture-pool.settings"), " ");
5794
6134
  i0.ɵɵadvance(5);
5795
6135
  i0.ɵɵrepeater(ctx.dmxChannels);
5796
6136
  i0.ɵɵadvance(3);
5797
6137
  i0.ɵɵconditional(ctx.selectedFixture && ctx.selectedFixtureProfile ? 69 : -1);
5798
6138
  i0.ɵɵadvance(3);
5799
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(73, 31, "designer.misc.cancel"), " ");
6139
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(73, 30, "designer.misc.cancel"), " ");
5800
6140
  i0.ɵɵadvance(3);
5801
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(76, 33, "designer.misc.ok"));
5802
- } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.MaxLengthValidator, i3$2.NgModel, i11.PopoverDirective, i7.SortablejsDirective, i4.TranslatePipe, ArraySortPipe], styles: [".list-group-item[_ngcontent-%COMP%]{padding:.3rem 1.25rem}.dmx-channel[_ngcontent-%COMP%]{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px}.dmx-channel-occupied[_ngcontent-%COMP%]{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start[_ngcontent-%COMP%]{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end[_ngcontent-%COMP%]{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected[_ngcontent-%COMP%]{background-color:#fd7e14}.dmx-channel-overlapped[_ngcontent-%COMP%]{background-color:red;border:1px solid red;cursor:move}.card[_ngcontent-%COMP%]{overflow:auto}"] }); }
6141
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(76, 32, "designer.misc.ok"));
6142
+ } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.MaxLengthValidator, i3$2.NgModel, i11.PopoverDirective, i4.TranslatePipe, ArraySortPipe], styles: [".list-group-item[_ngcontent-%COMP%]{padding:.3rem 1.25rem}.dmx-channel[_ngcontent-%COMP%]{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px}.dmx-channel-occupied[_ngcontent-%COMP%]{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start[_ngcontent-%COMP%]{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end[_ngcontent-%COMP%]{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected[_ngcontent-%COMP%]{background-color:#fd7e14}.dmx-channel-overlapped[_ngcontent-%COMP%]{background-color:red;border:1px solid red;cursor:move}.card[_ngcontent-%COMP%]{overflow:auto}"] }); }
5803
6143
  }
5804
6144
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixturePoolComponent, [{
5805
6145
  type: Component,
5806
- args: [{ selector: 'lib-app-fixture-pool', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel(); (false)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-6\">\n <!-- Search for profiles -->\n <input\n type=\"text\"\n class=\"form-control input-block mb-3\"\n id=\"searchSet\"\n placeholder=\"{{ 'designer.fixture-pool.search-profiles' | translate }}\"\n (input)=\"searchExpression = $event.target.value; filterProfiles()\"\n />\n\n <!-- Profiles -->\n <div class=\"card border-secondary w-100\" style=\"height: 200px\">\n <div class=\"card-body p-0 w-100\">\n <div class=\"list-group list-group-flush w-100\">\n @if (loadingProfiles) {\n <a class=\"list-group-item flex-column align-items-start d-flex\">\n <p class=\"mb-0 mx-auto\">\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\n </p>\n </a>\n } @for (profile of filteredProfiles | sort : 'uuid'; track profile) {\n <div class=\"list-group-item\">\n <div class=\"row\">\n <div class=\"col-10 col-auto my-auto pl-2\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(profile) }} mr-1\"></span> {{ profile.manufacturerName }} -\n {{ profile.name }}\n </p>\n </div>\n <div class=\"col-2 my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"addFixture(profile); (false)\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <p class=\"d-inline-block mb-0 mt-2\">\n Powered by the <a href=\"https://open-fixture-library.org/\" target=\"_blank\">Open Fixture Library</a>\n </p>\n <a\n class=\"mt-2 btn btn-secondary btn-sm float-right\"\n href=\"https://open-fixture-library.org/fixture-editor\"\n target=\"_blank\"\n role=\"button\"\n >\n <i class=\"fa fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.add-profile' | translate }}\n </a>\n @if (configService.localProfiles) {\n <button\n type=\"button\"\n class=\"mr-2 mt-2 btn btn-secondary btn-sm float-right\"\n (click)=\"updateProfiles(); (false)\"\n [disabled]=\"updatingProfiles\"\n role=\"button\"\n >\n @if (updatingProfiles) {\n <i class=\"fa fa-spinner fa-pulse fa-fw\" aria-hidden=\"true\"></i>\n } @if (!updatingProfiles) {\n <i class=\"fa fa-download fa-fw\" aria-hidden=\"true\"></i>\n }\n {{ 'designer.fixture-pool.update-profiles' | translate }}\n </button>\n }\n </div>\n </div>\n <div class=\"col-6\">\n <div class=\"row\">\n <!-- Add fixture from local profile -->\n <div class=\"col-6\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"createFixtureFromProfileFile()\" role=\"button\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.create-fixture-from-profile-file' | translate }}\n </button>\n </div>\n\n <!-- Universe -->\n <div class=\"col-6\">\n <div class=\"form-group row\">\n <label class=\"col-lg-4 col-form-label\">{{ 'designer.fixture-pool.universe' | translate }}</label>\n <div class=\"col-lg-8\">\n <select class=\"custom-select\">\n <option>Universe 1</option>\n </select>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fixtures -->\n <div class=\"card border-secondary\" style=\"height: 235px\">\n <div class=\"card-body p-0\">\n <div class=\"list-group list-group-flush\" [sortablejs]=\"fixturePool\">\n @for (fixture of fixturePool; track fixture) {\n <div\n class=\"list-group-item\"\n [class.active]=\"fixture == selectedFixture\"\n (click)=\"selectFixture(fixture)\"\n style=\"cursor: pointer\"\n >\n <div class=\"row\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"col-auto flex-grow pl-0 my-auto\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(fixtureService.getProfileByUuid(fixture.profileUuid)) }} mr-1\">\n </span\n >{{ fixture.name }}\n </p>\n </div>\n <div class=\"col-auto my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"removeFixture(fixture); (false)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col\">\n <!-- Tab navigation -->\n <ul class=\"nav nav-tabs mb-4\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\">\n <a class=\"nav-link active\" id=\"sets-tab\" data-toggle=\"tab\" href=\"#dmx\" role=\"tab\">\n <i class=\"icon-ola\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.dmx' | translate }}\n </a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link\" id=\"compositions-tab\" data-toggle=\"tab\" href=\"#settings\" role=\"tab\">\n <i class=\"fa fa-cog\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.settings' | translate }}\n </a>\n </li>\n </ul>\n\n <!-- Tab content -->\n <div class=\"tab-content\" id=\"myTabContent\">\n <!-- DMX universe overview-->\n <div class=\"tab-pane show active\" id=\"dmx\" role=\"tabpanel\">\n <!-- Show all channel numbers -->\n <!-- <div class=\"form-group row\">\n <div class=\"col-auto flex-grow\"></div>\n <div class=\"col-lg-3 d-flex\">\n <div class=\"form-check my-auto ml-auto\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"showChannelNumbers\" [ngModel]=\"showChannelNumbers\"\n (ngModelChange)=\"showChannelNumbers = $event\">\n <label class=\"form-check-label\" for=\"showChannelNumbers\">Show channels</label>\n </div>\n </div>\n </div> -->\n\n <!-- List of DMX channels -->\n <div>\n @for (channel of dmxChannels; track channel; let i = $index) {\n <div\n class=\"dmx-channel\"\n [class.dmx-channel-occupied]=\"channelOccupied(i)\"\n [class.dmx-channel-occupied-start]=\"channelOccupiedStart(i)\"\n [class.dmx-channel-occupied-end]=\"channelOccupiedEnd(i)\"\n [class.dmx-channel-selected]=\"channelSelected(i)\"\n [class.dmx-channel-overlapped]=\"channelOverlapped(i)\"\n (mousedown)=\"channelMouseDown($event)\"\n (mouseover)=\"channelMouseOver($event)\"\n [attr.data-index]=\"i\"\n popover=\"{{ i + 1 }}\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n >\n <div class=\"d-flex w-100 dmx-channel-text\" [attr.data-index]=\"i\" [class.dmx-channel-text-visible]=\"showChannelNumbers\">\n <div class=\"m-auto\" style=\"font-size: 11px\" [attr.data-index]=\"i\"><small [attr.data-index]=\"i\">&nbsp;</small></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Fixture settings -->\n <div class=\"tab-pane\" id=\"settings\" role=\"tabpanel\">\n @if (selectedFixture && selectedFixtureProfile) {\n <div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\" for=\"fixtureName\">{{ 'designer.fixture-pool.fixture-name' | translate }}</label>\n <div class=\"col-lg-9\">\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"selectedFixture.name\" maxlength=\"200\" id=\"fixtureName\" />\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.mode' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.modeShortName\"\n (ngModelChange)=\"selectedFixture.modeShortName = $event\"\n >\n @for (mode of selectedFixtureProfile.modes; track mode) {\n <option [ngValue]=\"mode.shortName || mode.name\">\n {{ mode.name }}\n - {{ fixtureService.getModeChannelCount(selectedFixtureProfile, mode) }}\n {{ 'designer.fixture-pool.channels' | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.first-channel' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.dmxFirstChannel\"\n (ngModelChange)=\"selectedFixture.dmxFirstChannel = $event\"\n >\n @for (channel of dmxChannels; track channel; let i = $index) {\n <option [ngValue]=\"i\">\n {{ i + 1 }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\"></label>\n <div class=\"col-lg-9\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"addCopy(selectedFixture)\">\n {{ 'designer.fixture.add-copy' | translate }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [".list-group-item{padding:.3rem 1.25rem}.dmx-channel{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px}.dmx-channel-occupied{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected{background-color:#fd7e14}.dmx-channel-overlapped{background-color:red;border:1px solid red;cursor:move}.card{overflow:auto}\n"] }]
6146
+ args: [{ selector: 'lib-app-fixture-pool', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel(); (false)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-6\">\n <!-- Search for profiles -->\n <input\n type=\"text\"\n class=\"form-control input-block mb-3\"\n id=\"searchSet\"\n placeholder=\"{{ 'designer.fixture-pool.search-profiles' | translate }}\"\n (input)=\"searchExpression = $event.target.value; filterProfiles()\"\n />\n\n <!-- Profiles -->\n <div class=\"card border-secondary w-100\" style=\"height: 200px\">\n <div class=\"card-body p-0 w-100\">\n <div class=\"list-group list-group-flush w-100\">\n @if (loadingProfiles) {\n <a class=\"list-group-item flex-column align-items-start d-flex\">\n <p class=\"mb-0 mx-auto\">\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\n </p>\n </a>\n } @for (profile of filteredProfiles | sort : 'uuid'; track profile) {\n <div class=\"list-group-item\">\n <div class=\"row\">\n <div class=\"col-10 col-auto my-auto pl-2\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(profile) }} mr-1\"></span> {{ profile.manufacturerName }} -\n {{ profile.name }}\n </p>\n </div>\n <div class=\"col-2 my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"addFixture(profile); (false)\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <p class=\"d-inline-block mb-0 mt-2\">\n Powered by the <a href=\"https://open-fixture-library.org/\" target=\"_blank\">Open Fixture Library</a>\n </p>\n <a\n class=\"mt-2 btn btn-secondary btn-sm float-right\"\n href=\"https://open-fixture-library.org/fixture-editor\"\n target=\"_blank\"\n role=\"button\"\n >\n <i class=\"fa fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.add-profile' | translate }}\n </a>\n @if (configService.localProfiles) {\n <button\n type=\"button\"\n class=\"mr-2 mt-2 btn btn-secondary btn-sm float-right\"\n (click)=\"updateProfiles(); (false)\"\n [disabled]=\"updatingProfiles\"\n role=\"button\"\n >\n @if (updatingProfiles) {\n <i class=\"fa fa-spinner fa-pulse fa-fw\" aria-hidden=\"true\"></i>\n } @if (!updatingProfiles) {\n <i class=\"fa fa-download fa-fw\" aria-hidden=\"true\"></i>\n }\n {{ 'designer.fixture-pool.update-profiles' | translate }}\n </button>\n }\n </div>\n </div>\n <div class=\"col-6\">\n <div class=\"row\">\n <!-- Add fixture from local profile -->\n <div class=\"col-6\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"createFixtureFromProfileFile()\" role=\"button\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.create-fixture-from-profile-file' | translate }}\n </button>\n </div>\n\n <!-- Universe -->\n <div class=\"col-6\">\n <div class=\"form-group row\">\n <label class=\"col-lg-4 col-form-label\">{{ 'designer.fixture-pool.universe' | translate }}</label>\n <div class=\"col-lg-8\">\n <select class=\"custom-select\">\n <option>Universe 1</option>\n </select>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fixtures -->\n <div class=\"card border-secondary\" style=\"height: 235px\">\n <div class=\"card-body p-0\">\n <!-- TODO sortablejs breaks sortable lists in base designer view, as soon as it disappears -->\n <!-- Reproduce: select a fixture, deselect a fixture, open pool, close pool without changes, fixture is \"half\"-selected again -->\n <!-- <div class=\"list-group list-group-flush\" [sortablejs]=\"fixturePool\"> -->\n <div class=\"list-group list-group-flush\">\n @for (fixture of fixturePool; track fixture) {\n <div\n class=\"list-group-item\"\n [class.active]=\"fixture == selectedFixture\"\n (click)=\"selectFixture(fixture)\"\n style=\"cursor: pointer\"\n >\n <div class=\"row\">\n <!-- <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div> -->\n <div class=\"col-auto flex-grow pl-0 my-auto\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(fixtureService.getProfileByUuid(fixture.profileUuid)) }} mr-1\">\n </span\n >{{ fixture.name }}\n </p>\n </div>\n <div class=\"col-auto my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"removeFixture(fixture); (false)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col\">\n <!-- Tab navigation -->\n <ul class=\"nav nav-tabs mb-4\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\">\n <a class=\"nav-link active\" id=\"sets-tab\" data-toggle=\"tab\" href=\"#dmx\" role=\"tab\">\n <i class=\"icon-ola\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.dmx' | translate }}\n </a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link\" id=\"compositions-tab\" data-toggle=\"tab\" href=\"#settings\" role=\"tab\">\n <i class=\"fa fa-cog\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.settings' | translate }}\n </a>\n </li>\n </ul>\n\n <!-- Tab content -->\n <div class=\"tab-content\" id=\"myTabContent\">\n <!-- DMX universe overview-->\n <div class=\"tab-pane show active\" id=\"dmx\" role=\"tabpanel\">\n <!-- Show all channel numbers -->\n <!-- <div class=\"form-group row\">\n <div class=\"col-auto flex-grow\"></div>\n <div class=\"col-lg-3 d-flex\">\n <div class=\"form-check my-auto ml-auto\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"showChannelNumbers\" [ngModel]=\"showChannelNumbers\"\n (ngModelChange)=\"showChannelNumbers = $event\">\n <label class=\"form-check-label\" for=\"showChannelNumbers\">Show channels</label>\n </div>\n </div>\n </div> -->\n\n <!-- List of DMX channels -->\n <div>\n @for (channel of dmxChannels; track $index; let i = $index) {\n <div\n class=\"dmx-channel\"\n [class.dmx-channel-occupied]=\"channelOccupied(i)\"\n [class.dmx-channel-occupied-start]=\"channelOccupiedStart(i)\"\n [class.dmx-channel-occupied-end]=\"channelOccupiedEnd(i)\"\n [class.dmx-channel-selected]=\"channelSelected(i)\"\n [class.dmx-channel-overlapped]=\"channelOverlapped(i)\"\n (mousedown)=\"channelMouseDown($event)\"\n (mouseover)=\"channelMouseOver($event)\"\n [attr.data-index]=\"i\"\n popover=\"{{ i + 1 }}\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n >\n <div class=\"d-flex w-100 dmx-channel-text\" [attr.data-index]=\"i\" [class.dmx-channel-text-visible]=\"showChannelNumbers\">\n <div class=\"m-auto\" style=\"font-size: 11px\" [attr.data-index]=\"i\"><small [attr.data-index]=\"i\">&nbsp;</small></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Fixture settings -->\n <div class=\"tab-pane\" id=\"settings\" role=\"tabpanel\">\n @if (selectedFixture && selectedFixtureProfile) {\n <div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\" for=\"fixtureName\">{{ 'designer.fixture-pool.fixture-name' | translate }}</label>\n <div class=\"col-lg-9\">\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"selectedFixture.name\" maxlength=\"200\" id=\"fixtureName\" />\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.mode' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.modeShortName\"\n (ngModelChange)=\"selectedFixture.modeShortName = $event\"\n >\n @for (mode of selectedFixtureProfile.modes; track mode) {\n <option [ngValue]=\"mode.shortName || mode.name\">\n {{ mode.name }}\n - {{ fixtureService.getModeChannelCount(selectedFixtureProfile, mode) }}\n {{ 'designer.fixture-pool.channels' | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.first-channel' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.dmxFirstChannel\"\n (ngModelChange)=\"selectedFixture.dmxFirstChannel = $event\"\n >\n @for (channel of dmxChannels; track $index; let i = $index) {\n <option [ngValue]=\"i\">\n {{ i + 1 }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\"></label>\n <div class=\"col-lg-9\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"addCopy(selectedFixture)\">\n {{ 'designer.fixture.add-copy' | translate }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [".list-group-item{padding:.3rem 1.25rem}.dmx-channel{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px}.dmx-channel-occupied{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected{background-color:#fd7e14}.dmx-channel-overlapped{background-color:red;border:1px solid red;cursor:move}.card{overflow:auto}\n"] }]
5807
6147
  }], () => [{ type: i1$1.BsModalRef }, { type: FixtureService }, { type: UuidService }, { type: ProjectService }, { type: PreviewService }, { type: i4.TranslateService }, { type: i3$1.ToastrService }, { type: PresetService }, { type: ConfigService }, { type: i1$1.BsModalService }], { mouseUp: [{
5808
6148
  type: HostListener,
5809
6149
  args: ['window:mouseup', ['$event']]
@@ -6940,7 +7280,7 @@ class TimelineGridComponent {
6940
7280
  }] }); })();
6941
7281
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TimelineGridComponent, { className: "TimelineGridComponent", filePath: "lib/timeline/timeline-grid/timeline-grid.component.ts", lineNumber: 11 }); })();
6942
7282
 
6943
- const _c0$5 = ["waveWrapper"];
7283
+ const _c0$6 = ["waveWrapper"];
6944
7284
  const _c1 = ["waveElement"];
6945
7285
  function TimelineComponent_Conditional_6_For_2_Template(rf, ctx) { if (rf & 1) {
6946
7286
  i0.ɵɵelementStart(0, "option", 14);
@@ -7273,7 +7613,7 @@ class TimelineComponent {
7273
7613
  }
7274
7614
  static { this.ɵfac = function TimelineComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TimelineComponent)(i0.ɵɵdirectiveInject(TimelineService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1$1.BsModalService), i0.ɵɵdirectiveInject(UuidService), i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(HotkeyTargetExcludeService), i0.ɵɵdirectiveInject(WarningDialogService), i0.ɵɵdirectiveInject(i1.HttpClient), i0.ɵɵdirectiveInject(ConfigService), i0.ɵɵdirectiveInject(PresetService), i0.ɵɵdirectiveInject(IntroService)); }; }
7275
7615
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TimelineComponent, selectors: [["lib-app-timeline"]], viewQuery: function TimelineComponent_Query(rf, ctx) { if (rf & 1) {
7276
- i0.ɵɵviewQuery(_c0$5, 5);
7616
+ i0.ɵɵviewQuery(_c0$6, 5);
7277
7617
  i0.ɵɵviewQuery(_c1, 5);
7278
7618
  } if (rf & 2) {
7279
7619
  let _t;
@@ -7312,7 +7652,7 @@ class TimelineComponent {
7312
7652
  i0.ɵɵconditional(ctx.timelineService.selectedComposition ? 11 : -1);
7313
7653
  i0.ɵɵadvance();
7314
7654
  i0.ɵɵconditional(ctx.projectService.project.compositions.length == 0 ? 12 : -1);
7315
- } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent, i11.PopoverDirective, i4.TranslatePipe], encapsulation: 2 }); }
7655
+ } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent, i11.PopoverDirective, i4.TranslatePipe], encapsulation: 2 }); }
7316
7656
  }
7317
7657
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TimelineComponent, [{
7318
7658
  type: Component,
@@ -7900,7 +8240,7 @@ class FixtureCapabilityDimmerComponent {
7900
8240
  i0.ɵɵproperty("ngModel", ctx.getValueText() >= 0 ? ctx.getValueText() : 0);
7901
8241
  i0.ɵɵadvance(2);
7902
8242
  i0.ɵɵproperty("value", ctx.getValue() >= 0 ? ctx.getValue() : 0)("orientation", "vertical")("reversed", true)("tooltip", "hide")("min", 0)("max", 1)("step", 0.001);
7903
- } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent], encapsulation: 2 }); }
8243
+ } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent], encapsulation: 2 }); }
7904
8244
  }
7905
8245
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityDimmerComponent, [{
7906
8246
  type: Component,
@@ -8011,7 +8351,7 @@ class FixtureCapabilityPanTiltComponent {
8011
8351
  i0.ɵɵproperty("ngModel", ctx.getValueTextTilt() >= 0 ? ctx.getValueTextTilt() : 0);
8012
8352
  i0.ɵɵadvance(2);
8013
8353
  i0.ɵɵproperty("value", ctx.getValueTilt() >= 0 ? ctx.getValueTilt() : 0)("orientation", "vertical")("reversed", true)("tooltip", "hide")("min", 0)("max", 1)("step", 0.001);
8014
- } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent, i4.TranslatePipe], encapsulation: 2 }); }
8354
+ } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent, i4.TranslatePipe], encapsulation: 2 }); }
8015
8355
  }
8016
8356
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityPanTiltComponent, [{
8017
8357
  type: Component,
@@ -8050,7 +8390,7 @@ function FixtureCapabilityColorWheelComponent_Conditional_10_For_2_Template(rf,
8050
8390
  } }
8051
8391
  function FixtureCapabilityColorWheelComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
8052
8392
  i0.ɵɵelementStart(0, "div", 7);
8053
- i0.ɵɵrepeaterCreate(1, FixtureCapabilityColorWheelComponent_Conditional_10_For_2_Template, 2, 4, "div", 8, i0.ɵɵrepeaterTrackByIdentity);
8393
+ i0.ɵɵrepeaterCreate(1, FixtureCapabilityColorWheelComponent_Conditional_10_For_2_Template, 2, 4, "div", 8, i0.ɵɵrepeaterTrackByIndex);
8054
8394
  i0.ɵɵelementEnd();
8055
8395
  } if (rf & 2) {
8056
8396
  const ctx_r0 = i0.ɵɵnextContext();
@@ -8204,11 +8544,11 @@ class FixtureCapabilityColorWheelComponent {
8204
8544
  i0.ɵɵconditional(ctx.showContainer ? 9 : -1);
8205
8545
  i0.ɵɵadvance();
8206
8546
  i0.ɵɵconditional(ctx.wheel ? 10 : -1);
8207
- } }, dependencies: [i8.NgStyle, i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4.TranslatePipe], styles: [".color[_ngcontent-%COMP%]{box-sizing:border-box;width:50px;height:50px;border:solid 1px #ced4da;cursor:pointer}.color-selected[_ngcontent-%COMP%]{border-width:3px;border-color:#fff}.color-selected-inner[_ngcontent-%COMP%]{border:solid 1px black;height:100%;width:100%}"] }); }
8547
+ } }, dependencies: [i9.NgStyle, i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4.TranslatePipe], styles: [".color[_ngcontent-%COMP%]{box-sizing:border-box;width:50px;height:50px;border:solid 1px #ced4da;cursor:pointer}.color-selected[_ngcontent-%COMP%]{border-width:3px;border-color:#fff}.color-selected-inner[_ngcontent-%COMP%]{border:solid 1px black;height:100%;width:100%}"] }); }
8208
8548
  }
8209
8549
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityColorWheelComponent, [{
8210
8550
  type: Component,
8211
- args: [{ selector: 'lib-app-fixture-capability-color-wheel', standalone: false, template: "<div class=\"card border-secondary h-100\" [class.capability-deactivated]=\"getCurrentSlotNumber() == undefined\" style=\"min-height: 200px\">\n <div class=\"card-header\">\n <div class=\"my-auto\">\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"capabilityColorWheel{{ wheelIndex }}Active\"\n [ngModel]=\"getCurrentSlotNumber() != undefined\"\n (ngModelChange)=\"changeActive($event)\"\n />\n <label class=\"form-check-label\" for=\"capabilityColorWheel{{ wheelIndex }}Active\">\n {{ 'designer.fixture.capability-color' | translate }}\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"card-body h-100\">\n @if (showContainer) {\n <div>{{ _fixtureProfile.name }} - {{ _channel.name }}</div>\n } @if (wheel) {\n <div class=\"d-flex\" style=\"max-width: 240px; flex-wrap: wrap\">\n @for (slot of slotCapabilities; track slot; let i = $index) {\n <div\n (click)=\"selectSlotNumber(slot.capability.slotNumber)\"\n class=\"m-1 color\"\n [class.color-selected]=\"slotCapabilityIsSelected(slot.capability.slotNumber)\"\n [ngStyle]=\"getCapabilityColorStyle(slot)\"\n >\n @if (slotCapabilityIsSelected(slot.capability.slotNumber)) {\n <div (click)=\"selectSlotNumber(slot.capability.slotNumber)\" class=\"color-selected-inner\"></div>\n }\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".color{box-sizing:border-box;width:50px;height:50px;border:solid 1px #ced4da;cursor:pointer}.color-selected{border-width:3px;border-color:#fff}.color-selected-inner{border:solid 1px black;height:100%;width:100%}\n"] }]
8551
+ args: [{ selector: 'lib-app-fixture-capability-color-wheel', standalone: false, template: "<div class=\"card border-secondary h-100\" [class.capability-deactivated]=\"getCurrentSlotNumber() == undefined\" style=\"min-height: 200px\">\n <div class=\"card-header\">\n <div class=\"my-auto\">\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"capabilityColorWheel{{ wheelIndex }}Active\"\n [ngModel]=\"getCurrentSlotNumber() != undefined\"\n (ngModelChange)=\"changeActive($event)\"\n />\n <label class=\"form-check-label\" for=\"capabilityColorWheel{{ wheelIndex }}Active\">\n {{ 'designer.fixture.capability-color' | translate }}\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"card-body h-100\">\n @if (showContainer) {\n <div>{{ _fixtureProfile.name }} - {{ _channel.name }}</div>\n } @if (wheel) {\n <div class=\"d-flex\" style=\"max-width: 240px; flex-wrap: wrap\">\n @for (slot of slotCapabilities; track $index; let i = $index) {\n <div\n (click)=\"selectSlotNumber(slot.capability.slotNumber)\"\n class=\"m-1 color\"\n [class.color-selected]=\"slotCapabilityIsSelected(slot.capability.slotNumber)\"\n [ngStyle]=\"getCapabilityColorStyle(slot)\"\n >\n @if (slotCapabilityIsSelected(slot.capability.slotNumber)) {\n <div (click)=\"selectSlotNumber(slot.capability.slotNumber)\" class=\"color-selected-inner\"></div>\n }\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".color{box-sizing:border-box;width:50px;height:50px;border:solid 1px #ced4da;cursor:pointer}.color-selected{border-width:3px;border-color:#fff}.color-selected-inner{border:solid 1px black;height:100%;width:100%}\n"] }]
8212
8552
  }], () => [{ type: PresetService }, { type: i0.ChangeDetectorRef }, { type: FixtureService }], { profile: [{
8213
8553
  type: Input
8214
8554
  }], channel: [{
@@ -8250,7 +8590,7 @@ function FixtureCapabilityComponent_Conditional_2_Template(rf, ctx) { if (rf & 1
8250
8590
  i0.ɵɵelementStart(0, "div", 4);
8251
8591
  i0.ɵɵconditionalCreate(1, FixtureCapabilityComponent_Conditional_2_Conditional_1_Template, 2, 0, "div", 5);
8252
8592
  i0.ɵɵconditionalCreate(2, FixtureCapabilityComponent_Conditional_2_Conditional_2_Template, 2, 0, "div", 5);
8253
- i0.ɵɵrepeaterCreate(3, FixtureCapabilityComponent_Conditional_2_For_4_Template, 2, 4, "div", 5, i0.ɵɵrepeaterTrackByIdentity);
8593
+ i0.ɵɵrepeaterCreate(3, FixtureCapabilityComponent_Conditional_2_For_4_Template, 2, 4, "div", 5, i0.ɵɵrepeaterTrackByIndex);
8254
8594
  i0.ɵɵpipe(5, "keyvalue");
8255
8595
  i0.ɵɵconditionalCreate(6, FixtureCapabilityComponent_Conditional_2_Conditional_6_Template, 2, 0, "div", 5);
8256
8596
  i0.ɵɵelementStart(7, "div", 6);
@@ -8391,11 +8731,11 @@ class FixtureCapabilityComponent {
8391
8731
  i0.ɵɵconditional(ctx.projectService.project.fixtures.length > 0 && ctx.presetService.selectedPreset && ctx.presetService.selectedPreset.fixtures.length == 0 ? 4 : -1);
8392
8732
  i0.ɵɵadvance();
8393
8733
  i0.ɵɵconditional(ctx.projectService.project.fixtures.length > 0 && ctx.presetService.selectedPreset && ctx.presetService.selectedPreset.fixtures.length > 0 && !ctx.hasCapabilityColorOrColorWheel && !ctx.hasCapabilityPanTilt ? 5 : -1);
8394
- } }, dependencies: [FixtureCapabilityColorComponent, FixtureCapabilityDimmerComponent, FixtureCapabilityPanTiltComponent, FixtureCapabilityColorWheelComponent, i8.KeyValuePipe, i4.TranslatePipe], encapsulation: 2, changeDetection: 0 }); }
8734
+ } }, dependencies: [FixtureCapabilityColorComponent, FixtureCapabilityDimmerComponent, FixtureCapabilityPanTiltComponent, FixtureCapabilityColorWheelComponent, i9.KeyValuePipe, i4.TranslatePipe], encapsulation: 2, changeDetection: 0 }); }
8395
8735
  }
8396
8736
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityComponent, [{
8397
8737
  type: Component,
8398
- args: [{ selector: 'lib-app-fixture-capability', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"card border-secondary panel h-100\" [class.card-intro-active]=\"introService.showStep('capabilities')\">\n <div class=\"card-body capability-container d-flex h-100\" style=\"overflow-y: hidden\">\n <!-- h-100 needs to be applied in chrome to avoid performance issues with many layouts (reflows)\n during sliding. But the class must not be applied e.g. in Safari, because the bottom margin is not correct\n anymore afterwards. -->\n @if (presetService.selectedPreset) {\n <div class=\"row m-0 pl-3\" [class.h-100]=\"isChrome()\">\n <!-- Dimmer -->\n <!-- the height attribute is required to avoid performance issues with many layouts (reflows)\n in Chrome during sliding -->\n @if (hasCapabilityDimmer) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-dimmer></lib-app-fixture-capability-dimmer>\n </div>\n }\n <!-- Color -->\n <!-- Also show for color wheel fixtures to approx. the color -->\n @if (hasCapabilityColorOrColorWheel) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-color></lib-app-fixture-capability-color>\n </div>\n }\n <!-- Color wheels -->\n @for (entry of colorWheelChannels | keyvalue; track entry; let i = $index) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-color-wheel\n [profile]=\"entry.key\"\n [channel]=\"entry.value\"\n [wheelIndex]=\"i\"\n [showContainer]=\"colorWheelChannels.size > 1\"\n ></lib-app-fixture-capability-color-wheel>\n </div>\n }\n <!-- Pan/Tilt -->\n @if (hasCapabilityPanTilt) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-pan-tilt></lib-app-fixture-capability-pan-tilt>\n </div>\n }\n <!-- Fix spacing at the end -->\n <div class=\"col col-auto p-0 my-3 mr-3\">&nbsp;</div>\n </div>\n } @if (projectService.project.fixtures.length == 0) {\n <div class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures' | translate }}</p>\n </div>\n } @if ( projectService.project.fixtures.length > 0 && presetService.selectedPreset && presetService.selectedPreset.fixtures.length == 0\n ) {\n <div class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures-selected' | translate }}</p>\n </div>\n } @if ( projectService.project.fixtures.length > 0 && presetService.selectedPreset && presetService.selectedPreset.fixtures.length > 0\n && !hasCapabilityColorOrColorWheel && !hasCapabilityPanTilt ) {\n <div class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-capabilities-available' | translate }}</p>\n </div>\n }\n </div>\n</div>\n" }]
8738
+ args: [{ selector: 'lib-app-fixture-capability', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"card border-secondary panel h-100\" [class.card-intro-active]=\"introService.showStep('capabilities')\">\n <div class=\"card-body capability-container d-flex h-100\" style=\"overflow-y: hidden\">\n <!-- h-100 needs to be applied in chrome to avoid performance issues with many layouts (reflows)\n during sliding. But the class must not be applied e.g. in Safari, because the bottom margin is not correct\n anymore afterwards. -->\n @if (presetService.selectedPreset) {\n <div class=\"row m-0 pl-3\" [class.h-100]=\"isChrome()\">\n <!-- Dimmer -->\n <!-- the height attribute is required to avoid performance issues with many layouts (reflows)\n in Chrome during sliding -->\n @if (hasCapabilityDimmer) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-dimmer></lib-app-fixture-capability-dimmer>\n </div>\n }\n <!-- Color -->\n <!-- Also show for color wheel fixtures to approx. the color -->\n @if (hasCapabilityColorOrColorWheel) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-color></lib-app-fixture-capability-color>\n </div>\n }\n <!-- Color wheels -->\n @for (entry of colorWheelChannels | keyvalue; track $index; let i = $index) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-color-wheel\n [profile]=\"entry.key\"\n [channel]=\"entry.value\"\n [wheelIndex]=\"i\"\n [showContainer]=\"colorWheelChannels.size > 1\"\n ></lib-app-fixture-capability-color-wheel>\n </div>\n }\n <!-- Pan/Tilt -->\n @if (hasCapabilityPanTilt) {\n <div class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-pan-tilt></lib-app-fixture-capability-pan-tilt>\n </div>\n }\n <!-- Fix spacing at the end -->\n <div class=\"col col-auto p-0 my-3 mr-3\">&nbsp;</div>\n </div>\n } @if (projectService.project.fixtures.length == 0) {\n <div class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures' | translate }}</p>\n </div>\n } @if ( projectService.project.fixtures.length > 0 && presetService.selectedPreset && presetService.selectedPreset.fixtures.length == 0\n ) {\n <div class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures-selected' | translate }}</p>\n </div>\n } @if ( projectService.project.fixtures.length > 0 && presetService.selectedPreset && presetService.selectedPreset.fixtures.length > 0\n && !hasCapabilityColorOrColorWheel && !hasCapabilityPanTilt ) {\n <div class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-capabilities-available' | translate }}</p>\n </div>\n }\n </div>\n</div>\n" }]
8399
8739
  }], () => [{ type: PresetService }, { type: FixtureService }, { type: i0.ChangeDetectorRef }, { type: ProjectService }, { type: IntroService }], null); })();
8400
8740
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FixtureCapabilityComponent, { className: "FixtureCapabilityComponent", filePath: "lib/fixture/fixture-capability/fixture-capability.component.ts", lineNumber: 18 }); })();
8401
8741
 
@@ -8448,20 +8788,20 @@ function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template(
8448
8788
  i0.ɵɵelementStart(35, "div", 11)(36, "div", 12);
8449
8789
  i0.ɵɵtext(37, "Y");
8450
8790
  i0.ɵɵelementEnd();
8451
- i0.ɵɵelementStart(38, "div", 13)(39, "mv-slider", 17);
8452
- i0.ɵɵlistener("change", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_mv_slider_change_39_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("z", $event.newValue)); });
8791
+ i0.ɵɵelementStart(38, "div", 13)(39, "mv-slider", 14);
8792
+ i0.ɵɵlistener("change", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_mv_slider_change_39_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("y", $event.newValue)); });
8453
8793
  i0.ɵɵelementEnd()();
8454
8794
  i0.ɵɵelementStart(40, "div", 15)(41, "input", 16);
8455
- i0.ɵɵlistener("ngModelChange", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_input_ngModelChange_41_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("z", $event)); });
8795
+ i0.ɵɵlistener("ngModelChange", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_input_ngModelChange_41_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("y", $event)); });
8456
8796
  i0.ɵɵelementEnd()()();
8457
8797
  i0.ɵɵelementStart(42, "div", 11)(43, "div", 12);
8458
8798
  i0.ɵɵtext(44, "Z");
8459
8799
  i0.ɵɵelementEnd();
8460
- i0.ɵɵelementStart(45, "div", 13)(46, "mv-slider", 14);
8461
- i0.ɵɵlistener("change", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_mv_slider_change_46_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("y", $event.newValue)); });
8800
+ i0.ɵɵelementStart(45, "div", 13)(46, "mv-slider", 17);
8801
+ i0.ɵɵlistener("change", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_mv_slider_change_46_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("z", $event.newValue)); });
8462
8802
  i0.ɵɵelementEnd()();
8463
8803
  i0.ɵɵelementStart(47, "div", 15)(48, "input", 16);
8464
- i0.ɵɵlistener("ngModelChange", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_input_ngModelChange_48_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("y", $event)); });
8804
+ i0.ɵɵlistener("ngModelChange", function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template_input_ngModelChange_48_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.changeRotationManual("z", $event)); });
8465
8805
  i0.ɵɵelementEnd()()()();
8466
8806
  } if (rf & 2) {
8467
8807
  const ctx_r1 = i0.ɵɵnextContext(2);
@@ -8482,17 +8822,17 @@ function FixtureSettingsPositionComponent_Conditional_4_Conditional_19_Template(
8482
8822
  i0.ɵɵadvance(2);
8483
8823
  i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(27, 40, "designer.fixture.setting-rotation"));
8484
8824
  i0.ɵɵadvance(6);
8485
- i0.ɵɵproperty("tooltip", "hide")("value", ctx_r1.rotationX ? ctx_r1.rotationX : (ctx_r1.rotationMin + ctx_r1.rotationMax) / 2)("min", ctx_r1.rotationMin)("max", ctx_r1.rotationMax)("step", 1);
8825
+ i0.ɵɵproperty("tooltip", "hide")("value", ctx_r1.rotationX ? ctx_r1.rotationX : 0)("min", ctx_r1.rotationMin)("max", ctx_r1.rotationMax)("step", 1);
8486
8826
  i0.ɵɵadvance(2);
8487
8827
  i0.ɵɵproperty("ngModel", ctx_r1.rotationX);
8488
8828
  i0.ɵɵadvance(5);
8489
- i0.ɵɵproperty("tooltip", "hide")("value", ctx_r1.rotationZ ? ctx_r1.rotationZ : (ctx_r1.rotationMin + ctx_r1.rotationMax) / 2)("min", ctx_r1.rotationMin)("max", ctx_r1.rotationMax)("step", 1);
8829
+ i0.ɵɵproperty("tooltip", "hide")("value", ctx_r1.rotationY ? ctx_r1.rotationY : 0)("min", ctx_r1.rotationMin)("max", ctx_r1.rotationMax)("step", 1);
8490
8830
  i0.ɵɵadvance(2);
8491
- i0.ɵɵproperty("ngModel", ctx_r1.rotationZ);
8831
+ i0.ɵɵproperty("ngModel", ctx_r1.rotationY);
8492
8832
  i0.ɵɵadvance(5);
8493
- i0.ɵɵproperty("tooltip", "hide")("value", ctx_r1.rotationY ? ctx_r1.rotationY : (ctx_r1.rotationMin + ctx_r1.rotationMax) / 2)("min", ctx_r1.rotationMin)("max", ctx_r1.rotationMax)("step", 1);
8833
+ i0.ɵɵproperty("tooltip", "hide")("value", ctx_r1.rotationZ ? ctx_r1.rotationZ : 0)("min", ctx_r1.rotationMin)("max", ctx_r1.rotationMax)("step", 1);
8494
8834
  i0.ɵɵadvance(2);
8495
- i0.ɵɵproperty("ngModel", ctx_r1.rotationY);
8835
+ i0.ɵɵproperty("ngModel", ctx_r1.rotationZ);
8496
8836
  } }
8497
8837
  function FixtureSettingsPositionComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
8498
8838
  const _r1 = i0.ɵɵgetCurrentView();
@@ -8551,9 +8891,10 @@ function FixtureSettingsPositionComponent_Conditional_5_Template(rf, ctx) { if (
8551
8891
  i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(3, 1, "designer.fixture.no-fixtures-settings-selected"));
8552
8892
  } }
8553
8893
  class FixtureSettingsPositionComponent {
8554
- constructor(fixtureService, presetService) {
8894
+ constructor(fixtureService, presetService, previewService) {
8555
8895
  this.fixtureService = fixtureService;
8556
8896
  this.presetService = presetService;
8897
+ this.previewService = previewService;
8557
8898
  this.selectUndefinedOptionValue = undefined;
8558
8899
  this.selectedPositioning = undefined;
8559
8900
  this.positionX = 0;
@@ -8570,11 +8911,14 @@ class FixtureSettingsPositionComponent {
8570
8911
  this.rotationZ = 0;
8571
8912
  this.rotationMin = 0;
8572
8913
  this.rotationMax = 360;
8573
- presetService.fixtureSelectionSettingsChanged.subscribe(() => {
8914
+ this.presetService.fixtureSelectionSettingsChanged.subscribe(() => {
8574
8915
  this.updateSelection();
8575
8916
  });
8576
8917
  }
8577
8918
  ngOnInit() { }
8919
+ updated() {
8920
+ this.previewService.doUpdateStageAndPositions.next();
8921
+ }
8578
8922
  updateSelection() {
8579
8923
  // get all display values for the selected settings fixtures
8580
8924
  this.selectedPositioning = undefined;
@@ -8667,6 +9011,7 @@ class FixtureSettingsPositionComponent {
8667
9011
  for (const fixture of this.fixtureService.selectedSettingsFixtures) {
8668
9012
  fixture.positioning = positioning;
8669
9013
  }
9014
+ this.updated();
8670
9015
  }
8671
9016
  changePositionManual(position, value) {
8672
9017
  if (isNaN(value)) {
@@ -8704,6 +9049,7 @@ class FixtureSettingsPositionComponent {
8704
9049
  }
8705
9050
  }
8706
9051
  }
9052
+ this.updated();
8707
9053
  }
8708
9054
  changeRotationManual(rotation, value) {
8709
9055
  if (isNaN(value) || value < this.rotationMin || value > this.rotationMax) {
@@ -8729,8 +9075,9 @@ class FixtureSettingsPositionComponent {
8729
9075
  fixture.rotationZ = +value;
8730
9076
  }
8731
9077
  }
9078
+ this.updated();
8732
9079
  }
8733
- static { this.ɵfac = function FixtureSettingsPositionComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixtureSettingsPositionComponent)(i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(PresetService)); }; }
9080
+ static { this.ɵfac = function FixtureSettingsPositionComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixtureSettingsPositionComponent)(i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(PresetService), i0.ɵɵdirectiveInject(PreviewService)); }; }
8734
9081
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FixtureSettingsPositionComponent, selectors: [["lib-app-fixture-settings-position"]], standalone: false, decls: 6, vars: 5, consts: [[1, "card", "border-secondary", "h-100"], [1, "card-header"], [1, "card-body", "h-100"], [1, "card-body", "h-100", "d-flex"], [1, "mb-2", 2, "width", "140px", 3, "ngModelChange", "change", "ngModel"], [3, "ngValue"], ["value", "topFront"], ["value", "bottomFront"], ["value", "topBack"], ["value", "bottomBack"], ["value", "manual"], [1, "d-flex"], [1, "my-auto", 2, "width", "20px", "float", "left"], [1, "my-auto", 2, "float", "left"], [2, "width", "80px", 3, "change", "tooltip", "value", "min", "max", "step"], [1, "my-auto"], ["type", "text", 1, "slider-input", 3, "ngModelChange", "ngModel"], [2, "width", "80px", "height", "18px", 3, "change", "tooltip", "value", "min", "max", "step"], [1, "mt-2"], [1, "m-auto", 2, "color", "#717171"]], template: function FixtureSettingsPositionComponent_Template(rf, ctx) { if (rf & 1) {
8735
9082
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1);
8736
9083
  i0.ɵɵtext(2);
@@ -8746,13 +9093,13 @@ class FixtureSettingsPositionComponent {
8746
9093
  i0.ɵɵconditional(ctx.fixtureService.selectedSettingsFixtures.length > 0 ? 4 : -1);
8747
9094
  i0.ɵɵadvance();
8748
9095
  i0.ɵɵconditional(ctx.fixtureService.selectedSettingsFixtures.length == 0 ? 5 : -1);
8749
- } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent, i4.TranslatePipe], encapsulation: 2 }); }
9096
+ } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent, i4.TranslatePipe], encapsulation: 2 }); }
8750
9097
  }
8751
9098
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureSettingsPositionComponent, [{
8752
9099
  type: Component,
8753
- args: [{ selector: 'lib-app-fixture-settings-position', standalone: false, template: "<div class=\"card border-secondary h-100\">\n <div class=\"card-header\">\n {{ 'designer.fixture.setting-position-title' | translate }}\n </div>\n\n @if (fixtureService.selectedSettingsFixtures.length > 0) {\n <div class=\"card-body h-100\">\n <div>\n <select class=\"mb-2\" style=\"width: 140px\" [(ngModel)]=\"selectedPositioning\" (change)=\"changePosition($event.target.value)\">\n <option [ngValue]=\"selectUndefinedOptionValue\"></option>\n <option value=\"topFront\">{{ 'designer.fixture.setting-position-front-top' | translate }}</option>\n <option value=\"bottomFront\">{{ 'designer.fixture.setting-position-front-bottom' | translate }}</option>\n <option value=\"topBack\">{{ 'designer.fixture.setting-position-back-top' | translate }}</option>\n <option value=\"bottomBack\">{{ 'designer.fixture.setting-position-back-bottom' | translate }}</option>\n <option value=\"manual\">{{ 'designer.fixture.setting-position-manual' | translate }}</option>\n </select>\n </div>\n @if (selectedPositioning == 'manual') {\n <div>\n <div>{{ 'designer.fixture.setting-position' | translate }}</div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">X</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"positionX ? positionX : (positionXMax + positionXMin) / 2\"\n [min]=\"positionXMin\"\n [max]=\"positionXMax\"\n [step]=\"1\"\n (change)=\"changePositionManual('x', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"positionX\" (ngModelChange)=\"changePositionManual('x', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Y</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [value]=\"positionZ ? positionZ : (positionZMax + positionZMin) / 2\"\n [min]=\"positionZMin\"\n [max]=\"positionZMax\"\n [step]=\"1\"\n (change)=\"changePositionManual('z', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"positionZ\" (ngModelChange)=\"changePositionManual('z', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Z</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"positionY ? positionY : (positionYMax + positionYMin) / 2\"\n [min]=\"positionYMin\"\n [max]=\"positionYMax\"\n [step]=\"1\"\n (change)=\"changePositionManual('y', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"positionY\" (ngModelChange)=\"changePositionManual('y', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"mt-2\">{{ 'designer.fixture.setting-rotation' | translate }}</div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">X</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"rotationX ? rotationX : (rotationMin + rotationMax) / 2\"\n [min]=\"rotationMin\"\n [max]=\"rotationMax\"\n [step]=\"1\"\n (change)=\"changeRotationManual('x', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"rotationX\" (ngModelChange)=\"changeRotationManual('x', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Y</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [value]=\"rotationZ ? rotationZ : (rotationMin + rotationMax) / 2\"\n [min]=\"rotationMin\"\n [max]=\"rotationMax\"\n [step]=\"1\"\n (change)=\"changeRotationManual('z', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"rotationZ\" (ngModelChange)=\"changeRotationManual('z', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Z</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"rotationY ? rotationY : (rotationMin + rotationMax) / 2\"\n [min]=\"rotationMin\"\n [max]=\"rotationMax\"\n [step]=\"1\"\n (change)=\"changeRotationManual('y', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"rotationY\" (ngModelChange)=\"changeRotationManual('y', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n </div>\n }\n </div>\n } @if (fixtureService.selectedSettingsFixtures.length == 0) {\n <div class=\"card-body h-100 d-flex\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures-settings-selected' | translate }}</p>\n </div>\n }\n</div>\n" }]
8754
- }], () => [{ type: FixtureService }, { type: PresetService }], null); })();
8755
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FixtureSettingsPositionComponent, { className: "FixtureSettingsPositionComponent", filePath: "lib/fixture/fixture-settings/fixture-settings-position/fixture-settings-position.component.ts", lineNumber: 12 }); })();
9100
+ args: [{ selector: 'lib-app-fixture-settings-position', standalone: false, template: "<div class=\"card border-secondary h-100\">\n <div class=\"card-header\">\n {{ 'designer.fixture.setting-position-title' | translate }}\n </div>\n\n @if (fixtureService.selectedSettingsFixtures.length > 0) {\n <div class=\"card-body h-100\">\n <div>\n <select class=\"mb-2\" style=\"width: 140px\" [(ngModel)]=\"selectedPositioning\" (change)=\"changePosition($event.target.value)\">\n <option [ngValue]=\"selectUndefinedOptionValue\"></option>\n <option value=\"topFront\">{{ 'designer.fixture.setting-position-front-top' | translate }}</option>\n <option value=\"bottomFront\">{{ 'designer.fixture.setting-position-front-bottom' | translate }}</option>\n <option value=\"topBack\">{{ 'designer.fixture.setting-position-back-top' | translate }}</option>\n <option value=\"bottomBack\">{{ 'designer.fixture.setting-position-back-bottom' | translate }}</option>\n <option value=\"manual\">{{ 'designer.fixture.setting-position-manual' | translate }}</option>\n </select>\n </div>\n @if (selectedPositioning == 'manual') {\n <div>\n <div>{{ 'designer.fixture.setting-position' | translate }}</div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">X</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"positionX ? positionX : (positionXMax + positionXMin) / 2\"\n [min]=\"positionXMin\"\n [max]=\"positionXMax\"\n [step]=\"1\"\n (change)=\"changePositionManual('x', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"positionX\" (ngModelChange)=\"changePositionManual('x', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Y</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [value]=\"positionZ ? positionZ : (positionZMax + positionZMin) / 2\"\n [min]=\"positionZMin\"\n [max]=\"positionZMax\"\n [step]=\"1\"\n (change)=\"changePositionManual('z', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"positionZ\" (ngModelChange)=\"changePositionManual('z', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Z</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"positionY ? positionY : (positionYMax + positionYMin) / 2\"\n [min]=\"positionYMin\"\n [max]=\"positionYMax\"\n [step]=\"1\"\n (change)=\"changePositionManual('y', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"positionY\" (ngModelChange)=\"changePositionManual('y', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"mt-2\">{{ 'designer.fixture.setting-rotation' | translate }}</div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">X</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"rotationX ? rotationX : 0\"\n [min]=\"rotationMin\"\n [max]=\"rotationMax\"\n [step]=\"1\"\n (change)=\"changeRotationManual('x', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"rotationX\" (ngModelChange)=\"changeRotationManual('x', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Y</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"rotationY ? rotationY : 0\"\n [min]=\"rotationMin\"\n [max]=\"rotationMax\"\n [step]=\"1\"\n (change)=\"changeRotationManual('y', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"rotationY\" (ngModelChange)=\"changeRotationManual('y', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 20px; float: left\">Z</div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [value]=\"rotationZ ? rotationZ : 0\"\n [min]=\"rotationMin\"\n [max]=\"rotationMax\"\n [step]=\"1\"\n (change)=\"changeRotationManual('z', $event.newValue)\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input [ngModel]=\"rotationZ\" (ngModelChange)=\"changeRotationManual('z', $event)\" type=\"text\" class=\"slider-input\" />\n </div>\n </div>\n </div>\n }\n </div>\n } @if (fixtureService.selectedSettingsFixtures.length == 0) {\n <div class=\"card-body h-100 d-flex\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures-settings-selected' | translate }}</p>\n </div>\n }\n</div>\n" }]
9101
+ }], () => [{ type: FixtureService }, { type: PresetService }, { type: PreviewService }], null); })();
9102
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FixtureSettingsPositionComponent, { className: "FixtureSettingsPositionComponent", filePath: "lib/fixture/fixture-settings/fixture-settings-position/fixture-settings-position.component.ts", lineNumber: 13 }); })();
8756
9103
 
8757
9104
  class FixtureSettingsStageComponent {
8758
9105
  constructor(projectService, previewService, changeDetectorRef) {
@@ -8764,6 +9111,7 @@ class FixtureSettingsStageComponent {
8764
9111
  update() {
8765
9112
  this.previewService.updateStage();
8766
9113
  this.changeDetectorRef.detectChanges();
9114
+ this.previewService.doUpdateStageAndPositions.next();
8767
9115
  }
8768
9116
  static { this.ɵfac = function FixtureSettingsStageComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FixtureSettingsStageComponent)(i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(PreviewService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
8769
9117
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FixtureSettingsStageComponent, selectors: [["lib-app-fixture-settings-stage"]], standalone: false, decls: 65, vars: 57, consts: [[1, "card", "border-secondary", "h-100"], [1, "card-header"], [1, "card-body", "h-100"], [1, "d-flex"], [1, "my-auto", 2, "width", "120px", "float", "left"], [1, "my-auto", 2, "float", "left"], [2, "width", "140px", 3, "change", "tooltip", "value", "min", "max", "step"], [1, "my-auto"], ["type", "text", "readonly", "", 1, "slider-input-readonly", 3, "ngModelChange", "ngModel"], [1, "my-auto", "ml-1", 2, "color", "darkgray"]], template: function FixtureSettingsStageComponent_Template(rf, ctx) { if (rf & 1) {
@@ -8888,7 +9236,7 @@ class FixtureSettingsStageComponent {
8888
9236
  i0.ɵɵproperty("tooltip", "hide")("value", ctx.projectService.project.stagePillarWidthCm)("min", 1)("max", 200)("step", 5);
8889
9237
  i0.ɵɵadvance(2);
8890
9238
  i0.ɵɵproperty("ngModel", ctx.projectService.project.stagePillarWidthCm);
8891
- } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent, i4.TranslatePipe], encapsulation: 2 }); }
9239
+ } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent, i4.TranslatePipe], encapsulation: 2 }); }
8892
9240
  }
8893
9241
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureSettingsStageComponent, [{
8894
9242
  type: Component,
@@ -8934,7 +9282,7 @@ class FixtureSettingsComponent {
8934
9282
  }], () => [{ type: FixtureService }, { type: PresetService }], null); })();
8935
9283
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FixtureSettingsComponent, { className: "FixtureSettingsComponent", filePath: "lib/fixture/fixture-settings/fixture-settings.component.ts", lineNumber: 11 }); })();
8936
9284
 
8937
- const _c0$4 = ["curveGrid"];
9285
+ const _c0$5 = ["curveGrid"];
8938
9286
  function EffectCurveComponent_For_74_Template(rf, ctx) { if (rf & 1) {
8939
9287
  const _r2 = i0.ɵɵgetCurrentView();
8940
9288
  i0.ɵɵelementStart(0, "div", 25)(1, "input", 27);
@@ -9015,26 +9363,19 @@ class EffectCurveComponent {
9015
9363
  // whether the effect is currently being edited (open) or not
9016
9364
  set isSelected(value) {
9017
9365
  if (value) {
9018
- // start the update timer
9019
- if (!this.gridUpdateSubscription) {
9020
- this.gridUpdateSubscription = timer(0, 15).subscribe(() => {
9021
- this.redraw();
9022
- });
9023
- }
9366
+ this.startUpdateTimer();
9024
9367
  }
9025
9368
  else {
9026
- // stop the update timer
9027
- if (this.gridUpdateSubscription) {
9028
- this.gridUpdateSubscription.unsubscribe();
9029
- this.gridUpdateSubscription = undefined;
9030
- }
9369
+ this.stopUpdateTimer();
9031
9370
  }
9032
9371
  }
9033
- constructor(presetService, animationService, fixtureService, translate) {
9372
+ constructor(presetService, animationService, fixtureService, translate, effectService, ngZone) {
9034
9373
  this.presetService = presetService;
9035
9374
  this.animationService = animationService;
9036
9375
  this.fixtureService = fixtureService;
9037
9376
  this.translate = translate;
9377
+ this.effectService = effectService;
9378
+ this.ngZone = ngZone;
9038
9379
  this.lengthMillisMin = 20;
9039
9380
  this.lengthMillisMax = 8000;
9040
9381
  this.amplitudeMin = 0;
@@ -9055,6 +9396,17 @@ class EffectCurveComponent {
9055
9396
  this.updateCapabilitiesAndChannels();
9056
9397
  });
9057
9398
  this.updateCapabilitiesAndChannels();
9399
+ this.effectsOpenChangedSubscription = this.effectService.effectsOpenChanged.subscribe(() => {
9400
+ if (this.effectService.effectsOpen) {
9401
+ this.startUpdateTimer();
9402
+ }
9403
+ else {
9404
+ this.stopUpdateTimer();
9405
+ }
9406
+ });
9407
+ if (this.effectService.effectsOpen) {
9408
+ this.startUpdateTimer();
9409
+ }
9058
9410
  }
9059
9411
  ngOnInit() {
9060
9412
  const canvas = this.curveGrid.nativeElement;
@@ -9063,6 +9415,26 @@ class EffectCurveComponent {
9063
9415
  this.maxHeight = canvas.height;
9064
9416
  this.redraw();
9065
9417
  }
9418
+ ngOnDestroy() {
9419
+ this.stopUpdateTimer();
9420
+ this.effectsOpenChangedSubscription.unsubscribe();
9421
+ }
9422
+ startUpdateTimer() {
9423
+ if (!this.gridUpdateSubscription) {
9424
+ // Avoid triggering change detection with each animation frame -> run outside zone
9425
+ this.ngZone.runOutsideAngular(() => {
9426
+ this.gridUpdateSubscription = timer(0, 15).subscribe(() => {
9427
+ this.redraw();
9428
+ });
9429
+ });
9430
+ }
9431
+ }
9432
+ stopUpdateTimer() {
9433
+ if (this.gridUpdateSubscription) {
9434
+ this.gridUpdateSubscription.unsubscribe();
9435
+ this.gridUpdateSubscription = undefined;
9436
+ }
9437
+ }
9066
9438
  drawCurrentValue(currMillis, radius, lineWidth, durationMillis, maxHeight) {
9067
9439
  const currVal = 1 - this.curve.getValueAtMillis(currMillis);
9068
9440
  const x = (this.maxWidth * (currMillis % durationMillis)) / durationMillis;
@@ -9300,9 +9672,9 @@ class EffectCurveComponent {
9300
9672
  this.presetService.previewLive();
9301
9673
  }
9302
9674
  }
9303
- static { this.ɵfac = function EffectCurveComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EffectCurveComponent)(i0.ɵɵdirectiveInject(PresetService), i0.ɵɵdirectiveInject(AnimationService), i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(i4.TranslateService)); }; }
9675
+ static { this.ɵfac = function EffectCurveComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EffectCurveComponent)(i0.ɵɵdirectiveInject(PresetService), i0.ɵɵdirectiveInject(AnimationService), i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(i4.TranslateService), i0.ɵɵdirectiveInject(EffectService), i0.ɵɵdirectiveInject(i0.NgZone)); }; }
9304
9676
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EffectCurveComponent, selectors: [["lib-app-effect-curve"]], viewQuery: function EffectCurveComponent_Query(rf, ctx) { if (rf & 1) {
9305
- i0.ɵɵviewQuery(_c0$4, 7);
9677
+ i0.ɵɵviewQuery(_c0$5, 7);
9306
9678
  } if (rf & 2) {
9307
9679
  let _t;
9308
9680
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.curveGrid = _t.first);
@@ -9397,7 +9769,7 @@ class EffectCurveComponent {
9397
9769
  i0.ɵɵelement(69, "i", 21);
9398
9770
  i0.ɵɵelementEnd()()();
9399
9771
  i0.ɵɵelementStart(70, "div", 22)(71, "div", 23)(72, "div", 24);
9400
- i0.ɵɵrepeaterCreate(73, EffectCurveComponent_For_74_Template, 5, 8, "div", 25, i0.ɵɵrepeaterTrackByIdentity);
9772
+ i0.ɵɵrepeaterCreate(73, EffectCurveComponent_For_74_Template, 5, 8, "div", 25, i0.ɵɵrepeaterTrackByIndex);
9401
9773
  i0.ɵɵelementEnd();
9402
9774
  i0.ɵɵelementStart(75, "div", 26);
9403
9775
  i0.ɵɵconditionalCreate(76, EffectCurveComponent_Conditional_76_Template, 4, 0, "div");
@@ -9470,12 +9842,12 @@ class EffectCurveComponent {
9470
9842
  i0.ɵɵconditional(ctx.availableProfiles.length > 1 ? 76 : -1);
9471
9843
  i0.ɵɵadvance();
9472
9844
  i0.ɵɵrepeater(i0.ɵɵpipeBind1(79, 81, ctx.availableChannels));
9473
- } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent, i11.PopoverDirective, i8.AsyncPipe, i8.KeyValuePipe, i4.TranslatePipe], styles: [".curve-grid[_ngcontent-%COMP%]{background-color:#000;border:1px solid white}.applying-to[_ngcontent-%COMP%]{overflow-y:scroll;height:130px!important;background-color:#222;border:1px solid white;padding:.5rem}.applying-to[_ngcontent-%COMP%] .form-check[_ngcontent-%COMP%]:last-child{margin-bottom:.5rem}"] }); }
9845
+ } }, dependencies: [i3$2.NgSelectOption, i3$2.ɵNgSelectMultipleOption, i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.SelectControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent, i11.PopoverDirective, i9.AsyncPipe, i9.KeyValuePipe, i4.TranslatePipe], styles: [".curve-grid[_ngcontent-%COMP%]{background-color:#000;border:1px solid white}.applying-to[_ngcontent-%COMP%]{overflow-y:scroll;height:130px!important;background-color:#222;border:1px solid white;padding:.5rem}.applying-to[_ngcontent-%COMP%] .form-check[_ngcontent-%COMP%]:last-child{margin-bottom:.5rem}"] }); }
9474
9846
  }
9475
9847
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EffectCurveComponent, [{
9476
9848
  type: Component,
9477
- args: [{ selector: 'lib-app-effect-curve', standalone: false, template: "<div class=\"mr-3\" style=\"float: left\">\n <div class=\"mb-2\">\n <select\n [ngModel]=\"curve.curveType\"\n (ngModelChange)=\"curve.curveType = $event; presetService.previewLive()\"\n style=\"width: 200px; height: 20px\"\n >\n <option [value]=\"'sine'\">\n {{ 'designer.effect.curve-sine' | translate }}\n </option>\n <option [value]=\"'square'\">\n {{ 'designer.effect.curve-square' | translate }}\n </option>\n <!-- <option [value]=\"'triangle'\">\n {{ 'designer.effect.curve-triangle' | translate }}</option>\n <option [value]=\"'sawtooth'\">\n {{ 'designer.effect.curve-sawtooth' | translate }}</option>\n <option [value]=\"'reverse-sawtooth'\">\n {{ 'designer.effect.curve-reverse-sawtooth' | translate }}</option> -->\n </select>\n </div>\n <div>\n <canvas #curveGrid class=\"curve-grid\" width=\"200\" height=\"100\" viewBox=\"150 -75 4850 5150\" preserveAspectRatio=\"none\"></canvas>\n </div>\n</div>\n\n<div class=\"mr-4\" style=\"float: left\">\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-length' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.lengthMillis\"\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"0\"\n [min]=\"lengthMillisMin\"\n [max]=\"lengthMillisMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.lengthMillis\" (ngModelChange)=\"setLengthMillis($event); redraw()\" />\n </div>\n <div class=\"my-auto ml-1\">\n {{ 'designer.misc.ms' | translate }}\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-amplitude' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.amplitude\"\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [min]=\"amplitudeMin\"\n [max]=\"amplitudeMax\"\n [step]=\"0.01\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.amplitude\" (ngModelChange)=\"setAmplitude($event); redraw()\" />\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-position' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.position\"\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [min]=\"percentageMin\"\n [max]=\"percentageMax\"\n [step]=\"0.01\"\n >\n </mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.position\" (ngModelChange)=\"setPosition($event); redraw()\" />\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-phase' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.phaseMillis\"\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [min]=\"phaseMillisMin\"\n [max]=\"phaseMillisMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.phaseMillis\" (ngModelChange)=\"setPhaseMillis($event); redraw()\" />\n </div>\n <div class=\"my-auto ml-1\">\n {{ 'designer.misc.ms' | translate }}\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-chase' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.phasingMillis\"\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [min]=\"phasingMillisMin\"\n [max]=\"phasingMillisMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.phasingMillis\" (ngModelChange)=\"setPhasingMillis($event); redraw()\" />\n </div>\n <div class=\"my-auto ml-1\">\n {{ 'designer.misc.ms' | translate }}\n </div>\n </div>\n</div>\n\n<!-- Menu for the capabilities and channels-->\n<div class=\"col col-auto p-0\" style=\"margin-right: 1px; width: 50px; float: left\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n id=\"v-pills-effect-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effect-capabilities-{{ curve?.uuid }}\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n id=\"v-pills-effect-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effect-channels-{{ curve?.uuid }}\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n</div>\n\n<div class=\"h-100\" class=\"applying-to\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-effect-capabilities-{{ curve?.uuid }}\" role=\"tabpanel\">\n <!-- Capabilities-->\n @for (capability of availableCapabilities; track capability; let i = $index) {\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"capability_{{ curve?.uuid + '_' + i }}\"\n [ngModel]=\"capabilityChecked(capability)\"\n (change)=\"toggleCapability($event, capability)\"\n />\n <label class=\"form-check-label\" for=\"capability_{{ curve?.uuid + '_' + i }}\">{{ getCapabilityName(capability) | async }}</label>\n </div>\n }\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-effect-channels-{{ curve?.uuid }}\" role=\"tabpanel\">\n <!-- channels -->\n\n <!-- profile selection -->\n @if (availableProfiles.length > 1) {\n <div>\n @for (profile of availableProfiles; track profile; let i = $index) {\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"channel_profile_{{ curve?.uuid }}_{{ profile.uuid }}\"\n [ngModel]=\"selectedProfiles.indexOf(profile) >= 0\"\n (change)=\"changeProfileSelection($event, profile)\"\n />\n <label class=\"form-check-label\" for=\"channel_profile_{{ curve?.uuid }}_{{ profile.uuid }}\">{{ profile.name }}</label>\n </div>\n }\n <hr class=\"my-1\" style=\"border-color: white\" />\n </div>\n }\n\n <!-- channels -->\n @for (entry of availableChannels | keyvalue; track entry; let i = $index) { @for (channel of entry.value; track channel; let j =\n $index) {\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"channel_{{ curve?.uuid }}_{{ i }}_{{ j }}\"\n [ngModel]=\"channelChecked(entry.key, channel)\"\n (change)=\"toggleChannel($event, entry.key, channel)\"\n />\n <label class=\"form-check-label\" for=\"channel_{{ curve?.uuid }}_{{ i }}_{{ j }}\">{{\n getChannelName(entry.key.name, channel.name)\n }}</label>\n </div>\n } }\n </div>\n </div>\n</div>\n", styles: [".curve-grid{background-color:#000;border:1px solid white}.applying-to{overflow-y:scroll;height:130px!important;background-color:#222;border:1px solid white;padding:.5rem}.applying-to .form-check:last-child{margin-bottom:.5rem}\n"] }]
9478
- }], () => [{ type: PresetService }, { type: AnimationService }, { type: FixtureService }, { type: i4.TranslateService }], { curve: [{
9849
+ args: [{ selector: 'lib-app-effect-curve', standalone: false, template: "<div class=\"mr-3\" style=\"float: left\">\n <div class=\"mb-2\">\n <select\n [ngModel]=\"curve.curveType\"\n (ngModelChange)=\"curve.curveType = $event; presetService.previewLive()\"\n style=\"width: 200px; height: 20px\"\n >\n <option [value]=\"'sine'\">\n {{ 'designer.effect.curve-sine' | translate }}\n </option>\n <option [value]=\"'square'\">\n {{ 'designer.effect.curve-square' | translate }}\n </option>\n <!-- <option [value]=\"'triangle'\">\n {{ 'designer.effect.curve-triangle' | translate }}</option>\n <option [value]=\"'sawtooth'\">\n {{ 'designer.effect.curve-sawtooth' | translate }}</option>\n <option [value]=\"'reverse-sawtooth'\">\n {{ 'designer.effect.curve-reverse-sawtooth' | translate }}</option> -->\n </select>\n </div>\n <div>\n <canvas #curveGrid class=\"curve-grid\" width=\"200\" height=\"100\" viewBox=\"150 -75 4850 5150\" preserveAspectRatio=\"none\"></canvas>\n </div>\n</div>\n\n<div class=\"mr-4\" style=\"float: left\">\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-length' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.lengthMillis\"\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [value]=\"0\"\n [min]=\"lengthMillisMin\"\n [max]=\"lengthMillisMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.lengthMillis\" (ngModelChange)=\"setLengthMillis($event); redraw()\" />\n </div>\n <div class=\"my-auto ml-1\">\n {{ 'designer.misc.ms' | translate }}\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-amplitude' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.amplitude\"\n style=\"width: 80px\"\n [tooltip]=\"'hide'\"\n [min]=\"amplitudeMin\"\n [max]=\"amplitudeMax\"\n [step]=\"0.01\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.amplitude\" (ngModelChange)=\"setAmplitude($event); redraw()\" />\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-position' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.position\"\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [min]=\"percentageMin\"\n [max]=\"percentageMax\"\n [step]=\"0.01\"\n >\n </mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.position\" (ngModelChange)=\"setPosition($event); redraw()\" />\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-phase' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.phaseMillis\"\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [min]=\"phaseMillisMin\"\n [max]=\"phaseMillisMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.phaseMillis\" (ngModelChange)=\"setPhaseMillis($event); redraw()\" />\n </div>\n <div class=\"my-auto ml-1\">\n {{ 'designer.misc.ms' | translate }}\n </div>\n </div>\n\n <div class=\"d-flex\">\n <div class=\"my-auto\" style=\"width: 100px; float: left\">\n {{ 'designer.effect.curve-chase' | translate }}\n </div>\n <div class=\"my-auto\" style=\"float: left\">\n <mv-slider\n (change)=\"redraw(); presetService.previewLive()\"\n [(value)]=\"curve.phasingMillis\"\n style=\"width: 80px; height: 18px\"\n [tooltip]=\"'hide'\"\n [min]=\"phasingMillisMin\"\n [max]=\"phasingMillisMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n <div class=\"my-auto\">\n <input type=\"text\" class=\"slider-input\" [ngModel]=\"curve.phasingMillis\" (ngModelChange)=\"setPhasingMillis($event); redraw()\" />\n </div>\n <div class=\"my-auto ml-1\">\n {{ 'designer.misc.ms' | translate }}\n </div>\n </div>\n</div>\n\n<!-- Menu for the capabilities and channels-->\n<div class=\"col col-auto p-0\" style=\"margin-right: 1px; width: 50px; float: left\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n id=\"v-pills-effect-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effect-capabilities-{{ curve?.uuid }}\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n id=\"v-pills-effect-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effect-channels-{{ curve?.uuid }}\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n</div>\n\n<div class=\"h-100\" class=\"applying-to\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-effect-capabilities-{{ curve?.uuid }}\" role=\"tabpanel\">\n <!-- Capabilities-->\n @for (capability of availableCapabilities; track $index; let i = $index) {\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"capability_{{ curve?.uuid + '_' + i }}\"\n [ngModel]=\"capabilityChecked(capability)\"\n (change)=\"toggleCapability($event, capability)\"\n />\n <label class=\"form-check-label\" for=\"capability_{{ curve?.uuid + '_' + i }}\">{{ getCapabilityName(capability) | async }}</label>\n </div>\n }\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-effect-channels-{{ curve?.uuid }}\" role=\"tabpanel\">\n <!-- channels -->\n\n <!-- profile selection -->\n @if (availableProfiles.length > 1) {\n <div>\n @for (profile of availableProfiles; track profile; let i = $index) {\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"channel_profile_{{ curve?.uuid }}_{{ profile.uuid }}\"\n [ngModel]=\"selectedProfiles.indexOf(profile) >= 0\"\n (change)=\"changeProfileSelection($event, profile)\"\n />\n <label class=\"form-check-label\" for=\"channel_profile_{{ curve?.uuid }}_{{ profile.uuid }}\">{{ profile.name }}</label>\n </div>\n }\n <hr class=\"my-1\" style=\"border-color: white\" />\n </div>\n }\n\n <!-- channels -->\n @for (entry of availableChannels | keyvalue; track entry; let i = $index) { @for (channel of entry.value; track channel; let j =\n $index) {\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"channel_{{ curve?.uuid }}_{{ i }}_{{ j }}\"\n [ngModel]=\"channelChecked(entry.key, channel)\"\n (change)=\"toggleChannel($event, entry.key, channel)\"\n />\n <label class=\"form-check-label\" for=\"channel_{{ curve?.uuid }}_{{ i }}_{{ j }}\">{{\n getChannelName(entry.key.name, channel.name)\n }}</label>\n </div>\n } }\n </div>\n </div>\n</div>\n", styles: [".curve-grid{background-color:#000;border:1px solid white}.applying-to{overflow-y:scroll;height:130px!important;background-color:#222;border:1px solid white;padding:.5rem}.applying-to .form-check:last-child{margin-bottom:.5rem}\n"] }]
9850
+ }], () => [{ type: PresetService }, { type: AnimationService }, { type: FixtureService }, { type: i4.TranslateService }, { type: EffectService }, { type: i0.NgZone }], { curve: [{
9479
9851
  type: Input
9480
9852
  }], isSelected: [{
9481
9853
  type: Input
@@ -9483,9 +9855,9 @@ class EffectCurveComponent {
9483
9855
  type: ViewChild,
9484
9856
  args: ['curveGrid', { static: true }]
9485
9857
  }] }); })();
9486
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EffectCurveComponent, { className: "EffectCurveComponent", filePath: "lib/effect/effect-curve/effect-curve.component.ts", lineNumber: 20 }); })();
9858
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EffectCurveComponent, { className: "EffectCurveComponent", filePath: "lib/effect/effect-curve/effect-curve.component.ts", lineNumber: 21 }); })();
9487
9859
 
9488
- const _c0$3 = ["panTiltGrid"];
9860
+ const _c0$4 = ["panTiltGrid"];
9489
9861
  class EffectPanTiltComponent {
9490
9862
  constructor() {
9491
9863
  this.points = [
@@ -9501,7 +9873,7 @@ class EffectPanTiltComponent {
9501
9873
  ngOnInit() { }
9502
9874
  static { this.ɵfac = function EffectPanTiltComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EffectPanTiltComponent)(); }; }
9503
9875
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EffectPanTiltComponent, selectors: [["lib-app-effect-pan-tilt"]], viewQuery: function EffectPanTiltComponent_Query(rf, ctx) { if (rf & 1) {
9504
- i0.ɵɵviewQuery(_c0$3, 5);
9876
+ i0.ɵɵviewQuery(_c0$4, 5);
9505
9877
  } if (rf & 2) {
9506
9878
  let _t;
9507
9879
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.panTiltGrid = _t.first);
@@ -9866,6 +10238,7 @@ class PresetSettingsComponent {
9866
10238
  }] }); })();
9867
10239
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PresetSettingsComponent, { className: "PresetSettingsComponent", filePath: "lib/preset/preset-settings/preset-settings.component.ts", lineNumber: 11 }); })();
9868
10240
 
10241
+ const _c0$3 = ["tree"];
9869
10242
  function PresetComponent_For_13_Template(rf, ctx) { if (rf & 1) {
9870
10243
  const _r1 = i0.ɵɵgetCurrentView();
9871
10244
  i0.ɵɵelementStart(0, "div", 9);
@@ -9899,8 +10272,89 @@ class PresetComponent {
9899
10272
  this.projectService = projectService;
9900
10273
  this.introService = introService;
9901
10274
  this.modalService = modalService;
10275
+ let actionMapping = {
10276
+ mouse: {
10277
+ click: (tree, node, $event) => {
10278
+ // block activation for folders (or any condition you want)
10279
+ if (node.data.isFolder) {
10280
+ // optional: make click expand/collapse instead
10281
+ TREE_ACTIONS.TOGGLE_EXPANDED(tree, node, $event);
10282
+ return;
10283
+ }
10284
+ TREE_ACTIONS.ACTIVATE(tree, node, $event);
10285
+ },
10286
+ },
10287
+ keys: {
10288
+ [KEYS.ENTER]: (tree, node, $event) => {
10289
+ if (!node.data.isFolder)
10290
+ TREE_ACTIONS.ACTIVATE(tree, node, $event);
10291
+ },
10292
+ [KEYS.SPACE]: (tree, node, $event) => {
10293
+ if (!node.data.isFolder)
10294
+ TREE_ACTIONS.ACTIVATE(tree, node, $event);
10295
+ },
10296
+ },
10297
+ };
10298
+ this.treeOptions = {
10299
+ displayField: 'name',
10300
+ isExpandedField: 'expanded',
10301
+ idField: 'uuid',
10302
+ hasChildrenField: 'nodes',
10303
+ // actionMapping: {
10304
+ // mouse: {
10305
+ // dblClick: (tree, node, $event) => {
10306
+ // if (node.hasChildren) TREE_ACTIONS.TOGGLE_EXPANDED(tree, node, $event);
10307
+ // }
10308
+ // },
10309
+ // keys: {
10310
+ // [KEYS.ENTER]: (tree, node, $event) => {
10311
+ // node.expandAll();
10312
+ // }
10313
+ // }
10314
+ // },
10315
+ actionMapping: actionMapping,
10316
+ nodeHeight: 23,
10317
+ allowDrag: (node) => {
10318
+ return true;
10319
+ },
10320
+ allowDrop: (node, target) => {
10321
+ // only allow dropping into folders or to the root level
10322
+ return !target.parent.data.id || target.parent.data.isFolder;
10323
+ },
10324
+ allowDragoverStyling: true,
10325
+ levelPadding: 10,
10326
+ useVirtualScroll: true,
10327
+ animateExpand: true,
10328
+ scrollOnActivate: true,
10329
+ animateSpeed: 10,
10330
+ animateAcceleration: 1.2,
10331
+ scrollContainer: document.documentElement,
10332
+ };
10333
+ this.treeNodes = [
10334
+ {
10335
+ id: 1,
10336
+ name: 'root1',
10337
+ isFolder: true,
10338
+ children: [
10339
+ { id: 2, name: 'child1', isFolder: false },
10340
+ { id: 3, name: 'child2', isFolder: false },
10341
+ ],
10342
+ },
10343
+ {
10344
+ id: 4,
10345
+ name: 'root1',
10346
+ isFolder: true,
10347
+ children: [
10348
+ { id: 5, name: 'child1', isFolder: false },
10349
+ { id: 6, name: 'child2', isFolder: false },
10350
+ ],
10351
+ },
10352
+ ];
9902
10353
  }
9903
10354
  ngOnInit() { }
10355
+ onActivate(event) {
10356
+ console.log(event);
10357
+ }
9904
10358
  selectPreset(index) {
9905
10359
  this.projectService.project.previewPreset = true;
9906
10360
  this.presetService.selectPreset(index);
@@ -9948,7 +10402,12 @@ class PresetComponent {
9948
10402
  });
9949
10403
  }
9950
10404
  static { this.ɵfac = function PresetComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PresetComponent)(i0.ɵɵdirectiveInject(PresetService), i0.ɵɵdirectiveInject(SceneService), i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(IntroService), i0.ɵɵdirectiveInject(i1$1.BsModalService)); }; }
9951
- static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PresetComponent, selectors: [["lib-app-preset"]], standalone: false, decls: 14, vars: 6, consts: [[1, "card", "border-secondary", "h-100", "panel", "card-intro-preset", "card-intro-active"], [1, "card-header", "d-flex"], [1, "ml-auto"], ["href", "#", 1, "btn", "btn-secondary", "m-0,", "p-0", 3, "click"], ["aria-hidden", "true", 1, "fa", "fa-minus-circle", "fa-fw"], ["aria-hidden", "true", 1, "fa", "fa-plus-circle", "fa-fw"], [1, "card-body", "h-100", "p-0"], [1, "list-group", 3, "sortablejs"], [1, "list-group-item", 3, "active-light", "active", "inactive-list-item"], [1, "list-group-item", 3, "click"], [1, "row", "d-flex", 2, "cursor", "pointer"], [1, "col-auto", "list-sort-handle", "my-auto", 2, "cursor", "move", "cursor", "-webkit-grabbing"], ["aria-hidden", "true", 1, "fa", "fa-bars"], [1, "form-check"], ["type", "checkbox", "id", "active", 1, "form-check-input", 3, "ngModelChange", "click", "disabled", "ngModel"], [1, "my-auto", "ml-auto", "mr-2", "d-flex"], ["type", "button", 1, "btn", "btn-secondary", "btn-sm", "py-0", "px-1", 3, "click"]], template: function PresetComponent_Template(rf, ctx) { if (rf & 1) {
10405
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PresetComponent, selectors: [["lib-app-preset"]], viewQuery: function PresetComponent_Query(rf, ctx) { if (rf & 1) {
10406
+ i0.ɵɵviewQuery(_c0$3, 5);
10407
+ } if (rf & 2) {
10408
+ let _t;
10409
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.tree = _t.first);
10410
+ } }, standalone: false, decls: 14, vars: 6, consts: [[1, "card", "border-secondary", "h-100", "panel", "card-intro-preset", "card-intro-active"], [1, "card-header", "d-flex"], [1, "ml-auto"], ["href", "#", 1, "btn", "btn-secondary", "m-0,", "p-0", 3, "click"], ["aria-hidden", "true", 1, "fa", "fa-minus-circle", "fa-fw"], ["aria-hidden", "true", 1, "fa", "fa-plus-circle", "fa-fw"], [1, "card-body", "h-100", "p-0"], [1, "list-group", 3, "sortablejs"], [1, "list-group-item", 3, "active-light", "active", "inactive-list-item"], [1, "list-group-item", 3, "click"], [1, "row", "d-flex", 2, "cursor", "pointer"], [1, "col-auto", "list-sort-handle", "my-auto", 2, "cursor", "move", "cursor", "-webkit-grabbing"], ["aria-hidden", "true", 1, "fa", "fa-bars"], [1, "form-check"], ["type", "checkbox", "id", "active", 1, "form-check-input", 3, "ngModelChange", "click", "disabled", "ngModel"], [1, "my-auto", "ml-auto", "mr-2", "d-flex"], ["type", "button", 1, "btn", "btn-secondary", "btn-sm", "py-0", "px-1", 3, "click"]], template: function PresetComponent_Template(rf, ctx) { if (rf & 1) {
9952
10411
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div");
9953
10412
  i0.ɵɵtext(3);
9954
10413
  i0.ɵɵpipe(4, "translate");
@@ -9976,9 +10435,12 @@ class PresetComponent {
9976
10435
  }
9977
10436
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PresetComponent, [{
9978
10437
  type: Component,
9979
- args: [{ selector: 'lib-app-preset', standalone: false, template: "<div\n class=\"card border-secondary h-100 panel card-intro-preset card-intro-active\"\n [class.card-intro-active]=\"introService.showStep('presets')\"\n>\n <div class=\"card-header d-flex\">\n <div>\n {{ 'designer.preset.title' | translate }}\n </div>\n <div class=\"ml-auto\">\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"removePreset(); (false)\"\n ><i class=\"fa fa-minus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"addPreset(); (false)\"\n ><i class=\"fa fa-plus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n </div>\n </div>\n\n <div class=\"card-body h-100 p-0\">\n <!-- List of presets -->\n <div class=\"list-group\" [sortablejs]=\"projectService.project.presets\">\n @for (item of projectService.project.presets; track item; let i = $index) {\n <div\n class=\"list-group-item\"\n (click)=\"selectPreset(i)\"\n [class.active-light]=\"!projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.active]=\"projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.inactive-list-item]=\"!sceneService.presetIsSelected(item)\"\n >\n <div class=\"row d-flex\" style=\"cursor: pointer\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n [disabled]=\"!enableCheckbox()\"\n class=\"form-check-input\"\n id=\"active\"\n [ngModel]=\"sceneService.presetIsSelected(item)\"\n (ngModelChange)=\"activatePreset($event, i)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n <div>\n {{ item.name }}\n </div>\n <div class=\"my-auto ml-auto mr-2 d-flex\">\n <button type=\"button\" class=\"btn btn-secondary btn-sm py-0 px-1\" (click)=\"openSettings(item); (false)\">...</button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
9980
- }], () => [{ type: PresetService }, { type: SceneService }, { type: ProjectService }, { type: IntroService }, { type: i1$1.BsModalService }], null); })();
9981
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PresetComponent, { className: "PresetComponent", filePath: "lib/preset/preset.component.ts", lineNumber: 16 }); })();
10438
+ args: [{ selector: 'lib-app-preset', standalone: false, template: "<div\n class=\"card border-secondary h-100 panel card-intro-preset card-intro-active\"\n [class.card-intro-active]=\"introService.showStep('presets')\"\n>\n <div class=\"card-header d-flex\">\n <div>\n {{ 'designer.preset.title' | translate }}\n </div>\n <div class=\"ml-auto\">\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"removePreset(); (false)\"\n ><i class=\"fa fa-minus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"addPreset(); (false)\"\n ><i class=\"fa fa-plus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n </div>\n </div>\n\n <div class=\"card-body h-100 p-0\">\n <!-- List of presets -->\n <!-- <tree-root #tree [nodes]=\"treeNodes\" [options]=\"treeOptions\" (activate)=\"onActivate($event)\">\n <ng-template #treeNodeTemplate let-node let-index=\"index\">\n <span>{{ node.data.name }}</span>\n </ng-template>\n </tree-root> -->\n\n <div class=\"list-group\" [sortablejs]=\"projectService.project.presets\">\n @for (item of projectService.project.presets; track item; let i = $index) {\n <div\n class=\"list-group-item\"\n (click)=\"selectPreset(i)\"\n [class.active-light]=\"!projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.active]=\"projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.inactive-list-item]=\"!sceneService.presetIsSelected(item)\"\n >\n <div class=\"row d-flex\" style=\"cursor: pointer\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n [disabled]=\"!enableCheckbox()\"\n class=\"form-check-input\"\n id=\"active\"\n [ngModel]=\"sceneService.presetIsSelected(item)\"\n (ngModelChange)=\"activatePreset($event, i)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n <div>\n {{ item.name }}\n </div>\n <div class=\"my-auto ml-auto mr-2 d-flex\">\n <button type=\"button\" class=\"btn btn-secondary btn-sm py-0 px-1\" (click)=\"openSettings(item); (false)\">...</button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
10439
+ }], () => [{ type: PresetService }, { type: SceneService }, { type: ProjectService }, { type: IntroService }, { type: i1$1.BsModalService }], { tree: [{
10440
+ type: ViewChild,
10441
+ args: ['tree']
10442
+ }] }); })();
10443
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PresetComponent, { className: "PresetComponent", filePath: "lib/preset/preset.component.ts", lineNumber: 17 }); })();
9982
10444
 
9983
10445
  class MasterDimmerComponent {
9984
10446
  constructor(projectService, changeDetectorRef, presetService) {
@@ -10030,7 +10492,7 @@ class MasterDimmerComponent {
10030
10492
  i0.ɵɵproperty("value", ctx.projectService == null ? null : ctx.projectService.project == null ? null : ctx.projectService.project.masterDimmerValue)("orientation", "horizontal")("tooltip", "hide")("min", 0)("max", 1)("step", 0.001);
10031
10493
  i0.ɵɵadvance(2);
10032
10494
  i0.ɵɵproperty("ngModel", ctx.getValue());
10033
- } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent], encapsulation: 2 }); }
10495
+ } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent], encapsulation: 2 }); }
10034
10496
  }
10035
10497
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MasterDimmerComponent, [{
10036
10498
  type: Component,
@@ -10382,7 +10844,7 @@ class FixtureCapabilityChannelComponent {
10382
10844
  i0.ɵɵconditional((ctx._channel.capabilities == null ? null : ctx._channel.capabilities.length) > 1 ? 9 : -1);
10383
10845
  i0.ɵɵadvance();
10384
10846
  i0.ɵɵconditional(ctx.hasRange ? 10 : -1);
10385
- } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.RadioControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i4$1.SliderComponent, i11.PopoverDirective, i4.TranslatePipe], styles: [".color[_ngcontent-%COMP%]{display:inline-block;margin-left:5px;border-radius:50%;width:12px;height:12px;border:1px white solid;vertical-align:middle}"], changeDetection: 0 }); }
10847
+ } }, dependencies: [i3$2.DefaultValueAccessor, i3$2.CheckboxControlValueAccessor, i3$2.RadioControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, i3$3.SliderComponent, i11.PopoverDirective, i4.TranslatePipe], styles: [".color[_ngcontent-%COMP%]{display:inline-block;margin-left:5px;border-radius:50%;width:12px;height:12px;border:1px white solid;vertical-align:middle}"], changeDetection: 0 }); }
10386
10848
  }
10387
10849
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityChannelComponent, [{
10388
10850
  type: Component,
@@ -10542,7 +11004,7 @@ class FixtureChannelComponent {
10542
11004
  i0.ɵɵconditional(ctx.projectService.project.fixtures.length == 0 ? 3 : -1);
10543
11005
  i0.ɵɵadvance();
10544
11006
  i0.ɵɵconditional(ctx.projectService.project.fixtures.length > 0 && ctx.presetService.selectedPreset && ctx.presetService.selectedPreset.fixtures.length == 0 ? 4 : -1);
10545
- } }, dependencies: [i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, FixtureCapabilityChannelComponent, i8.KeyValuePipe, i4.TranslatePipe], encapsulation: 2 }); }
11007
+ } }, dependencies: [i3$2.CheckboxControlValueAccessor, i3$2.NgControlStatus, i3$2.NgModel, FixtureCapabilityChannelComponent, i9.KeyValuePipe, i4.TranslatePipe], encapsulation: 2 }); }
10546
11008
  }
10547
11009
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureChannelComponent, [{
10548
11010
  type: Component,
@@ -10633,7 +11095,7 @@ class IntroComponent {
10633
11095
  i0.ɵɵconditionalCreate(0, IntroComponent_Conditional_0_Template, 17, 13, "div", 0);
10634
11096
  } if (rf & 2) {
10635
11097
  i0.ɵɵconditional(ctx.introService.showIntro ? 0 : -1);
10636
- } }, dependencies: [i8.NgStyle, i4.TranslatePipe], styles: [".card[_ngcontent-%COMP%]{position:absolute;width:400px;height:350px!important;z-index:999999}"] }); }
11098
+ } }, dependencies: [i9.NgStyle, i4.TranslatePipe], styles: [".card[_ngcontent-%COMP%]{position:absolute;width:400px;height:350px!important;z-index:999999}"] }); }
10637
11099
  }
10638
11100
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(IntroComponent, [{
10639
11101
  type: Component,
@@ -10821,7 +11283,7 @@ class DesignerComponent {
10821
11283
  set dropzoneChunking(value) {
10822
11284
  this.configService.dropzoneChunking = value;
10823
11285
  }
10824
- constructor(translateService, projectService, configService, fixturePoolService, hotkeyTargetExcludeService, modalService, userService, userEnsureLoginService, projectLoadService, warningDialogService, errorDialogService, fixtureService, timelineService, introService) {
11286
+ constructor(translateService, projectService, configService, fixturePoolService, hotkeyTargetExcludeService, modalService, userService, userEnsureLoginService, projectLoadService, warningDialogService, errorDialogService, fixtureService, timelineService, introService, effectService) {
10825
11287
  this.translateService = translateService;
10826
11288
  this.projectService = projectService;
10827
11289
  this.configService = configService;
@@ -10836,6 +11298,7 @@ class DesignerComponent {
10836
11298
  this.fixtureService = fixtureService;
10837
11299
  this.timelineService = timelineService;
10838
11300
  this.introService = introService;
11301
+ this.effectService = effectService;
10839
11302
  // the size of the menu used in the designer
10840
11303
  this.designerMenuSizePx = 20;
10841
11304
  this.splitGutterSizePx = 13;
@@ -10950,6 +11413,13 @@ class DesignerComponent {
10950
11413
  else {
10951
11414
  this.fixtureService.settingsSelection = false;
10952
11415
  }
11416
+ if (tab === 'effects') {
11417
+ this.effectService.effectsOpen = true;
11418
+ }
11419
+ else {
11420
+ this.effectService.effectsOpen = false;
11421
+ }
11422
+ this.effectService.effectsOpenChanged.next();
10953
11423
  this.currentTab = tab;
10954
11424
  }
10955
11425
  openFixturePool() {
@@ -11056,7 +11526,7 @@ class DesignerComponent {
11056
11526
  this.translateService.use(language);
11057
11527
  localStorage.setItem('language', language);
11058
11528
  }
11059
- static { this.ɵfac = function DesignerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DesignerComponent)(i0.ɵɵdirectiveInject(i4.TranslateService), i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(ConfigService), i0.ɵɵdirectiveInject(FixturePoolService), i0.ɵɵdirectiveInject(HotkeyTargetExcludeService), i0.ɵɵdirectiveInject(i1$1.BsModalService), i0.ɵɵdirectiveInject(UserService), i0.ɵɵdirectiveInject(UserEnsureLoginService), i0.ɵɵdirectiveInject(ProjectLoadService), i0.ɵɵdirectiveInject(WarningDialogService), i0.ɵɵdirectiveInject(ErrorDialogService), i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(TimelineService), i0.ɵɵdirectiveInject(IntroService)); }; }
11529
+ static { this.ɵfac = function DesignerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DesignerComponent)(i0.ɵɵdirectiveInject(i4.TranslateService), i0.ɵɵdirectiveInject(ProjectService), i0.ɵɵdirectiveInject(ConfigService), i0.ɵɵdirectiveInject(FixturePoolService), i0.ɵɵdirectiveInject(HotkeyTargetExcludeService), i0.ɵɵdirectiveInject(i1$1.BsModalService), i0.ɵɵdirectiveInject(UserService), i0.ɵɵdirectiveInject(UserEnsureLoginService), i0.ɵɵdirectiveInject(ProjectLoadService), i0.ɵɵdirectiveInject(WarningDialogService), i0.ɵɵdirectiveInject(ErrorDialogService), i0.ɵɵdirectiveInject(FixtureService), i0.ɵɵdirectiveInject(TimelineService), i0.ɵɵdirectiveInject(IntroService), i0.ɵɵdirectiveInject(EffectService)); }; }
11060
11530
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DesignerComponent, selectors: [["lib-designer"]], viewQuery: function DesignerComponent_Query(rf, ctx) { if (rf & 1) {
11061
11531
  i0.ɵɵviewQuery(PreviewComponent, 5);
11062
11532
  i0.ɵɵviewQuery(TimelineComponent, 5);
@@ -11219,12 +11689,12 @@ class DesignerComponent {
11219
11689
  i0.ɵɵconditional(ctx.projectService.project ? 76 : -1);
11220
11690
  i0.ɵɵadvance(2);
11221
11691
  i0.ɵɵconditional(ctx.projectService.project ? 78 : -1);
11222
- } }, dependencies: [i3.RouterLinkActive, i8.NgStyle, i11.PopoverDirective, PreviewComponent, SceneComponent, TimelineComponent, FixtureComponent, FixtureCapabilityComponent, FixtureSettingsComponent, EffectComponent, PresetComponent, MasterDimmerComponent, FixtureChannelComponent, IntroComponent, i4.TranslatePipe], styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"], encapsulation: 2 }); }
11692
+ } }, dependencies: [i3.RouterLinkActive, i9.NgStyle, i11.PopoverDirective, PreviewComponent, SceneComponent, TimelineComponent, FixtureComponent, FixtureCapabilityComponent, FixtureSettingsComponent, EffectComponent, PresetComponent, MasterDimmerComponent, FixtureChannelComponent, IntroComponent, i4.TranslatePipe], styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"], encapsulation: 2 }); }
11223
11693
  }
11224
11694
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DesignerComponent, [{
11225
11695
  type: Component,
11226
11696
  args: [{ selector: 'lib-designer', encapsulation: ViewEncapsulation.None, standalone: false, template: "<!-- Wrap the whole component inside a div to encapsulate the CSS -->\n\n<!-- 100 % height minus the menu bar above and the gutter size of 13 px -->\n<!-- <div style=\"height: calc(100% - 33px)\"> -->\n<div id=\"designer\" [ngStyle]=\"{ height: 'calc(100% - ' + totalMenuHeightPx + 'px)' }\">\n <!-- Navigation -->\n <nav\n class=\"navbar navbar-expand-lg navbar-light bg-light border-secondary\"\n style=\"border-bottom: 1px solid #444; height: 20px; z-index: 9999\"\n >\n <!-- Menu buttons -->\n <ul class=\"navbar-nav mr-auto\">\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"fileDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n {{ 'designer.navigation.project' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"fileDropdown\">\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectNew(); (false)\"\n ><i class=\"fa fa-fw fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-new' | translate }}</a\n >\n <a class=\"dropdown-item\" href=\"#\" (click)=\"projectOpen(); (false)\"\n ><i class=\"fa fa-fw fa-folder-open-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-open' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSave(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSaveAs(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save-as' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectImport(); (false)\"\n ><i class=\"fa fa-fw fa-download\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-import' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectExport(); (false)\"\n ><i class=\"fa fa-fw fa-external-link\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-export' | translate }}</a\n >\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.settings' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"openFixturePool(); (false)\">{{ 'designer.navigation.fixture-pool' | translate }}</a>\n <!-- <a class=\"dropdown-item\" href=\"#\">{{ 'designer.navigation.stage' | translate }}</a> -->\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.help' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"introService.reset(); (false)\">{{ 'designer.navigation.reset-intro' | translate }}</a>\n <a class=\"dropdown-item\" href=\"https://rocketshow.net/support/\" target=\"_blank\">{{\n 'designer.navigation.support' | translate\n }}</a>\n </div>\n </li>\n </ul>\n\n <ul class=\"navbar-nav\">\n @if (configService.shareAvailable) {\n <li class=\"nav-item d-none d-md-flex\" data-toggle=\"collapse\">\n <a class=\"nav-link text-primary\" (click)=\"projectShare(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-share-alt\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-share' | translate }}</a\n >\n </li>\n } @if (configService.languageSwitch) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-globe\" aria-hidden=\"true\"></i>\n {{ 'designer.misc.language' | translate }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('en'); (false)\" target=\"_blank\">English</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('de'); (false)\" target=\"_blank\">Deutsch</a>\n </div>\n </li>\n } @if (configService.loginAvailable && !userService.isLoggedIn()) {\n <li class=\"nav-item\" data-toggle=\"collapse\">\n <a class=\"nav-link\" (click)=\"userRegister(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i> {{ 'designer.navigation.login' | translate }}</a\n >\n </li>\n } @if (configService.loginAvailable && userService.isLoggedIn()) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i>\n {{ userService.username }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"userService.logout(); (false)\" target=\"_blank\"\n ><i class=\"fa fa-sign-out\" aria-hidden=\"true\"></i> {{ 'designer.navigation.logout' | translate }}</a\n >\n </div>\n </li>\n }\n </ul>\n </nav>\n\n <!-- The main content for larger screens -->\n <div class=\"h-100 d-none d-md-block\">\n <div id=\"row1\" class=\"split split-vertical clearfix pt-3 pl-3\">\n <!-- Scenes -->\n <div id=\"scenes\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n\n <!-- Presets -->\n <div id=\"presets\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-preset></lib-app-preset>\n }\n </div>\n\n <!-- 3D preview -->\n <div id=\"preview\" class=\"split split-horizontal h-100 pr-3\">\n @if (projectService.project) {\n <div class=\"card border-secondary h-100\" [class.card-intro-active]=\"introService.showStep('preview')\">\n <div class=\"card-body p-0 h-100\">\n <lib-app-preview class=\"h-100\"></lib-app-preview>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div id=\"row2\" class=\"split split-vertical clearfix px-3 d-flex\" style=\"flex-direction: row\">\n <!-- Menu for the capabilities, channels, effects and settings -->\n @if (projectService.project) {\n <div class=\"col col-auto p-0\" style=\"margin-right: -1px\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n (click)=\"openTab('capabilities')\"\n id=\"v-pills-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-capabilities\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('channels')\"\n id=\"v-pills-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-channels\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('effects')\"\n id=\"v-pills-effects-tab\"\n popover=\"{{ 'designer.misc.effects' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effects\"\n role=\"tab\"\n aria-controls=\"v-pills-effects\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-magic fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('settings')\"\n id=\"v-pills-settings-tab\"\n popover=\"{{ 'designer.misc.settings' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-settings\"\n role=\"tab\"\n aria-controls=\"v-pills-settings\"\n aria-selected=\"false\"\n style=\"border-top: 1px solid #444\"\n >\n <i class=\"fa fa-cog fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n }\n\n <div id=\"capabilities\" class=\"split split-horizontal h-100\" style=\"padding-left: 1rem\">\n @if (projectService.project) {\n <div class=\"row h-100\">\n <!-- Content for the capabilities, channels, effects and settings -->\n <div class=\"col pl-0 h-100\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-capabilities\" role=\"tabpanel\">\n <lib-app-fixture-capability></lib-app-fixture-capability>\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-channels\" role=\"tabpanel\">\n <lib-app-fixture-channel></lib-app-fixture-channel>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-effects\" role=\"tabpanel\" aria-labelledby=\"v-pills-profile-tab\">\n <lib-app-effect></lib-app-effect>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-settings\" role=\"tabpanel\" aria-labelledby=\"v-pills-home-tab\">\n <lib-app-fixture-settings></lib-app-fixture-settings>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Fixtures -->\n <div id=\"fixtures\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-fixture></lib-app-fixture>\n }\n </div>\n\n <!-- Master dimmer -->\n <div id=\"masterDimmer\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n </div>\n\n <!-- Timeline -->\n <div id=\"row3\" class=\"split split-vertical clearfix px-3 pb-3\" style=\"padding-bottom: 20px\">\n @if (projectService.project) {\n <lib-app-timeline></lib-app-timeline>\n }\n </div>\n </div>\n\n <!-- The components for small screens -->\n <div class=\"d-md-none p-3\">\n <div>\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n </div>\n</div>\n\n<lib-app-intro class=\"d-none d-md-flex\"></lib-app-intro>\n", styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"] }]
11227
- }], () => [{ type: i4.TranslateService }, { type: ProjectService }, { type: ConfigService }, { type: FixturePoolService }, { type: HotkeyTargetExcludeService }, { type: i1$1.BsModalService }, { type: UserService }, { type: UserEnsureLoginService }, { type: ProjectLoadService }, { type: WarningDialogService }, { type: ErrorDialogService }, { type: FixtureService }, { type: TimelineService }, { type: IntroService }], { menuHeightPx: [{
11697
+ }], () => [{ type: i4.TranslateService }, { type: ProjectService }, { type: ConfigService }, { type: FixturePoolService }, { type: HotkeyTargetExcludeService }, { type: i1$1.BsModalService }, { type: UserService }, { type: UserEnsureLoginService }, { type: ProjectLoadService }, { type: WarningDialogService }, { type: ErrorDialogService }, { type: FixtureService }, { type: TimelineService }, { type: IntroService }, { type: EffectService }], { menuHeightPx: [{
11228
11698
  type: Input
11229
11699
  }], externalCompositionsAvailable: [{
11230
11700
  type: Input
@@ -11260,7 +11730,7 @@ class DesignerComponent {
11260
11730
  type: HostListener,
11261
11731
  args: ['document:keydown', ['$event']]
11262
11732
  }] }); })();
11263
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DesignerComponent, { className: "DesignerComponent", filePath: "lib/designer.component.ts", lineNumber: 34 }); })();
11733
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DesignerComponent, { className: "DesignerComponent", filePath: "lib/designer.component.ts", lineNumber: 35 }); })();
11264
11734
 
11265
11735
  class DesignerModule {
11266
11736
  static { this.ɵfac = function DesignerModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DesignerModule)(); }; }
@@ -11278,7 +11748,8 @@ class DesignerModule {
11278
11748
  DropzoneModule,
11279
11749
  ToastrModule.forRoot({
11280
11750
  newestOnTop: true,
11281
- })] }); }
11751
+ }),
11752
+ TreeModule] }); }
11282
11753
  }
11283
11754
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DesignerModule, [{
11284
11755
  type: NgModule,
@@ -11339,6 +11810,7 @@ class DesignerModule {
11339
11810
  ToastrModule.forRoot({
11340
11811
  newestOnTop: true,
11341
11812
  }),
11813
+ TreeModule,
11342
11814
  ],
11343
11815
  providers: [provideHttpClient(withInterceptorsFromDi())],
11344
11816
  }]
@@ -11389,7 +11861,7 @@ class DesignerModule {
11389
11861
  PopoverModule,
11390
11862
  TypeaheadModule,
11391
11863
  SortablejsModule,
11392
- DropzoneModule, i3$1.ToastrModule], exports: [DesignerComponent] }); })();
11864
+ DropzoneModule, i3$1.ToastrModule, TreeModule], exports: [DesignerComponent] }); })();
11393
11865
 
11394
11866
  /*
11395
11867
  * Public API Surface of designer