fix(tslint): fix tslint errors (#114)

* fixing lint errors in fast viewer

* fixing tslint errors

* fixes browser-extension lint issues

* linting fast-animation

* style manager

* fixing the last lint errors

* updating travis build

* fixing animation tests

* fixing build errors

* adds build scripts to travis build step
This commit is contained in:
Nicholas Rice 2018-03-20 17:11:31 -07:00 коммит произвёл GitHub
Родитель 8450514fbd
Коммит 78fea3efd3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
40 изменённых файлов: 658 добавлений и 608 удалений

Просмотреть файл

@ -2,5 +2,13 @@
language: node_js
node_js:
- "lts/*"
install:
- npm install
- lerna bootstrap
script:
- lerna run build
- npm test
- lerna run test
- npm run tslint
dist: trusty
sudo: false

Просмотреть файл

@ -8,7 +8,7 @@ describe("Glyph string conversion", () => {
const glyphConverter: ConvertGlyphs = new ConvertGlyphs(({glyphFolderPath} as IConvertGlyphOptions));
// This will throw an error if the file does not exist
fs.readFile(`${glyphFolderPath}/index.js`, (err) => {
fs.readFile(`${glyphFolderPath}/index.js`, (err: Error) => {
if (err) {
throw err;
}

20
package-lock.json сгенерированный
Просмотреть файл

@ -24,17 +24,6 @@
"js-tokens": "3.0.2"
}
},
"@microsoft/fast-tslint-rules": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@microsoft/fast-tslint-rules/-/fast-tslint-rules-1.0.4.tgz",
"integrity": "sha512-x8jTmqdunBL6JH2YZ59oSqJW05ViV5Pz4U/UnHrVbUXVfGrBzkRTk58VzvKE8bmxia2qejFBzJDJS2XtFKAQhw==",
"dev": true,
"requires": {
"tslint": "5.9.1",
"tslint-react": "3.5.1",
"typescript": "2.7.2"
}
},
"@types/jest": {
"version": "22.2.0",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-22.2.0.tgz",
@ -8138,15 +8127,6 @@
"tsutils": "2.22.0"
}
},
"tslint-react": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/tslint-react/-/tslint-react-3.5.1.tgz",
"integrity": "sha512-ndS/iOOGrasATcf5YU3JxoIwPGVykjrKhzmlVsRdT1xzl/RbNg9n627rtAGbCjkQepyiaQYgxWQT5G/qUpQCaA==",
"dev": true,
"requires": {
"tsutils": "2.22.0"
}
},
"tsutils": {
"version": "2.22.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.22.0.tgz",

Просмотреть файл

@ -37,7 +37,6 @@
]
},
"devDependencies": {
"@microsoft/fast-tslint-rules": "^1.0.4",
"@types/jest": "^22.2.0",
"@types/node": "^9.4.7",
"jest": "^22.4.2",

Просмотреть файл

@ -1,11 +1,12 @@
import Animate from "../lib/animate";
/* tslint:disable:no-string-literal */
import Animate, { IAnimateOptions } from "../lib/animate";
class AnimateMock extends Animate {}
describe("Animate initilization", () => {
const fakeElement = document.createElement("div");
const fakeElement: HTMLElement = document.createElement("div");
test("should register a single input element", () => {
expect(new AnimateMock(fakeElement).animationTarget).toBe(fakeElement);
expect(new AnimateMock(fakeElement)["animationTarget"]).toBe(fakeElement);
});
test("should correctly assign options", () => {
@ -14,180 +15,201 @@ describe("Animate initilization", () => {
});
describe("getPropertiesToAnimate", () => {
const fakeElement = document.createElement("div");
const fakeElement: HTMLElement = document.createElement("div");
test("should return an empty array when no options are set", () => {
expect(new AnimateMock(fakeElement).getPropertiesToAnimate()).toEqual([]);
expect(new AnimateMock(fakeElement)["getPropertiesToAnimate"]()).toEqual([]);
});
test('should return an array with a single index of "transform" when x is set', () => {
expect(new AnimateMock(fakeElement, { x: 20 }).getPropertiesToAnimate()).toEqual(["transform"]);
expect(new AnimateMock(fakeElement, { x: 20 })["getPropertiesToAnimate"]()).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when y is set', () => {
expect(new AnimateMock(fakeElement, { y: 20 }).getPropertiesToAnimate()).toEqual(["transform"]);
expect(new AnimateMock(fakeElement, { y: 20 })["getPropertiesToAnimate"]()).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when rotate is set', () => {
expect(new AnimateMock(fakeElement, { rotate: 20 }).getPropertiesToAnimate()).toEqual(["transform"]);
expect(new AnimateMock(fakeElement, { rotate: 20 })["getPropertiesToAnimate"]()).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when scale is set', () => {
expect(new AnimateMock(fakeElement, { scale: 20 }).getPropertiesToAnimate()).toEqual(["transform"]);
expect(new AnimateMock(fakeElement, { scale: 20 })["getPropertiesToAnimate"]()).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when scale is set', () => {
expect(new AnimateMock(fakeElement, { scale: [20, 10] }).getPropertiesToAnimate()).toEqual(["transform"]);
expect(new AnimateMock(fakeElement, { scale: [20, 10] })["getPropertiesToAnimate"]()).toEqual(["transform"]);
});
test('should return an array with a single index of "opacity" when opacity is set', () => {
expect(new AnimateMock(fakeElement, { opacity: 0 }).getPropertiesToAnimate()).toEqual(["opacity"]);
expect(new AnimateMock(fakeElement, { opacity: 0 })["getPropertiesToAnimate"]()).toEqual(["opacity"]);
});
test('should return an array with both "transform" and "opacity" when opacity and x are set', () => {
const properties = new AnimateMock(fakeElement, { x: 20, opacity: 0 }).getPropertiesToAnimate();
const properties: string[] = new AnimateMock(fakeElement, { x: 20, opacity: 0 })["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with both "transform" and "opacity" when opacity and y are set', () => {
const properties = new AnimateMock(fakeElement, { y: 20, opacity: 0 }).getPropertiesToAnimate();
const properties: string[] = new AnimateMock(fakeElement, { y: 20, opacity: 0 })["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with both "transform" and "opacity" when opacity and rotate are set', () => {
const properties = new AnimateMock(fakeElement, { rotate: 20, opacity: 0 }).getPropertiesToAnimate();
const properties: string[] = new AnimateMock(fakeElement, { rotate: 20, opacity: 0 })["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with both "transform" and "opacity" when opacity and scale are set', () => {
const properties = new AnimateMock(fakeElement, { scale: 20, opacity: 0 }).getPropertiesToAnimate();
const properties: string[] = new AnimateMock(fakeElement, { scale: 20, opacity: 0 })["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with "transform" when more than one transform properties are set', () => {
const properties = new AnimateMock(fakeElement, { scale: 20, rotate: 20, x: 20, y: 20 }).getPropertiesToAnimate();
const properties: string[] = new AnimateMock(fakeElement, { scale: 20, rotate: 20, x: 20, y: 20 })["getPropertiesToAnimate"]();
expect(properties).toEqual(["transform"]);
});
});
describe("formatTransformFunction", () => {
const animation = new AnimateMock(document.createElement("div"));
const animation: AnimateMock = new AnimateMock(document.createElement("div"));
test("should correctly format the x config", () => {
expect(animation.formatTransformFunction("x", 20)).toBe("translateX(20px)");
expect(animation["formatTransformFunction"]("x", 20)).toBe("translateX(20px)");
});
test("should correctly format the y config", () => {
expect(animation.formatTransformFunction("y", 20)).toBe("translateY(20px)");
expect(animation["formatTransformFunction"]("y", 20)).toBe("translateY(20px)");
});
test("should correctly format the x config", () => {
expect(animation.formatTransformFunction("x", "20%")).toBe("translateX(20%)");
expect(animation["formatTransformFunction"]("x", "20%")).toBe("translateX(20%)");
});
test("should correctly format the y config", () => {
expect(animation.formatTransformFunction("y", "20%")).toBe("translateY(20%)");
expect(animation["formatTransformFunction"]("y", "20%")).toBe("translateY(20%)");
});
test("should correctly format the rotate config", () => {
expect(animation.formatTransformFunction("rotate", 20)).toBe("rotate(20deg)");
expect(animation["formatTransformFunction"]("rotate", 20)).toBe("rotate(20deg)");
});
test("should correctly format the scale config", () => {
expect(animation.formatTransformFunction("scale", 20)).toBe("scale(20)");
expect(animation["formatTransformFunction"]("scale", 20)).toBe("scale(20)");
});
test("should correctly format the scale config", () => {
expect(animation.formatTransformFunction("scale", [20, 10])).toBe("scale(20,10)");
expect(animation["formatTransformFunction"]("scale", [20, 10])).toBe("scale(20,10)");
});
test("should return empty string if function cannot be converted to a transform function", () => {
expect(animation.formatTransformFunction("foobar", 20)).toBe("");
expect(animation["formatTransformFunction"]("foobar", 20)).toBe("");
});
});
describe("getOptionKeyframeValues", () => {
const fakeElement = document.createElement("div");
const fakeElement: HTMLElement = document.createElement("div");
test("should correclty create opacity keyframe value", () => {
expect(new AnimateMock(fakeElement, { opacity: 0 }).getOptionKeyframeValues()).toEqual({ opacity: "0" });
expect(new AnimateMock(fakeElement, { opacity: 0 })["getOptionKeyframeValues"]()).toEqual({ opacity: "0" });
});
test("should correclty create x keyframe value", () => {
expect(new AnimateMock(fakeElement, { x: 20 }).getOptionKeyframeValues()).toEqual({ transform: "translateX(20px)" });
expect(new AnimateMock(fakeElement, { x: 20 })["getOptionKeyframeValues"]()).toEqual({ transform: "translateX(20px)" });
});
test("should correclty create opacity keyframe value", () => {
expect(new AnimateMock(fakeElement, { y: 20 }).getOptionKeyframeValues()).toEqual({ transform: "translateY(20px)" });
expect(new AnimateMock(fakeElement, { y: 20 })["getOptionKeyframeValues"]()).toEqual({ transform: "translateY(20px)" });
});
test("should correclty create x keyframe value", () => {
expect(new AnimateMock(fakeElement, { x: "20%" }).getOptionKeyframeValues()).toEqual({ transform: "translateX(20%)" });
expect(new AnimateMock(fakeElement, { x: "20%" })["getOptionKeyframeValues"]()).toEqual({ transform: "translateX(20%)" });
});
test("should correclty create opacity keyframe value", () => {
expect(new AnimateMock(fakeElement, { y: "20%" }).getOptionKeyframeValues()).toEqual({ transform: "translateY(20%)" });
expect(new AnimateMock(fakeElement, { y: "20%" })["getOptionKeyframeValues"]()).toEqual({ transform: "translateY(20%)" });
});
test("should correclty create opacity keyframe value", () => {
expect(new AnimateMock(fakeElement, { scale: 20 }).getOptionKeyframeValues()).toEqual({ transform: "scale(20)" });
expect(new AnimateMock(fakeElement, { scale: 20 })["getOptionKeyframeValues"]()).toEqual({ transform: "scale(20)" });
});
test("should correclty create opacity keyframe value", () => {
expect(new AnimateMock(fakeElement, { scale: [20, 10] }).getOptionKeyframeValues()).toEqual({ transform: "scale(20,10)" });
expect(new AnimateMock(fakeElement, { scale: [20, 10] })["getOptionKeyframeValues"]()).toEqual({ transform: "scale(20,10)" });
});
test("should correclty create opacity keyframe value", () => {
expect(new AnimateMock(fakeElement, { rotate: 20 }).getOptionKeyframeValues()).toEqual({ transform: "rotate(20deg)" });
expect(new AnimateMock(fakeElement, { rotate: 20 })["getOptionKeyframeValues"]()).toEqual({ transform: "rotate(20deg)" });
});
});
describe("getOptionKeyframes", () => {
const fakeElement = document.createElement("div");
const fakeElement: HTMLElement = document.createElement("div");
// All keyframes will have an initial property value of `undefined` because we rely on the native browsers 'getComputedStyle'
// method to obtain initial values, which in a node environment return undefined.
test("should correctly return keyframes with the x option", () => {
expect(new AnimateMock(fakeElement, { x: 20 }).getOptionKeyframes()).toEqual([{ transform: undefined }, { transform: "translateX(20px)" }]);
expect(new AnimateMock(fakeElement, { x: 20 })["getOptionKeyframes"]()).toEqual([
{ transform: undefined },
{ transform: "translateX(20px)" }
]);
});
test("should correctly return keyframes with the y option", () => {
expect(new AnimateMock(fakeElement, { y: 20 }).getOptionKeyframes()).toEqual([{ transform: undefined }, { transform: "translateY(20px)" }]);
expect(new AnimateMock(fakeElement, { y: 20 })["getOptionKeyframes"]()).toEqual([
{ transform: undefined },
{ transform: "translateY(20px)" }
]);
});
test("should correctly return keyframes with the scale option", () => {
expect(new AnimateMock(fakeElement, { scale: 20 }).getOptionKeyframes()).toEqual([{ transform: undefined }, { transform: "scale(20)" }]);
expect(new AnimateMock(fakeElement, { scale: 20 })["getOptionKeyframes"]()).toEqual([
{ transform: undefined },
{ transform: "scale(20)" }
]);
});
test("should correctly return keyframes with the independent scale option", () => {
expect(new AnimateMock(fakeElement, { scale: [20, 10] }).getOptionKeyframes()).toEqual([{ transform: undefined }, { transform: "scale(20,10)" }]);
expect(new AnimateMock(fakeElement, { scale: [20, 10] })["getOptionKeyframes"]()).toEqual([
{ transform: undefined },
{ transform: "scale(20,10)" }
]);
});
test("should correctly return keyframes with the rotate option", () => {
expect(new AnimateMock(fakeElement, { rotate: 20 }).getOptionKeyframes()).toEqual([{ transform: undefined }, { transform: "rotate(20deg)" }]);
expect(new AnimateMock(fakeElement, { rotate: 20 })["getOptionKeyframes"]()).toEqual([
{ transform: undefined },
{ transform: "rotate(20deg)" }
]);
});
test("should correctly return keyframes with the opacity option", () => {
expect(new AnimateMock(fakeElement, { opacity: 1 }).getOptionKeyframes()).toEqual([{ opacity: "" }, { opacity: "1" }]);
expect(new AnimateMock(fakeElement, { opacity: 1 })["getOptionKeyframes"]()).toEqual([{ opacity: "" }, { opacity: "1" }]);
});
test("should correctly return keyframes with both an opacity and transform function", () => {
expect(new AnimateMock(fakeElement, { opacity: 1, scale: 2 }).getOptionKeyframes()).toEqual([{ opacity: "", scale: undefined }, { opacity: "1", transform: "scale(2)" }]);
expect(new AnimateMock(fakeElement, { opacity: 1, scale: 2 })["getOptionKeyframes"]()).toEqual([
{ opacity: "", scale: undefined },
{ opacity: "1", transform: "scale(2)" }
]);
});
test("should correctly return keyframes with both two transform functions", () => {
expect(new AnimateMock(fakeElement, { x: 20, scale: 2 }).getOptionKeyframes()).toEqual([{ transform: undefined }, { transform: "translateX(20px) scale(2)" }]);
expect(new AnimateMock(fakeElement, { x: 20, scale: 2 })["getOptionKeyframes"]()).toEqual([
{ transform: undefined },
{ transform: "translateX(20px) scale(2)" }
]);
});
});
describe("sortOffsets", () => {
test("should sort offset strings in ascending order", () => {
const setOne = ["0", "1"];
const setTwo = ["1", "0"];
const setThree = [".72", ".29", "1", ".1", "0"];
const sortOffsets = new AnimateMock(document.createElement("div")).sortOffsets;
const setOne: string[] = ["0", "1"];
const setTwo: string[] = ["1", "0"];
const setThree: string[] = [".72", ".29", "1", ".1", "0"];
const sortOffsets: (offsets: string[]) => string[] = new AnimateMock(document.createElement("div"))["sortOffsets"];
expect(sortOffsets(setOne)).toEqual(["0", "1"]);
expect(sortOffsets(setTwo)).toEqual(["0", "1"]);
@ -196,10 +218,13 @@ describe("sortOffsets", () => {
});
describe("consolidateKeyframes", () => {
const fakeElement = document.createElement("div");
const expectedOne = [{offset: 0, opacity: "", transform: undefined}, {offset: 1, opacity: "0", transform: "translateX(20px) translateY(20px) rotate(20deg) scale(20)"}];
const sourceOne = { scale: 20, opacity: 0, rotate: 20, x: 20, y: 20 };
const customKeyframes = [
const fakeElement: HTMLElement = document.createElement("div");
const expectedOne: any[] = [
{offset: 0, opacity: "", transform: undefined},
{offset: 1, opacity: "0", transform: "translateX(20px) translateY(20px) rotate(20deg) scale(20)"}
];
const sourceOne: IAnimateOptions = { scale: 20, opacity: 0, rotate: 20, x: 20, y: 20 };
const customKeyframes: AnimationKeyFrame[] = [
{ opacity: 0 },
{ opacity: 0.75, offset: 0.75 },
{ opacity: 1 }
@ -209,16 +234,20 @@ describe("consolidateKeyframes", () => {
});
test("should correctly consolidate after addKeyframes is used", () => {
const mock = new AnimateMock(fakeElement);
const mock: AnimateMock = new AnimateMock(fakeElement);
mock.addKeyframes(customKeyframes);
expect(mock.keyframes).toEqual([{opacity: 0, offset: 0}, {opacity: 0.75, offset: 0.75 }, {opacity: 1, offset: 1}]);
});
test("should correclty consolidate both options and added keyframes", () => {
const mock = new AnimateMock(fakeElement, { scale: 20 });
const mock: AnimateMock = new AnimateMock(fakeElement, { scale: 20 });
mock.addKeyframes(customKeyframes);
expect(mock.keyframes).toEqual([{offset: 0, opacity: 0, transform: undefined}, {offset: 0.75, opacity: 0.75}, {offset: 1, opacity: 1, transform: "scale(20)"}]);
expect(mock.keyframes).toEqual([
{offset: 0, opacity: 0, transform: undefined},
{offset: 0.75, opacity: 0.75},
{offset: 1, opacity: 1, transform: "scale(20)"}
]);
});
});

Просмотреть файл

@ -14,7 +14,7 @@ class App extends React.Component {
* Renders the component
* @return {function}
*/
public render() {
public render(): JSX.Element {
return (
<TestPage />
);

Просмотреть файл

@ -1,11 +1,12 @@
/* tslint:disable:no-var-requires */
import * as React from "react";
const doSvg1 = require("../assets/images/do-check.svg");
const easingCurve = require("../assets/images/easing-curve.svg");
const sass = require("../assets/styles/test-page.scss");
const doSvg1: string = require("../assets/images/do-check.svg");
const easingCurve: string = require("../assets/images/easing-curve.svg");
const sass: string = require("../assets/styles/test-page.scss");
import ScrollTrigger from "[lib]/triggers/ScrollTrigger";
import ViewEnterTrigger from "[lib]/triggers/ViewEnterTrigger";
import ViewExitTrigger from "[lib]/triggers/ViewExitTrigger";
import { IAnimateOptions } from "[lib]/animate";
import AnimateTo from "[lib]/animateTo";
import AnimateFrom from "[lib]/animateFrom";
import { cubicBezier } from "[lib]/curves";
@ -15,201 +16,14 @@ import AnimateSequence from "[lib]/animateSequence";
class TestPage extends React.Component {
private scrollElement: HTMLElement;
public clickContainer1 = (e) => {
const highlighter = document.getElementById("highlighter");
const circle = e.currentTarget.querySelector(".ui-circle");
const slide = new AnimateTo(highlighter, { y: e.currentTarget.offsetTop - 87 }, { duration: 250 });
const bounceKeyframes = [
{ transform: "scale(1)" },
{ transform: "scale(2.2)", offset: .35 },
{ transform: "scale(2)", offset: .55 },
{ transform: "scale(0.9)", offset: .93 },
{ transform: "scale(1)" }
];
public componentDidMount(): void {
const overlapping: HTMLElement = document.getElementById("overlapping");
const scrollTrigger: ScrollTrigger = new ScrollTrigger();
const viewEnterTrigger: ViewEnterTrigger = new ViewEnterTrigger();
const viewExitTrigger: ViewExitTrigger = new ViewExitTrigger();
const circleAnimation = new AnimateTo(circle, {}, { duration: 500, easing: cubicBezier("fastInOut") });
circleAnimation.addKeyframes(bounceKeyframes);
circleAnimation.play();
slide.play();
}
public clickContainer2 = (e) => {
const bar = e.currentTarget.querySelector(".bar-fill");
const diamond = e.currentTarget.querySelector("#diamond");
const checkmark = e.currentTarget.querySelector("#doSvg1");
const diamondFrames = [
{ transform: "scale(1)" },
{ transform: "scale(1.6)", offset: 0.75 },
{ transform: "scale(0.95)", offset: 0.78 },
{ transform: "scale(1.05)", offset: 0.81 },
{ transform: "scale(0.97)", offset: 0.83 },
{ transform: "scale(1)", offset: 0.85 },
{ transform: "scale(1)"}
];
const doSvgFrames = [
{ transform: "scale(0)" },
{ transform: "scale(1.1)", offset: .9 },
{ transform: "scale(1)" }
];
const barAnimation = new AnimateTo(bar, { scale: [138, 1], transformOrigin: "0 0"}, { duration: 5000 });
const diamondAnimation = new AnimateTo(diamond, { transformOrigin: "50% 100%" }, { duration: 5000 });
const doAnimation = new AnimateTo(checkmark, { transformOrigin: "50% 100%" }, { fill: "both", delay: 4650, duration: 250 });
doAnimation.addKeyframes(doSvgFrames);
diamondAnimation.addKeyframes(diamondFrames);
new AnimateGroup([
barAnimation,
diamondAnimation,
doAnimation
]).play();
}
public hoverContainer4 = (e) => {
const darkSquare = e.currentTarget.querySelector(".dark-square");
const mediumSquare = e.currentTarget.querySelector(".medium-square");
const lightSquare = e.currentTarget.querySelector(".light-square");
const fadeOptions = { opacity: 0 };
const fadeEffect = { duration: 100 };
const slideOptions = { x: 180 };
const slideEffect = {
duration: 300,
fill: "backwards" as AnimationEffectTimingFillMode,
easing: cubicBezier("easeOut")
};
new AnimateGroup([
new AnimateFrom(darkSquare, fadeOptions, fadeEffect),
new AnimateFrom(darkSquare, slideOptions, slideEffect),
new AnimateFrom(mediumSquare, fadeOptions, fadeEffect),
new AnimateFrom(mediumSquare, slideOptions, Object.assign({}, slideEffect, { delay: 40 })),
new AnimateFrom(lightSquare, fadeOptions, fadeEffect),
new AnimateFrom(lightSquare, slideOptions, Object.assign({}, slideEffect, { delay: 80 })),
]).play();
}
public clickContainer5 = (e) => {
const lilsquare = e.currentTarget;
const filler = lilsquare.parentNode.querySelector(".size-fill");
const fillAnimation =
new AnimateTo(filler, { scale: 1, transformOrigin: " 0 0" }, { duration: 250, easing: cubicBezier("easeOutSmooth") });
fillAnimation.play();
lilsquare.classList.add("dark-square");
lilsquare.classList.remove("medium-square");
function swapBack() {
lilsquare.classList.remove("dark-square");
lilsquare.classList.add("medium-square");
}
window.setTimeout(() => {
fillAnimation.reverse();
swapBack();
}, 2750);
}
public clickContainer6 = (e) => {
const lilcircle = e.currentTarget.querySelector(".circle-small");
const bigcircle = e.currentTarget.querySelector(".circle-large");
new AnimateGroup([
new AnimateTo(lilcircle, { x: 200 }, { duration: 3000 }),
new AnimateTo(lilcircle, { bottom: 260 }, { duration: 3000, easing: cubicBezier("fastInOut") }),
new AnimateTo(bigcircle, { y: -200 }, { duration: 3000, easing: cubicBezier("fastInOut") })
]).play();
}
public hoverContainer7 = (e) => {
const square = e.currentTarget.querySelector(".medium-square");
new AnimateFrom(square, { opacity: 0, x: 60 }, { duration: 250, easing: cubicBezier("easeOut") }).play();
}
public clickContainer8 = (e) => {
const row1 = e.currentTarget.querySelector(".row1");
const row2 = e.currentTarget.querySelector(".row2");
const row3 = e.currentTarget.querySelector(".row3");
const fadeInOptions = { opacity: 1 };
const fadeOutOptions = { opacity: 0 };
const fadeInEffect = {
duration: 350,
easing: cubicBezier("fastInOut")
};
const fadeOutEffect = {
delay: 4000,
easing: cubicBezier("easeOut")
};
const sequence = new AnimateSequence([
new AnimateTo(row1, fadeInOptions, fadeInEffect),
new AnimateTo(row2, fadeInOptions, fadeInEffect),
new AnimateTo(row3, fadeInOptions, fadeInEffect)
]);
sequence.onFinish = () => {
sequence.onFinish = sequence.pause.bind(sequence);
window.setTimeout(sequence.reverse.bind(sequence), 3000);
};
sequence.play();
}
public clickContainer9 = (e) => {
const square1 = e.currentTarget.querySelector("#fourSquare1");
const square2 = e.currentTarget.querySelector("#fourSquare2");
const square3 = e.currentTarget.querySelector("#fourSquare3");
const square4 = e.currentTarget.querySelector("#fourSquare4");
const fadeKeyFrames = [
{ opacity: 1 },
{ opacity: 0, offset: 0.4 },
{ opacity: 0, offset: 0.8 },
{ opacity: 1 }
];
const slideOptions = { y: -100 };
const slideEffect = { duration: 750, easing: cubicBezier("fastInOut") };
const square1Animation = new AnimateTo(square1, slideOptions, slideEffect);
const square2Animation = new AnimateTo(square2, slideOptions, slideEffect);
const square3Animation = new AnimateTo(square3, slideOptions, slideEffect);
const square4Animation = new AnimateTo(square4, slideOptions, slideEffect);
square1Animation.addKeyframes(fadeKeyFrames);
square2Animation.addKeyframes(fadeKeyFrames);
square2Animation.effectTiming.delay = 100;
square3Animation.addKeyframes(fadeKeyFrames);
square3Animation.effectTiming.delay = 200;
square4Animation.addKeyframes(fadeKeyFrames);
square4Animation.effectTiming.delay = 2000;
const group = new AnimateGroup([
square1Animation,
square2Animation,
square3Animation,
square4Animation
]);
group.play();
window.setTimeout(() => {
group.reverse();
}, 3000);
}
public componentDidMount() {
const overlapping = document.getElementById("overlapping");
const scrollTrigger = new ScrollTrigger();
const viewEnterTrigger = new ViewEnterTrigger();
const viewExitTrigger = new ViewExitTrigger();
const lightSquare = overlapping.querySelector(".light-square");
const mediumSquare = overlapping.querySelector(".medium-square");
const lightSquare: HTMLElement = overlapping.querySelector(".light-square");
const mediumSquare: HTMLElement = overlapping.querySelector(".medium-square");
const effects: AnimationEffectTiming = {
duration: 750,
@ -226,22 +40,22 @@ class TestPage extends React.Component {
scale: 2.27, transformOrigin: "100% 50%"
}, Object.assign({}, effects, { delay: 750 })).play();
scrollTrigger.subscribe(this.scrollElement, (distance) => {
scrollTrigger.subscribe(this.scrollElement, (distance: number) => {
// Save for debugging
// console.log(distance);
});
viewEnterTrigger.subscribe(this.scrollElement, (distance) => {
viewEnterTrigger.subscribe(this.scrollElement, (distance: number) => {
// Save for debugging
// console.log('entered');
});
viewExitTrigger.subscribe(this.scrollElement, (distance) => {
viewExitTrigger.subscribe(this.scrollElement, (distance: number) => {
// Save for debugging
// console.log('exited');
});
}
public render() {
public render(): JSX.Element {
return (
<div>
<div className="container-square">
@ -288,7 +102,7 @@ class TestPage extends React.Component {
<div className="circle-small"/>
<div className="circle-large"/>
</div>
<div className="container-square" onMouseEnter={this.hoverContainer7} ref={(ref) => { this.scrollElement = ref; }}>
<div className="container-square" onMouseEnter={this.hoverContainer7} ref={this.setRef("scrollElement")}>
<div className="centerizer large single">
<div className="medium-square size-large"/>
</div>
@ -319,6 +133,203 @@ class TestPage extends React.Component {
</div>
);
}
private setRef(name: string): (ref: HTMLElement) => void {
return (ref: HTMLElement): void => {
this[name] = ref;
};
}
private clickContainer1 = (e: React.MouseEvent<any>): void => {
const highlighter: HTMLElement = document.getElementById("highlighter");
const circle: HTMLElement = e.currentTarget.querySelector(".ui-circle");
const slide: AnimateTo = new AnimateTo(highlighter, { y: e.currentTarget.offsetTop - 87 }, { duration: 250 });
const bounceKeyframes: AnimationKeyFrame[] = [
{ transform: "scale(1)" },
{ transform: "scale(2.2)", offset: .35 },
{ transform: "scale(2)", offset: .55 },
{ transform: "scale(0.9)", offset: .93 },
{ transform: "scale(1)" }
];
const circleAnimation: AnimateTo = new AnimateTo(circle, {}, { duration: 500, easing: cubicBezier("fastInOut") });
circleAnimation.addKeyframes(bounceKeyframes);
circleAnimation.play();
slide.play();
}
private clickContainer2 = (e: React.MouseEvent<any>): void => {
const bar: HTMLElement = e.currentTarget.querySelector(".bar-fill");
const diamond: HTMLElement = e.currentTarget.querySelector("#diamond");
const checkmark: HTMLElement = e.currentTarget.querySelector("#doSvg1");
const diamondFrames: AnimationKeyFrame[] = [
{ transform: "scale(1)" },
{ transform: "scale(1.6)", offset: 0.75 },
{ transform: "scale(0.95)", offset: 0.78 },
{ transform: "scale(1.05)", offset: 0.81 },
{ transform: "scale(0.97)", offset: 0.83 },
{ transform: "scale(1)", offset: 0.85 },
{ transform: "scale(1)"}
];
const doSvgFrames: AnimationKeyFrame[] = [
{ transform: "scale(0)" },
{ transform: "scale(1.1)", offset: .9 },
{ transform: "scale(1)" }
];
const barAnimation: AnimateTo = new AnimateTo(bar, { scale: [138, 1], transformOrigin: "0 0"}, { duration: 5000 });
const diamondAnimation: AnimateTo = new AnimateTo(diamond, { transformOrigin: "50% 100%" }, { duration: 5000 });
const doAnimation: AnimateTo = new AnimateTo(
checkmark,
{ transformOrigin: "50% 100%" },
{ fill: "both", delay: 4650, duration: 250 }
);
doAnimation.addKeyframes(doSvgFrames);
diamondAnimation.addKeyframes(diamondFrames);
new AnimateGroup([
barAnimation,
diamondAnimation,
doAnimation
]).play();
}
private hoverContainer4 = (e: React.MouseEvent<any>): void => {
const darkSquare: HTMLElement = e.currentTarget.querySelector(".dark-square");
const mediumSquare: HTMLElement = e.currentTarget.querySelector(".medium-square");
const lightSquare: HTMLElement = e.currentTarget.querySelector(".light-square");
const fadeOptions: IAnimateOptions = { opacity: 0 };
const fadeEffect: AnimationEffectTiming = { duration: 100 };
const slideOptions: IAnimateOptions = { x: 180 };
const slideEffect: AnimationEffectTiming = {
duration: 300,
fill: "backwards" as AnimationEffectTimingFillMode,
easing: cubicBezier("easeOut")
};
new AnimateGroup([
new AnimateFrom(darkSquare, fadeOptions, fadeEffect),
new AnimateFrom(darkSquare, slideOptions, slideEffect),
new AnimateFrom(mediumSquare, fadeOptions, fadeEffect),
new AnimateFrom(mediumSquare, slideOptions, Object.assign({}, slideEffect, { delay: 40 })),
new AnimateFrom(lightSquare, fadeOptions, fadeEffect),
new AnimateFrom(lightSquare, slideOptions, Object.assign({}, slideEffect, { delay: 80 })),
]).play();
}
private clickContainer5 = (e: React.MouseEvent<any>): void => {
const lilsquare: HTMLElement = e.currentTarget;
const filler: HTMLElement = (lilsquare.parentNode as HTMLElement).querySelector(".size-fill");
const fillAnimation: AnimateTo =
new AnimateTo(filler, { scale: 1, transformOrigin: " 0 0" }, { duration: 250, easing: cubicBezier("easeOutSmooth") });
fillAnimation.play();
lilsquare.classList.add("dark-square");
lilsquare.classList.remove("medium-square");
function swapBack(): void {
lilsquare.classList.remove("dark-square");
lilsquare.classList.add("medium-square");
}
window.setTimeout((): void => {
fillAnimation.reverse();
swapBack();
}, 2750);
}
private clickContainer6 = (e: React.MouseEvent<any>): void => {
const lilcircle: HTMLElement = e.currentTarget.querySelector(".circle-small");
const bigcircle: HTMLElement = e.currentTarget.querySelector(".circle-large");
new AnimateGroup([
new AnimateTo(lilcircle, { x: 200 }, { duration: 3000 }),
new AnimateTo(lilcircle, { bottom: 260 }, { duration: 3000, easing: cubicBezier("fastInOut") }),
new AnimateTo(bigcircle, { y: -200 }, { duration: 3000, easing: cubicBezier("fastInOut") })
]).play();
}
private hoverContainer7 = (e: React.MouseEvent<any>): void => {
const square: HTMLElement = e.currentTarget.querySelector(".medium-square");
new AnimateFrom(square, { opacity: 0, x: 60 }, { duration: 250, easing: cubicBezier("easeOut") }).play();
}
private clickContainer8 = (e: React.MouseEvent<any>): void => {
const row1: HTMLElement = e.currentTarget.querySelector(".row1");
const row2: HTMLElement = e.currentTarget.querySelector(".row2");
const row3: HTMLElement = e.currentTarget.querySelector(".row3");
const fadeInOptions: IAnimateOptions = { opacity: 1 };
const fadeOutOptions: IAnimateOptions = { opacity: 0 };
const fadeInEffect: AnimationEffectTiming = {
duration: 350,
easing: cubicBezier("fastInOut")
};
const fadeOutEffect: AnimationEffectTiming = {
delay: 4000,
easing: cubicBezier("easeOut")
};
const sequence: AnimateSequence = new AnimateSequence([
new AnimateTo(row1, fadeInOptions, fadeInEffect),
new AnimateTo(row2, fadeInOptions, fadeInEffect),
new AnimateTo(row3, fadeInOptions, fadeInEffect)
]);
sequence.onFinish = (): void => {
sequence.onFinish = sequence.pause.bind(sequence);
window.setTimeout(sequence.reverse.bind(sequence), 3000);
};
sequence.play();
}
private clickContainer9 = (e: React.MouseEvent<any>): void => {
const square1: HTMLElement = e.currentTarget.querySelector("#fourSquare1");
const square2: HTMLElement = e.currentTarget.querySelector("#fourSquare2");
const square3: HTMLElement = e.currentTarget.querySelector("#fourSquare3");
const square4: HTMLElement = e.currentTarget.querySelector("#fourSquare4");
const fadeKeyFrames: AnimationKeyFrame[] = [
{ opacity: 1 },
{ opacity: 0, offset: 0.4 },
{ opacity: 0, offset: 0.8 },
{ opacity: 1 }
];
const slideOptions: IAnimateOptions = { y: -100 };
const slideEffect: AnimationEffectTiming = { duration: 750, easing: cubicBezier("fastInOut") };
const square1Animation: AnimateTo = new AnimateTo(square1, slideOptions, slideEffect);
const square2Animation: AnimateTo = new AnimateTo(square2, slideOptions, slideEffect);
const square3Animation: AnimateTo = new AnimateTo(square3, slideOptions, slideEffect);
const square4Animation: AnimateTo = new AnimateTo(square4, slideOptions, slideEffect);
square1Animation.addKeyframes(fadeKeyFrames);
square2Animation.addKeyframes(fadeKeyFrames);
square2Animation.effectTiming.delay = 100;
square3Animation.addKeyframes(fadeKeyFrames);
square3Animation.effectTiming.delay = 200;
square4Animation.addKeyframes(fadeKeyFrames);
square4Animation.effectTiming.delay = 2000;
const group: AnimateGroup = new AnimateGroup([
square1Animation,
square2Animation,
square3Animation,
square4Animation
]);
group.play();
window.setTimeout(() => {
group.reverse();
}, 3000);
}
}
export default TestPage;

Просмотреть файл

@ -1,4 +1,4 @@
export interface AnimateOptions {
export interface IAnimateOptions {
/**
* The x position change of the animation
*/
@ -58,7 +58,7 @@ export interface AnimateOptions {
/**
* Enumerates all properties that can be animated, outside of properties supplied directly via Animate.addKeyframes()
*/
export interface AnimationProperties extends AnimationKeyFrame {
export interface IAnimationProperties extends AnimationKeyFrame {
top?: string;
right?: string;
bottom?: string;
@ -80,16 +80,35 @@ export enum AnimationMode {
animateFrom
}
/**
* Maps css property names to animation options
*/
export interface IPropertyMap {
opacity: string[];
transform: string[];
top: string[];
left: string[];
bottom: string[];
right: string[];
}
export default abstract class Animate {
/**
* Tracks if the animation should animate toward an elements natural position or away from it
* A mapping between animation options and the css property names they apply to
*/
protected mode: AnimationMode;
private static propertyMap: IPropertyMap = {
opacity: ["opacity"],
transform: ["x", "y", "rotate", "scale"],
top: ["top"],
left: ["left"],
bottom: ["bottom"],
right: ["right"]
};
/**
* Stores animation options
*/
public options: AnimateOptions;
public options: IAnimateOptions;
/**
* Stores animation timing functions
@ -100,6 +119,16 @@ export default abstract class Animate {
duration: 500
};
/**
* Callback to call when the animation is canceled
*/
public onCancel: () => void;
/**
* Tracks if the animation should animate toward an elements natural position or away from it
*/
protected mode: AnimationMode;
/**
* Stores the HTML element to be animated
*/
@ -113,11 +142,18 @@ export default abstract class Animate {
/**
* Callback to call when the animation finishes playing
*/
private _onFinish: () => {};
public get onFinish() {
private _onFinish: () => void;
/**
* Stores animation keyframe sets and is accessed by a getter
*/
private _keyframes: AnimationKeyFrame[][] = [];
public get onFinish(): () => void {
return this._onFinish;
}
public set onFinish(callback: any) {
public set onFinish(callback: () => void) {
this._onFinish = callback;
if (Boolean(this.animation)) {
@ -125,24 +161,7 @@ export default abstract class Animate {
}
}
/**
* Callback to call when the animation is canceled
*/
public onCancel: () => void;
/**
* A mapping between animation options and the css property names they apply to
*/
private static propertyMap = {
opacity: ["opacity"],
transform: ["x", "y", "rotate", "scale"],
top: ["top"],
left: ["left"],
bottom: ["bottom"],
right: ["right"]
};
constructor(element: HTMLElement, options?: AnimateOptions, effectTiming?: AnimationEffectTiming) {
constructor(element: HTMLElement, options?: IAnimateOptions, effectTiming?: AnimationEffectTiming) {
this.animationTarget = element;
if (Boolean(effectTiming)) {
@ -165,7 +184,7 @@ export default abstract class Animate {
/**
* plays the animation
*/
public play = () => {
public play = (): void => {
this.ensureAnimationObjectExists();
this.animation.play();
}
@ -173,7 +192,7 @@ export default abstract class Animate {
/**
* pauses the animation
*/
public pause = () => {
public pause = (): void => {
this.ensureAnimationObjectExists();
this.animation.pause();
}
@ -181,7 +200,7 @@ export default abstract class Animate {
/**
* finishes the animation
*/
public finish = () => {
public finish = (): void => {
this.ensureAnimationObjectExists();
this.animation.finish();
}
@ -189,7 +208,7 @@ export default abstract class Animate {
/**
* cancels the animation
*/
public cancel = () => {
public cancel = (): void => {
this.ensureAnimationObjectExists();
this.animation.cancel();
}
@ -197,7 +216,7 @@ export default abstract class Animate {
/**
* reverses an animation
*/
public reverse = () => {
public reverse = (): void => {
this.ensureAnimationObjectExists();
this.animation.reverse();
}
@ -205,14 +224,14 @@ export default abstract class Animate {
/**
* adds a set of keyframes to set of animation keyframes the animation should execute
*/
public addKeyframes = (keyframes: AnimationKeyFrame[]) => {
public addKeyframes = (keyframes: AnimationKeyFrame[]): void => {
this._keyframes.push(keyframes);
}
/**
* Ensure animation object
*/
private ensureAnimationObjectExists() {
private ensureAnimationObjectExists(): void {
if (typeof this.animation === "undefined") {
this.createAnimationObject();
}
@ -220,7 +239,7 @@ export default abstract class Animate {
/**
* Creates the animation object
*/
private createAnimationObject() {
private createAnimationObject(): void {
this.animation = new Animation(this.keyframeEffect, document.timeline);
if (typeof this.onFinish !== "undefined") {
@ -236,7 +255,7 @@ export default abstract class Animate {
* Returns a list of properties that will be animated based options
*/
private getPropertiesToAnimate(): string[] {
return Object.keys(Animate.propertyMap).filter((property) => {
return Object.keys(Animate.propertyMap).filter((property: string) => {
// Filter out all properties that don't need to be set based on our options
return Animate.propertyMap[property].reduce((hasProperty: boolean, animationProp: string) => {
return typeof this.options[animationProp] !== "undefined" || hasProperty;
@ -250,25 +269,29 @@ export default abstract class Animate {
* to known-working starting values
*/
private normalizeInitialValue(property: string, value: string): string {
const coercedReturn = "0.01";
if (value === undefined) {
return;
}
const coercedReturn: string = "0.01";
switch (property) {
case "transform":
const matrixValuesRegex = /matrix\((.+)\)/;
const matrixValues = value.match(matrixValuesRegex);
const matrixValuesRegex: RegExp = /matrix\((.+)\)/;
const matrixValues: string[] | null = value.match(matrixValuesRegex);
if (Array.isArray(matrixValues)) {
const normalizedValues = matrixValues[1]
const normalizedValues: string[] = matrixValues[1]
.split(",")
.map((value, index) => {
const parsedValueIsZero = parseFloat(value) === 0;
.map((matchedValue: string, index: number) => {
const parsedValueIsZero: boolean = parseFloat(value) === 0;
if (!parsedValueIsZero) {
return value;
return matchedValue;
}
// If this is the scaleX index or the scaleY index, return the coerced value
return index === 0 || index === 3 ? coercedReturn : value;
return index === 0 || index === 3 ? coercedReturn : matchedValue;
});
return `matrix(${normalizedValues.join(",")})`;
@ -285,16 +308,16 @@ export default abstract class Animate {
/**
* Returns the initial values for all properties being animated
*/
private getInitialKeyframeValues(): AnimationProperties {
private getInitialKeyframeValues(): IAnimationProperties {
if (!(this.animationTarget instanceof HTMLElement) || typeof window === "undefined") {
return {};
}
const animatedProperties = this.getPropertiesToAnimate();
const computedStyle = window.getComputedStyle(this.animationTarget);
const initialKeyframeValues: AnimationProperties = {};
const animatedProperties: string[] = this.getPropertiesToAnimate();
const computedStyle: CSSStyleDeclaration = window.getComputedStyle(this.animationTarget);
const initialKeyframeValues: IAnimationProperties = {};
animatedProperties.forEach((property) => {
animatedProperties.forEach((property: string) => {
initialKeyframeValues[property] = this.normalizeInitialValue(property, computedStyle[property]);
});
@ -304,7 +327,7 @@ export default abstract class Animate {
/**
* Formats a config option into a transform function
*/
private formatTransformFunction(functionType, value): string {
private formatTransformFunction(functionType: string, value: string | number | number[]): string {
// If `functionType` can't be converted into a transform function, just return empty string
if (!Animate.propertyMap.transform.includes(functionType)) {
return "";
@ -336,20 +359,20 @@ export default abstract class Animate {
/**
* Converts a number to a pixel string
*/
private pixelify(num: number) {
private pixelify(num: number): string {
return `${num}px`;
}
/**
* Returns keyframe values based on option configuration
*/
private getOptionKeyframeValues(): AnimationProperties {
const animateProperties = this.getPropertiesToAnimate();
const keyframeValues: AnimationProperties = {};
private getOptionKeyframeValues(): IAnimationProperties {
const animateProperties: string[] = this.getPropertiesToAnimate();
const keyframeValues: IAnimationProperties = {};
animateProperties.forEach((property) => {
keyframeValues[property] = Animate.propertyMap[property].map((option) => {
const value = this.options[option];
animateProperties.forEach((property: string) => {
keyframeValues[property] = Animate.propertyMap[property].map((option: string): string => {
const value: string | number = this.options[option];
if (typeof value === "undefined") {
return null;
@ -367,7 +390,7 @@ export default abstract class Animate {
return this.formatTransformFunction(option, value);
}
})
.filter((option) => Boolean(option))
.filter((option: string) => Boolean(option))
.join(" ");
});
@ -390,9 +413,9 @@ export default abstract class Animate {
* Sorts an array of offset keys in ascending order
*/
private sortOffsets(offsets: string[]): string[] {
return offsets.sort((a: string, b: string) => {
const A = parseFloat(a);
const B = parseFloat(b);
return offsets.sort((a: string, b: string): number => {
const A: number = parseFloat(a);
const B: number = parseFloat(b);
if (A < B) {
return -1;
@ -408,39 +431,34 @@ export default abstract class Animate {
* Consolidates all keyframe arrays into a single keyframe array
*/
private consolidateKeyframes(keyframeSets: AnimationKeyFrame[][]): AnimationKeyFrame[] {
const frames = {};
const frames: Partial<AnimationKeyFrame[]> = {};
// Merge all keyframes into a single frames object where each key is a keyframe offset
keyframeSets.forEach((keyframeSet) => {
keyframeSet.forEach((keyframe, index) => {
let offset = keyframe.offset;
keyframeSets.forEach((keyframeSet: AnimationKeyFrame[]) => {
keyframeSet.forEach((keyframe: AnimationKeyFrame, index: number) => {
let offset: number | number[] = keyframe.offset;
if (typeof offset === "undefined") {
offset = index === 0 ? 0 : 1;
keyframe.offset = offset;
}
const offsetKey = offset.toString();
const offsetKey: string = offset.toString();
frames[offsetKey] = typeof frames[offsetKey] === "undefined" ? keyframe : Object.assign(frames[offsetKey], keyframe);
});
});
return this.sortOffsets(Object.keys(frames)).map((offset) => {
return this.sortOffsets(Object.keys(frames)).map((offset: string) => {
return frames[offset];
});
}
/**
* Stores animation keyframe sets and is accessed by a getter
*/
private _keyframes: AnimationKeyFrame[][] = [];
/**
* Returns the animation's keyframes
*/
public get keyframes() {
const optionKeyframes = this.getOptionKeyframes();
public get keyframes(): AnimationKeyFrame[] {
const optionKeyframes: AnimationKeyFrame[] = this.getOptionKeyframes();
return this.consolidateKeyframes(this._keyframes.concat([this.getOptionKeyframes()]));
}
@ -448,7 +466,7 @@ export default abstract class Animate {
/**
* Returns the key frame effect object
*/
public get keyframeEffect() {
public get keyframeEffect(): KeyframeEffect {
return new KeyframeEffect(this.animationTarget, this.keyframes, this.effectTiming);
}
}

Просмотреть файл

@ -1,5 +1,5 @@
import Animate, { AnimationMode } from "../animate";
export default class extends Animate {
protected mode = AnimationMode.animateFrom;
protected mode: AnimationMode = AnimationMode.animateFrom;
}

Просмотреть файл

@ -2,39 +2,20 @@ import AnimateTo from "../animateTo";
import AnimateFrom from "../animateFrom";
class AnimateGroup {
constructor(animations: Array<AnimateTo | AnimateFrom>) {
this.animations = animations;
}
/**
* Stores the onFinish callback
*/
private _onFinish: () => void;
/**
* Stores the group effect object
*/
private animations: Array<AnimateTo | AnimateFrom>;
/**
* Returns the longest running animation in the group
*/
private getLongestAnimation(): AnimateTo | AnimateFrom {
return this.animations.reduce((previousValue, currentValue) => {
const previousDuration = this.getAnimationDuration(previousValue.effectTiming);
const currentDuration = this.getAnimationDuration(currentValue.effectTiming);
return currentDuration > previousDuration ? currentValue : previousValue;
});
constructor(animations: Array<AnimateTo | AnimateFrom>) {
this.animations = animations;
}
/**
* Returns the cumulative time it will take to complete an animation
*/
private getAnimationDuration(effectTiming: AnimationEffectTiming): number {
return (effectTiming.delay || 0) + (effectTiming.duration || 0);
}
/**
* Stores the onFinish callback
*/
private _onFinish: () => void;
/**
* Expose onFinish callback
*/
@ -47,36 +28,55 @@ class AnimateGroup {
/**
* Play the group of animations
*/
public play() {
this.animations.forEach((animation) => animation.play());
public play(): void {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.play());
}
/**
* Reverses all animations in the group
*/
public reverse() {
this.animations.forEach((animation) => animation.reverse());
public reverse(): void {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.reverse());
}
/**
* Pauses all animations in the group
*/
public pause = () => {
this.animations.forEach((animation) => animation.pause());
public pause = (): void => {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.pause());
}
/**
* Finishes all animations in the group
*/
public finish = () => {
this.animations.forEach((animation) => animation.finish());
public finish = (): void => {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.finish());
}
/**
* Cancels all animations in the group
*/
public cancel = () => {
this.animations.forEach((animation) => animation.cancel());
public cancel = (): void => {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.cancel());
}
/**
* Returns the longest running animation in the group
*/
private getLongestAnimation(): AnimateTo | AnimateFrom {
return this.animations.reduce((previousValue: AnimateTo | AnimateFrom, currentValue: AnimateTo | AnimateFrom) => {
const previousDuration: number = this.getAnimationDuration(previousValue.effectTiming);
const currentDuration: number = this.getAnimationDuration(currentValue.effectTiming);
return currentDuration > previousDuration ? currentValue : previousValue;
});
}
/**
* Returns the cumulative time it will take to complete an animation
*/
private getAnimationDuration(effectTiming: AnimationEffectTiming): number {
return (effectTiming.delay || 0) + (effectTiming.duration || 0);
}
}

Просмотреть файл

@ -2,28 +2,63 @@ import AnimateTo from "../animateTo";
import AnimateFrom from "../animateFrom";
class AnimateSequence {
constructor(animations: Array<AnimateTo | AnimateFrom>) {
this.animations = animations;
}
private animations: Array<AnimateTo | AnimateFrom>;
/**
* onFinish callback method
*/
public onFinish: () => void;
private animations: Array<AnimateTo | AnimateFrom>;
constructor(animations: Array<AnimateTo | AnimateFrom>) {
this.animations = animations;
}
/**
* Play the sequence of animations
*/
public play = (): void => {
this.applySequencedCallback(this.animations, "play");
}
/**
* Play the sequence in reverse
*/
public reverse = (): void => {
this.applySequencedCallback(this.animations.reverse(), "reverse");
}
/**
* Pauses all animations in the sequence
*/
public pause = (): void => {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.pause());
}
/**
* Finishes all animations in the sequence
*/
public finish = (): void => {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.finish());
}
/**
* Cancels all animations in the sequence
*/
public cancel = (): void => {
this.animations.forEach((animation: AnimateTo | AnimateFrom) => animation.cancel());
}
/**
* Sequences a set of animations and calls the specified method
*/
private applySequencedCallback(animations: Array<AnimateTo | AnimateFrom>, method: string) {
const animationCount = animations.length;
private applySequencedCallback(animations: Array<AnimateTo | AnimateFrom>, method: string): void {
const animationCount: number = animations.length;
if (animationCount <= 0) {
return;
}
animations.forEach((animation, index) => {
animations.forEach((animation: AnimateTo | AnimateFrom, index: number) => {
// If this is not the last animation, format animation sequence chain
if (index < animationCount - 1) {
animation.onFinish = this.animations[index + 1][method];
@ -35,41 +70,6 @@ class AnimateSequence {
animations[0][method]();
}
/**
* Play the sequence of animations
*/
public play = () => {
this.applySequencedCallback(this.animations, "play");
}
/**
* Play the sequence in reverse
*/
public reverse = () => {
this.applySequencedCallback(this.animations.reverse(), "reverse");
}
/**
* Pauses all animations in the sequence
*/
public pause = () => {
this.animations.forEach((animation) => animation.pause());
}
/**
* Finishes all animations in the sequence
*/
public finish = () => {
this.animations.forEach((animation) => animation.finish());
}
/**
* Cancels all animations in the sequence
*/
public cancel = () => {
this.animations.forEach((animation) => animation.cancel());
}
}
export default AnimateSequence;

Просмотреть файл

@ -1,5 +1,5 @@
import Animate, { AnimationMode } from "../animate";
export default class extends Animate {
protected mode = AnimationMode.animateTo;
protected mode: AnimationMode = AnimationMode.animateTo;
}

Просмотреть файл

@ -1,58 +1,58 @@
import { BezierCurve } from "./";
import { IBezierCurve } from "./index";
export const linear: BezierCurve = [
export const linear: IBezierCurve = [
[0, 0],
[1, 1]
];
export const easeOut: BezierCurve = [
export const easeOut: IBezierCurve = [
[0, 0],
[0.58, 1]
];
export const easeOutSmooth: BezierCurve = [
export const easeOutSmooth: IBezierCurve = [
[0, 0.35],
[0.15, 1]
];
export const easeIn: BezierCurve = [
export const easeIn: IBezierCurve = [
[0.25, 0.1],
[0.25, 1]
];
export const drillIn: BezierCurve = [
export const drillIn: IBezierCurve = [
[0.17, 0.17],
[0, 1]
];
export const backToApp: BezierCurve = [
export const backToApp: IBezierCurve = [
[0.5, 0],
[0.6, 1]
];
export const appToApp: BezierCurve = [
export const appToApp: IBezierCurve = [
[0.5, 0],
[1, 0.9]
];
export const fastIn: BezierCurve = [
export const fastIn: IBezierCurve = [
[0.1, 0.9],
[0.2, 1]
];
export const fastOut: BezierCurve = [
export const fastOut: IBezierCurve = [
[0.9, 0.1],
[1, 0.2]
];
export const fastInOut: BezierCurve = [
export const fastInOut: IBezierCurve = [
[0.8, 0],
[0.2, 1]
];
export const exponential: BezierCurve = [
export const exponential: IBezierCurve = [
[0.1, 0.25],
[0.75, 0.9]
];
export const fastInFortySevenPercent: BezierCurve = [
export const fastInFortySevenPercent: IBezierCurve = [
[0.11, 0.5],
[0.24, 0.96]
];
export const exponentialReversed: BezierCurve = [
export const exponentialReversed: IBezierCurve = [
[0.25, 0.1],
[0.9, 0.75]
];
export const navPane: BezierCurve = [
export const navPane: IBezierCurve = [
[0.1, 0.7],
[0.1, 1]
];

Просмотреть файл

@ -1,18 +1,19 @@
import * as bezierCurves from "./config";
export type BezierCurvePoint = [number, number];
/**
* Defines interface for a cubic bezier curve
*/
export interface BezierCurve {
export interface IBezierCurve {
/**
* Control point 1 (P0)
*/
0: [number, number];
0: BezierCurvePoint;
/**
* Control point 2 (P1)
*/
1: [number, number];
1: BezierCurvePoint;
}
export function cubicBezier(name: string): string {
@ -22,13 +23,13 @@ export function cubicBezier(name: string): string {
/**
* Formats a cubic bezier config into a cubic bezier string
*/
export function formatCubicBezier(points) {
export function formatCubicBezier(points: IBezierCurve): string {
if (!Array.isArray(points) || !Array.isArray(points[0]) || !Array.isArray(points[1])) {
return "";
}
const p0 = points[0];
const p1 = points[1];
const p0: BezierCurvePoint = points[0];
const p1: BezierCurvePoint = points[1];
return `cubic-bezier(${p0[0]}, ${p0[1]}, ${p1[0]}, ${p1[1]})`;
}

Просмотреть файл

@ -4,7 +4,7 @@ import AnimateTo from "../animateTo";
/**
* Key frame object for fade-in animations
*/
export const fadeInKeyframes = [
export const fadeInKeyframes: AnimationKeyFrame[] = [
{ opacity: 0.01 }, // Start at 0.01 due to a bug animating from 0
{ opacity: 1 }
];
@ -12,7 +12,7 @@ export const fadeInKeyframes = [
/**
* Key frame object for fade-out animations
*/
export const fadeOutKeyframes = [
export const fadeOutKeyframes: AnimationKeyFrame[] = [
{ opacity: 1 },
{ opacity: 0 }
];
@ -20,7 +20,7 @@ export const fadeOutKeyframes = [
/**
* EffectTiming defaults for fade animations
*/
export const fadeEffectTiming = {
export const fadeEffectTiming: AnimationEffectTiming = {
easing: "linear",
duration: 500
};
@ -29,22 +29,22 @@ export const fadeEffectTiming = {
* Creates an animation to fade an element into view
*/
export function fadeIn(element: HTMLElement, effectTiming: AnimationEffectTiming = {}): AnimateTo {
const fadeInEffectTiming = Object.assign({}, fadeEffectTiming, effectTiming);
const fadeIn = new AnimateTo(element, null, fadeInEffectTiming);
const fadeInEffectTiming: AnimationEffectTiming = Object.assign({}, fadeEffectTiming, effectTiming);
const fadeInAnimation: AnimateTo = new AnimateTo(element, null, fadeInEffectTiming);
fadeIn.addKeyframes(fadeInKeyframes);
fadeInAnimation.addKeyframes(fadeInKeyframes);
return fadeIn;
return fadeInAnimation;
}
/**
* Creates an animation to fade an element out of view
*/
export function fadeOut(element: HTMLElement, effectTiming: AnimationEffectTiming = {}): AnimateTo {
const fadeOutEffectTiming = Object.assign({}, fadeEffectTiming, effectTiming);
const fadeOut = new AnimateTo(element, null, fadeOutEffectTiming);
const fadeOutEffectTiming: AnimationEffectTiming = Object.assign({}, fadeEffectTiming, effectTiming);
const fadeOutAnimation: AnimateTo = new AnimateTo(element, null, fadeOutEffectTiming);
fadeOut.addKeyframes(fadeOutKeyframes);
fadeOutAnimation.addKeyframes(fadeOutKeyframes);
return fadeOut;
return fadeOutAnimation;
}

Просмотреть файл

@ -7,15 +7,24 @@ import scrollY from "../utilities/scrollY";
*/
export type ScrollTriggerCallback = (distance: any) => void;
/**
* Export subscription interface
*/
export interface IScrollTriggerSubscription {
element: HTMLElement;
callback: ScrollTriggerCallback;
inView: boolean;
}
/**
* Scroll trigger base-class that handles event binding and element/callback registration.
*/
export default abstract class ScrollTrigger {
protected subscriptions: any = [];
protected subscriptions: IScrollTriggerSubscription[] = [];
protected scrollDistance: number = 0;
private openRequestAnimationFrame: boolean = false;
private useRequestAnimationFrame: boolean = false;
private lastScrollY: number;
protected scrollDistance: number = 0;
constructor() {
this.useRequestAnimationFrame = window.hasOwnProperty("requestAnimationFrame");
@ -27,19 +36,10 @@ export default abstract class ScrollTrigger {
this.update = this.update.bind(this);
}
/**
* Checks to see if element/callback pairs have been registered so we don't duplicate registration.
*/
private isSubscribed(element: HTMLElement, callback: ScrollTriggerCallback) {
return !!this.subscriptions.filter((subscription) => {
return element === subscription.element && callback === subscription.callback;
}).length;
}
/**
* Subscribe an element and callback for scroll triggers
*/
public subscribe(element: HTMLElement, callback: ScrollTriggerCallback) {
public subscribe(element: HTMLElement, callback: ScrollTriggerCallback): void {
if (!(element instanceof HTMLElement) || typeof callback !== "function" || this.isSubscribed(element, callback)) {
return;
}
@ -60,8 +60,8 @@ export default abstract class ScrollTrigger {
/**
* Unsubscribe an element and callback for scroll triggers
*/
public unsubscribe(element: HTMLElement, callback: ScrollTriggerCallback) {
this.subscriptions = this.subscriptions.filter((subscription) => {
public unsubscribe(element: HTMLElement, callback: ScrollTriggerCallback): void {
this.subscriptions = this.subscriptions.filter((subscription: IScrollTriggerSubscription) => {
return !(element === subscription.element && callback === subscription.callback);
});
@ -70,10 +70,29 @@ export default abstract class ScrollTrigger {
}
}
/**
* Make any arbitrary updates to UI
*/
protected update(): void {
const yOffset: number = scrollY();
this.openRequestAnimationFrame = false;
this.scrollDistance = yOffset - this.lastScrollY;
this.lastScrollY = yOffset;
}
/**
* Checks to see if element/callback pairs have been registered so we don't duplicate registration.
*/
private isSubscribed(element: HTMLElement, callback: ScrollTriggerCallback): boolean {
return !!this.subscriptions.filter((subscription: IScrollTriggerSubscription): boolean => {
return element === subscription.element && callback === subscription.callback;
}).length;
}
/**
* Request's an animation frame if there are currently no open animation frame requests
*/
private requestFrame = () => {
private requestFrame = (): void => {
if (this.openRequestAnimationFrame) {
return;
}
@ -81,14 +100,4 @@ export default abstract class ScrollTrigger {
this.openRequestAnimationFrame = true;
window.requestAnimationFrame(this.update);
}
/**
* Make any arbitrary updates to UI
*/
protected update() {
const yOffset = scrollY();
this.openRequestAnimationFrame = false;
this.scrollDistance = yOffset - this.lastScrollY;
this.lastScrollY = yOffset;
}
}

Просмотреть файл

@ -1,5 +1,5 @@
import isElementInView from "../utilities/isElementInView";
import ScrollBase from "./ScrollBase";
import ScrollBase, { IScrollTriggerSubscription } from "./ScrollBase";
/**
* Utility for registering element/callback pairs where the callback will be called on scroll while the element is in view.
@ -8,11 +8,11 @@ export default class ScrollTrigger extends ScrollBase {
/**
* Check if elements are in view-port and apply scroll method if they are
*/
protected update() {
protected update(): void {
super.update();
this.subscriptions.forEach((subscription) => {
const inView = isElementInView(subscription.element);
this.subscriptions.forEach((subscription: IScrollTriggerSubscription) => {
const inView: boolean = isElementInView(subscription.element);
if (inView) {
subscription.callback(this.scrollDistance);

Просмотреть файл

@ -1,5 +1,5 @@
import isElementInView from "../utilities/isElementInView";
import ScrollBase from "./ScrollBase";
import ScrollBase, { IScrollTriggerSubscription } from "./ScrollBase";
/**
* Utility for registering element/callback pairs where the callback will be called when the element enters the view-port
@ -8,11 +8,11 @@ export default class ViewEnterTrigger extends ScrollBase {
/**
* Check if elements are in view-port and apply scroll method if they are
*/
protected update() {
protected update(): void {
super.update();
this.subscriptions.forEach((subscription, index) => {
const inView = isElementInView(subscription.element);
this.subscriptions.forEach((subscription: IScrollTriggerSubscription, index: number) => {
const inView: boolean = isElementInView(subscription.element);
// If the element is in view but previously wasn't
if (inView && !subscription.inView) {

Просмотреть файл

@ -1,5 +1,5 @@
import isElementInView from "../utilities/isElementInView";
import ScrollBase from "./ScrollBase";
import ScrollBase, { IScrollTriggerSubscription } from "./ScrollBase";
/**
* Utility for registering element/callback pairs where the callback will be called when the element exits the view-port
@ -8,11 +8,11 @@ export default class ViewExitTrigger extends ScrollBase {
/**
* Check if elements are in view-port and apply scroll method if they are
*/
protected update() {
protected update(): void {
super.update();
this.subscriptions.forEach((subscription, index) => {
const inView = isElementInView(subscription.element);
this.subscriptions.forEach((subscription: IScrollTriggerSubscription, index: number) => {
const inView: boolean = isElementInView(subscription.element);
// If the element is out of view but previously was in view
if (!inView && subscription.inView) {

Просмотреть файл

@ -7,7 +7,7 @@
"lib": [ "es2015", "es2017", "dom", "scripthost" ],
"module": "CommonJS",
"moduleResolution": "node",
"outDir": "../build",
"outDir": "../dist",
"removeComments": true,
"rootDir": "./",
"sourceMap": true,

Просмотреть файл

@ -2,7 +2,7 @@
* Checks to see if any part of an element is within the viewport
*/
export default function isElementInView(el: HTMLElement): boolean {
const rect = el.getBoundingClientRect();
const rect: ClientRect = el.getBoundingClientRect();
return (
rect.bottom >= 0

Просмотреть файл

@ -3,7 +3,7 @@
"version": "0.0.12-beta.0",
"description": "An animation library that simplifies interactions and animations using the Web Animation API.",
"license": "MIT",
"main": "build/index.js",
"main": "dist/index.js",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/fast-animation.git"
@ -20,7 +20,7 @@
"test": "jest",
"build:react": "webpack --config webpack/webpack.react.config.js",
"dev-server:react": "node_modules/.bin/webpack-dev-server --config webpack/webpack.react.config.js --progress",
"prepack": "npm run build"
"prepare": "npm run build"
},
"devDependencies": {
"@types/jest": "^21.1.2",

Просмотреть файл

@ -4,7 +4,7 @@ export default class ContextMenuApi {
/**
* Remove a context menu by id
*/
public remove(menuId: string) {
public remove(menuId: string): void {
switch (getApiSupport()) {
case APIName.chrome:
chrome.contextMenus.remove(menuId);

Просмотреть файл

@ -1,21 +1,8 @@
import { getApiSupport, APIName } from "../";
export class OnMessageExternal {
public addListener(callback: (message: any, sender: chrome.runtime.MessageSender, sendResponse: (response: any) => void) => void) {
switch (getApiSupport()) {
case APIName.chrome:
chrome.runtime.onMessageExternal.addListener(callback);
break;
case APIName.browser:
browser.runtime.onMessageExternal.addListener(callback);
}
}
}
import OnMessageExternal from "./on-message-external";
export default class RuntimeApi {
/**
* Remove a context menu by id
*/
public onMessageExternal = new OnMessageExternal();
public onMessageExternal: OnMessageExternal = new OnMessageExternal();
}

Просмотреть файл

@ -0,0 +1,16 @@
import { getApiSupport, APIName } from "../";
export type SendResponse = (response: any) => void;
export default class OnMessageExternal {
public addListener(callback: (message: any, sender: chrome.runtime.MessageSender, sendResponse: SendResponse) => void): void {
switch (getApiSupport()) {
case APIName.chrome:
chrome.runtime.onMessageExternal.addListener(callback);
break;
case APIName.browser:
browser.runtime.onMessageExternal.addListener(callback);
break;
}
}
}

Просмотреть файл

@ -10,10 +10,10 @@ export default class TabsApi {
public query(queryInfo: chrome.tabs.QueryInfo, callback?: (results: chrome.tabs.Tab[]) => void): void {
switch (getApiSupport()) {
case APIName.chrome:
chrome.tabs.query(queryInfo, callback) as any;
chrome.tabs.query(queryInfo, callback);
break;
case APIName.browser:
browser.tabs.query(queryInfo).then(callback, (error) => {
browser.tabs.query(queryInfo).then(callback, (error: Error) => {
throw error;
});
break;
@ -24,7 +24,7 @@ export default class TabsApi {
/**
* Tab sendMessage wrapper
*/
public sendMessage(tabId: number, message: any) {
public sendMessage(tabId: number, message: any): void {
switch (getApiSupport()) {
case APIName.chrome:
chrome.tabs.sendMessage(tabId, message);

Просмотреть файл

@ -3,9 +3,9 @@ import TabsApi from "./TabsApi";
import RuntimeApi from "./RuntimeApi";
class ExtensionApi {
public contextMenus = new ContextMenuApi();
public tabs = new TabsApi();
public runtime = new RuntimeApi();
public contextMenus: ContextMenuApi = new ContextMenuApi();
public tabs: TabsApi = new TabsApi();
public runtime: RuntimeApi = new RuntimeApi();
}
export default new ExtensionApi();

Просмотреть файл

@ -1,13 +1,13 @@
/**
* Define configuration values
*/
const validDomains = [ "*://*.fluentweb.com/*" ];
const extensionId = "jhhigejkemaekdcempcigaapebobmimg";
const validDomains: string[] = [ "*://*.fluentweb.com/*" ];
const extensionId: string = "jhhigejkemaekdcempcigaapebobmimg";
/**
* Define event names
*/
export const menuItemClickEvent = "FW_EXTENSION::MENU_ITEM_CLICK";
export const menuItemClickEvent: string = "FW_EXTENSION::MENU_ITEM_CLICK";
/**
* Non-production environments

Просмотреть файл

@ -4,7 +4,7 @@ import { menuItemClickEvent } from "./config";
/**
* Subscribe to messages from background scripts
*/
chrome.runtime.onMessage.addListener((menuItem: IContextMenuItem, sender, sendResponse) => {
const e = new CustomEvent(menuItemClickEvent, { detail: menuItem });
chrome.runtime.onMessage.addListener((menuItem: IContextMenuItem, sender: any, sendResponse: any) => {
const e: CustomEvent = new CustomEvent(menuItemClickEvent, { detail: menuItem });
window.dispatchEvent(e);
});

Просмотреть файл

@ -29,7 +29,7 @@ export interface IContextMenus {
// Store menu ids for later reference
let menuIdStore: {[key: string]: IContextMenuItem} = {};
const rootId = createContextMenu();
const rootId: string = createContextMenu();
/**
* Creates the FW context menu
@ -46,9 +46,9 @@ function createContextMenu(): string {
/**
* Create submenu items for a root menu
*/
function createSubmenuItems(config: IContextMenus, rootId: string) {
Object.keys(config).map((key, index) => {
const menuConfigs = config[key].slice(0);
function createSubmenuItems(config: IContextMenus, id: string): void {
Object.keys(config).map((key: string, index: number) => {
const menuConfigs: IContextMenuItem[] = config[key].slice(0);
if (index !== 0) {
// Add a separator before all groups
@ -57,15 +57,15 @@ function createSubmenuItems(config: IContextMenus, rootId: string) {
}
menuConfigs
.map((config) => {
return Object.assign({}, config, {
parentId: rootId,
.map((menuConfig: any) => {
return Object.assign({}, menuConfig, {
parentId: id,
onclick: handleContextMenuItemClick
});
})
.forEach((config) => {
const menuId = ExtensionApi.contextMenus.create(config);
menuIdStore[menuId] = config;
.forEach((menuConfig: any) => {
const menuId: string = ExtensionApi.contextMenus.create(menuConfig);
menuIdStore[menuId] = menuConfig;
});
});
}
@ -73,13 +73,13 @@ function createSubmenuItems(config: IContextMenus, rootId: string) {
/**
* Handles menu item click events
*/
function handleContextMenuItemClick(info) {
ExtensionApi.tabs.query({active: true, currentWindow: true}, (tabs) => {
function handleContextMenuItemClick(info: any): void {
ExtensionApi.tabs.query({active: true, currentWindow: true}, (tabs: any[]) => {
if (!Array.isArray(tabs) || !tabs.length) {
return;
}
const clickedMenuConfig = menuIdStore[info.menuItemId];
const clickedMenuConfig: IContextMenuItem = menuIdStore[info.menuItemId];
if (clickedMenuConfig !== undefined) {
ExtensionApi.tabs.sendMessage(tabs[0].id, clickedMenuConfig);
@ -90,15 +90,15 @@ function handleContextMenuItemClick(info) {
/**
* Remove all context menu items
*/
function removeAllContextMenuItems() {
Object.keys(menuIdStore).forEach((menuId) => {
function removeAllContextMenuItems(): void {
Object.keys(menuIdStore).forEach((menuId: string) => {
ExtensionApi.contextMenus.remove(menuId);
});
menuIdStore = {};
}
function handleExternalMessages(message: CreateMessage, sender, sendResponse) {
function handleExternalMessages(message: CreateMessage, sender: any, sendResponse: any): void {
switch (message.type) {
case CREATE_MENUS_MESSAGE:
removeAllContextMenuItems();

Просмотреть файл

@ -10,7 +10,7 @@ const manifest: any = {
description: "Configure Fluent Web content and domains",
externally_connectable: {
// '<all_urls>' is not valid in this property
matches: validDomains.filter((domain) => domain !== "<all_urls>")
matches: validDomains.filter((domain: string) => domain !== "<all_urls>")
},
permissions: [
"activeTab",

Просмотреть файл

@ -61,7 +61,7 @@ export function onMenuItemClick(callback: (e: Event) => void): void {
* Communicates from a client to the browser extension that a new menu system should be created
*/
export function createContextMenus(config: IContextMenus): void {
const data = {
const data: ICreateMessage = {
type: CREATE_MENUS_MESSAGE,
data: config
};

Просмотреть файл

@ -3,8 +3,8 @@ import { SheetsManager } from "jss";
const manager: any = new SheetsManager();
class Theme {
private Background: string = "blue";
private Width: string = "100%";
private _background: string = "blue";
private _width: string = "100%";
private updateStylesheets(): void {
manager.sheets.forEach((sheet: any) => {
@ -13,20 +13,20 @@ class Theme {
}
get background(): string {
return this.Background;
return this._background;
}
set background(value: string) {
this.Background = value;
this._background = value;
this.updateStylesheets();
}
get width(): string {
return this.Width;
return this._width;
}
set width(value: string) {
this.Width = value;
this._width = value;
this.updateStylesheets();
}
}

Просмотреть файл

@ -1,14 +1,30 @@
/* tslint:disable:no-console */
import React = require("react");
import renderer = require("react-test-renderer");
export interface IExampleProps {
component: React.ReactNode;
data: any;
styles: string;
}
export interface IExample {
title: string;
props?: IExampleProps;
children?: React.ReactNode;
}
export interface IExampes {
data: IExample[];
}
/**
* @name generateSnapshots
* @description Automates taking snapshot tests from code examples.
@param {Array} examples - a set of example objects that contain a title, a
@param {Function} reactComponent - The react component to render
react component, props for that component, and children for that component
* Automates taking snapshot tests from code examples.
* @param {Array} examples - a set of example objects that contain a title, a
* @param {Function} reactComponent - The react component to render
* react component, props for that component, and children for that component
*/
const generateSnapshots = (examples, reactComponent) => {
export default (examples: IExampes, reactComponent: React.ComponentClass<any>): void => {
if (!reactComponent) {
console.error("No component param passed to generateSnapshots.");
return;
@ -25,19 +41,15 @@ const generateSnapshots = (examples, reactComponent) => {
);
}
for (let i = 0; i < examples.data.length; i++) {
const example = examples.data[i];
test(example.title, () => {
const component = renderer.create(
for (const example of examples.data) {
test(example.title, (): void => {
const component: any = renderer.create(
React.createElement(reactComponent, example.props, example.children)
);
const json = component.toJSON();
const json: any = component.toJSON();
expect(json).toMatchSnapshot();
});
}
};
export default generateSnapshots;

Просмотреть файл

@ -1,30 +0,0 @@
const spawn = require("cross-spawn");
const argv = require("yargs").argv;
const chalk = require("chalk");
/**
* Run jest unit tests
*/
let args = [
"--notify"
];
if (argv.update || argv.u) {
args.push("-u");
}
let jestInstance = spawn("./node_modules/.bin/jest", args, {stdio: "inherit"});
/**
* If we pass --throw, we should throw an error if the process
* fails. This allows us to block `git push` if unit tests fail.
*/
if (argv.throw) {
jestInstance.on("close", (code) => {
if (code > 0) {
const error = new Error("Error: One or more Jest tests failed.");
console.error(chalk.red(error.message));
throw error;
}
});
}

Просмотреть файл

@ -4,8 +4,8 @@
"description": "An initial implementation of a React iframe that is self-contained.",
"main": "dist/index.js",
"scripts": {
"test": "ts-node helpers/unitTests.ts",
"test:update": "ts-node helpers/unitTests.ts --update",
"test": "jest",
"test:update": "jest --update",
"dev-server": "webpack-dev-server --progress",
"build": "tsc -p ./src/tsconfig.json",
"prepack": "npm run build"

Просмотреть файл

@ -1,7 +1,9 @@
import * as React from "react";
import Example from "../../../../app/components/example";
function handleGetStyles(style: string): void {}
function handleGetStyles(style: string): void {
return void 0;
}
export default {
data: [

Просмотреть файл

@ -4,7 +4,7 @@ import Viewer from "../";
import data from "./data";
describe("viewer", () => {
const viewerComponent = renderer.create(
const viewerComponent: any = renderer.create(
React.createElement(Viewer, data.data[0].props)
);
@ -13,6 +13,7 @@ describe("viewer", () => {
});
it("returns the content element", () => {
/* tslint:disable-next-line */
expect(viewerComponent.getInstance().getContentElement()).toBe("<div class=\"div-0-0-1\"><input type=\"text\" value=\"Hello World\"/><img src=\"https://placehold.it/300x300\"/><link href=\"https://fluentweb.com/fw-d192a11b84ce02288037ba0722f3ee33.min.css\" rel=\"stylesheet\"/></div>");
});
});

Просмотреть файл

@ -28,6 +28,13 @@
"member-variable-declaration",
"object-destructuring",
"array-destructuring"
],
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-leading-underscore",
"allow-pascal-case"
]
},
"rulesDirectory": []

Просмотреть файл

@ -1,5 +1,5 @@
{
"extends": "@microsoft/fast-tslint-rules",
"extends": "./packages/fast-tslint-rules/tslint.json",
"linterOptions": {
"exclude": ["node_modules/**/*.*", "**/*/node_modules/**/*.*", "**/*.d.ts"]
}