@grandgular/rive-angular 0.3.0 → 0.4.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.
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { inject, PLATFORM_ID, Injectable, InjectionToken, viewChild, DestroyRef, NgZone, input, output, signal, effect, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { isPlatformBrowser } from '@angular/common';
4
4
  import { Fit, Alignment, Layout, Rive, RiveFile, EventType } from '@rive-app/canvas';
5
- export { Alignment, EventType, Fit, Layout, Rive, RiveFile, StateMachineInput } from '@rive-app/canvas';
5
+ export { Alignment, EventType, Fit, Layout, Rive, RiveFile, StateMachineInput, ViewModelInstance } from '@rive-app/canvas';
6
6
 
7
7
  /**
8
8
  * Re-export Rive SDK types for consumer convenience
@@ -61,6 +61,7 @@ class RiveValidationError extends Error {
61
61
  * - RIVE_1xx: Load errors (file not found, network, bad format)
62
62
  * - RIVE_2xx: Validation errors (artboard, animation, state machine mismatch)
63
63
  * - RIVE_3xx: Configuration/Usage errors (missing source, bad canvas)
64
+ * - RIVE_4xx: Data Binding errors (ViewModel, property not found, type mismatch)
64
65
  */
65
66
  var RiveErrorCode;
66
67
  (function (RiveErrorCode) {
@@ -77,6 +78,10 @@ var RiveErrorCode;
77
78
  // Configuration Errors
78
79
  RiveErrorCode["NoSource"] = "RIVE_301";
79
80
  RiveErrorCode["InvalidCanvas"] = "RIVE_302";
81
+ // Data Binding Errors
82
+ RiveErrorCode["ViewModelNotFound"] = "RIVE_401";
83
+ RiveErrorCode["DataBindingPropertyNotFound"] = "RIVE_402";
84
+ RiveErrorCode["DataBindingTypeMismatch"] = "RIVE_403";
80
85
  })(RiveErrorCode || (RiveErrorCode = {}));
81
86
  /**
82
87
  * Template messages for each error code.
@@ -93,6 +98,9 @@ const ERROR_MESSAGES = {
93
98
  [RiveErrorCode.TextRunNotFound]: 'Text run "{name}" not found',
94
99
  [RiveErrorCode.NoSource]: 'No animation source provided',
95
100
  [RiveErrorCode.InvalidCanvas]: 'Invalid canvas element',
101
+ [RiveErrorCode.ViewModelNotFound]: 'ViewModel "{name}" not found',
102
+ [RiveErrorCode.DataBindingPropertyNotFound]: 'Data binding property "{path}" not found in ViewModel',
103
+ [RiveErrorCode.DataBindingTypeMismatch]: 'Data binding type mismatch for "{path}": expected {expected}, got {actual}',
96
104
  };
97
105
  /**
98
106
  * Formats an error message by replacing placeholders with actual values.
@@ -401,6 +409,123 @@ function validateConfiguration(rive, config, logger) {
401
409
  return errors;
402
410
  }
403
411
 
412
+ /**
413
+ * Parses various color input formats into a normalized RiveColor object.
414
+ *
415
+ * Supported formats:
416
+ * - Hex string: '#RRGGBB' or '#RRGGBBAA'
417
+ * - ARGB integer: 0xAARRGGBB (32-bit integer)
418
+ * - RiveColor object: { r, g, b, a? }
419
+ *
420
+ * @param input - Color in any supported format
421
+ * @returns Normalized RiveColor object with all components in 0-255 range
422
+ * @throws Error if the input format is invalid
423
+ *
424
+ * @example
425
+ * parseRiveColor('#FF5733') // { r: 255, g: 87, b: 51, a: 255 }
426
+ * parseRiveColor('#FF573380') // { r: 255, g: 87, b: 51, a: 128 }
427
+ * parseRiveColor(0x80FF5733) // { r: 255, g: 87, b: 51, a: 128 }
428
+ * parseRiveColor({ r: 255, g: 0, b: 0 }) // { r: 255, g: 0, b: 0, a: 255 }
429
+ */
430
+ function parseRiveColor(input) {
431
+ // If already a RiveColor object, normalize it
432
+ if (typeof input === 'object' && input !== null) {
433
+ return {
434
+ r: clamp(Math.round(input.r), 0, 255),
435
+ g: clamp(Math.round(input.g), 0, 255),
436
+ b: clamp(Math.round(input.b), 0, 255),
437
+ a: clamp(Math.round(input.a ?? 255), 0, 255),
438
+ };
439
+ }
440
+ // If hex string
441
+ if (typeof input === 'string') {
442
+ return parseHexColor(input);
443
+ }
444
+ // If ARGB integer
445
+ if (typeof input === 'number') {
446
+ return parseArgbInteger(input);
447
+ }
448
+ throw new Error(`Invalid color format: ${input}. Expected hex string, ARGB integer, or RiveColor object.`);
449
+ }
450
+ /**
451
+ * Converts a RiveColor object to an ARGB 32-bit integer.
452
+ *
453
+ * @param color - RiveColor object
454
+ * @returns ARGB integer in format 0xAARRGGBB
455
+ *
456
+ * @example
457
+ * riveColorToArgb({ r: 255, g: 0, b: 0, a: 255 }) // 0xFFFF0000
458
+ * riveColorToArgb({ r: 0, g: 128, b: 255, a: 128 }) // 0x800080FF
459
+ */
460
+ function riveColorToArgb(color) {
461
+ const a = clamp(Math.round(color.a), 0, 255);
462
+ const r = clamp(Math.round(color.r), 0, 255);
463
+ const g = clamp(Math.round(color.g), 0, 255);
464
+ const b = clamp(Math.round(color.b), 0, 255);
465
+ return ((a << 24) | (r << 16) | (g << 8) | b) >>> 0;
466
+ }
467
+ /**
468
+ * Converts a RiveColor object to a hex string.
469
+ *
470
+ * @param color - RiveColor object
471
+ * @returns Hex string in format '#RRGGBBAA'
472
+ *
473
+ * @example
474
+ * riveColorToHex({ r: 255, g: 0, b: 0, a: 255 }) // '#FF0000FF'
475
+ * riveColorToHex({ r: 0, g: 128, b: 255, a: 128 }) // '#0080FF80'
476
+ */
477
+ function riveColorToHex(color) {
478
+ const r = clamp(Math.round(color.r), 0, 255)
479
+ .toString(16)
480
+ .padStart(2, '0');
481
+ const g = clamp(Math.round(color.g), 0, 255)
482
+ .toString(16)
483
+ .padStart(2, '0');
484
+ const b = clamp(Math.round(color.b), 0, 255)
485
+ .toString(16)
486
+ .padStart(2, '0');
487
+ const a = clamp(Math.round(color.a), 0, 255)
488
+ .toString(16)
489
+ .padStart(2, '0');
490
+ return `#${r}${g}${b}${a}`.toUpperCase();
491
+ }
492
+ /**
493
+ * Parses a hex color string into a RiveColor object.
494
+ * Supports both '#RRGGBB' and '#RRGGBBAA' formats.
495
+ */
496
+ function parseHexColor(hex) {
497
+ // Remove '#' if present
498
+ const cleanHex = hex.startsWith('#') ? hex.slice(1) : hex;
499
+ // Validate hex string
500
+ if (!/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/.test(cleanHex)) {
501
+ throw new Error(`Invalid hex color format: ${hex}. Expected '#RRGGBB' or '#RRGGBBAA'.`);
502
+ }
503
+ const r = parseInt(cleanHex.slice(0, 2), 16);
504
+ const g = parseInt(cleanHex.slice(2, 4), 16);
505
+ const b = parseInt(cleanHex.slice(4, 6), 16);
506
+ const a = cleanHex.length === 8 ? parseInt(cleanHex.slice(6, 8), 16) : 255;
507
+ return { r, g, b, a };
508
+ }
509
+ /**
510
+ * Parses an ARGB 32-bit integer into a RiveColor object.
511
+ * Format: 0xAARRGGBB
512
+ */
513
+ function parseArgbInteger(argb) {
514
+ // Ensure it's a valid 32-bit unsigned integer
515
+ const value = argb >>> 0;
516
+ const a = (value >> 24) & 0xff;
517
+ const r = (value >> 16) & 0xff;
518
+ const g = (value >> 8) & 0xff;
519
+ const b = value & 0xff;
520
+ return { r, g, b, a };
521
+ }
522
+ /**
523
+ * Clamps a value between min and max.
524
+ */
525
+ function clamp(value, min, max) {
526
+ return Math.max(min, Math.min(max, value));
527
+ }
528
+
404
529
  /**
405
530
  * Standalone Angular component for Rive animations
406
531
  *
@@ -475,6 +600,30 @@ class RiveCanvasComponent {
475
600
  * Values are applied reactively when input changes.
476
601
  */
477
602
  textRuns = input(...(ngDevMode ? [undefined, { debugName: "textRuns" }] : []));
603
+ /**
604
+ * Name of the ViewModel to use for data binding.
605
+ * If not provided, uses the default ViewModel for the artboard.
606
+ * Only relevant if the .riv file contains ViewModels.
607
+ */
608
+ viewModelName = input(...(ngDevMode ? [undefined, { debugName: "viewModelName" }] : []));
609
+ /**
610
+ * Record of ViewModel property paths to values for declarative data binding.
611
+ * Keys present in this input are CONTROLLED — the input is the source of truth.
612
+ * Keys absent from this input are UNCONTROLLED — managed imperatively.
613
+ * Values are applied reactively when input changes.
614
+ *
615
+ * Supports multiple data types: number, string, boolean, RiveColor.
616
+ * The component auto-detects the property type from the ViewModel.
617
+ *
618
+ * @example
619
+ * [dataBindings]="{
620
+ * backgroundColor: '#FF5733',
621
+ * score: 42,
622
+ * playerName: 'Alice',
623
+ * isActive: true
624
+ * }"
625
+ */
626
+ dataBindings = input(...(ngDevMode ? [undefined, { debugName: "dataBindings" }] : []));
478
627
  // Outputs (Events)
479
628
  loaded = output();
480
629
  loadError = output();
@@ -494,11 +643,18 @@ class RiveCanvasComponent {
494
643
  * Note: This fires AFTER the animation is loaded, not just instantiated.
495
644
  */
496
645
  riveReady = output();
646
+ /**
647
+ * Emitted when a ViewModel property changes from within the animation.
648
+ * Enables two-way data binding between the animation and Angular application.
649
+ * Only fires if the .riv file uses ViewModels with callbacks.
650
+ */
651
+ dataBindingChange = output();
497
652
  // Private writable signals
498
653
  #isPlaying = signal(false, ...(ngDevMode ? [{ debugName: "#isPlaying" }] : []));
499
654
  #isPaused = signal(false, ...(ngDevMode ? [{ debugName: "#isPaused" }] : []));
500
655
  #isLoaded = signal(false, ...(ngDevMode ? [{ debugName: "#isLoaded" }] : []));
501
656
  #riveInstance = signal(null, ...(ngDevMode ? [{ debugName: "#riveInstance" }] : []));
657
+ #viewModelInstance = signal(null, ...(ngDevMode ? [{ debugName: "#viewModelInstance" }] : []));
502
658
  // Public readonly signals
503
659
  isPlaying = this.#isPlaying.asReadonly();
504
660
  isPaused = this.#isPaused.asReadonly();
@@ -508,12 +664,20 @@ class RiveCanvasComponent {
508
664
  * Use this to access advanced Rive SDK features.
509
665
  */
510
666
  riveInstance = this.#riveInstance.asReadonly();
667
+ /**
668
+ * Public signal providing access to the ViewModel instance.
669
+ * Use this to access advanced ViewModel features for data binding.
670
+ * Returns null if the .riv file doesn't use ViewModels.
671
+ */
672
+ viewModelInstance = this.#viewModelInstance.asReadonly();
511
673
  // Private state
512
674
  #rive = null;
513
675
  logger;
514
676
  resizeObserver = null;
515
677
  isInitialized = false;
516
678
  isPausedByIntersectionObserver = false;
679
+ #viewModelSubscriptionDisposers = new Set();
680
+ #localMutationSuppressions = new Map();
517
681
  retestIntersectionTimeoutId = null;
518
682
  resizeRafId = null;
519
683
  lastWidth = 0;
@@ -561,6 +725,27 @@ class RiveCanvasComponent {
561
725
  }
562
726
  });
563
727
  });
728
+ // Effect to apply data bindings when input changes or animation loads
729
+ effect(() => {
730
+ const bindings = this.dataBindings();
731
+ const isLoaded = this.#isLoaded();
732
+ const vmi = this.#viewModelInstance();
733
+ untracked(() => {
734
+ if (bindings && isLoaded && vmi) {
735
+ this.applyDataBindings(bindings);
736
+ }
737
+ });
738
+ });
739
+ // Effect to reinitialize ViewModel when viewModelName changes after load
740
+ effect(() => {
741
+ const viewModelName = this.viewModelName();
742
+ const isLoaded = this.#isLoaded();
743
+ untracked(() => {
744
+ if (isLoaded && this.#rive) {
745
+ this.initializeViewModel();
746
+ }
747
+ });
748
+ });
564
749
  // Auto cleanup on destroy
565
750
  this.#destroyRef.onDestroy(() => {
566
751
  this.cleanupRive();
@@ -796,6 +981,8 @@ class RiveCanvasComponent {
796
981
  animations: riveWithMetadata.animationNames,
797
982
  stateMachines: riveWithMetadata.stateMachineNames,
798
983
  });
984
+ // Initialize ViewModel if available
985
+ this.initializeViewModel();
799
986
  }
800
987
  this.#ngZone.run(() => {
801
988
  this.#isLoaded.set(true);
@@ -1032,6 +1219,204 @@ class RiveCanvasComponent {
1032
1219
  }
1033
1220
  });
1034
1221
  }
1222
+ // ========================================================================
1223
+ // Data Binding (ViewModel) Methods
1224
+ // ========================================================================
1225
+ /**
1226
+ * Set a data binding value in the ViewModel.
1227
+ * Auto-detects the property type and applies the value accordingly.
1228
+ * Warning: If the property is controlled by dataBindings input, this change
1229
+ * will be overwritten on the next input update.
1230
+ */
1231
+ setDataBinding(path, value) {
1232
+ const vmi = this.#viewModelInstance();
1233
+ if (!vmi) {
1234
+ this.logger.warn('No ViewModel instance available');
1235
+ return;
1236
+ }
1237
+ // Check if this key is controlled by dataBindings input
1238
+ const controlledBindings = this.dataBindings();
1239
+ if (controlledBindings && path in controlledBindings) {
1240
+ this.logger.warn(`Data binding "${path}" is controlled by dataBindings input. This change will be overwritten on next input update.`);
1241
+ }
1242
+ this.#ngZone.runOutsideAngular(() => {
1243
+ this.withLocalMutation(path, () => {
1244
+ const resolved = this.resolveViewModelProperty(vmi, path);
1245
+ if (!resolved) {
1246
+ this.logger.warn(`Data binding property "${path}" not found in ViewModel`);
1247
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.DataBindingPropertyNotFound, {
1248
+ path,
1249
+ }), RiveErrorCode.DataBindingPropertyNotFound)));
1250
+ return;
1251
+ }
1252
+ this.tryApplyBinding(path, value, resolved);
1253
+ });
1254
+ });
1255
+ }
1256
+ /**
1257
+ * Get a data binding value from the ViewModel.
1258
+ * Auto-detects the property type and returns the value accordingly.
1259
+ * Returns undefined if the property doesn't exist or ViewModel is not loaded.
1260
+ */
1261
+ getDataBinding(path) {
1262
+ const vmi = this.#viewModelInstance();
1263
+ if (!vmi)
1264
+ return undefined;
1265
+ return this.#ngZone.runOutsideAngular(() => {
1266
+ // Try each property type
1267
+ const colorProp = vmi.color(path);
1268
+ if (colorProp) {
1269
+ const argb = colorProp.value;
1270
+ const a = (argb >> 24) & 0xff;
1271
+ const r = (argb >> 16) & 0xff;
1272
+ const g = (argb >> 8) & 0xff;
1273
+ const b = argb & 0xff;
1274
+ return { r, g, b, a };
1275
+ }
1276
+ const numberProp = vmi.number(path);
1277
+ if (numberProp)
1278
+ return numberProp.value;
1279
+ const stringProp = vmi.string(path);
1280
+ if (stringProp)
1281
+ return stringProp.value;
1282
+ const boolProp = vmi.boolean(path);
1283
+ if (boolProp)
1284
+ return boolProp.value;
1285
+ const enumProp = vmi.enum(path);
1286
+ if (enumProp)
1287
+ return enumProp.value;
1288
+ return undefined;
1289
+ });
1290
+ }
1291
+ /**
1292
+ * Fire a trigger property in the ViewModel.
1293
+ * Use this for ViewModel-based triggers (data binding).
1294
+ * For state machine triggers, use fireTrigger(stateMachineName, triggerName).
1295
+ */
1296
+ fireViewModelTrigger(path) {
1297
+ const vmi = this.#viewModelInstance();
1298
+ if (!vmi) {
1299
+ this.logger.warn('No ViewModel instance available');
1300
+ return;
1301
+ }
1302
+ this.#ngZone.runOutsideAngular(() => {
1303
+ const triggerProp = vmi.trigger(path);
1304
+ if (triggerProp) {
1305
+ triggerProp.trigger();
1306
+ this.logger.debug(`ViewModel trigger "${path}" fired`);
1307
+ }
1308
+ else {
1309
+ this.logger.warn(`ViewModel trigger "${path}" not found`);
1310
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.DataBindingPropertyNotFound, {
1311
+ path,
1312
+ }), RiveErrorCode.DataBindingPropertyNotFound)));
1313
+ }
1314
+ });
1315
+ }
1316
+ /**
1317
+ * Set a color value in the ViewModel.
1318
+ * Accepts hex string ('#RRGGBB' or '#RRGGBBAA'), ARGB integer, or RiveColor object.
1319
+ * Warning: If the property is controlled by dataBindings input, this change
1320
+ * will be overwritten on the next input update.
1321
+ */
1322
+ setColor(path, color) {
1323
+ const vmi = this.#viewModelInstance();
1324
+ if (!vmi) {
1325
+ this.logger.warn('No ViewModel instance available');
1326
+ return;
1327
+ }
1328
+ // Check if this key is controlled by dataBindings input
1329
+ const controlledBindings = this.dataBindings();
1330
+ if (controlledBindings && path in controlledBindings) {
1331
+ this.logger.warn(`Color "${path}" is controlled by dataBindings input. This change will be overwritten on next input update.`);
1332
+ }
1333
+ this.#ngZone.runOutsideAngular(() => {
1334
+ this.withLocalMutation(path, () => {
1335
+ const colorProp = vmi.color(path);
1336
+ if (!colorProp) {
1337
+ this.logger.warn(`Color property "${path}" not found in ViewModel`);
1338
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.DataBindingPropertyNotFound, {
1339
+ path,
1340
+ }), RiveErrorCode.DataBindingPropertyNotFound)));
1341
+ return;
1342
+ }
1343
+ try {
1344
+ const parsedColor = parseRiveColor(color);
1345
+ colorProp.rgba(parsedColor.r, parsedColor.g, parsedColor.b, parsedColor.a);
1346
+ this.logger.debug(`Color "${path}" set to:`, parsedColor);
1347
+ }
1348
+ catch (error) {
1349
+ this.logger.warn(`Failed to set color "${path}":`, error);
1350
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(`Failed to parse color value for "${path}": ${error instanceof Error ? error.message : String(error)}`, RiveErrorCode.DataBindingTypeMismatch)));
1351
+ }
1352
+ });
1353
+ });
1354
+ }
1355
+ /**
1356
+ * Get a color value from the ViewModel.
1357
+ * Returns undefined if the property doesn't exist or ViewModel is not loaded.
1358
+ */
1359
+ getColor(path) {
1360
+ const vmi = this.#viewModelInstance();
1361
+ if (!vmi)
1362
+ return undefined;
1363
+ return this.#ngZone.runOutsideAngular(() => {
1364
+ const colorProp = vmi.color(path);
1365
+ if (!colorProp)
1366
+ return undefined;
1367
+ const argb = colorProp.value;
1368
+ const a = (argb >> 24) & 0xff;
1369
+ const r = (argb >> 16) & 0xff;
1370
+ const g = (argb >> 8) & 0xff;
1371
+ const b = argb & 0xff;
1372
+ return { r, g, b, a };
1373
+ });
1374
+ }
1375
+ /**
1376
+ * Set a color value using RGBA components (0-255).
1377
+ * Warning: If the property is controlled by dataBindings input, this change
1378
+ * will be overwritten on the next input update.
1379
+ */
1380
+ setColorRgba(path, r, g, b, a = 255) {
1381
+ this.setColor(path, { r, g, b, a });
1382
+ }
1383
+ /**
1384
+ * Set the opacity of a color (0.0-1.0) while preserving RGB values.
1385
+ * Warning: If the property is controlled by dataBindings input, this change
1386
+ * will be overwritten on the next input update.
1387
+ */
1388
+ setColorOpacity(path, opacity) {
1389
+ const vmi = this.#viewModelInstance();
1390
+ if (!vmi) {
1391
+ this.logger.warn('No ViewModel instance available');
1392
+ return;
1393
+ }
1394
+ // Validate opacity range
1395
+ if (opacity < 0 || opacity > 1) {
1396
+ this.logger.warn(`Invalid opacity value ${opacity}: must be between 0.0 and 1.0`);
1397
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(`Invalid opacity value for "${path}": ${opacity}. Expected value between 0.0 and 1.0.`, RiveErrorCode.DataBindingTypeMismatch)));
1398
+ return;
1399
+ }
1400
+ // Check if this key is controlled by dataBindings input
1401
+ const controlledBindings = this.dataBindings();
1402
+ if (controlledBindings && path in controlledBindings) {
1403
+ this.logger.warn(`Color "${path}" is controlled by dataBindings input. This change will be overwritten on next input update.`);
1404
+ }
1405
+ this.#ngZone.runOutsideAngular(() => {
1406
+ this.withLocalMutation(path, () => {
1407
+ const colorProp = vmi.color(path);
1408
+ if (!colorProp) {
1409
+ this.logger.warn(`Color property "${path}" not found in ViewModel`);
1410
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.DataBindingPropertyNotFound, {
1411
+ path,
1412
+ }), RiveErrorCode.DataBindingPropertyNotFound)));
1413
+ return;
1414
+ }
1415
+ colorProp.opacity(opacity);
1416
+ this.logger.debug(`Color "${path}" opacity set to ${opacity}`);
1417
+ });
1418
+ });
1419
+ }
1035
1420
  /**
1036
1421
  * Apply all text runs from input (controlled keys).
1037
1422
  * Called on every input change or load.
@@ -1050,10 +1435,450 @@ class RiveCanvasComponent {
1050
1435
  }
1051
1436
  });
1052
1437
  }
1438
+ /**
1439
+ * Initialize ViewModel instance if available in the loaded file.
1440
+ * Called once after animation loads successfully.
1441
+ */
1442
+ initializeViewModel() {
1443
+ if (!this.#rive)
1444
+ return;
1445
+ this.#ngZone.runOutsideAngular(() => {
1446
+ try {
1447
+ const viewModelName = this.viewModelName();
1448
+ let viewModel;
1449
+ // Get ViewModel by name or use default
1450
+ if (viewModelName) {
1451
+ viewModel = this.#rive.viewModelByName(viewModelName);
1452
+ if (!viewModel) {
1453
+ this.logger.warn(`ViewModel "${viewModelName}" not found. Available ViewModels:`, this.getAvailableViewModelNames());
1454
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.ViewModelNotFound, {
1455
+ name: viewModelName,
1456
+ }), RiveErrorCode.ViewModelNotFound, this.getAvailableViewModelNames())));
1457
+ return;
1458
+ }
1459
+ }
1460
+ else {
1461
+ viewModel = this.#rive.defaultViewModel();
1462
+ }
1463
+ // If no ViewModel found (file doesn't use ViewModels), that's OK
1464
+ if (!viewModel) {
1465
+ this.logger.debug('No ViewModel found in file (file may not use ViewModels)');
1466
+ return;
1467
+ }
1468
+ // Get ViewModel instance
1469
+ const viewModelInstance = viewModel.instance();
1470
+ if (!viewModelInstance) {
1471
+ this.logger.warn('Failed to create ViewModel instance');
1472
+ return;
1473
+ }
1474
+ // Bind to artboard
1475
+ this.#rive.bindViewModelInstance(viewModelInstance);
1476
+ // Update signal
1477
+ this.#ngZone.run(() => {
1478
+ this.#viewModelInstance.set(viewModelInstance);
1479
+ });
1480
+ // Log ViewModel info in debug mode
1481
+ this.logger.debug('ViewModel initialized:', {
1482
+ name: viewModel.name,
1483
+ properties: this.getViewModelPropertyInfo(viewModelInstance),
1484
+ });
1485
+ // Subscribe to ViewModel property changes for two-way binding
1486
+ this.subscribeToViewModelChanges(viewModelInstance);
1487
+ }
1488
+ catch (error) {
1489
+ this.logger.error('Error initializing ViewModel:', error);
1490
+ }
1491
+ });
1492
+ }
1493
+ /**
1494
+ * Get list of available ViewModel names for error messages.
1495
+ */
1496
+ getAvailableViewModelNames() {
1497
+ if (!this.#rive)
1498
+ return [];
1499
+ const names = [];
1500
+ const count = this.#rive.viewModelCount;
1501
+ for (let i = 0; i < count; i++) {
1502
+ const vm = this.#rive.viewModelByIndex(i);
1503
+ if (vm)
1504
+ names.push(vm.name);
1505
+ }
1506
+ return names;
1507
+ }
1508
+ /**
1509
+ * Get ViewModel property information for debug logging.
1510
+ */
1511
+ getViewModelPropertyInfo(vmi) {
1512
+ const info = {};
1513
+ try {
1514
+ const properties = vmi.properties;
1515
+ for (const prop of properties) {
1516
+ // Property has name and type
1517
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1518
+ const propAny = prop;
1519
+ const propertyType = this.normalizeViewModelPropertyType(propAny?.type);
1520
+ info[propAny.name || 'unknown'] = propertyType ?? (propAny.type || 'unknown');
1521
+ }
1522
+ }
1523
+ catch (error) {
1524
+ this.logger.warn('Failed to get ViewModel property info:', error);
1525
+ }
1526
+ return info;
1527
+ }
1528
+ /**
1529
+ * Subscribe to ViewModel property changes for two-way data binding.
1530
+ * Emits dataBindingChange output when properties change from within the animation.
1531
+ */
1532
+ subscribeToViewModelChanges(vmi) {
1533
+ this.cleanupViewModelSubscriptions();
1534
+ const properties = vmi.properties ?? [];
1535
+ if (!Array.isArray(properties)) {
1536
+ return;
1537
+ }
1538
+ for (const property of properties) {
1539
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1540
+ const propertyAny = property;
1541
+ const path = propertyAny?.name;
1542
+ if (typeof path !== 'string') {
1543
+ continue;
1544
+ }
1545
+ const resolved = this.resolveViewModelProperty(vmi, path);
1546
+ if (!resolved) {
1547
+ continue;
1548
+ }
1549
+ const subscription = this.subscribeToPropertyChanges(path, resolved.type, resolved.accessor);
1550
+ if (subscription) {
1551
+ this.#viewModelSubscriptionDisposers.add(subscription);
1552
+ }
1553
+ }
1554
+ }
1555
+ /**
1556
+ * Subscribe to changes for a specific ViewModel property.
1557
+ * Uses multiple event APIs to maximize compatibility with @rive-app/canvas runtime versions.
1558
+ */
1559
+ subscribeToPropertyChanges(path, propertyType, property) {
1560
+ const propertyAny = property;
1561
+ if (!propertyAny) {
1562
+ return undefined;
1563
+ }
1564
+ const callback = () => {
1565
+ if (this.shouldSuppressLocalMutation(path)) {
1566
+ return;
1567
+ }
1568
+ const value = this.readPropertyValue(propertyType, propertyAny);
1569
+ if (value === undefined)
1570
+ return;
1571
+ this.#ngZone.run(() => {
1572
+ this.dataBindingChange.emit({
1573
+ path,
1574
+ value,
1575
+ propertyType,
1576
+ });
1577
+ });
1578
+ };
1579
+ let unsubscribe;
1580
+ try {
1581
+ if (typeof propertyAny.on === 'function') {
1582
+ try {
1583
+ const handler = propertyAny.on(callback);
1584
+ unsubscribe = this.buildUnsubscribeFromHandler(propertyAny, callback, handler);
1585
+ }
1586
+ catch {
1587
+ const handler = propertyAny.on('change', callback);
1588
+ unsubscribe = this.buildUnsubscribeFromHandler(propertyAny, callback, handler, true);
1589
+ }
1590
+ }
1591
+ else if (typeof propertyAny.subscribe === 'function') {
1592
+ const handler = propertyAny.subscribe(callback);
1593
+ unsubscribe = this.buildUnsubscribeFromHandler(propertyAny, callback, handler);
1594
+ }
1595
+ else if (typeof propertyAny.addEventListener === 'function') {
1596
+ propertyAny.addEventListener('change', callback);
1597
+ unsubscribe = () => propertyAny.removeEventListener?.('change', callback);
1598
+ }
1599
+ else if (typeof propertyAny.addListener === 'function') {
1600
+ propertyAny.addListener('change', callback);
1601
+ unsubscribe = () => propertyAny.removeListener?.('change', callback);
1602
+ }
1603
+ else if (typeof propertyAny.onChange === 'function') {
1604
+ const handler = propertyAny.onChange(callback);
1605
+ unsubscribe = this.buildUnsubscribeFromHandler(propertyAny, callback, handler);
1606
+ }
1607
+ }
1608
+ catch (error) {
1609
+ this.logger.warn(`Failed to subscribe to ViewModel property "${path}":`, error);
1610
+ }
1611
+ if (!unsubscribe) {
1612
+ this.logger.warn(`No supported subscription API found for ViewModel property "${path}"`);
1613
+ }
1614
+ return unsubscribe;
1615
+ }
1616
+ buildUnsubscribeFromHandler(property, callback, handler, useLegacyEventApi) {
1617
+ if (typeof handler === 'function') {
1618
+ return handler;
1619
+ }
1620
+ if (handler && typeof handler === 'object' && 'unsubscribe' in handler && typeof handler.unsubscribe === 'function') {
1621
+ return () => handler.unsubscribe();
1622
+ }
1623
+ if (handler && typeof handler === 'object' && 'dispose' in handler && typeof handler.dispose === 'function') {
1624
+ return () => handler.dispose();
1625
+ }
1626
+ if (typeof property.off === 'function') {
1627
+ return useLegacyEventApi
1628
+ ? () => property.off('change', callback)
1629
+ : () => property.off(callback);
1630
+ }
1631
+ if (typeof property.remove === 'function') {
1632
+ return useLegacyEventApi
1633
+ ? () => property.remove('change', callback)
1634
+ : () => property.remove(callback);
1635
+ }
1636
+ if (typeof property.removeListener === 'function') {
1637
+ return useLegacyEventApi
1638
+ ? () => property.removeListener('change', callback)
1639
+ : () => property.removeListener(callback);
1640
+ }
1641
+ if (typeof property.unsubscribe === 'function') {
1642
+ return () => property.unsubscribe();
1643
+ }
1644
+ return undefined;
1645
+ }
1646
+ withLocalMutation(path, fn) {
1647
+ const previous = this.#localMutationSuppressions.get(path) ?? 0;
1648
+ this.#localMutationSuppressions.set(path, previous + 1);
1649
+ try {
1650
+ fn();
1651
+ }
1652
+ finally {
1653
+ setTimeout(() => {
1654
+ const current = this.#localMutationSuppressions.get(path) ?? 0;
1655
+ if (current <= 1) {
1656
+ this.#localMutationSuppressions.delete(path);
1657
+ }
1658
+ else {
1659
+ this.#localMutationSuppressions.set(path, current - 1);
1660
+ }
1661
+ });
1662
+ }
1663
+ }
1664
+ shouldSuppressLocalMutation(path) {
1665
+ const current = this.#localMutationSuppressions.get(path);
1666
+ if (current === undefined || current <= 0) {
1667
+ return false;
1668
+ }
1669
+ if (current <= 1) {
1670
+ this.#localMutationSuppressions.delete(path);
1671
+ }
1672
+ else {
1673
+ this.#localMutationSuppressions.set(path, current - 1);
1674
+ }
1675
+ return true;
1676
+ }
1677
+ readPropertyValue(propertyType, property) {
1678
+ if (propertyType === 'color') {
1679
+ if (!property || typeof property.value !== 'number')
1680
+ return undefined;
1681
+ const argb = property.value;
1682
+ const a = (argb >> 24) & 0xff;
1683
+ const r = (argb >> 16) & 0xff;
1684
+ const g = (argb >> 8) & 0xff;
1685
+ const b = argb & 0xff;
1686
+ return { r, g, b, a };
1687
+ }
1688
+ if (propertyType === 'number' && typeof property.value === 'number') {
1689
+ return property.value;
1690
+ }
1691
+ if (propertyType === 'string' && typeof property.value === 'string') {
1692
+ return property.value;
1693
+ }
1694
+ if (propertyType === 'boolean' && typeof property.value === 'boolean') {
1695
+ return property.value;
1696
+ }
1697
+ if (propertyType === 'enum' && typeof property.value === 'string') {
1698
+ return property.value;
1699
+ }
1700
+ if (propertyType === 'trigger') {
1701
+ // Triggers don't have a meaningful value, but we return true to indicate the trigger fired
1702
+ return true;
1703
+ }
1704
+ return undefined;
1705
+ }
1706
+ normalizeViewModelPropertyType(type) {
1707
+ if (typeof type !== 'string') {
1708
+ return null;
1709
+ }
1710
+ const normalized = type.toLowerCase();
1711
+ if (normalized.includes('color'))
1712
+ return 'color';
1713
+ if (normalized.includes('number'))
1714
+ return 'number';
1715
+ if (normalized.includes('string'))
1716
+ return 'string';
1717
+ if (normalized.includes('boolean'))
1718
+ return 'boolean';
1719
+ if (normalized.includes('enum'))
1720
+ return 'enum';
1721
+ if (normalized.includes('trigger'))
1722
+ return 'trigger';
1723
+ return null;
1724
+ }
1725
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1726
+ resolveViewModelProperty(vmi, path) {
1727
+ const color = vmi.color(path);
1728
+ if (color)
1729
+ return { accessor: color, type: 'color' };
1730
+ const number = vmi.number(path);
1731
+ if (number)
1732
+ return { accessor: number, type: 'number' };
1733
+ const string = vmi.string(path);
1734
+ if (string)
1735
+ return { accessor: string, type: 'string' };
1736
+ const bool = vmi.boolean(path);
1737
+ if (bool)
1738
+ return { accessor: bool, type: 'boolean' };
1739
+ const enumProp = vmi.enum(path);
1740
+ if (enumProp)
1741
+ return { accessor: enumProp, type: 'enum' };
1742
+ const trigger = vmi.trigger(path);
1743
+ if (trigger)
1744
+ return { accessor: trigger, type: 'trigger' };
1745
+ return null;
1746
+ }
1747
+ emitDataBindingTypeMismatch(path, expectedType, actualType) {
1748
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.DataBindingTypeMismatch, {
1749
+ path,
1750
+ expected: expectedType,
1751
+ actual: actualType,
1752
+ }), RiveErrorCode.DataBindingTypeMismatch)));
1753
+ }
1754
+ cleanupViewModelSubscriptions() {
1755
+ this.#viewModelSubscriptionDisposers.forEach((disposer) => {
1756
+ try {
1757
+ disposer();
1758
+ }
1759
+ catch (error) {
1760
+ this.logger.warn('Error during ViewModel subscription cleanup:', error);
1761
+ }
1762
+ });
1763
+ this.#viewModelSubscriptionDisposers.clear();
1764
+ }
1765
+ /**
1766
+ * Apply all data bindings from input (controlled keys).
1767
+ * Called on every input change or load.
1768
+ * Auto-detects property type from ViewModel and applies the value accordingly.
1769
+ */
1770
+ applyDataBindings(bindings) {
1771
+ const vmi = this.#viewModelInstance();
1772
+ if (!vmi)
1773
+ return;
1774
+ this.#ngZone.runOutsideAngular(() => {
1775
+ for (const [path, value] of Object.entries(bindings)) {
1776
+ try {
1777
+ const resolved = this.resolveViewModelProperty(vmi, path);
1778
+ if (!resolved) {
1779
+ this.logger.warn(`Data binding property "${path}" not found in ViewModel`);
1780
+ this.#ngZone.run(() => this.loadError.emit(new RiveValidationError(formatErrorMessage(RiveErrorCode.DataBindingPropertyNotFound, {
1781
+ path,
1782
+ }), RiveErrorCode.DataBindingPropertyNotFound)));
1783
+ continue;
1784
+ }
1785
+ let applied = false;
1786
+ this.withLocalMutation(path, () => {
1787
+ applied = this.tryApplyBinding(path, value, resolved);
1788
+ });
1789
+ if (applied) {
1790
+ this.logger.debug(`Data binding "${path}" set to:`, value);
1791
+ }
1792
+ else {
1793
+ this.logger.warn(`Data binding property "${path}" has a type mismatch for value type ${typeof value}`);
1794
+ }
1795
+ }
1796
+ catch (error) {
1797
+ this.logger.warn(`Failed to set data binding "${path}":`, error);
1798
+ }
1799
+ }
1800
+ });
1801
+ }
1802
+ /**
1803
+ * Try to apply a binding value to a resolved ViewModel property.
1804
+ * Returns true if successful, false on type mismatch.
1805
+ */
1806
+ tryApplyBinding(path, value, resolved) {
1807
+ const { accessor, type } = resolved;
1808
+ if (type === 'color') {
1809
+ if (typeof value === 'object' && value !== null && 'r' in value) {
1810
+ // RiveColor object
1811
+ const color = value;
1812
+ accessor.rgba(color.r, color.g, color.b, color.a);
1813
+ return true;
1814
+ }
1815
+ if (typeof value === 'string' || typeof value === 'number') {
1816
+ // Hex string or ARGB integer
1817
+ const color = parseRiveColor(value);
1818
+ accessor.rgba(color.r, color.g, color.b, color.a);
1819
+ return true;
1820
+ }
1821
+ this.logger.warn(`Invalid color value for "${path}": expected string, number, or RiveColor`);
1822
+ this.emitDataBindingTypeMismatch(path, type, typeof value);
1823
+ return false;
1824
+ }
1825
+ if (type === 'number') {
1826
+ if (typeof value === 'number') {
1827
+ accessor.value = value;
1828
+ return true;
1829
+ }
1830
+ this.logger.warn(`Invalid number value for "${path}": expected number, got ${typeof value}`);
1831
+ this.emitDataBindingTypeMismatch(path, type, typeof value);
1832
+ return false;
1833
+ }
1834
+ if (type === 'string') {
1835
+ if (typeof value === 'string') {
1836
+ accessor.value = value;
1837
+ return true;
1838
+ }
1839
+ this.logger.warn(`Invalid string value for "${path}": expected string, got ${typeof value}`);
1840
+ this.emitDataBindingTypeMismatch(path, type, typeof value);
1841
+ return false;
1842
+ }
1843
+ if (type === 'boolean') {
1844
+ if (typeof value === 'boolean') {
1845
+ accessor.value = value;
1846
+ return true;
1847
+ }
1848
+ this.logger.warn(`Invalid boolean value for "${path}": expected boolean, got ${typeof value}`);
1849
+ this.emitDataBindingTypeMismatch(path, type, typeof value);
1850
+ return false;
1851
+ }
1852
+ if (type === 'enum') {
1853
+ if (typeof value === 'string') {
1854
+ accessor.value = value;
1855
+ return true;
1856
+ }
1857
+ this.logger.warn(`Invalid enum value for "${path}": expected string, got ${typeof value}`);
1858
+ this.emitDataBindingTypeMismatch(path, type, typeof value);
1859
+ return false;
1860
+ }
1861
+ if (type === 'trigger') {
1862
+ this.logger.warn(`Cannot set trigger property "${path}" via setDataBinding`);
1863
+ return false;
1864
+ }
1865
+ return false;
1866
+ }
1053
1867
  /**
1054
1868
  * Clean up Rive instance only
1055
1869
  */
1056
1870
  cleanupRive() {
1871
+ this.cleanupViewModelSubscriptions();
1872
+ this.#localMutationSuppressions.clear();
1873
+ const vmi = this.#viewModelInstance();
1874
+ if (vmi) {
1875
+ try {
1876
+ vmi.cleanup();
1877
+ }
1878
+ catch (error) {
1879
+ this.logger.warn('Error during ViewModel cleanup:', error);
1880
+ }
1881
+ }
1057
1882
  if (this.#rive) {
1058
1883
  try {
1059
1884
  this.#rive.cleanup();
@@ -1065,12 +1890,13 @@ class RiveCanvasComponent {
1065
1890
  }
1066
1891
  // Reset signals
1067
1892
  this.#riveInstance.set(null);
1893
+ this.#viewModelInstance.set(null);
1068
1894
  this.#isLoaded.set(false);
1069
1895
  this.#isPlaying.set(false);
1070
1896
  this.#isPaused.set(false);
1071
1897
  }
1072
1898
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: RiveCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1073
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.4", type: RiveCanvasComponent, isStandalone: true, selector: "rive-canvas", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, buffer: { classPropertyName: "buffer", publicName: "buffer", isSignal: true, isRequired: false, transformFunction: null }, riveFile: { classPropertyName: "riveFile", publicName: "riveFile", isSignal: true, isRequired: false, transformFunction: null }, artboard: { classPropertyName: "artboard", publicName: "artboard", isSignal: true, isRequired: false, transformFunction: null }, animations: { classPropertyName: "animations", publicName: "animations", isSignal: true, isRequired: false, transformFunction: null }, stateMachines: { classPropertyName: "stateMachines", publicName: "stateMachines", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, fit: { classPropertyName: "fit", publicName: "fit", isSignal: true, isRequired: false, transformFunction: null }, alignment: { classPropertyName: "alignment", publicName: "alignment", isSignal: true, isRequired: false, transformFunction: null }, useOffscreenRenderer: { classPropertyName: "useOffscreenRenderer", publicName: "useOffscreenRenderer", isSignal: true, isRequired: false, transformFunction: null }, shouldUseIntersectionObserver: { classPropertyName: "shouldUseIntersectionObserver", publicName: "shouldUseIntersectionObserver", isSignal: true, isRequired: false, transformFunction: null }, shouldDisableRiveListeners: { classPropertyName: "shouldDisableRiveListeners", publicName: "shouldDisableRiveListeners", isSignal: true, isRequired: false, transformFunction: null }, automaticallyHandleEvents: { classPropertyName: "automaticallyHandleEvents", publicName: "automaticallyHandleEvents", isSignal: true, isRequired: false, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, textRuns: { classPropertyName: "textRuns", publicName: "textRuns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", loadError: "loadError", stateChange: "stateChange", riveEvent: "riveEvent", riveReady: "riveReady" }, viewQueries: [{ propertyName: "canvas", first: true, predicate: ["canvas"], descendants: true, isSignal: true }], ngImport: i0, template: `
1899
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.4", type: RiveCanvasComponent, isStandalone: true, selector: "rive-canvas", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, buffer: { classPropertyName: "buffer", publicName: "buffer", isSignal: true, isRequired: false, transformFunction: null }, riveFile: { classPropertyName: "riveFile", publicName: "riveFile", isSignal: true, isRequired: false, transformFunction: null }, artboard: { classPropertyName: "artboard", publicName: "artboard", isSignal: true, isRequired: false, transformFunction: null }, animations: { classPropertyName: "animations", publicName: "animations", isSignal: true, isRequired: false, transformFunction: null }, stateMachines: { classPropertyName: "stateMachines", publicName: "stateMachines", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, fit: { classPropertyName: "fit", publicName: "fit", isSignal: true, isRequired: false, transformFunction: null }, alignment: { classPropertyName: "alignment", publicName: "alignment", isSignal: true, isRequired: false, transformFunction: null }, useOffscreenRenderer: { classPropertyName: "useOffscreenRenderer", publicName: "useOffscreenRenderer", isSignal: true, isRequired: false, transformFunction: null }, shouldUseIntersectionObserver: { classPropertyName: "shouldUseIntersectionObserver", publicName: "shouldUseIntersectionObserver", isSignal: true, isRequired: false, transformFunction: null }, shouldDisableRiveListeners: { classPropertyName: "shouldDisableRiveListeners", publicName: "shouldDisableRiveListeners", isSignal: true, isRequired: false, transformFunction: null }, automaticallyHandleEvents: { classPropertyName: "automaticallyHandleEvents", publicName: "automaticallyHandleEvents", isSignal: true, isRequired: false, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, textRuns: { classPropertyName: "textRuns", publicName: "textRuns", isSignal: true, isRequired: false, transformFunction: null }, viewModelName: { classPropertyName: "viewModelName", publicName: "viewModelName", isSignal: true, isRequired: false, transformFunction: null }, dataBindings: { classPropertyName: "dataBindings", publicName: "dataBindings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", loadError: "loadError", stateChange: "stateChange", riveEvent: "riveEvent", riveReady: "riveReady", dataBindingChange: "dataBindingChange" }, viewQueries: [{ propertyName: "canvas", first: true, predicate: ["canvas"], descendants: true, isSignal: true }], ngImport: i0, template: `
1074
1900
  <canvas #canvas [style.width.%]="100" [style.height.%]="100"></canvas>
1075
1901
  `, isInline: true, styles: [":host{display:block;width:100%;height:100%}canvas{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1076
1902
  }
@@ -1079,7 +1905,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImpor
1079
1905
  args: [{ selector: 'rive-canvas', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
1080
1906
  <canvas #canvas [style.width.%]="100" [style.height.%]="100"></canvas>
1081
1907
  `, styles: [":host{display:block;width:100%;height:100%}canvas{display:block}\n"] }]
