Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dimensions2d class #54

Merged
merged 29 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
186a79a
Add Dimensions2d class
amacdonald-google Dec 15, 2020
03c524f
Port additional Toolbox components.
amacdonald-google Dec 15, 2020
eef4b7c
Consolidate and cull duplicated or similar functionality.
amacdonald-google Dec 17, 2020
7cb8ef4
Remove `getStyle`
amacdonald-google Dec 17, 2020
bb22936
Add comments, cull unnecessary code
amacdonald-google Dec 17, 2020
85d9b41
Handle 0 area canvases in WebGlImageSequence
amacdonald-google Dec 17, 2020
821fea8
Polish pass in response to review
amacdonald-google Dec 17, 2020
26c328d
Restore x
amacdonald-google Dec 18, 2020
1276663
Re-work Map classes
amacdonald-google Dec 18, 2020
f4a5c40
Cleanup arrayf
amacdonald-google Dec 18, 2020
4c353d0
Remove unused code
amacdonald-google Dec 18, 2020
6d5393f
Restore ArrayMap
amacdonald-google Dec 18, 2020
0569c8b
Remove unused code in CachedElementVector
amacdonald-google Dec 18, 2020
2731d26
Inline method ComputedStyleService
amacdonald-google Dec 18, 2020
93e665f
Extract duplicated code to ComputedStyleService
amacdonald-google Dec 18, 2020
c93396a
Remove unused Dimensions2dDom code
amacdonald-google Dec 18, 2020
f0bb5f9
Simplify Dimensions cached vector
amacdonald-google Dec 18, 2020
55e56b2
Simplify MatrixDom
amacdonald-google Dec 18, 2020
41c3a4f
Consolidate visible distance from root functions
amacdonald-google Dec 18, 2020
bbc0ccf
Inline single call init function
amacdonald-google Dec 18, 2020
6c394d7
Add comment explaining inspiration for DynamicDefaultMap
amacdonald-google Dec 19, 2020
de16706
Simplify visible-distance-from-root-service
amacdonald-google Dec 19, 2020
9624a3a
Yank abstract classes
amacdonald-google Dec 19, 2020
937735b
Remove a lot of caching classes.
amacdonald-google Dec 19, 2020
726a46e
Remove caching
amacdonald-google Dec 19, 2020
f65bd63
Correct comment
amacdonald-google Dec 19, 2020
2f16261
Remove now unused code
amacdonald-google Dec 19, 2020
c7bf8f5
Respond to review feedback with improvements
amacdonald-google Dec 19, 2020
5debf79
Address feedback from PR
amacdonald-google Jan 5, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
"module": "lib/index.es.js",
"types": "lib/index.d.ts",
"directories": {
"lib": "lib"
"lib": "lib",
"src": "src"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/grow/yano-js.git"
},
"files": [
"lib"
"lib",
"src"
],
"ava": {
"compileEnhancements": false,
Expand Down
41 changes: 41 additions & 0 deletions src/arrayf/arrayf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,45 @@ export class arrayf {
return maxValue;
}

/**
* Determines if the value in each index of each list matches the
* corresponding value in that same index in each other list.
* @param lists
*/
static areArrayValuesIdentical<T>(lists: T[][]): boolean {
angusm marked this conversation as resolved.
Show resolved Hide resolved
angusm marked this conversation as resolved.
Show resolved Hide resolved
// Short circuit on null values
if (!lists.every((list: T[]) => list instanceof Array)) {
return false;
}
// Check all lists have same lengths
const lengths = lists.map((list: T[]) => list.length);
const haveIdenticalLengths = arrayf.containsIdenticalValues(lengths);
if (!haveIdenticalLengths) {
return false;
}
// Verify that values match across lists
return arrayf.zip(...lists)
.every((zippedValues: T[]) => {
return arrayf.containsIdenticalValues(zippedValues);
});
}

/**
* Determine if each value in the array is the same value.
*/
static containsIdenticalValues<T>(values: T[]): boolean {
return values.length === 0 ||
values.every((value) => values[0] === value);
}

/**
* Removes the first instance of a value from the given array.
* Useful when tracking the places a singleton or service is used so that
* it can dispose of itself when no longer needed.
*/
static removeFirstInstance<T>(values: T[], value: T): T[] {
angusm marked this conversation as resolved.
Show resolved Hide resolved
return [
...values.slice(0, values.indexOf(value)),
...values.slice(values.indexOf(value) + 1)];
}
}
125 changes: 125 additions & 0 deletions src/dom/cached-element-vector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { DynamicDefaultMap } from '../map/dynamic-default';
import { MultiValueDynamicDefaultMap } from '../map/multi-value-dynamic-default';
import { MultiDimensionalVector } from '../mathf/geometry/multi-dimensional-vector';
import { Raf } from '..';

const VALUE_LIMIT: number = 2;

type InnerCache = MultiValueDynamicDefaultMap<any, any>;
const caches: DynamicDefaultMap<any, InnerCache> =
DynamicDefaultMap.usingFunction<any, any>(
angusm marked this conversation as resolved.
Show resolved Hide resolved
(Class) => {
return MultiValueDynamicDefaultMap.usingFunction(
(args: any[]) => new Class(...args));
});
const uses: DynamicDefaultMap<CachedElementVector<MultiDimensionalVector>, Set<any>> =
DynamicDefaultMap.usingFunction(() => new Set());

export abstract class CachedElementVector<T extends MultiDimensionalVector> {
static getForElement(use: any, args: any[] = [null]): any {
const instance = caches.get(this).get(args);
uses.get(instance).add(use);
return instance;
}

static getSingleton(use: any): any {
return this.getForElement(use);
}
protected static VectorClass: typeof MultiDimensionalVector =
MultiDimensionalVector;
protected static VALUE_LIMIT: number = VALUE_LIMIT;

protected element: HTMLElement;

private readonly args: any[];
private values: T[];
private destroyed: boolean;
private destroyTimeout: number;
private raf: Raf;

protected constructor(element: any = null, ...args: any[]) {
const instanceByElement = caches.get(this.constructor);
this.raf = new Raf(() => this.loop());
this.args = [element, ...args];

if (instanceByElement.has([element, ...args])) {
if (element) {
throw new Error('Please use getForElement instead of new.');
} else {
throw new Error('Please use getSingleton instead of new.');
}
}

this.destroyTimeout = null;
this.destroyed = false;
this.element = element;
this.values = <T[]>[this.getCurrentVector()];
this.init();
}

getLastValue(): T {
return this.values.slice(-1)[0];
}

getDelta(): T {
const values = this.getCurrentAndLastValue();
return <T>this.getVectorClass().subtract(values[0], values[1]);
}

hasChanged(): boolean {
return !this.getVectorClass().areEqual(...this.getCurrentAndLastValue());
}

destroy(use: any): void {
uses.get(this).delete(use);
clearTimeout(this.destroyTimeout);
// Hang tight for a minute so we aren't creating and destroying these caches
// too rapidly.
this.destroyTimeout = window.setTimeout(
() => {
if (uses.size <= 0) {
caches.get(this.constructor).delete(this.args);
this.raf.dispose();
this.destroyed = true;
}
},
1000);
}

protected getValues(): number[] {
throw new Error('getValues must be overridden by child class');
}

private init(): void {
// Init values so that instances can be created during a measure step if
// necessary.
this.raf.start();
angusm marked this conversation as resolved.
Show resolved Hide resolved
}

private getVectorClass(): typeof MultiDimensionalVector {
return (<typeof CachedElementVector>this.constructor).VectorClass;
}

private getCurrentVector(): T {
return <T>new (this.getVectorClass())(...this.getValues());
}

private getValueLimit_(): number {
return (<typeof CachedElementVector>this.constructor).VALUE_LIMIT;
}

private loop(): void {
this.raf.preRead(() => this.measureValues());
}

private measureValues(): void {
this.values =
this.values
.slice(-(this.getValueLimit_() - 1))
.concat([this.getCurrentVector()]);
}

private getCurrentAndLastValue(): MultiDimensionalVector[] {
return this.values.slice(-2);
}
}
3 changes: 3 additions & 0 deletions src/dom/get-scroll-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function getScrollElement(): Element {
angusm marked this conversation as resolved.
Show resolved Hide resolved
return document.scrollingElement || document.documentElement;
}
46 changes: 46 additions & 0 deletions src/dom/position/dimensions-2d-dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Dimensions2d } from '../../mathf/geometry/dimensions-2d';
import { getScrollElement } from '../get-scroll-element';

export class Dimensions2dDom extends Dimensions2d {
static fromCanvas<T extends Dimensions2dDom>(
element: HTMLCanvasElement = null
): T {
return <T>new this(element.width, element.height);
}

static fromVideo<T extends Dimensions2dDom>(
element: HTMLVideoElement = null
): T {
return <T>new this(element.videoWidth, element.videoHeight);
}

static fromElementOffset<T extends Dimensions2dDom>(
element: HTMLElement = null
): T {
if (element) {
return <T>new this(element.offsetWidth, element.offsetHeight);
} else {
return this.fromInnerWindow();
}
}

static fromRootElement<T extends Dimensions2dDom>() {
return <T>new this(
document.documentElement.clientWidth,
document.documentElement.clientHeight);
}

static fromScrollElementClient<T extends Dimensions2dDom>() {
return <T>new this(
getScrollElement().clientWidth, getScrollElement().clientHeight);
}

static fromInnerWindow<T extends Dimensions2dDom>() {
return <T>new this(window.innerWidth, window.innerHeight);
}

sizeElement(element: HTMLElement): void {
element.style.width = `${this.width}px`;
element.style.height = `${this.height}px`;
}
}
26 changes: 26 additions & 0 deletions src/dom/position/dimensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CachedElementVector } from '../cached-element-vector';
import { Dimensions2dDom } from './dimensions-2d-dom';
import { getAncestorDimensions } from './get-ancestor-dimensions';

export class Dimensions extends CachedElementVector<Dimensions2dDom> {
angusm marked this conversation as resolved.
Show resolved Hide resolved
static getForElement(use: any, args: any[] = [null]): Dimensions {
return <Dimensions>CachedElementVector.getForElement.bind(this)(use, args);
}

static getSingleton(use: any): Dimensions {
return <Dimensions>CachedElementVector.getSingleton.bind(this)(use);
}
protected static VectorClass: typeof Dimensions2dDom = Dimensions2dDom;

constructor(element: HTMLElement = null) {
super(element);
}

getDimensions(): Dimensions2dDom {
return this.getLastValue();
}

protected getValues(): number[] {
return getAncestorDimensions(this.element).getValues();
}
}
11 changes: 11 additions & 0 deletions src/dom/position/get-ancestor-dimensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Dimensions2dDom } from './dimensions-2d-dom';

export function getAncestorDimensions(
ancestor: HTMLElement = null
): Dimensions2dDom {
if (ancestor) {
return Dimensions2dDom.fromElementOffset<Dimensions2dDom>(ancestor);
} else {
return Dimensions2dDom.fromRootElement<Dimensions2dDom>();
}
}
5 changes: 5 additions & 0 deletions src/dom/position/is-fixed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { getStyle } from '../style/get-style';

export function isFixed(element: HTMLElement) {
return getStyle(element, 'position') === 'fixed';
}
Loading