index.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. class Filpper {
  2. duration: 1000;
  3. templateAsStr: string;
  4. tokens: string[];
  5. templateData = {
  6. curSize: '',
  7. curVal: '0',
  8. nextSize: '',
  9. nextVal: '0'
  10. };
  11. constructor(
  12. private el: HTMLElement,
  13. private template: HTMLTemplateElement,
  14. private startVal = 0,
  15. private startDir: 'up'|'down' = 'up'
  16. ) {
  17. this.templateAsStr = template.innerHTML as string;
  18. this.tokens = getSubstitutionTokens(this.templateAsStr);
  19. this.templateData.curVal = startVal.toString();
  20. this.setDirection(startDir);
  21. this.update(startVal);
  22. }
  23. update(newVal: number|string): void {
  24. this.templateData.nextVal = newVal.toString();
  25. this.setSizes();
  26. this.runAnimation();
  27. }
  28. setDirection(direction: 'up'|'down'): void {
  29. if (direction.toLowerCase() === 'up') {
  30. this.el.classList.add('up');
  31. this.el.classList.remove('down');
  32. } else {
  33. this.el.classList.add('down');
  34. this.el.classList.remove('up');
  35. }
  36. }
  37. setSizes(): void {
  38. const td = this.templateData;
  39. td.curSize = this.getSize(td.curVal);
  40. td.nextSize = this.getSize(td.nextVal);
  41. }
  42. getSize(val: number|string): string {
  43. return val.toString().length > 1 ? 'small' : '';
  44. }
  45. runAnimation(): void {
  46. const inner = processTemplateSubstitutions(this.templateAsStr, this.tokens, this.templateData);
  47. this.el.innerHTML = inner;
  48. window.requestAnimationFrame(() => {
  49. this.el.classList.remove('changed');
  50. window.requestAnimationFrame(() => this.el.classList.add('changing'));
  51. });
  52. setTimeout(() => {
  53. this.el.classList.add('changed');
  54. this.el.classList.remove('changing');
  55. this.templateData.curVal = this.templateData.nextVal;
  56. }, this.duration * 0.9);
  57. }
  58. };
  59. // --------------------------------------------------------------------------------------------------------------------
  60. // Teplating Helpers
  61. const processTemplateSubstitutions = (template: string, tokens: string[], data: { [key: string]: any }): string => {
  62. const processedTemplate = tokens.reduce(
  63. (str, token) => str.replace(new RegExp(`{{\\s${token}\\s+}}`, 'gm'), path(data, token)),
  64. template
  65. );
  66. return processedTemplate;
  67. }
  68. const getSubstitutionTokens = (template: string) => {
  69. let resArr = [];
  70. let tempArr;
  71. const regex = new RegExp('{{\\s+(.*?)\\s+}}', 'g');
  72. while ((tempArr = regex.exec(template)) !== null) {
  73. resArr.push(tempArr[1]);
  74. }
  75. return Array.from(new Set(resArr));
  76. }
  77. const path = (obj: { [key: string]: any }, path: string, defaultval = '') => {
  78. // Safely get nested properties without triggering type errors
  79. // Usage: fetch(obj, 'a.b.c.d')
  80. // Returns d if d exists, else defaultval or an empty string
  81. if (typeof defaultval === 'undefined') defaultval = null;
  82. if (typeof obj !== 'object') return defaultval;
  83. if (typeof path !== 'string') return defaultval;
  84. var props = path.split('.');
  85. var test_first = (obj: { [key: string]: any }, props: string[]): any => {
  86. if (props.length) {
  87. var first = props[0];
  88. if (obj.hasOwnProperty(first)) {
  89. var rest = props.slice(1);
  90. return test_first(obj[first], rest); // Recursive chomp
  91. } else {
  92. return defaultval; // The default value, or null
  93. }
  94. } else {
  95. return obj; // The chomped object's last primitive property if it exists
  96. }
  97. }
  98. return test_first(obj, props);
  99. }
  100. // --------------------------------------------------------------------------------------------------------------------
  101. // Run the flipper
  102. var card = document.querySelector('.flipper-card');
  103. var template = document.querySelector('#flipper-card-template');
  104. var c = new (Filpper as any)(card, template);
  105. c.update(1);
  106. setTimeout(() => c.update(2), 1000);
  107. setTimeout(() => c.update(3), 2000);
  108. setTimeout(() => c.update(4), 3000);
  109. setTimeout(() => c.update(5), 4000);
  110. setTimeout(() => c.update('A'), 5000);
  111. setTimeout(() => c.update('B'), 6000);
  112. setTimeout(() => c.update('C'), 7000);
  113. setTimeout(() => c.update('D'), 8000);
  114. setTimeout(() => c.update('E'), 9000);