1082
- }], ctorParameters: () => [], propDecorators: { canvas: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], buffer: [{ type: i0.Input, args: [{ isSignal: true, alias: "buffer", required: false }] }], riveFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "riveFile", required: false }] }], artboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "artboard", required: false }] }], animations: [{ type: i0.Input, args: [{ isSignal: true, alias: "animations", required: false }] }], stateMachines: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateMachines", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], fit: [{ type: i0.Input, args: [{ isSignal: true, alias: "fit", required: false }] }], alignment: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignment", required: false }] }], useOffscreenRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "useOffscreenRenderer", required: false }] }], shouldUseIntersectionObserver: [{ type: i0.Input, args: [{ isSignal: true, alias: "shouldUseIntersectionObserver", required: false }] }], shouldDisableRiveListeners: [{ type: i0.Input, args: [{ isSignal: true, alias: "shouldDisableRiveListeners", required: false }] }], automaticallyHandleEvents: [{ type: i0.Input, args: [{ isSignal: true, alias: "automaticallyHandleEvents", required: false }] }], debugMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "debugMode", required: false }] }], textRuns: [{ type: i0.Input, args: [{ isSignal: true, alias: "textRuns", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], loadError: [{ type: i0.Output, args: ["loadError"] }], stateChange: [{ type: i0.Output, args: ["stateChange"] }], riveEvent: [{ type: i0.Output, args: ["riveEvent"] }], riveReady: [{ type: i0.Output, args: ["riveReady"] }] } });
1908
+ }], ctorParameters: () => [], propDecorators: { canvas: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], buffer: [{ type: i0.Input, args: [{ isSignal: true, alias: "buffer", required: false }] }], riveFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "riveFile", required: false }] }], artboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "artboard", required: false }] }], animations: [{ type: i0.Input, args: [{ isSignal: true, alias: "animations", required: false }] }], stateMachines: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateMachines", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], fit: [{ type: i0.Input, args: [{ isSignal: true, alias: "fit", required: false }] }], alignment: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignment", required: false }] }], useOffscreenRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "useOffscreenRenderer", required: false }] }], shouldUseIntersectionObserver: [{ type: i0.Input, args: [{ isSignal: true, alias: "shouldUseIntersectionObserver", required: false }] }], shouldDisableRiveListeners: [{ type: i0.Input, args: [{ isSignal: true, alias: "shouldDisableRiveListeners", required: false }] }], automaticallyHandleEvents: [{ type: i0.Input, args: [{ isSignal: true, alias: "automaticallyHandleEvents", required: false }] }], debugMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "debugMode", required: false }] }], textRuns: [{ type: i0.Input, args: [{ isSignal: true, alias: "textRuns", required: false }] }], viewModelName: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewModelName", required: false }] }], dataBindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataBindings", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], loadError: [{ type: i0.Output, args: ["loadError"] }], stateChange: [{ type: i0.Output, args: ["stateChange"] }], riveEvent: [{ type: i0.Output, args: ["riveEvent"] }], riveReady: [{ type: i0.Output, args: ["riveReady"] }], dataBindingChange: [{ type: i0.Output, args: ["dataBindingChange"] }] } });
1083
1909
 
1084
1910
  /**
1085
1911
  * Service for preloading and caching Rive files.
@@ -1294,5 +2120,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImpor
1294
2120
  * Generated bundle index. Do not edit.
1295
2121
  */
1296
2122
 
1297
- export { RiveCanvasComponent, RiveErrorCode, RiveFileService, RiveLoadError, RiveValidationError, provideRiveDebug };
2123
+ export { RiveCanvasComponent, RiveErrorCode, RiveFileService, RiveLoadError, RiveValidationError, parseRiveColor, provideRiveDebug, riveColorToArgb, riveColorToHex };
1298
2124
  //# sourceMappingURL=grandgular-rive-angular.mjs.map