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:
Родитель
8450514fbd
Коммит
78fea3efd3
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче