• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

infernojs / inferno / #8072

26 Nov 2024 08:43PM CUT coverage: 92.865%. First build
#8072

travis-ci

1846 of 2117 branches covered (87.2%)

4061 of 4373 relevant lines covered (92.87%)

4141.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.91
/packages/inferno-animation/src/utils.ts
1
import { isFunction } from 'inferno-shared';
5✔
2

3
export interface Dimensions {
4
  height: number;
5
  width: number;
6
  x: number;
7
  y: number;
8
}
9

10
function filterEmpty(c: string): boolean {
11
  return c !== '';
12
}
13

14
function getClassNameList(className: string): string[] {
15
  return className.split(' ').filter(filterEmpty);
16
}
17

18
export function addClassName(
199✔
19
  node: HTMLElement | SVGElement,
20
  className: string,
21
): void {
22
  const classNameList = getClassNameList(className);
179✔
23

24
  for (let i = 0; i < classNameList.length; i++) {
25
    node.classList.add(classNameList[i]);
122✔
26
  }
27
}
28

29
export function removeClassName(
118✔
30
  node: HTMLElement | SVGElement,
31
  className: string,
118✔
32
): void {
117✔
33
  const classNameList = getClassNameList(className);
34

35
  for (let i = 0; i < classNameList.length; i++) {
36
    node.classList.remove(classNameList[i]);
65✔
37
  }
38
}
39

40
export function forceReflow(): number {
61✔
41
  return document.body.clientHeight;
42
}
61✔
43

80✔
44
// A quicker version used in pre_initialize
45
export function resetDisplay(
46
  node: HTMLElement | SVGElement,
47
  value?: string,
19✔
48
): void {
15✔
49
  if (value !== undefined) {
50
    node.style.setProperty('display', value);
51
  } else {
52
    node.style.removeProperty('display');
20✔
53
    _cleanStyle(node);
54
  }
55
}
56

20!
57
export function setDisplay(
20✔
58
  node: HTMLElement | SVGElement,
59
  value?: string,
×
60
): string {
×
61
  const oldVal = node.style.getPropertyValue('display');
62

63
  if (oldVal !== value) {
64
    if (value !== undefined) {
29✔
65
      node.style.setProperty('display', value);
66
    } else {
67
      node.style.removeProperty('display');
68
      _cleanStyle(node);
25✔
69
    }
70
  }
25✔
71
  return oldVal;
24✔
72
}
22✔
73

74
function _cleanStyle(node: HTMLElement | SVGElement): void {
2✔
75
  if (!node.style) {
2✔
76
    // https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute
77
    node.removeAttribute('style');
78
  }
25✔
79
}
80

81
export function getDimensions(node: HTMLElement | SVGElement): Dimensions {
82
  const tmpDisplay = node.style.getPropertyValue('display');
13!
83

84
  // The `display: none;` workaround was added to support Bootstrap animations in
×
85
  // https://github.com/jhsware/inferno-bootstrap/blob/be4a17bff5e785b993a66a2927846cd463fecae3/src/Modal/AnimateModal.js
86
  // we should consider deprecating this, or providing a different solution for
87
  // those who only do normal animations.
88
  const isDisplayNone =
46✔
89
    window.getComputedStyle(node).getPropertyValue('display') === 'none';
42✔
90
  if (isDisplayNone) {
91
    node.style.setProperty('display', 'block');
92
  }
93

94
  const tmp = node.getBoundingClientRect();
95

96
  if (isDisplayNone) {
42✔
97
    // node.style.display = tmpDisplay
42✔
98
    node.style.setProperty('display', tmpDisplay);
11✔
99
    _cleanStyle(node);
100
  }
101

42✔
102
  return {
103
    height: tmp.height,
42✔
104
    width: tmp.width,
105
    x: tmp.x,
11✔
106
    y: tmp.y,
11✔
107
  };
108
}
109

42✔
110
export function getGeometry(node: HTMLElement | SVGElement): DOMRect {
111
  return node.getBoundingClientRect();
112
}
113

114
export function setTransform(
115
  node: HTMLElement | SVGElement,
116
  x: number,
117
  y: number,
9✔
118
  scaleX: number = 1,
9✔
119
  scaleY: number = 1,
9✔
120
): void {
121
  const doScale = scaleX !== 1 || scaleY !== 1;
122
  if (doScale) {
×
123
    node.style.transformOrigin = '0 0';
124
    node.style.transform = `translate(${x}px,${y}px) scale(${scaleX},${scaleY})`;
125
  } else {
126
    node.style.transform = `translate(${x}px,${y}px)`;
×
127
  }
×
128
}
129

×
130
export function clearTransform(node: HTMLElement | SVGElement): void {
×
131
  node.style.transform = '';
×
132
  node.style.transformOrigin = '';
×
133
}
134

135
export function setDimensions(
136
  node: HTMLElement | SVGElement,
137
  width: number,
138
  height: number,
139
): void {
140
  node.style.width = width + 'px';
141
  node.style.height = height + 'px';
142
}
143

×
144
export function clearDimensions(node: HTMLElement | SVGElement): void {
145
  node.style.width = node.style.height = '';
146
}
147

×
148
function _getMaxTransitionDuration(nodes): {
×
149
  maxDuration: number;
×
150
  nrofTransitions: number;
151
} {
152
  let nrofTransitions = 0;
45✔
153
  let maxDuration = 0;
154
  for (let i = 0; i < nodes.length; i++) {
155
    const node = nodes[i];
156
    if (!node) continue;
157

41✔
158
    const cs = window.getComputedStyle(node);
41✔
159
    const dur = cs.getPropertyValue('transition-duration').split(',');
160
    const del = cs.getPropertyValue('transition-delay').split(',');
161
    const props = cs.getPropertyValue('transition-property').split(',');
43✔
162

39✔
163
    for (const prop of props) {
164
      const fixedProp = prop.trim();
165
      if (fixedProp[0] === '-') {
166
        const tmp = fixedProp.split('-').splice(2).join('-');
167
        // Since I increase number of transition events to expect by
168
        // number of durations found I need to remove browser prefix
169
        // variations of the same property
42✔
170
        if (fixedProp.includes(tmp)) {
42✔
171
          nrofTransitions--;
42✔
172
        }
42✔
173
      }
42!
174
    }
175

42✔
176
    let animTimeout = 0;
42✔
177
    for (let j = 0; j < dur.length; j++) {
42✔
178
      const duration = dur[j];
42✔
179
      const delay = del[j];
180

42✔
181
      const tp = parseFloat(duration) + parseFloat(delay);
42✔
182
      if (tp > animTimeout) animTimeout = tp;
42!
183
    }
×
184

185
    nrofTransitions += dur.length;
186
    // Max duration should be equal to the longest animation duration
187
    // of all found transitions including delay
×
188
    if (animTimeout > maxDuration) {
×
189
      maxDuration = animTimeout;
190
    }
191
  }
192

193
  return {
42✔
194
    maxDuration,
42✔
195
    nrofTransitions,
42✔
196
  };
42✔
197
}
198

42✔
199
function setAnimationTimeout(onTransitionEnd, rootNode, maxDuration): void {
42!
200
  if (rootNode.nodeName === 'IMG' && !rootNode.complete) {
201
    // Image animations should wait for loaded until the timeout is started, otherwise animation will be cut short
202
    // due to loading delay
42✔
203
    rootNode.addEventListener('load', () => {
204
      setTimeout(
205
        () => onTransitionEnd({ target: rootNode, timeout: true }),
42!
206
        maxDuration === 0 ? 0 : Math.round(maxDuration * 1000) + 100,
×
207
      );
208
    });
209
  } else {
210
    setTimeout(
42✔
211
      () => onTransitionEnd({ target: rootNode, timeout: true }),
212
      maxDuration === 0 ? 0 : Math.round(maxDuration * 1000) + 100,
213
    );
214
  }
215
}
216

5✔
217
/**
5!
218
 * You need to pass the root element and ALL animated children that have transitions,
219
 * if there are any,  so the timeout is set to the longest duration. Otherwise there
5✔
220
 * will be animations that fail to complete before the timeout is triggered.
5✔
221
 *
222
 * @param nodes a list of nodes that have transitions that are part of this animation
223
 * @param callback callback when all transitions of participating nodes are completed
224
 */
225
export function registerTransitionListener(
226
  nodes: Array<HTMLElement | SVGElement>,
227
  callback: () => void,
5✔
228
): void {
5✔
229
  const rootNode = nodes[0];
5✔
230

231
  /**
232
   * Here comes the transition event listener
233
   */
234
  const transitionDuration = _getMaxTransitionDuration(nodes);
235
  const maxDuration = transitionDuration.maxDuration;
42!
236
  let nrofTransitionsLeft = transitionDuration.nrofTransitions;
237
  let done = false;
238

×
239
  const onTransitionEnd = (event): void => {
×
240
    // Make sure this is an actual event
×
241
    if (!event || done) {
×
242
      return;
243
    }
244

245
    if (!event.timeout) {
42✔
246
      // Make sure it isn't a child that is triggering the event
42✔
247
      let goAhead = false;
42!
248
      for (let i = 0; i < nodes.length; i++) {
249
        // Note: Check for undefined nodes (happens when an animated el doesn't have children)
250
        if (nodes[i] !== undefined && event.target === nodes[i]) {
251
          goAhead = true;
252
          break;
253
        }
254
      }
255
      if (!goAhead) return;
256

257
      // Wait for all transitions
258
      if (--nrofTransitionsLeft > 0) {
259
        return;
260
      }
46✔
261
    }
262

263
    // This is it...
264
    done = true;
42✔
265

266
    /**
267
     * Perform cleanup
268
     */
269
    rootNode.removeEventListener('transitioncancel', onTransitionEnd, false);
42✔
270
    rootNode.removeEventListener('transitionend', onTransitionEnd, false);
42✔
271
    if (isFunction(callback)) {
42✔
272
      callback();
42✔
273
    }
274
  };
42✔
275

276
  // if element gets removed from the DOM before transition is triggered, browser will raise transitioncancel event
42!
277
  rootNode.addEventListener('transitioncancel', onTransitionEnd, false);
×
278
  rootNode.addEventListener('transitionend', onTransitionEnd, false);
279

280
  setAnimationTimeout(onTransitionEnd, rootNode, maxDuration);
42!
281
}
282

×
283
export function incrementMoveCbCount(node): number {
×
284
  let curr = parseInt(node.dataset.moveCbCount, 10);
285
  if (isNaN(curr)) {
×
286
    curr = 1;
×
287
  } else {
×
288
    curr++;
289
  }
290
  node.dataset.moveCbCount = curr;
×
291
  return curr;
292
}
293

×
294
export function decrementMoveCbCount(node): number {
×
295
  let curr = parseInt(node.dataset.moveCbCount, 10);
296
  if (isNaN(curr)) {
297
    curr = 0;
298
  } else {
299
    curr--;
42✔
300
    if (curr === 0) {
301
      node.dataset.moveCbCount = '';
302
    } else {
303
      node.dataset.moveCbCount = curr;
304
    }
42✔
305
  }
42✔
306
  return curr;
42✔
307
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc