Merge pull request #9324 from home-assistant/dev

This commit is contained in:
Bram Kragten 2021-06-01 11:56:02 +02:00 committed by GitHub
commit 3d788d6056
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 4213 additions and 22 deletions

View File

@ -104,5 +104,6 @@
"lit/attribute-value-entities": 0
},
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
"processor": "disable/disable"
"processor": "disable/disable",
"ignorePatterns": ["src/resources/lit-virtualizer/*"]
}

21
.gitignore vendored
View File

@ -1,10 +1,17 @@
.DS_Store
.reify-cache
# build
build
build-translations/*
hass_frontend/*
dist
# yarn
.yarn
yarn-error.log
node_modules/*
npm-debug.log
.DS_Store
hass_frontend/*
.reify-cache
# Python stuff
*.py[cod]
@ -14,11 +21,8 @@ hass_frontend/*
# venv stuff
pyvenv.cfg
pip-selfcheck.json
venv
venv/*
.venv
lib
bin
dist
# vscode
.vscode/*
@ -31,9 +35,8 @@ src/cast/dev_const.ts
# Secrets
.lokalise_token
yarn-error.log
#asdf
# asdf
.tool-versions
# Home Assistant config

View File

@ -116,8 +116,9 @@ const createWebpackConfig = ({
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
new webpack.NormalModuleReplacementPlugin(
new RegExp(
require.resolve(
"@lit-labs/virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
path.resolve(
paths.polymer_dir,
"src/resources/lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
)
),
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")

View File

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20210531.1",
version="20210601.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",

View File

@ -1,4 +1,4 @@
import { Layout1d, scroll } from "@lit-labs/virtualizer";
import { Layout1d, scroll } from "../../resources/lit-virtualizer";
import deepClone from "deep-clone-simple";
import {
css,
@ -340,11 +340,10 @@ export class HaDataTable extends LitElement {
${scroll({
items: this._items,
layout: Layout1d,
// @ts-expect-error
renderItem: (row: DataTableRowData, index) => {
// not sure how this happens...
if (!row) {
return "";
return html``;
}
if (row.append) {
return html`

View File

@ -108,7 +108,7 @@ class MoreInfoWeather extends LitElement {
this.stateObj.attributes.pressure,
this.hass.locale
)}
${getWeatherUnit(this.hass, "air_pressure")}
${getWeatherUnit(this.hass, "pressure")}
</div>
</div>
`

View File

@ -1,4 +1,4 @@
import { Layout1d, scroll } from "@lit-labs/virtualizer";
import { Layout1d, scroll } from "../../resources/lit-virtualizer";
import "@material/mwc-list/mwc-list";
import type { List } from "@material/mwc-list/mwc-list";
import { SingleSelectedEvent } from "@material/mwc-list/mwc-list-foundation";
@ -188,7 +188,6 @@ export class QuickBar extends LitElement {
${scroll({
items,
layout: Layout1d,
// @ts-expect-error
renderItem: (item: QuickBarItem, index) =>
this._renderItem(item, index),
})}
@ -224,7 +223,7 @@ export class QuickBar extends LitElement {
private _renderItem(item: QuickBarItem, index?: number) {
if (!item) {
return undefined;
return html``;
}
return isCommandItem(item)
? this._renderCommandItem(item, index)

View File

@ -1,4 +1,4 @@
import { Layout1d, scroll } from "@lit-labs/virtualizer";
import { Layout1d, scroll } from "../../resources/lit-virtualizer";
import {
css,
CSSResultGroup,
@ -100,7 +100,6 @@ class HaLogbook extends LitElement {
? scroll({
items: this.entries,
layout: Layout1d,
// @ts-expect-error
renderItem: (item: LogbookEntry, index) =>
this._renderLogbookItem(item, index),
})

View File

@ -0,0 +1,39 @@
import { LitElement, TemplateResult } from "lit";
import {
LayoutSpecifier,
Layout,
LayoutConstructor,
} from "./uni-virtualizer/lib/layouts/Layout.js";
/**
* A LitElement wrapper of the scroll directive.
*
* Import this module to declare the lit-virtualizer custom element.
* Pass an items array, renderItem method, and scroll target as properties
* to the <lit-virtualizer> element.
*/
export declare class LitVirtualizer extends LitElement {
renderItem?: (item: any, index?: number) => TemplateResult;
items: Array<unknown>;
scrollTarget: Element | Window;
keyFunction: ((item: unknown) => unknown) | undefined;
private _layout;
private _scrollToIndex;
createRenderRoot(): this;
/**
* The method used for rendering each item.
*/
set layout(layout: Layout | LayoutConstructor | LayoutSpecifier | null);
get layout(): Layout | LayoutConstructor | LayoutSpecifier | null;
/**
* Scroll to the specified index, placing that item at the given position
* in the scroll view.
*/
scrollToIndex(index: number, position?: string): Promise<void>;
render(): TemplateResult;
}
declare global {
interface HTMLElementTagNameMap {
"lit-virtualizer": LitVirtualizer;
}
}
//# sourceMappingURL=lit-virtualizer.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"lit-virtualizer.d.ts","sourceRoot":"","sources":["../src/lib/lit-virtualizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAKvD,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAErG;;;;;;GAMG;AACH,qBACa,cAAe,SAAQ,UAAU;IAE1C,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC;IAG7D,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAM;IAG3B,YAAY,EAAE,OAAO,GAAG,MAAM,CAAQ;IAGtC,WAAW,EAAE,CAAC,CAAC,IAAI,EAAC,OAAO,KAAK,OAAO,CAAC,GAAG,SAAS,CAAa;IAEjE,OAAO,CAAC,OAAO,CAA6D;IAE5E,OAAO,CAAC,cAAc,CAAkD;IAExE,gBAAgB;IAahB;;OAEG;IAWH,IACI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,eAAe,GAAG,IAAI,EAIrE;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,iBAAiB,GAAG,eAAe,GAAG,IAAI,CAEhE;IAGD;;;OAGG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAgB;IAO7D,MAAM,IAAI,cAAc;CAO3B;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,iBAAiB,EAAE,cAAc,CAAC;KACrC;CACJ"}

View File

@ -0,0 +1,118 @@
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r =
c < 3
? target
: desc === null
? (desc = Object.getOwnPropertyDescriptor(target, key))
: desc,
d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if ((d = decorators[i]))
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { html, LitElement } from "lit";
import { customElement } from "lit/decorators/custom-element.js";
import { property } from "lit/decorators/property.js";
import { scroll } from "./scroll.js";
import { scrollerRef } from "./uni-virtualizer/lib/VirtualScroller.js";
/**
* A LitElement wrapper of the scroll directive.
*
* Import this module to declare the lit-virtualizer custom element.
* Pass an items array, renderItem method, and scroll target as properties
* to the <lit-virtualizer> element.
*/
let LitVirtualizer = class LitVirtualizer extends LitElement {
constructor() {
super(...arguments);
this.items = [];
this.scrollTarget = this;
this.keyFunction = undefined;
this._layout = null;
this._scrollToIndex = null;
}
createRenderRoot() {
return this;
}
// get items() {
// return this._items;
// }
// set items(items) {
// this._items = items;
// this._scroller.totalItems = items.length;
// }
/**
* The method used for rendering each item.
*/
// get renderItem() {
// return this._renderItem;
// }
// set renderItem(renderItem) {
// if (renderItem !== this.renderItem) {
// this._renderItem = renderItem;
// this.requestUpdate();
// }
// }
set layout(layout) {
// TODO (graynorton): Shouldn't have to set this here
this._layout = layout;
this.requestUpdate();
}
get layout() {
return this[scrollerRef].layout;
}
/**
* Scroll to the specified index, placing that item at the given position
* in the scroll view.
*/
async scrollToIndex(index, position = "start") {
this._scrollToIndex = { index, position };
this.requestUpdate();
await this.updateComplete;
this._scrollToIndex = null;
}
render() {
const { items, renderItem, keyFunction, scrollTarget } = this;
const layout = this._layout;
return html`
${scroll({
items,
renderItem,
layout,
keyFunction,
scrollTarget,
scrollToIndex: this._scrollToIndex,
})}
`;
}
};
__decorate([property()], LitVirtualizer.prototype, "renderItem", void 0);
__decorate(
[property({ attribute: false })],
LitVirtualizer.prototype,
"items",
void 0
);
__decorate(
[property({ attribute: false })],
LitVirtualizer.prototype,
"scrollTarget",
void 0
);
__decorate([property()], LitVirtualizer.prototype, "keyFunction", void 0);
__decorate(
[property({ attribute: false })],
LitVirtualizer.prototype,
"layout",
null
);
LitVirtualizer = __decorate([customElement("lit-virtualizer")], LitVirtualizer);
export { LitVirtualizer };
//# sourceMappingURL=lit-virtualizer.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"lit-virtualizer.js","sourceRoot":"","sources":["../src/lib/lit-virtualizer.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAkB,MAAM,KAAK,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAoB,MAAM,0CAA0C,CAAC;AAGzF;;;;;;GAMG;AAEH,IAAa,cAAc,GAA3B,MAAa,cAAe,SAAQ,UAAU;IAA9C;;QAKI,UAAK,GAAmB,EAAE,CAAC;QAG3B,iBAAY,GAAqB,IAAI,CAAC;QAGtC,gBAAW,GAA4C,SAAS,CAAC;QAEzD,YAAO,GAAwD,IAAI,CAAC;QAEpE,mBAAc,GAA6C,IAAI,CAAC;IA0D5E,CAAC;IAxDG,gBAAgB;QACZ,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,gBAAgB;IAChB,0BAA0B;IAC1B,IAAI;IAEJ,qBAAqB;IACrB,2BAA2B;IAC3B,gDAAgD;IAChD,IAAI;IAEJ;;OAEG;IACH,qBAAqB;IACrB,+BAA+B;IAC/B,IAAI;IACJ,+BAA+B;IAC/B,4CAA4C;IAC5C,yCAAyC;IACzC,gCAAgC;IAChC,QAAQ;IACR,IAAI;IAGJ,IAAI,MAAM,CAAC,MAA2D;QAClE,qDAAqD;QACrD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,aAAa,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,MAAM;QACN,OAAQ,IAAyB,CAAC,WAAW,CAAE,CAAC,MAAM,CAAC;IAC3D,CAAC;IAGD;;;OAGG;IACH,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,WAAmB,OAAO;QACzD,IAAI,CAAC,cAAc,GAAG,EAAC,KAAK,EAAE,QAAQ,EAAC,CAAC;QACxC,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,MAAM,IAAI,CAAC,cAAc,CAAC;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED,MAAM;QACF,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;QAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,OAAO,IAAI,CAAA;cACL,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC;SACzG,CAAC;IACN,CAAC;CACJ,CAAA;AAvEG;IADC,QAAQ,EAAE;kDACkD;AAG7D;IADC,QAAQ,CAAC,EAAC,SAAS,EAAE,KAAK,EAAC,CAAC;6CACF;AAG3B;IADC,QAAQ,CAAC,EAAC,SAAS,EAAE,KAAK,EAAC,CAAC;oDACS;AAGtC;IADC,QAAQ,EAAE;mDACsD;AAiCjE;IADC,QAAQ,CAAC,EAAC,SAAS,EAAC,KAAK,EAAC,CAAC;4CAK3B;AAhDQ,cAAc;IAD1B,aAAa,CAAC,iBAAiB,CAAC;GACpB,cAAc,CAyE1B;SAzEY,cAAc","sourcesContent":["import { html, LitElement, TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators/custom-element.js';\nimport { property } from 'lit/decorators/property.js';\nimport { scroll } from './scroll.js';\nimport { scrollerRef, ContainerElement } from './uni-virtualizer/lib/VirtualScroller.js';\nimport { LayoutSpecifier, Layout, LayoutConstructor } from './uni-virtualizer/lib/layouts/Layout.js';\n\n/**\n * A LitElement wrapper of the scroll directive.\n *\n * Import this module to declare the lit-virtualizer custom element.\n * Pass an items array, renderItem method, and scroll target as properties\n * to the <lit-virtualizer> element.\n */\n@customElement('lit-virtualizer')\nexport class LitVirtualizer extends LitElement {\n @property()\n renderItem?: ((item: any, index?: number) => TemplateResult);\n\n @property({attribute: false})\n items: Array<unknown> = [];\n\n @property({attribute: false})\n scrollTarget: Element | Window = this;\n\n @property()\n keyFunction: ((item:unknown) => unknown) | undefined = undefined;\n\n private _layout: Layout | LayoutConstructor | LayoutSpecifier | null = null;\n\n private _scrollToIndex: {index: number, position: string} | null = null;\n \n createRenderRoot() {\n return this;\n }\n\n // get items() {\n // return this._items;\n // }\n\n // set items(items) {\n // this._items = items;\n // this._scroller.totalItems = items.length;\n // }\n\n /**\n * The method used for rendering each item.\n */\n // get renderItem() {\n // return this._renderItem;\n // }\n // set renderItem(renderItem) {\n // if (renderItem !== this.renderItem) {\n // this._renderItem = renderItem;\n // this.requestUpdate();\n // }\n // }\n\n @property({attribute:false})\n set layout(layout: Layout | LayoutConstructor | LayoutSpecifier | null) {\n // TODO (graynorton): Shouldn't have to set this here\n this._layout = layout;\n this.requestUpdate();\n }\n\n get layout(): Layout | LayoutConstructor | LayoutSpecifier | null {\n return (this as ContainerElement)[scrollerRef]!.layout;\n }\n \n \n /**\n * Scroll to the specified index, placing that item at the given position\n * in the scroll view.\n */\n async scrollToIndex(index: number, position: string = 'start') {\n this._scrollToIndex = {index, position};\n this.requestUpdate();\n await this.updateComplete;\n this._scrollToIndex = null;\n }\n\n render(): TemplateResult {\n const { items, renderItem, keyFunction, scrollTarget } = this;\n const layout = this._layout;\n return html`\n ${scroll({ items, renderItem, layout, keyFunction, scrollTarget, scrollToIndex: this._scrollToIndex })}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'lit-virtualizer': LitVirtualizer;\n }\n}"]}

View File

@ -0,0 +1,61 @@
import { TemplateResult, ChildPart } from "lit";
import { PartInfo } from "lit/directive.js";
import { AsyncDirective } from "lit/async-directive.js";
import {
Layout,
LayoutConstructor,
LayoutSpecifier,
} from "./uni-virtualizer/lib/layouts/Layout.js";
import {
VirtualScroller,
ScrollToIndexValue,
} from "./uni-virtualizer/lib/VirtualScroller.js";
/**
* Configuration options for the scroll directive.
*/
interface ScrollConfig {
/**
* A function that returns a lit-html TemplateResult. It will be used
* to generate the DOM for each item in the virtual list.
*/
renderItem?: (item: any, index?: number) => TemplateResult;
keyFunction?: (item: any) => unknown;
layout?: Layout | LayoutConstructor | LayoutSpecifier | null;
/**
* An element that receives scroll events for the virtual scroller.
*/
scrollTarget?: Element | Window;
/**
* The list of items to display via the renderItem function.
*/
items?: Array<any>;
/**
* Limit for the number of items to display. Defaults to the length of the
* items array.
*/
totalItems?: number;
/**
* Index and position of the item to scroll to.
*/
scrollToIndex?: ScrollToIndexValue;
}
export declare const defaultKeyFunction: (item: any) => any;
export declare const defaultRenderItem: (item: any) => TemplateResult<1>;
declare class ScrollDirective extends AsyncDirective {
container: HTMLElement | null;
scroller: VirtualScroller | null;
first: number;
last: number;
renderItem: (item: any, index?: number) => TemplateResult;
keyFunction: (item: any) => unknown;
items: Array<unknown>;
constructor(part: PartInfo);
render(config?: ScrollConfig): unknown;
update(part: ChildPart, [config]: [ScrollConfig]): unknown;
private _initialize;
}
export declare const scroll: (
config?: ScrollConfig | undefined
) => import("lit-html/directive").DirectiveResult<typeof ScrollDirective>;
export {};
//# sourceMappingURL=scroll.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../src/lib/scroll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAW,SAAS,EAAQ,MAAM,KAAK,CAAC;AAC/D,OAAO,EAAa,QAAQ,EAAY,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAE/F;;GAEG;AACH,UAAU,YAAY;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,cAAc,CAAC;IAE3D,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;IAGrC,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG,eAAe,GAAG,IAAI,CAAC;IAE7D;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAEhC;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAEH,eAAO,MAAM,kBAAkB,SAAU,GAAG,QAAS,CAAC;AACtD,eAAO,MAAM,iBAAiB,SAAU,GAAG,sBAA2C,CAAC;AAEvF,cAAM,eAAgB,SAAQ,cAAc;IACxC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAO;IACpC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAO;IACvC,KAAK,SAAI;IACT,IAAI,SAAK;IACT,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,cAAc,CAAqB;IAC9E,WAAW,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAsB;IACzD,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAK;gBAEd,IAAI,EAAE,QAAQ;IAO1B,MAAM,CAAC,MAAM,CAAC,EAAE,YAAY;IAc5B,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC;IAehD,OAAO,CAAC,WAAW;CAiBtB;AAED,eAAO,MAAM,MAAM,6GAA6B,CAAC"}

View File

@ -0,0 +1,79 @@
import { nothing, html } from "lit";
import { directive, PartType } from "lit/directive.js";
import { AsyncDirective } from "lit/async-directive.js";
import { repeat } from "lit/directives/repeat.js";
import { VirtualScroller } from "./uni-virtualizer/lib/VirtualScroller.js";
export const defaultKeyFunction = (item) => item;
export const defaultRenderItem = (item) =>
html`${JSON.stringify(item, null, 2)}`;
class ScrollDirective extends AsyncDirective {
constructor(part) {
super(part);
this.container = null;
this.scroller = null;
this.first = 0;
this.last = -1;
this.renderItem = defaultRenderItem;
this.keyFunction = defaultKeyFunction;
this.items = [];
if (part.type !== PartType.CHILD) {
throw new Error(
"The scroll directive can only be used in child expressions"
);
}
}
render(config) {
if (config) {
this.renderItem = config.renderItem || this.renderItem;
this.keyFunction = config.keyFunction || this.keyFunction;
}
const itemsToRender = [];
if (this.first >= 0 && this.last >= this.first) {
for (let i = this.first; i < this.last + 1; i++) {
itemsToRender.push(this.items[i]);
}
}
return repeat(
itemsToRender,
this.keyFunction || defaultKeyFunction,
this.renderItem
);
}
update(part, [config]) {
var _a;
if (this.scroller || this._initialize(part, config)) {
const { scroller } = this;
this.items = scroller.items = config.items || [];
scroller.totalItems =
config.totalItems ||
((_a = config.items) === null || _a === void 0 ? void 0 : _a.length) ||
0;
scroller.layout = config.layout || null;
scroller.scrollTarget = config.scrollTarget || this.container;
if (config.scrollToIndex) {
scroller.scrollToIndex = config.scrollToIndex;
}
return this.render(config);
}
return nothing;
}
_initialize(part, config) {
const container = (this.container = part.parentNode);
if (container && container.nodeType === 1) {
this.scroller = new VirtualScroller({ container });
container.addEventListener("rangeChanged", (e) => {
this.first = e.detail.first;
this.last = e.detail.last;
this.setValue(this.render());
});
return true;
}
// TODO (GN): This seems to be needed in the case where the `scroll`
// directive is used within the `LitVirtualizer` element. Figure out why
// and see if there's a cleaner solution.
Promise.resolve().then(() => this.update(part, [config]));
return false;
}
}
export const scroll = directive(ScrollDirective);
//# sourceMappingURL=scroll.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,230 @@
import {
ItemBox,
Layout,
LayoutConstructor,
LayoutSpecifier,
} from "./layouts/Layout.js";
export declare const scrollerRef: unique symbol;
export declare type RangeChangeEvent = {
first: number;
last: number;
firstVisible: number;
lastVisible: number;
};
interface ElementWithOptionalScrollerRef extends Element {
[scrollerRef]?: VirtualScroller;
}
interface ShadowRootWithOptionalScrollerRef extends ShadowRoot {
[scrollerRef]?: VirtualScroller;
}
declare type Container =
| ElementWithOptionalScrollerRef
| ShadowRootWithOptionalScrollerRef;
export declare type ContainerElement = ElementWithOptionalScrollerRef;
declare type ChildMeasurements = {
[key: number]: ItemBox;
};
export declare type ScrollToIndexValue = {
index: number;
position?: string;
} | null;
export interface VirtualScrollerConfig {
layout?: Layout | LayoutConstructor | LayoutSpecifier;
/**
* An element that receives scroll events for the virtual scroller.
*/
scrollTarget?: Element | Window;
/**
* The parent of all child nodes to be rendered.
*/
container: Element | ShadowRoot;
}
/**
* Provides virtual scrolling boilerplate.
*
* Extensions of this class must set container, layout, and scrollTarget.
*
* Extensions of this class must also override VirtualRepeater's DOM
* manipulation methods.
*/
export declare class VirtualScroller {
private _benchmarkStart;
/**
* Whether the layout should receive an updated viewport size on the next
* render.
*/
private _layout;
/**
* The element that generates scroll events and defines the container
* viewport. Set by scrollTarget.
*/
private _scrollTarget;
/**
* A sentinel element that sizes the container when it is a scrolling
* element. This ensures the scroll bar accurately reflects the total
* size of the list.
*/
private _sizer;
/**
* Layout provides these values, we set them on _render().
* TODO @straversi: Can we find an XOR type, usable for the key here?
*/
private _scrollSize;
/**
* Difference between scroll target's current and required scroll offsets.
* Provided by layout.
*/
private _scrollErr;
/**
* A list of the positions (top, left) of the children in the current range.
*/
private _childrenPos;
private _childMeasurements;
private _toBeMeasured;
private _rangeChanged;
private _itemsChanged;
private _visibilityChanged;
/**
* Containing element. Set by container.
*/
protected _container: Container | null;
/**
* The parent of all child nodes to be rendered. Set by container.
*/
private _containerElement;
/**
* Keep track of original inline style of the container, so it can be
* restored when container is changed.
*/
private _containerInlineStyle;
/**
* Size of the container.
*/
private _containerSize;
/**
* Resize observer attached to container.
*/
private _containerRO;
/**
* Resize observer attached to children.
*/
private _childrenRO;
private _mutationObserver;
private _mutationPromise;
private _mutationPromiseResolver;
private _mutationsObserved;
private _loadListener;
/**
* Index and position of item to scroll to.
*/
private _scrollToIndex;
/**
* Items to render. Set by items.
*/
private _items;
/**
* Total number of items to render. Set by totalItems.
*/
private _totalItems;
/**
* Index of the first child in the range, not necessarily the first visible child.
* TODO @straversi: Consider renaming these.
*/
protected _first: number;
/**
* Index of the last child in the range.
*/
protected _last: number;
/**
* Index of the first item intersecting the container element.
*/
private _firstVisible;
/**
* Index of the last item intersecting the container element.
*/
private _lastVisible;
protected _scheduled: WeakSet<object>;
/**
* Invoked at the end of each render cycle: children in the range are
* measured, and their dimensions passed to this callback. Use it to layout
* children as needed.
*/
protected _measureCallback: ((sizes: ChildMeasurements) => void) | null;
protected _measureChildOverride:
| ((element: Element, item: unknown) => ItemBox)
| null;
constructor(config?: VirtualScrollerConfig);
set items(items: Array<unknown> | undefined);
/**
* The total number of items, regardless of the range, that can be rendered
* as child nodes.
*/
get totalItems(): number;
set totalItems(num: number);
/**
* The parent of all child nodes to be rendered.
*/
get container(): Container | null;
set container(container: Container | null);
get layout(): Layout | LayoutConstructor | LayoutSpecifier | null;
set layout(layout: Layout | LayoutConstructor | LayoutSpecifier | null);
startBenchmarking(): void;
stopBenchmarking(): {
timeElapsed: number;
virtualizationTime: number;
} | null;
private _measureChildren;
/**
* Returns the width, height, and margins of the given child.
*/
_measureChild(element: Element): ItemBox;
/**
* The element that generates scroll events and defines the container
* viewport. The value `null` (default) corresponds to `window` as scroll
* target.
*/
get scrollTarget(): Element | Window | null;
set scrollTarget(target: Element | Window | null);
/**
* Index and position of item to scroll to. The scroller will fix to that point
* until the user scrolls.
*/
set scrollToIndex(newValue: ScrollToIndexValue);
protected _schedule(method: Function): Promise<void>;
_updateDOM(): Promise<void>;
_updateLayout(): void;
private _handleScrollEvent;
handleEvent(event: CustomEvent): void;
private _initResizeObservers;
private _createContainerSizer;
get _children(): Array<HTMLElement>;
private _updateView;
/**
* Styles the _sizer element or the container so that its size reflects the
* total size of all items.
*/
private _sizeContainer;
/**
* Sets the top and left transform style of the children from the values in
* pos.
*/
private _positionChildren;
private _adjustRange;
private _correctScrollError;
/**
* Emits a rangechange event with the current first, last, firstVisible, and
* lastVisible.
*/
private _notifyRange;
private _notifyVisibility;
/**
* Render and update the view at the next opportunity with the given
* container size.
*/
private _containerSizeChanged;
private _observeMutations;
private _childLoaded;
private _childrenSizeChanged;
}
export {};
//# sourceMappingURL=VirtualScroller.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"VirtualScroller.d.ts","sourceRoot":"","sources":["../../../src/lib/uni-virtualizer/lib/VirtualScroller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,725 @@
import getResizeObserver from "./polyfillLoaders/ResizeObserver.js";
export const scrollerRef = Symbol("scrollerRef");
/**
* Provides virtual scrolling boilerplate.
*
* Extensions of this class must set container, layout, and scrollTarget.
*
* Extensions of this class must also override VirtualRepeater's DOM
* manipulation methods.
*/
export class VirtualScroller {
constructor(config) {
this._benchmarkStart = null;
/**
* Whether the layout should receive an updated viewport size on the next
* render.
*/
// private _needsUpdateView: boolean = false;
this._layout = null;
/**
* The element that generates scroll events and defines the container
* viewport. Set by scrollTarget.
*/
this._scrollTarget = null;
/**
* A sentinel element that sizes the container when it is a scrolling
* element. This ensures the scroll bar accurately reflects the total
* size of the list.
*/
this._sizer = null;
/**
* Layout provides these values, we set them on _render().
* TODO @straversi: Can we find an XOR type, usable for the key here?
*/
this._scrollSize = null;
/**
* Difference between scroll target's current and required scroll offsets.
* Provided by layout.
*/
this._scrollErr = null;
/**
* A list of the positions (top, left) of the children in the current range.
*/
this._childrenPos = null;
// TODO: (graynorton): type
this._childMeasurements = null;
this._toBeMeasured = new Map();
this._rangeChanged = true;
this._itemsChanged = true;
this._visibilityChanged = true;
/**
* Containing element. Set by container.
*/
this._container = null;
/**
* The parent of all child nodes to be rendered. Set by container.
*/
this._containerElement = null;
/**
* Keep track of original inline style of the container, so it can be
* restored when container is changed.
*/
this._containerInlineStyle = null;
/**
* Size of the container.
*/
this._containerSize = null;
/**
* Resize observer attached to container.
*/
this._containerRO = null;
/**
* Resize observer attached to children.
*/
this._childrenRO = null;
this._mutationObserver = null;
this._mutationPromise = null;
this._mutationPromiseResolver = null;
this._mutationsObserved = false;
// TODO (graynorton): Rethink, per longer comment below
this._loadListener = this._childLoaded.bind(this);
/**
* Index and position of item to scroll to.
*/
this._scrollToIndex = null;
/**
* Items to render. Set by items.
*/
this._items = [];
/**
* Total number of items to render. Set by totalItems.
*/
this._totalItems = null;
/**
* Index of the first child in the range, not necessarily the first visible child.
* TODO @straversi: Consider renaming these.
*/
this._first = 0;
/**
* Index of the last child in the range.
*/
this._last = 0;
/**
* Index of the first item intersecting the container element.
*/
this._firstVisible = 0;
/**
* Index of the last item intersecting the container element.
*/
this._lastVisible = 0;
this._scheduled = new WeakSet();
/**
* Invoked at the end of each render cycle: children in the range are
* measured, and their dimensions passed to this callback. Use it to layout
* children as needed.
*/
this._measureCallback = null;
this._measureChildOverride = null;
this._first = -1;
this._last = -1;
if (config) {
Object.assign(this, config);
}
}
set items(items) {
if (Array.isArray(items) && items !== this._items) {
this._itemsChanged = true;
this._items = items;
this._schedule(this._updateLayout);
}
}
/**
* The total number of items, regardless of the range, that can be rendered
* as child nodes.
*/
get totalItems() {
return this._totalItems === null ? this._items.length : this._totalItems;
}
set totalItems(num) {
if (typeof num !== "number" && num !== null) {
throw new Error("New value must be a number.");
}
// TODO(valdrin) should we check if it is a finite number?
// Technically, Infinity would break Layout, not VirtualRepeater.
if (num !== this._totalItems) {
this._totalItems = num;
this._schedule(this._updateLayout);
}
}
/**
* The parent of all child nodes to be rendered.
*/
get container() {
return this._container;
}
set container(container) {
if (container === this._container) {
return;
}
if (this._container) {
// Remove children from old container.
// TODO (graynorton): Decide whether we'd rather fire an event to clear
// the range and let the renderer take care of removing the DOM children
this._children.forEach((child) => child.parentNode.removeChild(child));
}
this._container = container;
this._schedule(this._updateLayout);
this._initResizeObservers().then(() => {
const oldEl = this._containerElement;
// Consider document fragments as shadowRoots.
const newEl =
container && container.nodeType === Node.DOCUMENT_FRAGMENT_NODE
? container.host
: container;
if (oldEl === newEl) {
return;
}
this._containerRO.disconnect();
this._containerSize = null;
if (oldEl) {
if (this._containerInlineStyle) {
oldEl.setAttribute("style", this._containerInlineStyle);
} else {
oldEl.removeAttribute("style");
}
this._containerInlineStyle = null;
if (oldEl === this._scrollTarget) {
oldEl.removeEventListener("scroll", this, { passive: true });
this._sizer && this._sizer.remove();
}
oldEl.removeEventListener("load", this._loadListener, true);
this._mutationObserver.disconnect();
} else {
// First time container was setup, add listeners only now.
addEventListener("scroll", this, { passive: true });
}
this._containerElement = newEl;
if (newEl) {
this._containerInlineStyle = newEl.getAttribute("style") || null;
// https://github.com/PolymerLabs/uni-virtualizer/issues/104
// Would rather set these CSS properties on the host using Shadow Root
// style scoping (and fall back to a global stylesheet where native
// Shadow DOM is not available), but this Mobile Safari bug is preventing
// that from working: https://bugs.webkit.org/show_bug.cgi?id=226195
const style = newEl.style;
style.display = style.display || "block";
style.position = style.position || "relative";
style.overflow = style.overflow || "auto";
style.contain = style.contain || "strict";
if (newEl === this._scrollTarget) {
this._sizer = this._sizer || this._createContainerSizer();
this._container.insertBefore(this._sizer, this._container.firstChild);
}
this._schedule(this._updateLayout);
this._containerRO.observe(newEl);
this._mutationObserver.observe(newEl, { childList: true });
this._mutationPromise = new Promise(
(resolve) => (this._mutationPromiseResolver = resolve)
);
if (this._layout && this._layout.listenForChildLoadEvents) {
newEl.addEventListener("load", this._loadListener, true);
}
}
});
}
// This will always actually return a layout instance,
// but TypeScript wants the getter and setter types to be the same
get layout() {
return this._layout;
}
set layout(layout) {
if (this._layout === layout) {
return;
}
let _layout = null;
let _config = {};
if (typeof layout === "object") {
if (layout.type !== undefined) {
_layout = layout.type;
// delete (layout as LayoutSpecifier).type;
}
_config = layout;
} else {
_layout = layout;
}
if (typeof _layout === "function") {
if (this._layout instanceof _layout) {
if (_config) {
this._layout.config = _config;
}
return;
} else {
_layout = new _layout(_config);
}
}
if (this._layout) {
this._measureCallback = null;
this._measureChildOverride = null;
this._layout.removeEventListener("scrollsizechange", this);
this._layout.removeEventListener("scrollerrorchange", this);
this._layout.removeEventListener("itempositionchange", this);
this._layout.removeEventListener("rangechange", this);
delete this.container[scrollerRef];
this.container.removeEventListener("load", this._loadListener, true);
// Reset container size so layout can get correct viewport size.
if (this._containerElement) {
this._sizeContainer(undefined);
}
}
this._layout = _layout;
if (this._layout) {
if (
this._layout.measureChildren &&
typeof this._layout.updateItemSizes === "function"
) {
if (typeof this._layout.measureChildren === "function") {
this._measureChildOverride = this._layout.measureChildren;
}
this._measureCallback = this._layout.updateItemSizes.bind(this._layout);
}
this._layout.addEventListener("scrollsizechange", this);
this._layout.addEventListener("scrollerrorchange", this);
this._layout.addEventListener("itempositionchange", this);
this._layout.addEventListener("rangechange", this);
this._container[scrollerRef] = this;
if (this._layout.listenForChildLoadEvents) {
this._container.addEventListener("load", this._loadListener, true);
}
this._schedule(this._updateLayout);
}
}
// TODO (graynorton): Rework benchmarking so that it has no API and
// instead is always on except in production builds
startBenchmarking() {
if (this._benchmarkStart === null) {
this._benchmarkStart = window.performance.now();
}
}
stopBenchmarking() {
if (this._benchmarkStart !== null) {
const now = window.performance.now();
const timeElapsed = now - this._benchmarkStart;
const entries = performance.getEntriesByName(
"uv-virtualizing",
"measure"
);
const virtualizationTime = entries
.filter((e) => e.startTime >= this._benchmarkStart && e.startTime < now)
.reduce((t, m) => t + m.duration, 0);
this._benchmarkStart = null;
return { timeElapsed, virtualizationTime };
}
return null;
}
_measureChildren() {
const mm = {};
const children = this._children;
const fn = this._measureChildOverride || this._measureChild;
for (let i = 0; i < children.length; i++) {
const child = children[i];
const idx = this._first + i;
if (this._itemsChanged || this._toBeMeasured.has(child)) {
mm[idx] = fn.call(
this,
child,
this._items[idx] /*as unknown as object*/
);
}
}
this._childMeasurements = mm;
this._schedule(this._updateLayout);
this._toBeMeasured.clear();
}
/**
* Returns the width, height, and margins of the given child.
*/
_measureChild(element) {
// offsetWidth doesn't take transforms in consideration, so we use
// getBoundingClientRect which does.
const { width, height } = element.getBoundingClientRect();
return Object.assign({ width, height }, getMargins(element));
}
/**
* The element that generates scroll events and defines the container
* viewport. The value `null` (default) corresponds to `window` as scroll
* target.
*/
get scrollTarget() {
return this._scrollTarget;
}
set scrollTarget(target) {
// Consider window as null.
if (target === window) {
target = null;
}
if (this._scrollTarget === target) {
return;
}
this._sizeContainer(undefined);
if (this._scrollTarget) {
this._scrollTarget.removeEventListener("scroll", this, { passive: true });
if (this._sizer && this._scrollTarget === this._containerElement) {
this._sizer.remove();
}
}
this._scrollTarget = target;
if (target) {
target.addEventListener("scroll", this, { passive: true });
if (target === this._containerElement) {
this._sizer = this._sizer || this._createContainerSizer();
this._container.insertBefore(this._sizer, this._container.firstChild);
}
}
}
/**
* Index and position of item to scroll to. The scroller will fix to that point
* until the user scrolls.
*/
set scrollToIndex(newValue) {
this._scrollToIndex = newValue;
this._schedule(this._updateLayout);
}
async _schedule(method) {
if (!this._scheduled.has(method)) {
this._scheduled.add(method);
await Promise.resolve();
this._scheduled.delete(method);
method.call(this);
}
}
async _updateDOM() {
const { _rangeChanged, _itemsChanged } = this;
if (this._visibilityChanged) {
this._notifyVisibility();
this._visibilityChanged = false;
}
if (_rangeChanged || _itemsChanged) {
this._notifyRange();
this._rangeChanged = false;
this._itemsChanged = false;
await this._mutationPromise;
}
if (this._layout.measureChildren) {
this._children.forEach((child) => this._childrenRO.observe(child));
}
this._positionChildren(this._childrenPos);
this._sizeContainer(this._scrollSize);
if (this._scrollErr) {
this._correctScrollError(this._scrollErr);
this._scrollErr = null;
}
if (this._benchmarkStart && "mark" in window.performance) {
window.performance.mark("uv-end");
}
}
_updateLayout() {
this._layout.totalItems = this._totalItems;
if (this._scrollToIndex !== null) {
this._layout.scrollToIndex(
this._scrollToIndex.index,
this._scrollToIndex.position
);
this._scrollToIndex = null;
}
this._updateView();
if (this._childMeasurements !== null) {
// If the layout has been changed, we may have measurements but no callback
if (this._measureCallback) {
this._measureCallback(this._childMeasurements);
}
this._childMeasurements = null;
}
this._layout.reflowIfNeeded(this._itemsChanged);
if (this._benchmarkStart && "mark" in window.performance) {
window.performance.mark("uv-end");
}
}
_handleScrollEvent() {
if (this._benchmarkStart && "mark" in window.performance) {
try {
window.performance.measure("uv-virtualizing", "uv-start", "uv-end");
} catch (e) {
console.warn("Error measuring performance data: ", e);
}
window.performance.mark("uv-start");
}
this._schedule(this._updateLayout);
}
handleEvent(event) {
switch (event.type) {
case "scroll":
if (!this._scrollTarget || event.target === this._scrollTarget) {
this._handleScrollEvent();
}
break;
case "scrollsizechange":
this._scrollSize = event.detail;
this._schedule(this._updateDOM);
break;
case "scrollerrorchange":
this._scrollErr = event.detail;
this._schedule(this._updateDOM);
break;
case "itempositionchange":
this._childrenPos = event.detail;
this._schedule(this._updateDOM);
break;
case "rangechange":
this._adjustRange(event.detail);
this._schedule(this._updateDOM);
break;
default:
console.warn("event not handled", event);
}
}
async _initResizeObservers() {
if (this._containerRO === null) {
const ResizeObserver = await getResizeObserver();
this._containerRO = new ResizeObserver((entries) =>
this._containerSizeChanged(entries[0].contentRect)
);
this._childrenRO = new ResizeObserver(
this._childrenSizeChanged.bind(this)
);
this._mutationObserver = new MutationObserver(
this._observeMutations.bind(this)
);
}
}
_createContainerSizer() {
const sizer = document.createElement("div");
// When the scrollHeight is large, the height of this element might be
// ignored. Setting content and font-size ensures the element has a size.
Object.assign(sizer.style, {
position: "absolute",
margin: "-2px 0 0 0",
padding: 0,
visibility: "hidden",
fontSize: "2px",
});
sizer.innerHTML = "&nbsp;";
sizer.id = "uni-virtualizer-spacer";
return sizer;
}
get _children() {
const arr = [];
let next = this.container.firstElementChild;
while (next) {
// Skip our spacer. TODO (graynorton): Feels a bit hacky. Anything better?
if (next.id !== "uni-virtualizer-spacer") {
arr.push(next);
}
next = next.nextElementSibling;
}
return arr;
}
_updateView() {
if (!this.container || !this._containerElement || !this._layout) {
return;
}
let width, height, top, left;
if (
this._scrollTarget === this._containerElement &&
this._containerSize !== null
) {
width = this._containerSize.width;
height = this._containerSize.height;
left = this._containerElement.scrollLeft;
top = this._containerElement.scrollTop;
} else {
const containerBounds = this._containerElement.getBoundingClientRect();
const scrollBounds = this._scrollTarget
? this._scrollTarget.getBoundingClientRect()
: {
top: containerBounds.top + window.pageYOffset,
left: containerBounds.left + window.pageXOffset,
width: innerWidth,
height: innerHeight,
};
const scrollerWidth = scrollBounds.width;
const scrollerHeight = scrollBounds.height;
const xMin = Math.max(
0,
Math.min(scrollerWidth, containerBounds.left - scrollBounds.left)
);
const yMin = Math.max(
0,
Math.min(scrollerHeight, containerBounds.top - scrollBounds.top)
);
// TODO (graynorton): Direction is intended to be a layout-level concept, not a scroller-level concept,
// so this feels like a factoring problem
const xMax =
this._layout.direction === "vertical"
? Math.max(
0,
Math.min(scrollerWidth, containerBounds.right - scrollBounds.left)
)
: scrollerWidth;
const yMax =
this._layout.direction === "vertical"
? scrollerHeight
: Math.max(
0,
Math.min(
scrollerHeight,
containerBounds.bottom - scrollBounds.top
)
);
width = xMax - xMin;
height = yMax - yMin;
left = Math.max(0, -(containerBounds.left - scrollBounds.left));
top = Math.max(0, -(containerBounds.top - scrollBounds.top));
}
this._layout.viewportSize = { width, height };
this._layout.viewportScroll = { top, left };
}
/**
* Styles the _sizer element or the container so that its size reflects the
* total size of all items.
*/
_sizeContainer(size) {
if (this._scrollTarget === this._containerElement) {
const left = size && size.width ? size.width - 1 : 0;
const top = size && size.height ? size.height - 1 : 0;
if (this._sizer) {
this._sizer.style.transform = `translate(${left}px, ${top}px)`;
}
} else {
if (this._containerElement) {
const style = this._containerElement.style;
style.minWidth = size && size.width ? size.width + "px" : null;
style.minHeight = size && size.height ? size.height + "px" : null;
}
}
}
/**
* Sets the top and left transform style of the children from the values in
* pos.
*/
_positionChildren(pos) {
if (pos) {
const children = this._children;
Object.keys(pos).forEach((key) => {
const idx = key - this._first;
const child = children[idx];
if (child) {
const { top, left, width, height } = pos[key];
child.style.position = "absolute";
child.style.boxSizing = "border-box";
child.style.transform = `translate(${left}px, ${top}px)`;
if (width !== undefined) {
child.style.width = width + "px";
}
if (height !== undefined) {
child.style.height = height + "px";
}
}
});
}
}
async _adjustRange(range) {
const { _first, _last, _firstVisible, _lastVisible } = this;
this._first = range.first;
this._last = range.last;
this._firstVisible = range.firstVisible;
this._lastVisible = range.lastVisible;
this._rangeChanged =
this._rangeChanged || this._first !== _first || this._last !== _last;
this._visibilityChanged =
this._visibilityChanged ||
this._firstVisible !== _firstVisible ||
this._lastVisible !== _lastVisible;
}
_correctScrollError(err) {
if (this._scrollTarget) {
this._scrollTarget.scrollTop -= err.top;
this._scrollTarget.scrollLeft -= err.left;
} else {
window.scroll(
window.pageXOffset - err.left,
window.pageYOffset - err.top
);
}
}
/**
* Emits a rangechange event with the current first, last, firstVisible, and
* lastVisible.
*/
_notifyRange() {
// TODO (graynorton): Including visibility here for backward compat, but
// may decide to remove at some point. The rationale for separating is that
// range change events are mainly intended for "internal" consumption by the
// renderer, whereas visibility change events are mainly intended for "external"
// consumption by application code.
this._container.dispatchEvent(
new CustomEvent("rangeChanged", {
detail: {
first: this._first,
last: this._last,
firstVisible: this._firstVisible,
lastVisible: this._lastVisible,
},
})
);
}
_notifyVisibility() {
this._container.dispatchEvent(
new CustomEvent("visibilityChanged", {
detail: {
first: this._first,
last: this._last,
firstVisible: this._firstVisible,
lastVisible: this._lastVisible,
},
})
);
}
/**
* Render and update the view at the next opportunity with the given
* container size.
*/
_containerSizeChanged(size) {
const { width, height } = size;
this._containerSize = { width, height };
this._schedule(this._updateLayout);
}
async _observeMutations() {
if (!this._mutationsObserved) {
this._mutationsObserved = true;
this._mutationPromiseResolver();
this._mutationPromise = new Promise(
(resolve) => (this._mutationPromiseResolver = resolve)
);
this._mutationsObserved = false;
}
}
// TODO (graynorton): Rethink how this works. Probably child loading is too specific
// to have dedicated support for; might want some more generic lifecycle hooks for
// layouts to use. Possibly handle measurement this way, too, or maybe that remains
// a first-class feature?
_childLoaded() {
// this.requestRemeasure();
}
_childrenSizeChanged(changes) {
for (const change of changes) {
this._toBeMeasured.set(change.target, change.contentRect);
}
this._measureChildren();
this._schedule(this._updateLayout);
}
}
function getMargins(el) {
const style = window.getComputedStyle(el);
return {
marginTop: getMarginValue(style.marginTop),
marginRight: getMarginValue(style.marginRight),
marginBottom: getMarginValue(style.marginBottom),
marginLeft: getMarginValue(style.marginLeft),
};
}
function getMarginValue(value) {
const float = value ? parseFloat(value) : NaN;
return Number.isNaN(float) ? 0 : float;
}
//# sourceMappingURL=VirtualScroller.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,90 @@
export declare type dimension = "height" | "width";
export declare type Size = {
[key in dimension]: number;
};
export declare type Margins = {
marginTop: number;
marginRight: number;
marginBottom: number;
marginLeft: number;
};
export declare type ItemBox = Size | (Size & Margins);
export declare type position = "left" | "top";
export declare type Positions = {
left: number;
top: number;
width?: number;
height?: number;
};
export declare type LayoutConstructor = new (config?: object) => Layout;
export interface LayoutSpecifier {
type: LayoutConstructor;
}
export declare type LayoutSpecifierFactory = (
config?: object
) => LayoutSpecifier;
export declare type ScrollDirection = "vertical" | "horizontal";
/**
* Interface for layouts consumed by VirtualScroller or VirtualRepeater.
*/
export interface Layout {
config?: object;
totalItems: number;
direction: ScrollDirection;
viewportSize: Size;
viewportScroll: Positions;
readonly measureChildren?: boolean | ((e: Element, i: unknown) => ItemBox);
readonly listenForChildLoadEvents?: boolean;
updateItemSizes?: (sizes: { [key: number]: ItemBox }) => void;
addEventListener: Function;
removeEventListener: Function;
scrollToIndex: (index: number, position: string) => void;
/**
* Called by a VirtualRepeater or VirtualScroller when an update that
* potentially affects layout has occurred. For example, a viewport size
* change.
*
* The layout is in turn responsible for dispatching events, as necessary,
* to the VirtualRepeater or VirtualScroller. Each of the following events
* represents an update that should be determined during a reflow. Dispatch
* each event at maximum once during a single reflow.
*
* Events that should be dispatched:
* - scrollsizechange
* Dispatch when the total length of all items in the scrolling direction,
* including spacing, changes.
* detail: {
* 'height' | 'width': number
* }
* - rangechange
* Dispatch when the range of children that should be displayed changes
* (based on layout calculations and the size of the container) or when
* the first or last item to intersect the container changes.
* detail: {
* first: number,
* last: number,
* num: number,
* stable: boolean,
* remeasure: boolean,
* firstVisible: number,
* lastVisible: number,
* }
* - itempositionchange
* Dispatch when the child positions change, for example due to a range
* change.
* detail {
* [number]: {
* left: number,
* top: number
* }
* }
* - scrollerrorchange
* Dispatch when the set viewportScroll offset is not what it should be.
* detail {
* height: number,
* width: number,
* }
*/
reflowIfNeeded: (force: boolean) => void;
}
//# sourceMappingURL=Layout.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC3C,oBAAY,IAAI,GAAG;KAChB,GAAG,IAAI,SAAS,GAAG,MAAM;CAC3B,CAAC;AAEF,oBAAY,OAAO,GAAG;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAC;AAEF,oBAAY,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;AAE9C,oBAAY,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AACtC,oBAAY,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAC;AAGF,oBAAY,iBAAiB,GAAG,KAAI,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,iBAAiB,CAAA;CACxB;AAED,oBAAY,sBAAsB,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,eAAe,CAAC;AAG1E,oBAAY,eAAe,GAAG,UAAU,GAAG,YAAY,CAAC;AAExD;;GAEG;AACF,MAAM,WAAW,MAAM;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,eAAe,CAAC;IAE3B,YAAY,EAAE,IAAI,CAAC;IAEnB,cAAc,EAAE,SAAS,CAAC;IAE1B,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;IAE3E,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAE5C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,KAAK,IAAI,CAAC;IAEX,gBAAgB,EAAE,QAAQ,CAAC;IAE3B,mBAAmB,EAAE,QAAQ,CAAC;IAE9B,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C"}

View File

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=Layout.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout.js","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout.ts"],"names":[],"mappings":"","sourcesContent":["export type dimension = 'height' | 'width';\nexport type Size = {\n [key in dimension]: number\n};\n\nexport type Margins = {\n marginTop: number,\n marginRight: number,\n marginBottom: number,\n marginLeft: number\n};\n\nexport type ItemBox = Size | (Size & Margins);\n\nexport type position = 'left' | 'top';\nexport type Positions = {\n left: number,\n top: number,\n width?: number,\n height?: number\n};\n\n\nexport type LayoutConstructor = new(config?: object) => Layout;\n\nexport interface LayoutSpecifier {\n type: LayoutConstructor\n}\n\nexport type LayoutSpecifierFactory = (config?: object) => LayoutSpecifier;\n\n\nexport type ScrollDirection = 'vertical' | 'horizontal';\n\n/**\n * Interface for layouts consumed by VirtualScroller or VirtualRepeater.\n */\n export interface Layout {\n config?: object;\n \n totalItems: number;\n\n direction: ScrollDirection;\n\n viewportSize: Size;\n\n viewportScroll: Positions;\n\n readonly measureChildren?: boolean | ((e: Element, i: unknown) => ItemBox);\n\n readonly listenForChildLoadEvents?: boolean;\n\n updateItemSizes?: (sizes: {\n [key: number]: ItemBox\n }) => void;\n\n addEventListener: Function;\n\n removeEventListener: Function;\n\n scrollToIndex: (index: number, position: string) => void;\n\n /**\n * Called by a VirtualRepeater or VirtualScroller when an update that\n * potentially affects layout has occurred. For example, a viewport size\n * change.\n *\n * The layout is in turn responsible for dispatching events, as necessary,\n * to the VirtualRepeater or VirtualScroller. Each of the following events\n * represents an update that should be determined during a reflow. Dispatch\n * each event at maximum once during a single reflow.\n *\n * Events that should be dispatched:\n * - scrollsizechange\n * Dispatch when the total length of all items in the scrolling direction,\n * including spacing, changes.\n * detail: {\n * 'height' | 'width': number\n * }\n * - rangechange\n * Dispatch when the range of children that should be displayed changes\n * (based on layout calculations and the size of the container) or when\n * the first or last item to intersect the container changes.\n * detail: {\n * first: number,\n * last: number,\n * num: number,\n * stable: boolean,\n * remeasure: boolean,\n * firstVisible: number,\n * lastVisible: number,\n * }\n * - itempositionchange\n * Dispatch when the child positions change, for example due to a range\n * change.\n * detail {\n * [number]: {\n * left: number,\n * top: number\n * }\n * }\n * - scrollerrorchange\n * Dispatch when the set viewportScroll offset is not what it should be.\n * detail {\n * height: number,\n * width: number,\n * }\n */\n reflowIfNeeded: (force: boolean) => void;\n}"]}

View File

@ -0,0 +1,108 @@
import { Layout1dBase, Layout1dBaseConfig } from "./Layout1dBase.js";
import { ItemBox, Positions, Size } from "./Layout.js";
declare type ItemBounds = {
pos: number;
size: number;
};
declare type Layout1dConstructor = {
prototype: Layout1d;
new (config?: Layout1dBaseConfig): Layout1d;
};
declare type Layout1dSpecifier = Layout1dBaseConfig & {
type: Layout1dConstructor;
};
declare type Layout1dSpecifierFactory = (
config?: Layout1dBaseConfig
) => Layout1dSpecifier;
export declare const layout1d: Layout1dSpecifierFactory;
export declare class Layout1d extends Layout1dBase<Layout1dBaseConfig> {
/**
* Indices of children mapped to their (position and length) in the scrolling
* direction. Used to keep track of children that are in range.
*/
_physicalItems: Map<number, ItemBounds>;
/**
* Used in tandem with _physicalItems to track children in range across
* reflows.
*/
_newPhysicalItems: Map<number, ItemBounds>;
/**
* Width and height of children by their index.
*/
_metrics: Map<number, Size>;
/**
* anchorIdx is the anchor around which we reflow. It is designed to allow
* jumping to any point of the scroll size. We choose it once and stick with
* it until stable. _first and _last are deduced around it.
*/
_anchorIdx: number | null;
/**
* Position in the scrolling direction of the anchor child.
*/
_anchorPos: number | null;
/**
* Whether all children in range were in range during the previous reflow.
*/
_stable: boolean;
/**
* Whether to remeasure children during the next reflow.
*/
_needsRemeasure: boolean;
/**
* Number of children to lay out.
*/
private _nMeasured;
/**
* Total length in the scrolling direction of the laid out children.
*/
private _tMeasured;
private _measureChildren;
_estimate: boolean;
get measureChildren(): boolean;
/**
* Determine the average size of all children represented in the sizes
* argument.
*/
updateItemSizes(sizes: { [key: number]: ItemBox }): void;
/**
* Set the average item size based on the total length and number of children
* in range.
*/
_updateItemSize(): void;
_getMetrics(idx: number): ItemBox;
_getPhysicalItem(idx: number): ItemBounds | undefined;
_getSize(idx: number): number | undefined;
/**
* Returns the position in the scrolling direction of the item at idx.
* Estimates it if the item at idx is not in the DOM.
*/
_getPosition(idx: number): number;
_calculateAnchor(lower: number, upper: number): number;
_getAnchor(lower: number, upper: number): number;
/**
* Updates _first and _last based on items that should be in the current
* viewed range.
*/
_getActiveItems(): void;
/**
* Sets the range to empty.
*/
_clearItems(): void;
_getItems(): void;
_calculateError(): number;
_updateScrollSize(): void;
_reflow(): void;
_resetReflowState(): void;
/**
* Returns the top and left positioning of the item at idx.
*/
_getItemPosition(idx: number): Positions;
/**
* Returns the height and width of the item at idx.
*/
_getItemSize(idx: number): Size;
_viewDim2Changed(): void;
_emitRange(): void;
}
export {};
//# sourceMappingURL=Layout1d.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1d.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAU,MAAM,aAAa,CAAC;AAE9D,aAAK,UAAU,GAAG;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAC;AAEF,aAAK,mBAAmB,GAAG;IACzB,SAAS,EAAE,QAAQ,CAAC;IACpB,KAAI,MAAM,CAAC,EAAE,kBAAkB,GAAG,QAAQ,CAAA;CAC3C,CAAA;AAED,aAAK,iBAAiB,GAAG,kBAAkB,GAAG;IAC5C,IAAI,EAAE,mBAAmB,CAAA;CAC1B,CAAA;AAED,aAAK,wBAAwB,GAAG,CAAC,MAAM,CAAC,EAAE,kBAAkB,KAAK,iBAAiB,CAAC;AAEnF,eAAO,MAAM,QAAQ,EAAE,wBAEb,CAAC;AAGX,qBAAa,QAAS,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IAC5D;;;OAGG;IACH,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAa;IAEpD;;;OAGG;IACH,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAa;IAEvD;;OAEG;IACH,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAa;IAExC;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEjC;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEjC;;OAEG;IACH,OAAO,UAAQ;IAEf;;OAEG;IACH,eAAe,UAAS;IAExB;;OAEG;IACH,OAAO,CAAC,UAAU,CAAK;IAEvB;;OAEG;IACH,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,gBAAgB,CAAQ;IAEhC,SAAS,UAAQ;IAUjB,IAAI,eAAe,YAElB;IAED;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAC;IAmC/C;;;OAGG;IACH,eAAe;IAMf,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IASjC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIrD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKzC;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKjC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IActD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAqDhD;;;OAGG;IACH,eAAe;IAQf;;OAEG;IACH,WAAW;IAeX,SAAS;IA+GT,eAAe,IAAI,MAAM;IAezB,iBAAiB;IAQjB,OAAO;IA0BP,iBAAiB;IAMjB;;OAEG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAOxC;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAO/B,gBAAgB;IAKhB,UAAU;CAMX"}

View File

@ -0,0 +1,406 @@
import { Layout1dBase } from "./Layout1dBase.js";
export const layout1d = (config) =>
Object.assign(
{
type: Layout1d,
},
config
);
export class Layout1d extends Layout1dBase {
constructor() {
super(...arguments);
/**
* Indices of children mapped to their (position and length) in the scrolling
* direction. Used to keep track of children that are in range.
*/
this._physicalItems = new Map();
/**
* Used in tandem with _physicalItems to track children in range across
* reflows.
*/
this._newPhysicalItems = new Map();
/**
* Width and height of children by their index.
*/
this._metrics = new Map();
/**
* anchorIdx is the anchor around which we reflow. It is designed to allow
* jumping to any point of the scroll size. We choose it once and stick with
* it until stable. _first and _last are deduced around it.
*/
this._anchorIdx = null;
/**
* Position in the scrolling direction of the anchor child.
*/
this._anchorPos = null;
/**
* Whether all children in range were in range during the previous reflow.
*/
this._stable = true;
/**
* Whether to remeasure children during the next reflow.
*/
this._needsRemeasure = false;
/**
* Number of children to lay out.
*/
this._nMeasured = 0;
/**
* Total length in the scrolling direction of the laid out children.
*/
this._tMeasured = 0;
this._measureChildren = true;
this._estimate = true;
}
// protected _defaultConfig: Layout1dBaseConfig = Object.assign({}, super._defaultConfig, {
// })
// constructor(config: Layout1dConfig) {
// super(config);
// }
get measureChildren() {
return this._measureChildren;
}
/**
* Determine the average size of all children represented in the sizes
* argument.
*/
updateItemSizes(sizes) {
Object.keys(sizes).forEach((key) => {
const metrics = sizes[Number(key)],
mi = this._getMetrics(Number(key)),
prevSize = mi[this._sizeDim];
// TODO(valdrin) Handle margin collapsing.
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing
mi.width =
metrics.width + (metrics.marginLeft || 0) + (metrics.marginRight || 0);
mi.height =
metrics.height + (metrics.marginTop || 0) + (metrics.marginBottom || 0);
const size = mi[this._sizeDim];
const item = this._getPhysicalItem(Number(key));
if (item) {
let delta = 0;
if (size !== undefined) {
item.size = size;
if (prevSize === -1) {
delta = size;
this._nMeasured++;
} else {
delta = size - prevSize;
}
}
this._tMeasured = this._tMeasured + delta;
}
});
if (this._nMeasured) {
this._updateItemSize();
this._scheduleReflow();
}
}
/**
* Set the average item size based on the total length and number of children
* in range.
*/
_updateItemSize() {
// Keep integer values.
this._itemSize[this._sizeDim] = Math.round(
this._tMeasured / this._nMeasured
);
}
_getMetrics(idx) {
let metrics = this._metrics.get(idx);
if (metrics === undefined) {
metrics = { height: -1, width: -1 };
this._metrics.set(idx, metrics);
}
return metrics;
}
_getPhysicalItem(idx) {
return this._newPhysicalItems.get(idx) || this._physicalItems.get(idx);
}
_getSize(idx) {
const item = this._getPhysicalItem(idx);
return item && item.size;
}
/**
* Returns the position in the scrolling direction of the item at idx.
* Estimates it if the item at idx is not in the DOM.
*/
_getPosition(idx) {
const item = this._getPhysicalItem(idx);
return item ? item.pos : idx * this._delta + this._spacing;
}
_calculateAnchor(lower, upper) {
if (lower === 0) {
return 0;
}
if (upper > this._scrollSize - this._viewDim1) {
return this._totalItems - 1;
}
return Math.max(
0,
Math.min(
this._totalItems - 1,
Math.floor((lower + upper) / 2 / this._delta)
)
);
}
_getAnchor(lower, upper) {
if (this._physicalItems.size === 0) {
return this._calculateAnchor(lower, upper);
}
if (this._first < 0) {
console.error("_getAnchor: negative _first");
return this._calculateAnchor(lower, upper);
}
if (this._last < 0) {
console.error("_getAnchor: negative _last");
return this._calculateAnchor(lower, upper);
}
const firstItem = this._getPhysicalItem(this._first),
lastItem = this._getPhysicalItem(this._last),
firstMin = firstItem.pos,
firstMax = firstMin + firstItem.size,
lastMin = lastItem.pos,
lastMax = lastMin + lastItem.size;
if (lastMax < lower) {
// Window is entirely past physical items, calculate new anchor
return this._calculateAnchor(lower, upper);
}
if (firstMin > upper) {
// Window is entirely before physical items, calculate new anchor
return this._calculateAnchor(lower, upper);
}
if (firstMin >= lower || firstMax >= lower) {
// First physical item overlaps window, choose it
return this._first;
}
if (lastMax <= upper || lastMin <= upper) {
// Last physical overlaps window, choose it
return this._last;
}
// Window contains a physical item, but not the first or last
let maxIdx = this._last,
minIdx = this._first;
while (true) {
const candidateIdx = Math.round((maxIdx + minIdx) / 2),
candidate = this._physicalItems.get(candidateIdx),
cMin = candidate.pos,
cMax = cMin + candidate.size;
if (
(cMin >= lower && cMin <= upper) ||
(cMax >= lower && cMax <= upper)
) {
return candidateIdx;
} else if (cMax < lower) {
minIdx = candidateIdx + 1;
} else if (cMin > upper) {
maxIdx = candidateIdx - 1;
}
}
}
/**
* Updates _first and _last based on items that should be in the current
* viewed range.
*/
_getActiveItems() {
if (this._viewDim1 === 0 || this._totalItems === 0) {
this._clearItems();
} else {
this._getItems();
}
}
/**
* Sets the range to empty.
*/
_clearItems() {
this._first = -1;
this._last = -1;
this._physicalMin = 0;
this._physicalMax = 0;
const items = this._newPhysicalItems;
this._newPhysicalItems = this._physicalItems;
this._newPhysicalItems.clear();
this._physicalItems = items;
this._stable = true;
}
/*
* Updates _first and _last based on items that should be in the given range.
*/
_getItems() {
const items = this._newPhysicalItems;
let lower, upper;
// The anchorIdx is the anchor around which we reflow. It is designed to
// allow jumping to any point of the scroll size. We choose it once and
// stick with it until stable. first and last are deduced around it.
if (this._scrollToIndex >= 0) {
// If we have a scrollToIndex, we anchor on the given
// index and set the scroll position accordingly
this._anchorIdx = this._scrollToIndex;
this._anchorPos = this._getPosition(this._anchorIdx);
this._scrollIfNeeded();
lower = Math.max(0, this._scrollPosition - this._overhang);
upper = Math.min(
this._scrollSize,
this._scrollPosition + this._viewDim1 + this._overhang
);
} else {
// Otherwise, we find an appropriate index to anchor on
// given the current scroll position
upper = Math.min(
this._scrollSize,
this._scrollPosition + this._viewDim1 + this._overhang
);
lower = Math.max(0, upper - this._viewDim1 - 2 * this._overhang);
if (this._anchorIdx === null || this._anchorPos === null) {
this._anchorIdx = this._getAnchor(lower, upper);
this._anchorPos = this._getPosition(this._anchorIdx);
}
}
let anchorSize = this._getSize(this._anchorIdx);
if (anchorSize === undefined) {
anchorSize = this._itemDim1;
}
// Anchor might be outside bounds, so prefer correcting the error and keep
// that anchorIdx.
let anchorErr = 0;
if (this._anchorPos + anchorSize + this._spacing < lower) {
anchorErr = lower - (this._anchorPos + anchorSize + this._spacing);
}
if (this._anchorPos > upper) {
anchorErr = upper - this._anchorPos;
}
if (anchorErr) {
this._scrollPosition -= anchorErr;
lower -= anchorErr;
upper -= anchorErr;
this._scrollError += anchorErr;
}
// TODO @straversi: If size is always itemDim1, then why keep track of it?
items.set(this._anchorIdx, { pos: this._anchorPos, size: anchorSize });
this._first = this._last = this._anchorIdx;
this._physicalMin = this._physicalMax = this._anchorPos;
this._stable = true;
while (this._physicalMin > lower && this._first > 0) {
let size = this._getSize(--this._first);
if (size === undefined) {
this._stable = false;
size = this._itemDim1;
}
const pos = (this._physicalMin -= size + this._spacing);
items.set(this._first, { pos, size });
if (this._stable === false && this._estimate === false) {
break;
}
}
while (this._physicalMax < upper && this._last < this._totalItems) {
let size = this._getSize(this._last);
if (size === undefined) {
this._stable = false;
size = this._itemDim1;
}
items.set(this._last++, { pos: this._physicalMax, size });
if (this._stable === false && this._estimate === false) {
break;
} else {
this._physicalMax += size + this._spacing;
}
}
this._last--;
// This handles the cases where we were relying on estimated sizes.
const extentErr = this._calculateError();
if (extentErr) {
this._physicalMin -= extentErr;
this._physicalMax -= extentErr;
this._anchorPos -= extentErr;
this._scrollPosition -= extentErr;
items.forEach((item) => (item.pos -= extentErr));
this._scrollError += extentErr;
}
if (this._stable) {
this._newPhysicalItems = this._physicalItems;
this._newPhysicalItems.clear();
this._physicalItems = items;
}
}
_calculateError() {
if (this._first === 0) {
return this._physicalMin;
} else if (this._physicalMin <= 0) {
return this._physicalMin - this._first * this._delta;
} else if (this._last === this._totalItems - 1) {
return this._physicalMax - this._scrollSize;
} else if (this._physicalMax >= this._scrollSize) {
return (
this._physicalMax -
this._scrollSize +
(this._totalItems - 1 - this._last) * this._delta
);
}
return 0;
}
_updateScrollSize() {
// Reuse previously calculated physical max, as it might be higher than the
// estimated size.
super._updateScrollSize();
this._scrollSize = Math.max(this._physicalMax, this._scrollSize);
}
// TODO: Can this be made to inherit from base, with proper hooks?
_reflow() {
const { _first, _last, _scrollSize } = this;
this._updateScrollSize();
this._getActiveItems();
if (this._scrollSize !== _scrollSize) {
this._emitScrollSize();
}
this._updateVisibleIndices();
this._emitRange();
if (this._first === -1 && this._last === -1) {
this._resetReflowState();
} else if (
this._first !== _first ||
this._last !== _last ||
this._needsRemeasure
) {
this._emitChildPositions();
this._emitScrollError();
} else {
this._emitChildPositions();
this._emitScrollError();
this._resetReflowState();
}
}
_resetReflowState() {
this._anchorIdx = null;
this._anchorPos = null;
this._stable = true;
}
/**
* Returns the top and left positioning of the item at idx.
*/
_getItemPosition(idx) {
return {
[this._positionDim]: this._getPosition(idx),
[this._secondaryPositionDim]: 0,
};
}
/**
* Returns the height and width of the item at idx.
*/
_getItemSize(idx) {
return {
[this._sizeDim]: this._getSize(idx) || this._itemDim1,
[this._secondarySizeDim]: this._itemDim2,
};
}
_viewDim2Changed() {
this._needsRemeasure = true;
this._scheduleReflow();
}
_emitRange() {
const remeasure = this._needsRemeasure;
const stable = this._stable;
this._needsRemeasure = false;
super._emitRange({ remeasure, stable });
}
}
//# sourceMappingURL=Layout1d.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,236 @@
import {
Layout,
Positions,
ScrollDirection,
Size,
dimension,
position,
} from "./Layout.js";
declare type UpdateVisibleIndicesOptions = {
emit?: boolean;
};
export interface Layout1dBaseConfig {
direction?: ScrollDirection;
totalItems?: number;
}
export declare abstract class Layout1dBase<C extends Layout1dBaseConfig>
implements Layout {
/**
* The last set viewport scroll position.
*/
private _latestCoords;
/**
* Scrolling direction.
*/
private _direction;
/**
* Dimensions of the viewport.
*/
private _viewportSize;
/**
* Flag for debouncing asynchnronous reflow requests.
*/
private _pendingReflow;
private _pendingLayoutUpdate;
/**
* Index of the item that has been scrolled to via the public API. When the
* container is otherwise scrolled, this value is set back to -1.
*/
protected _scrollToIndex: number;
/**
* When a child is scrolled to, the offset from the top of the child and the
* top of the viewport. Value is a proportion of the item size.
*/
private _scrollToAnchor;
/**
* The index of the first item intersecting the viewport.
*/
private _firstVisible;
/**
* The index of the last item intersecting the viewport.
*/
private _lastVisible;
private _eventTargetPromise;
/**
* Pixel offset in the scroll direction of the first child.
*/
protected _physicalMin: number;
/**
* Pixel offset in the scroll direction of the last child.
*/
protected _physicalMax: number;
/**
* Index of the first child.
*/
protected _first: number;
/**
* Index of the last child.
*/
protected _last: number;
/**
* The _estimated_ size of a child.
*/
protected _itemSize: Size;
/**
* Space in pixels between children.
*/
protected _spacing: number;
/**
* Length in the scrolling direction.
*/
protected _sizeDim: dimension;
/**
* Length in the non-scrolling direction.
*/
protected _secondarySizeDim: dimension;
/**
* Position in the scrolling direction.
*/
protected _positionDim: position;
/**
* Position in the non-scrolling direction.
*/
protected _secondaryPositionDim: position;
/**
* Current scroll offset in pixels.
*/
protected _scrollPosition: number;
/**
* Difference between current scroll offset and scroll offset calculated due
* to a reflow.
*/
protected _scrollError: number;
/**
* Total number of items that could possibly be displayed. Used to help
* calculate the scroll size.
*/
protected _totalItems: number;
/**
* The total (estimated) length of all items in the scrolling direction.
*/
protected _scrollSize: number;
/**
* Number of pixels beyond the visible size of the container to still include
* in the active range of items.
*/
protected _overhang: number;
private _eventTarget;
protected _spacingChanged: boolean;
protected _defaultConfig: C;
constructor(config?: C);
set config(config: C);
get config(): C;
/**
* Maximum index of children + 1, to help estimate total height of the scroll
* space.
*/
get totalItems(): number;
set totalItems(num: number);
/**
* Primary scrolling direction.
*/
get direction(): ScrollDirection;
set direction(dir: ScrollDirection);
/**
* Estimate of the dimensions of a single child.
*/
get itemSize(): Size;
set itemSize(dims: Size);
/**
* Amount of space in between items.
*/
get spacing(): number;
set spacing(px: number);
/**
* Height and width of the viewport.
*/
get viewportSize(): Size;
set viewportSize(dims: Size);
/**
* Scroll offset of the viewport.
*/
get viewportScroll(): Positions;
set viewportScroll(coords: Positions);
/**
* Perform a reflow if one has been scheduled.
*/
reflowIfNeeded(force: boolean): void;
/**
* Scroll to the child at the given index, and the given position within that
* child.
*/
scrollToIndex(index: number, position?: string): void;
dispatchEvent(evt: Event): Promise<void>;
addEventListener(
type: string,
listener: EventListener | EventListenerObject | null,
options?: boolean | AddEventListenerOptions | undefined
): Promise<void>;
removeEventListener(
type: string,
callback: EventListener | EventListenerObject | null,
options?: boolean | EventListenerOptions | undefined
): Promise<void>;
/**
* Get the top and left positioning of the item at idx.
*/
abstract _getItemPosition(idx: number): Positions;
/**
* Update _first and _last based on items that should be in the current
* range.
*/
abstract _getActiveItems(): void;
protected _itemDim2Changed(): void;
protected _viewDim2Changed(): void;
protected _updateLayout(): void;
protected _getItemSize(_idx: number): Size;
/**
* The size of an item in the scrolling direction + space between items.
*/
protected get _delta(): number;
/**
* The height or width of an item, whichever corresponds to the scrolling direction.
*/
protected get _itemDim1(): number;
/**
* The height or width of an item, whichever does NOT correspond to the scrolling direction.
*/
protected get _itemDim2(): number;
/**
* The height or width of the viewport, whichever corresponds to the scrolling direction.
*/
protected get _viewDim1(): number;
/**
* The height or width of the viewport, whichever does NOT correspond to the scrolling direction.
*/
protected get _viewDim2(): number;
protected _scheduleReflow(): void;
protected _scheduleLayoutUpdate(): void;
protected _reflow(): void;
/**
* Estimates the total length of all items in the scrolling direction, including spacing.
*/
protected _updateScrollSize(): void;
protected _scrollIfNeeded(): void;
protected _emitRange(inProps?: unknown): void;
protected _emitScrollSize(): void;
protected _emitScrollError(): void;
/**
* Get or estimate the top and left positions of items in the current range.
* Emit an itempositionchange event with these positions.
*/
protected _emitChildPositions(): void;
/**
* Number of items to display.
*/
private get _num();
private _checkThresholds;
/**
* Find the indices of the first and last items to intersect the viewport.
* Emit a visibleindiceschange event when either index changes.
*/
protected _updateVisibleIndices(options?: UpdateVisibleIndicesOptions): void;
private _scrollPositionChanged;
}
export {};
//# sourceMappingURL=Layout1dBase.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dBase.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dBase.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,477 @@
import EventTarget from "../polyfillLoaders/EventTarget.js";
export class Layout1dBase {
constructor(config) {
/**
* The last set viewport scroll position.
*/
this._latestCoords = { left: 0, top: 0 };
/**
* Scrolling direction.
*/
this._direction = "vertical";
/**
* Dimensions of the viewport.
*/
this._viewportSize = { width: 0, height: 0 };
/**
* Flag for debouncing asynchnronous reflow requests.
*/
this._pendingReflow = false;
this._pendingLayoutUpdate = false;
/**
* Index of the item that has been scrolled to via the public API. When the
* container is otherwise scrolled, this value is set back to -1.
*/
this._scrollToIndex = -1;
/**
* When a child is scrolled to, the offset from the top of the child and the
* top of the viewport. Value is a proportion of the item size.
*/
this._scrollToAnchor = 0;
/**
* The index of the first item intersecting the viewport.
*/
this._firstVisible = 0;
/**
* The index of the last item intersecting the viewport.
*/
this._lastVisible = 0;
this._eventTargetPromise = EventTarget().then((Ctor) => {
this._eventTarget = new Ctor();
});
/**
* Pixel offset in the scroll direction of the first child.
*/
this._physicalMin = 0;
/**
* Pixel offset in the scroll direction of the last child.
*/
this._physicalMax = 0;
/**
* Index of the first child.
*/
this._first = -1;
/**
* Index of the last child.
*/
this._last = -1;
/**
* The _estimated_ size of a child.
*/
this._itemSize = { width: 100, height: 100 };
/**
* Space in pixels between children.
*/
this._spacing = 0;
/**
* Length in the scrolling direction.
*/
this._sizeDim = "height";
/**
* Length in the non-scrolling direction.
*/
this._secondarySizeDim = "width";
/**
* Position in the scrolling direction.
*/
this._positionDim = "top";
/**
* Position in the non-scrolling direction.
*/
this._secondaryPositionDim = "left";
/**
* Current scroll offset in pixels.
*/
this._scrollPosition = 0;
/**
* Difference between current scroll offset and scroll offset calculated due
* to a reflow.
*/
this._scrollError = 0;
/**
* Total number of items that could possibly be displayed. Used to help
* calculate the scroll size.
*/
this._totalItems = 0;
/**
* The total (estimated) length of all items in the scrolling direction.
*/
this._scrollSize = 1;
/**
* Number of pixels beyond the visible size of the container to still include
* in the active range of items.
*/
// TODO (graynorton): Probably want to make this something we calculate based
// on viewport size, item size, other factors, possibly still with a dial of some kind
this._overhang = 1000;
this._eventTarget = null;
this._spacingChanged = false;
this._defaultConfig = {
direction: "vertical",
};
this.config = config || this._defaultConfig;
}
set config(config) {
Object.assign(this, Object.assign({}, this._defaultConfig, config));
}
get config() {
return {
direction: this.direction,
};
}
/**
* Maximum index of children + 1, to help estimate total height of the scroll
* space.
*/
get totalItems() {
return this._totalItems;
}
set totalItems(num) {
const _num = Number(num);
if (_num !== this._totalItems) {
this._totalItems = _num;
this._scheduleReflow();
}
}
/**
* Primary scrolling direction.
*/
get direction() {
return this._direction;
}
set direction(dir) {
// Force it to be either horizontal or vertical.
dir = dir === "horizontal" ? dir : "vertical";
if (dir !== this._direction) {
this._direction = dir;
this._sizeDim = dir === "horizontal" ? "width" : "height";
this._secondarySizeDim = dir === "horizontal" ? "height" : "width";
this._positionDim = dir === "horizontal" ? "left" : "top";
this._secondaryPositionDim = dir === "horizontal" ? "top" : "left";
this._scheduleLayoutUpdate();
}
}
/**
* Estimate of the dimensions of a single child.
*/
get itemSize() {
return this._itemSize;
}
set itemSize(dims) {
const { _itemDim1, _itemDim2 } = this;
Object.assign(this._itemSize, dims);
if (_itemDim1 !== this._itemDim1 || _itemDim2 !== this._itemDim2) {
if (_itemDim2 !== this._itemDim2) {
this._itemDim2Changed();
} else {
this._scheduleLayoutUpdate();
}
}
}
/**
* Amount of space in between items.
*/
get spacing() {
return this._spacing;
}
set spacing(px) {
const _px = Number(px);
if (_px !== this._spacing) {
this._spacing = _px;
this._scheduleLayoutUpdate();
}
}
/**
* Height and width of the viewport.
*/
get viewportSize() {
return this._viewportSize;
}
set viewportSize(dims) {
const { _viewDim1, _viewDim2 } = this;
Object.assign(this._viewportSize, dims);
if (_viewDim2 !== this._viewDim2) {
this._viewDim2Changed();
} else if (_viewDim1 !== this._viewDim1) {
this._checkThresholds();
}
}
/**
* Scroll offset of the viewport.
*/
get viewportScroll() {
return this._latestCoords;
}
set viewportScroll(coords) {
Object.assign(this._latestCoords, coords);
const oldPos = this._scrollPosition;
this._scrollPosition = this._latestCoords[this._positionDim];
if (oldPos !== this._scrollPosition) {
this._scrollPositionChanged(oldPos, this._scrollPosition);
this._updateVisibleIndices({ emit: true });
}
this._checkThresholds();
}
/**
* Perform a reflow if one has been scheduled.
*/
reflowIfNeeded(force) {
if (force || this._pendingReflow) {
this._pendingReflow = false;
this._reflow();
}
}
/**
* Scroll to the child at the given index, and the given position within that
* child.
*/
scrollToIndex(index, position = "start") {
if (!Number.isFinite(index)) return;
index = Math.min(this.totalItems, Math.max(0, index));
this._scrollToIndex = index;
if (position === "nearest") {
position = index > this._first + this._num / 2 ? "end" : "start";
}
switch (position) {
case "start":
this._scrollToAnchor = 0;
break;
case "center":
this._scrollToAnchor = 0.5;
break;
case "end":
this._scrollToAnchor = 1;
break;
default:
throw new TypeError(
"position must be one of: start, center, end, nearest"
);
}
this._scheduleReflow();
}
async dispatchEvent(evt) {
await this._eventTargetPromise;
this._eventTarget.dispatchEvent(evt);
}
async addEventListener(type, listener, options) {
await this._eventTargetPromise;
this._eventTarget.addEventListener(type, listener, options);
}
async removeEventListener(type, callback, options) {
await this._eventTargetPromise;
this._eventTarget.removeEventListener(type, callback, options);
}
_itemDim2Changed() {
// Override
}
_viewDim2Changed() {
// Override
}
_updateLayout() {
// Override
}
_getItemSize(_idx) {
return {
[this._sizeDim]: this._itemDim1,
[this._secondarySizeDim]: this._itemDim2,
};
}
/**
* The size of an item in the scrolling direction + space between items.
*/
get _delta() {
return this._itemDim1 + this._spacing;
}
/**
* The height or width of an item, whichever corresponds to the scrolling direction.
*/
get _itemDim1() {
return this._itemSize[this._sizeDim];
}
/**
* The height or width of an item, whichever does NOT correspond to the scrolling direction.
*/
get _itemDim2() {
return this._itemSize[this._secondarySizeDim];
}
/**
* The height or width of the viewport, whichever corresponds to the scrolling direction.
*/
get _viewDim1() {
return this._viewportSize[this._sizeDim];
}
/**
* The height or width of the viewport, whichever does NOT correspond to the scrolling direction.
*/
get _viewDim2() {
return this._viewportSize[this._secondarySizeDim];
}
_scheduleReflow() {
this._pendingReflow = true;
}
_scheduleLayoutUpdate() {
this._pendingLayoutUpdate = true;
this._scheduleReflow();
}
_reflow() {
const { _first, _last, _scrollSize } = this;
if (this._pendingLayoutUpdate) {
this._updateLayout();
this._pendingLayoutUpdate = false;
}
this._updateScrollSize();
this._getActiveItems();
this._scrollIfNeeded();
this._updateVisibleIndices();
if (this._scrollSize !== _scrollSize) {
this._emitScrollSize();
}
if (this._first === -1 && this._last === -1) {
// TODO: have default empty object for emitRange instead
this._emitRange();
} else if (
this._first !== _first ||
this._last !== _last ||
this._spacingChanged
) {
// TODO: have default empty object for emitRange instead
this._emitRange();
this._emitChildPositions();
}
this._emitScrollError();
}
/**
* Estimates the total length of all items in the scrolling direction, including spacing.
*/
_updateScrollSize() {
// Ensure we have at least 1px - this allows getting at least 1 item to be
// rendered.
this._scrollSize = Math.max(1, this._totalItems * this._delta);
}
_scrollIfNeeded() {
if (this._scrollToIndex === -1) {
return;
}
const index = this._scrollToIndex;
const anchor = this._scrollToAnchor;
const pos = this._getItemPosition(index)[this._positionDim];
const size = this._getItemSize(index)[this._sizeDim];
const curAnchorPos = this._scrollPosition + this._viewDim1 * anchor;
const newAnchorPos = pos + size * anchor;
// Ensure scroll position is an integer within scroll bounds.
const scrollPosition = Math.floor(
Math.min(
this._scrollSize - this._viewDim1,
Math.max(0, this._scrollPosition - curAnchorPos + newAnchorPos)
)
);
this._scrollError += this._scrollPosition - scrollPosition;
this._scrollPosition = scrollPosition;
}
_emitRange(inProps = undefined) {
const detail = Object.assign(
{
first: this._first,
last: this._last,
num: this._num,
stable: true,
firstVisible: this._firstVisible,
lastVisible: this._lastVisible,
},
inProps
);
this.dispatchEvent(new CustomEvent("rangechange", { detail }));
}
_emitScrollSize() {
const detail = {
[this._sizeDim]: this._scrollSize,
};
this.dispatchEvent(new CustomEvent("scrollsizechange", { detail }));
}
_emitScrollError() {
if (this._scrollError) {
const detail = {
[this._positionDim]: this._scrollError,
[this._secondaryPositionDim]: 0,
};
this.dispatchEvent(new CustomEvent("scrollerrorchange", { detail }));
this._scrollError = 0;
}
}
/**
* Get or estimate the top and left positions of items in the current range.
* Emit an itempositionchange event with these positions.
*/
_emitChildPositions() {
const detail = {};
for (let idx = this._first; idx <= this._last; idx++) {
detail[idx] = this._getItemPosition(idx);
}
this.dispatchEvent(new CustomEvent("itempositionchange", { detail }));
}
/**
* Number of items to display.
*/
get _num() {
if (this._first === -1 || this._last === -1) {
return 0;
}
return this._last - this._first + 1;
}
_checkThresholds() {
if (this._viewDim1 === 0 && this._num > 0) {
this._scheduleReflow();
} else {
const min = Math.max(0, this._scrollPosition - this._overhang);
const max = Math.min(
this._scrollSize,
this._scrollPosition + this._viewDim1 + this._overhang
);
if (this._physicalMin > min || this._physicalMax < max) {
this._scheduleReflow();
}
}
}
/**
* Find the indices of the first and last items to intersect the viewport.
* Emit a visibleindiceschange event when either index changes.
*/
_updateVisibleIndices(options) {
if (this._first === -1 || this._last === -1) return;
let firstVisible = this._first;
while (
Math.round(
this._getItemPosition(firstVisible)[this._positionDim] +
this._getItemSize(firstVisible)[this._sizeDim]
) <= Math.round(this._scrollPosition)
) {
firstVisible++;
}
let lastVisible = this._last;
while (
Math.round(this._getItemPosition(lastVisible)[this._positionDim]) >=
Math.round(this._scrollPosition + this._viewDim1)
) {
lastVisible--;
}
if (
firstVisible !== this._firstVisible ||
lastVisible !== this._lastVisible
) {
this._firstVisible = firstVisible;
this._lastVisible = lastVisible;
if (options && options.emit) {
this._emitRange();
}
}
}
_scrollPositionChanged(oldPos, newPos) {
// When both values are bigger than the max scroll position, keep the
// current _scrollToIndex, otherwise invalidate it.
const maxPos = this._scrollSize - this._viewDim1;
if (oldPos < maxPos || newPos < maxPos) {
this._scrollToIndex = -1;
}
}
}
//# sourceMappingURL=Layout1dBase.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,65 @@
import { Layout1dBase, Layout1dBaseConfig } from "./Layout1dBase";
import { ItemBox, Positions, Size } from "./Layout";
interface Layout1dFlexConfig extends Layout1dBaseConfig {
spacing?: number;
idealSize?: number;
}
declare type Layout1dFlexSpecifier = Layout1dFlexConfig & {
type: new (config?: Layout1dFlexConfig) => Layout1dFlex;
};
declare type Layout1dFlexSpecifierFactory = (
config?: Layout1dFlexConfig
) => Layout1dFlexSpecifier;
export declare const layout1dFlex: Layout1dFlexSpecifierFactory;
interface Rolumn {
_startIdx: number;
_endIdx: number;
_startPos: number;
_size: number;
}
interface Chunk {
_itemPositions: Array<Positions>;
_rolumns: Array<Rolumn>;
_size: number;
_dirty: boolean;
}
/**
* TODO @straversi: document and test this Layout.
*/
export declare class Layout1dFlex extends Layout1dBase<Layout1dFlexConfig> {
private _itemSizes;
private _chunkSize;
private _chunks;
private _aspectRatios;
private _numberOfAspectRatiosMeasured;
protected _idealSize: number | null;
protected _config: Layout1dFlexConfig;
protected _defaultConfig: Layout1dFlexConfig;
listenForChildLoadEvents: boolean;
/**
* TODO graynorton@ Don't hard-code Flickr - probably need a config option
*/
measureChildren: (e: Element, i: unknown) => ItemBox;
set idealSize(px: number | null);
get idealSize(): number | null;
updateItemSizes(sizes: { [key: number]: ItemBox }): void;
_newChunk(): {
_rolumns: never[];
_itemPositions: never[];
_size: number;
_dirty: boolean;
};
_getChunk(idx: number | string): Chunk;
_recordAspectRatio(dims: ItemBox): void;
_getRandomAspectRatio(): Size;
_viewDim2Changed(): void;
_getActiveItems(): void;
_getItemPosition(idx: number): Positions;
_getItemSize(idx: number): Size;
_getNaturalItemDims(idx: number): Size;
_layOutChunk(startIdx: number): Chunk;
_updateLayout(): void;
_updateScrollSize(): void;
}
export {};
//# sourceMappingURL=Layout1dFlex.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dFlex.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dFlex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAC,MAAM,UAAU,CAAC;AAElD,UAAU,kBAAmB,SAAQ,kBAAkB;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,aAAK,qBAAqB,GAAG,kBAAkB,GAAG;IAChD,IAAI,EAAE,KAAI,MAAM,CAAC,EAAE,kBAAkB,KAAK,YAAY,CAAA;CACvD,CAAA;AAED,aAAK,4BAA4B,GAAG,CAAC,MAAM,CAAC,EAAE,kBAAkB,KAAK,qBAAqB,CAAC;AAE3F,eAAO,MAAM,YAAY,EAAE,4BAEjB,CAAC;AAEX,UAAU,MAAM;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,UAAU,KAAK;IACb,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAA;CAChB;AAeD;;GAEG;AACH,qBAAa,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IAChE,OAAO,CAAC,UAAU,CAAmB;IAIrC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,6BAA6B,CAAa;IAClD,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3C,SAAS,CAAC,OAAO,EAAE,kBAAkB,CAAM;IAC3C,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAGzC;IAEH,wBAAwB,UAAQ;IAElC;;OAEG;IACD,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAUvD;IAED,IAAI,SAAS,CAAC,EAAE,eAAA,EAMf;IAED,IAAI,SAAS,kBAEZ;IAED,eAAe,CAAC,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAC;IAqB/C,SAAS;;;;;;IAST,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI9B,kBAAkB,CAAC,IAAI,EAAE,OAAO;IAahC,qBAAqB,IAAI,IAAI;IAa7B,gBAAgB;IAIhB,eAAe;IA2Bf,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAKxC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAM/B,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAStC,YAAY,CAAC,QAAQ,EAAE,MAAM;IA+D7B,aAAa,IAAI,IAAI;IAYrB,iBAAiB;CAOlB"}

View File

@ -0,0 +1,264 @@
import { Layout1dBase } from "./Layout1dBase";
export const layout1dFlex = (config) =>
Object.assign(
{
type: Layout1dFlex,
},
config
);
/**
* TODO @straversi: document and test this Layout.
*/
export class Layout1dFlex extends Layout1dBase {
constructor() {
super(...arguments);
this._itemSizes = [];
// private _itemPositions: Array<Positions> = [];
// private _rolumnStartIdx: Array<number> = [];
// private _rolumnStartPos: Array<number> = [];
this._chunkSize = null;
this._chunks = [];
this._aspectRatios = {};
this._numberOfAspectRatiosMeasured = 0;
this._idealSize = null;
this._config = {};
this._defaultConfig = Object.assign({}, super._defaultConfig, {
spacing: 0,
idealSize: 200,
});
this.listenForChildLoadEvents = true;
/**
* TODO graynorton@ Don't hard-code Flickr - probably need a config option
*/
this.measureChildren = function (e, i) {
const { naturalWidth, naturalHeight } = e;
if (naturalWidth !== undefined && naturalHeight != undefined) {
return { width: naturalWidth, height: naturalHeight };
}
const { o_width, o_height } = i;
if (o_width !== undefined && o_height !== undefined) {
return { width: o_width, height: o_height };
}
return { width: -1, height: -1 };
};
}
set idealSize(px) {
const _px = Number(px);
if (_px !== this._idealSize) {
this._idealSize = _px;
this._scheduleLayoutUpdate();
}
}
get idealSize() {
return this._idealSize;
}
updateItemSizes(sizes) {
let dirty;
Object.keys(sizes).forEach((key) => {
const n = Number(key);
const chunk = this._getChunk(n);
const dims = sizes[n];
const prevDims = this._itemSizes[n];
if (dims.width && dims.height) {
if (
!prevDims ||
prevDims.width !== dims.width ||
prevDims.height !== dims.height
) {
chunk._dirty = true;
dirty = true;
this._itemSizes[n] = sizes[n];
this._recordAspectRatio(sizes[n]);
}
}
});
if (dirty) {
this._scheduleLayoutUpdate();
}
}
_newChunk() {
return {
["_rolumns"]: [],
_itemPositions: [],
_size: 0,
_dirty: false,
};
}
_getChunk(idx) {
return (
this._chunks[Math.floor(Number(idx) / this._chunkSize)] ||
this._newChunk()
);
}
_recordAspectRatio(dims) {
if (dims.width && dims.height) {
const bucket = Math.round((dims.width / dims.height) * 10) / 10;
if (this._aspectRatios[bucket]) {
this._aspectRatios[bucket]++;
} else {
this._aspectRatios[bucket] = 1;
}
this._numberOfAspectRatiosMeasured++;
}
}
_getRandomAspectRatio() {
if (this._numberOfAspectRatiosMeasured === 0) {
return { width: 1, height: 1 };
}
const n = Math.random() * this._numberOfAspectRatiosMeasured;
const buckets = Object.keys(this._aspectRatios);
let i = -1,
m = 0;
while (m < n && i < buckets.length) {
m += this._aspectRatios[buckets[++i]];
}
return { width: Number(buckets[i]), height: 1 };
}
_viewDim2Changed() {
this._scheduleLayoutUpdate();
}
_getActiveItems() {
const chunk = this._getChunk(0);
if (chunk._rolumns.length === 0) return;
const scrollPos = Math.max(
0,
Math.min(this._scrollPosition, this._scrollSize - this._viewDim1)
);
const min = Math.max(0, scrollPos - this._overhang);
const max = Math.min(
this._scrollSize,
scrollPos + this._viewDim1 + this._overhang
);
const mid = (min + max) / 2;
const estMidRolumn = Math.round(
(mid / this._scrollSize) * chunk._rolumns.length
);
let idx = estMidRolumn;
while (chunk._rolumns[idx]._startPos < min) {
idx++;
}
while (chunk._rolumns[idx]._startPos > min) {
idx--;
}
this._first = chunk._rolumns[idx]._startIdx;
this._physicalMin = chunk._rolumns[idx]._startPos;
let rolumnMax;
while (
(rolumnMax =
chunk._rolumns[idx]._startPos +
chunk._rolumns[idx]._size +
this._spacing * 2) < max
) {
idx++;
}
this._last = chunk._rolumns[idx]._endIdx;
this._physicalMax = rolumnMax;
}
_getItemPosition(idx) {
const chunk = this._getChunk(0);
return chunk._itemPositions[idx];
}
_getItemSize(idx) {
const chunk = this._getChunk(0);
const { width, height } = chunk._itemPositions[idx];
return { width, height };
}
_getNaturalItemDims(idx) {
let itemDims = this._itemSizes[idx];
if (
itemDims === undefined ||
itemDims.width === -1 ||
itemDims.height === -1
) {
itemDims = this._getRandomAspectRatio();
}
return itemDims;
}
_layOutChunk(startIdx) {
const chunk = this._newChunk();
let startPos = this._spacing;
let idx = 0;
let rolumnSize2 = 0;
let lastRatio = Infinity;
const finishRolumn = (lastIdx) => {
const rolumn = {
_startIdx: startIdx,
_endIdx: lastIdx,
_startPos: startPos - this._spacing,
_size: 0,
};
chunk._rolumns.push(rolumn);
let itemStartPos = this._spacing;
for (let i = startIdx; i <= lastIdx; i++) {
const pos = chunk._itemPositions[i];
pos.width = pos.width * lastRatio;
pos.height = pos.height * lastRatio;
pos.left = this._positionDim === "left" ? startPos : itemStartPos;
pos.top = this._positionDim === "top" ? startPos : itemStartPos;
itemStartPos += pos[this._secondarySizeDim] + this._spacing;
}
rolumn._size = chunk._itemPositions[lastIdx][this._sizeDim];
};
while (idx < this._chunkSize) {
const itemDims = this._getNaturalItemDims(idx);
const availableSpace =
this._viewDim2 - this._spacing * (idx - startIdx + 2);
const itemSize = itemDims[this._sizeDim];
const itemSize2 = itemDims[this._secondarySizeDim];
const idealScaleFactor = this._idealSize / itemSize;
const adjItemSize = idealScaleFactor * itemSize;
const adjItemSize2 = idealScaleFactor * itemSize2;
chunk._itemPositions[idx] = {
left: 0,
top: 0,
width: this._sizeDim === "width" ? adjItemSize : adjItemSize2,
height: this._sizeDim === "height" ? adjItemSize : adjItemSize2,
};
const ratio = availableSpace / (rolumnSize2 + adjItemSize2);
if (Math.abs(1 - ratio) > Math.abs(1 - lastRatio)) {
// rolumn is better without adding this item
finishRolumn(idx - 1);
startIdx = idx;
startPos += this._idealSize * lastRatio + this._spacing;
lastRatio = (this._viewDim2 - 2 * this._spacing) / adjItemSize2;
rolumnSize2 = adjItemSize2;
} else {
// add this item and continue
rolumnSize2 += adjItemSize2;
lastRatio = ratio;
}
if (idx === this._chunkSize - 1) {
finishRolumn(idx);
}
idx++;
}
const lastRolumn = chunk._rolumns[chunk._rolumns.length - 1];
chunk._size = lastRolumn._startPos + lastRolumn._size;
return chunk;
}
_updateLayout() {
if (/*this._rolumnStartIdx === undefined ||*/ this._viewDim2 === 0) return;
this._chunkSize = Math.ceil(
(2 * (this._viewDim1 * this._viewDim2)) /
(this._idealSize * this._idealSize)
);
console.log("chunkSize", this._chunkSize);
// TODO: An odd place to do this, need to think through the logistics of getting size info to the layout
// in all cases
// this._itemSizes.length = 100;//this._totalItems;
this._chunks[0] = this._layOutChunk(0);
// TODO (graynorton): This is a hack to force reflow
this._spacingChanged = true;
}
_updateScrollSize() {
const chunk = this._chunks[0];
this._scrollSize =
!chunk || chunk._rolumns.length === 0
? 1
: chunk._size + 2 * this._spacing;
// chunk._rolumns[chunk._rolumns.length - 1]._startPos +
// chunk._itemPositions[chunk._rolumns.length - 1][this._sizeDim] +
// (this._spacing * 2);
}
}
//# sourceMappingURL=Layout1dFlex.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
import { Layout1dBase } from "./Layout1dBase.js";
/**
* TODO @straversi: document and test this Layout.
*/
export declare abstract class Layout1dGrid<
Config
> extends Layout1dBase<Config> {
protected _rolumns: number;
_viewDim2Changed(): void;
_itemDim2Changed(): void;
_getActiveItems(): void;
_getItemPosition(
idx: number
): {
top: number;
left: number;
};
_updateScrollSize(): void;
}
//# sourceMappingURL=Layout1dGrid.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dGrid.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAE/C;;GAEG;AACH,8BAAsB,YAAY,CAAC,MAAM,CAAE,SAAQ,YAAY,CAAC,MAAM,CAAC;IACrE,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAK;IAO/B,gBAAgB;IAIhB,gBAAgB;IAIhB,eAAe;IAef,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC;IAQ1D,iBAAiB;CAIlB"}

View File

@ -0,0 +1,52 @@
import { Layout1dBase } from "./Layout1dBase.js";
/**
* TODO @straversi: document and test this Layout.
*/
export class Layout1dGrid extends Layout1dBase {
constructor() {
super(...arguments);
this._rolumns = 1;
}
// constructor(config) {
// super(config);
// this._rolumns = 1;
// }
_viewDim2Changed() {
this._scheduleLayoutUpdate();
}
_itemDim2Changed() {
this._scheduleLayoutUpdate();
}
_getActiveItems() {
const min = Math.max(0, this._scrollPosition - this._overhang);
const max = Math.min(
this._scrollSize,
this._scrollPosition + this._viewDim1 + this._overhang
);
const firstCow = Math.floor(min / this._delta);
const lastCow = Math.ceil(max / this._delta) - 1;
this._first = firstCow * this._rolumns;
this._last = Math.min(
(lastCow + 1) * this._rolumns - 1,
this._totalItems - 1
);
this._physicalMin = this._delta * firstCow;
this._physicalMax = this._delta * (lastCow + 1);
}
_getItemPosition(idx) {
return {
[this._positionDim]:
this._spacing + Math.floor(idx / this._rolumns) * this._delta,
[this._secondaryPositionDim]:
this._spacing +
(idx % this._rolumns) * (this._spacing + this._itemDim2),
};
}
_updateScrollSize() {
this._scrollSize = Math.max(
1,
Math.ceil(this._totalItems / this._rolumns) * this._delta + this._spacing
);
}
}
//# sourceMappingURL=Layout1dGrid.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dGrid.js","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAE/C;;GAEG;AACH,MAAM,OAAgB,YAAqB,SAAQ,YAAoB;IAAvE;;QACY,aAAQ,GAAW,CAAC,CAAC;IA0CjC,CAAC;IAxCC,wBAAwB;IACxB,mBAAmB;IACnB,uBAAuB;IACvB,IAAI;IAEJ,gBAAgB;QACd,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAC/B,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAC/B,CAAC;IAED,eAAe;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAChB,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAEjD,IAAI,CAAC,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QACvC,IAAI,CAAC,KAAK;YACN,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,gBAAgB,CAAC,GAAW;QAC1B,OAAO;YACL,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM;YAC9E,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,IAAI,CAAC,QAAQ;gBAC3C,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;SACrB,CAAC;IAC9C,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,WAAW;YACZ,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7F,CAAC;CACF","sourcesContent":["import {Layout1dBase} from './Layout1dBase.js';\n\n/**\n * TODO @straversi: document and test this Layout.\n */\nexport abstract class Layout1dGrid<Config> extends Layout1dBase<Config> {\n protected _rolumns: number = 1;\n\n // constructor(config) {\n // super(config);\n // this._rolumns = 1;\n // }\n\n _viewDim2Changed() {\n this._scheduleLayoutUpdate();\n }\n\n _itemDim2Changed() {\n this._scheduleLayoutUpdate();\n }\n\n _getActiveItems() {\n const min = Math.max(0, this._scrollPosition - this._overhang);\n const max = Math.min(\n this._scrollSize,\n this._scrollPosition + this._viewDim1 + this._overhang);\n const firstCow = Math.floor(min / this._delta);\n const lastCow = Math.ceil(max / this._delta) - 1;\n\n this._first = firstCow * this._rolumns;\n this._last =\n Math.min(((lastCow + 1) * this._rolumns) - 1, this._totalItems - 1);\n this._physicalMin = this._delta * firstCow;\n this._physicalMax = this._delta * (lastCow + 1);\n }\n\n _getItemPosition(idx: number): {top: number, left: number} {\n return {\n [this._positionDim]: this._spacing + Math.floor(idx / this._rolumns) * this._delta,\n [this._secondaryPositionDim]: this._spacing +\n ((idx % this._rolumns) * (this._spacing + this._itemDim2))\n } as unknown as {top: number, left: number};\n }\n\n _updateScrollSize() {\n this._scrollSize =\n Math.max(1, Math.ceil(this._totalItems / this._rolumns) * this._delta + this._spacing);\n }\n}\n"]}

View File

@ -0,0 +1,8 @@
import { Layout1dGrid } from "./Layout1dGrid.js";
import { Layout1dBaseConfig } from "./Layout1dBase.js";
import { ItemBox } from "./Layout";
export declare class Layout1dNaturalSizeGrid extends Layout1dGrid<Layout1dBaseConfig> {
updateItemSizes(sizes: { [key: number]: ItemBox }): void;
_updateLayout(): void;
}
//# sourceMappingURL=Layout1dNaturalSizeGrid.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dNaturalSizeGrid.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dNaturalSizeGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAC,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAC,OAAO,EAAC,MAAM,UAAU,CAAC;AAEjC,qBAAa,uBAAwB,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,eAAe,CAAC,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAC;IAQ7C,aAAa;CAYlB"}

View File

@ -0,0 +1,23 @@
import { Layout1dGrid } from "./Layout1dGrid.js";
export class Layout1dNaturalSizeGrid extends Layout1dGrid {
updateItemSizes(sizes) {
// Assume all items have the same size.
const size = Object.values(sizes)[0];
if (size) {
this.itemSize = size;
}
}
_updateLayout() {
const { _spacing } = this;
this._rolumns = Math.max(1, Math.floor(this._viewDim2 / this._itemDim2));
if (this._rolumns > 1) {
this._spacing =
(this._viewDim2 % (this._rolumns * this._itemDim2)) /
(this._rolumns + 1);
} else {
this._spacing = 0;
}
this._spacingChanged = !(_spacing === this._spacing);
}
}
//# sourceMappingURL=Layout1dNaturalSizeGrid.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dNaturalSizeGrid.js","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dNaturalSizeGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAI/C,MAAM,OAAO,uBAAwB,SAAQ,YAAgC;IACzE,eAAe,CAAC,KAA+B;QAC3C,uCAAuC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,IAAI,EAAE;YACR,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;SACtB;IACH,CAAC;IAED,aAAa;QACX,MAAM,EAAC,QAAQ,EAAC,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACzE,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE;YACrB,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC/D,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;SACzB;aACI;YACH,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;SACnB;QACD,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvD,CAAC;CACN","sourcesContent":["import {Layout1dGrid} from './Layout1dGrid.js';\nimport {Layout1dBaseConfig} from './Layout1dBase.js';\nimport {ItemBox} from './Layout';\n\nexport class Layout1dNaturalSizeGrid extends Layout1dGrid<Layout1dBaseConfig> {\n updateItemSizes(sizes: {[key: number]: ItemBox}) {\n // Assume all items have the same size.\n const size = Object.values(sizes)[0];\n if (size) {\n this.itemSize = size;\n }\n }\n \n _updateLayout() {\n const {_spacing} = this;\n this._rolumns = Math.max(1, Math.floor(this._viewDim2 / this._itemDim2));\n if (this._rolumns > 1) {\n this._spacing = (this._viewDim2 % (this._rolumns * this._itemDim2)) /\n (this._rolumns + 1);\n }\n else {\n this._spacing = 0;\n }\n this._spacingChanged = !(_spacing === this._spacing);\n } \n}"]}

View File

@ -0,0 +1,22 @@
import { Layout1dBaseConfig } from "./Layout1dBase.js";
import { Layout1dGrid } from "./Layout1dGrid.js";
import { Positions } from "./Layout.js";
interface Layout1dSquareGridConfig extends Layout1dBaseConfig {
spacing?: number;
idealSize?: number;
}
declare type Layout1dSquareGridSpecifier = Layout1dSquareGridConfig & {
type: new (config?: Layout1dSquareGridConfig) => Layout1dSquareGrid;
};
declare type Layout1dSquareGridSpecifierFactory = (
config?: Layout1dSquareGridConfig
) => Layout1dSquareGridSpecifier;
export declare const layout1dSquareGrid: Layout1dSquareGridSpecifierFactory;
export declare class Layout1dSquareGrid extends Layout1dGrid<Layout1dSquareGridConfig> {
protected _idealSize: number;
set idealSize(px: number);
_getItemPosition(idx: number): Positions;
_updateLayout(): void;
}
export {};
//# sourceMappingURL=Layout1dSquareGrid.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dSquareGrid.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dSquareGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAEtC,UAAU,wBAAyB,SAAQ,kBAAkB;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,aAAK,2BAA2B,GAAG,wBAAwB,GAAG;IAC5D,IAAI,EAAE,KAAI,MAAM,CAAC,EAAE,wBAAwB,KAAK,kBAAkB,CAAA;CACnE,CAAA;AAED,aAAK,kCAAkC,GAAG,CAAC,MAAM,CAAC,EAAE,wBAAwB,KAAK,2BAA2B,CAAC;AAE7G,eAAO,MAAM,kBAAkB,EAAE,kCAEvB,CAAC;AAEX,qBAAa,kBAAmB,SAAQ,YAAY,CAAC,wBAAwB,CAAC;IAC5E,SAAS,CAAC,UAAU,EAAE,MAAM,CAAO;IASnC,IAAI,SAAS,CAAC,EAAE,EAAE,MAAM,EAKvB;IAED,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAIxC,aAAa;CASd"}

View File

@ -0,0 +1,41 @@
import { Layout1dGrid } from "./Layout1dGrid.js";
export const layout1dSquareGrid = (config) =>
Object.assign(
{
type: Layout1dSquareGrid,
},
config
);
export class Layout1dSquareGrid extends Layout1dGrid {
constructor() {
super(...arguments);
this._idealSize = 200;
}
// constructor(config) {
// super(config);
// if (config.idealSize === undefined) {
// this._idealSize = 200;
// }
// }
set idealSize(px) {
if (px !== this._idealSize) {
this._idealSize = px;
this._scheduleLayoutUpdate();
}
}
_getItemPosition(idx) {
return Object.assign(super._getItemPosition(idx), this._itemSize);
}
_updateLayout() {
const frolumns = this._viewDim2 / this._idealSize;
this._rolumns =
frolumns % 1 < 0.5 ? Math.floor(frolumns) : Math.ceil(frolumns);
const adjSize =
(this._viewDim2 - (this._rolumns + 1) * this._spacing) / this._rolumns;
if (adjSize !== this._itemSize.width) {
this._itemSize = { width: adjSize, height: adjSize };
this._spacingChanged = true;
}
}
}
//# sourceMappingURL=Layout1dSquareGrid.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Layout1dSquareGrid.js","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/layouts/Layout1dSquareGrid.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAc/C,MAAM,CAAC,MAAM,kBAAkB,GAAuC,CAAC,MAAiC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;IACzH,IAAI,EAAE,kBAAkB;CACzB,EAAE,MAAM,CAAC,CAAC;AAEX,MAAM,OAAO,kBAAmB,SAAQ,YAAsC;IAA9E;;QACY,eAAU,GAAW,GAAG,CAAC;IA6BrC,CAAC;IA3BC,wBAAwB;IACxB,mBAAmB;IACnB,0CAA0C;IAC1C,+BAA+B;IAC/B,MAAM;IACN,IAAI;IAEJ,IAAI,SAAS,CAAC,EAAU;QACtB,IAAI,EAAE,KAAK,IAAI,CAAC,UAAU,EAAE;YAC1B,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;YACrB,IAAI,CAAC,qBAAqB,EAAE,CAAC;SAC9B;IACH,CAAC;IAED,gBAAgB,CAAC,GAAW;QAC1B,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACpE,CAAC;IAED,aAAa;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC;QAClD,IAAI,CAAC,QAAQ,GAAG,QAAQ,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChF,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QACzF,IAAI,OAAO,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;YACpC,IAAI,CAAC,SAAS,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YACrD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;SAC7B;IACH,CAAC;CACF","sourcesContent":["import {Layout1dBaseConfig} from './Layout1dBase.js';\nimport {Layout1dGrid} from './Layout1dGrid.js';\nimport {Positions} from './Layout.js';\n\ninterface Layout1dSquareGridConfig extends Layout1dBaseConfig {\n spacing?: number,\n idealSize?: number\n}\n\ntype Layout1dSquareGridSpecifier = Layout1dSquareGridConfig & {\n type: new(config?: Layout1dSquareGridConfig) => Layout1dSquareGrid\n}\n\ntype Layout1dSquareGridSpecifierFactory = (config?: Layout1dSquareGridConfig) => Layout1dSquareGridSpecifier;\n\nexport const layout1dSquareGrid: Layout1dSquareGridSpecifierFactory = (config?: Layout1dSquareGridConfig) => Object.assign({\n type: Layout1dSquareGrid\n}, config);\n\nexport class Layout1dSquareGrid extends Layout1dGrid<Layout1dSquareGridConfig> {\n protected _idealSize: number = 200;\n\n // constructor(config) {\n // super(config);\n // if (config.idealSize === undefined) {\n // this._idealSize = 200;\n // }\n // }\n\n set idealSize(px: number) {\n if (px !== this._idealSize) {\n this._idealSize = px;\n this._scheduleLayoutUpdate();\n }\n }\n\n _getItemPosition(idx: number): Positions {\n return Object.assign(super._getItemPosition(idx), this._itemSize);\n }\n\n _updateLayout() {\n const frolumns = this._viewDim2 / this._idealSize;\n this._rolumns = frolumns % 1 < 0.5 ? Math.floor(frolumns) : Math.ceil(frolumns);\n const adjSize = (this._viewDim2 - ((this._rolumns + 1) * this._spacing)) / this._rolumns;\n if (adjSize !== this._itemSize.width) {\n this._itemSize = { width: adjSize, height: adjSize };\n this._spacingChanged = true;\n }\n } \n}"]}

View File

@ -0,0 +1,6 @@
interface EventTargetConstructor {
new (): EventTarget;
}
export default function EventTarget(): Promise<EventTargetConstructor>;
export {};
//# sourceMappingURL=EventTarget.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"EventTarget.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.ts"],"names":[],"mappings":"AAEA,UAAU,sBAAsB;IAC5B,QAAO,WAAW,CAAA;CACrB;AAKD,wBAA8B,WAAW,oCAExC"}

View File

@ -0,0 +1,15 @@
let _ET;
let ET;
export default async function EventTarget() {
return ET || init();
}
async function init() {
_ET = window.EventTarget;
try {
new _ET();
} catch (_a) {
_ET = (await import("event-target-shim")).EventTarget;
}
return (ET = _ET);
}
//# sourceMappingURL=EventTarget.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"EventTarget.js","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.ts"],"names":[],"mappings":"AAMA,IAAI,GAA+C,CAAC;AACpD,IAAI,EAA0B,CAAC;AAE/B,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,WAAW;IACrC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,IAAI;IACf,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC;IACzB,IAAI;QACA,IAAI,GAAG,EAAE,CAAC;KACb;IACD,WAAM;QACF,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,WAAW,CAAC;KACzD;IACD,OAAO,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;AACtB,CAAC","sourcesContent":["type EventTargetModule = typeof import('event-target-shim');\n\ninterface EventTargetConstructor {\n new(): EventTarget\n}\n\nlet _ET: EventTargetModule | EventTargetConstructor;\nlet ET: EventTargetConstructor;\n\nexport default async function EventTarget() {\n return ET || init();\n}\n\nasync function init() {\n _ET = window.EventTarget;\n try {\n new _ET();\n }\n catch {\n _ET = (await import('event-target-shim')).EventTarget;\n }\n return (ET = _ET);\n}"]}

View File

@ -0,0 +1,3 @@
import { ResizeObserverConstructor } from "../polyfills/resize-observer-polyfill/ResizeObserver.js";
export default function ResizeObserver(): Promise<ResizeObserverConstructor>;
//# sourceMappingURL=ResizeObserver.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"ResizeObserver.d.ts","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/polyfillLoaders/ResizeObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,yDAAyD,CAAC;AAMpG,wBAA8B,cAAc,uCAE3C"}

View File

@ -0,0 +1,20 @@
let _RO;
let RO;
export default async function ResizeObserver() {
return RO || init();
}
async function init() {
if (_RO) {
return (await _RO).default;
} else {
_RO = window.ResizeObserver;
try {
new _RO(function () {});
} catch (e) {
_RO = import("../polyfills/resize-observer-polyfill/ResizeObserver.js");
_RO = (await _RO).default;
}
return (RO = _RO);
}
}
//# sourceMappingURL=ResizeObserver.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"ResizeObserver.js","sourceRoot":"","sources":["../../../../src/lib/uni-virtualizer/lib/polyfillLoaders/ResizeObserver.ts"],"names":[],"mappings":"AAGA,IAAI,GAAqD,CAAC;AAC1D,IAAI,EAA6B,CAAC;AAElC,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,cAAc;IACxC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,IAAI;IACf,IAAI,GAAG,EAAE;QACL,OAAO,CAAC,MAAM,GAA4B,CAAA,CAAC,OAAO,CAAC;KACtD;SACI;QACD,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC;QAC5B,IAAI;YACA,IAAI,GAAG,CAAC,cAAY,CAAC,CAAC,CAAC;SAC1B;QACD,OAAO,CAAC,EAAE;YACN,GAAG,GAAG,MAAM,CAAC,yDAAyD,CAAoC,CAAC;YAC3G,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC;SAC7B;QACD,OAAO,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;KACrB;AACL,CAAC","sourcesContent":["import { ResizeObserverConstructor } from '../polyfills/resize-observer-polyfill/ResizeObserver.js';\ntype ResizeObserverModule = typeof import('../polyfills/resize-observer-polyfill/ResizeObserver.js');\n\nlet _RO: ResizeObserverModule | ResizeObserverConstructor;\nlet RO: ResizeObserverConstructor;\n\nexport default async function ResizeObserver() {\n return RO || init();\n}\n\nasync function init() {\n if (_RO) {\n return (await _RO as ResizeObserverModule).default;\n }\n else {\n _RO = window.ResizeObserver;\n try {\n new _RO(function() {});\n }\n catch (e) {\n _RO = import('../polyfills/resize-observer-polyfill/ResizeObserver.js') as unknown as ResizeObserverModule;\n _RO = (await _RO).default;\n }\n return (RO = _RO); \n }\n}"]}

View File

@ -0,0 +1,960 @@
/**
* Detects whether window and document objects are available in current environment.
*/
var isBrowser =
typeof window !== "undefined" &&
typeof document !== "undefined" &&
window.document === document;
// Returns global object of a current environment.
var global$1 = (function () {
if (typeof global !== "undefined" && global.Math === Math) {
return global;
}
if (typeof self !== "undefined" && self.Math === Math) {
return self;
}
if (typeof window !== "undefined" && window.Math === Math) {
return window;
}
// eslint-disable-next-line no-new-func
return Function("return this")();
})();
/**
* A shim for the requestAnimationFrame which falls back to the setTimeout if
* first one is not supported.
*
* @returns {number} Requests' identifier.
*/
var requestAnimationFrame$1 = (function () {
if (typeof requestAnimationFrame === "function") {
// It's required to use a bounded function because IE sometimes throws
// an "Invalid calling object" error if rAF is invoked without the global
// object on the left hand side.
return requestAnimationFrame.bind(global$1);
}
var result = function (callback) {
return setTimeout(function () {
callback(+Date.now());
}, 1000 / 60);
};
return result;
})();
// Defines minimum timeout before adding a trailing call.
var trailingTimeout = 2;
/**
* Creates a wrapper function which ensures that provided callback will be
* invoked only once during the specified delay period.
*
* @param callback - Function to be invoked after the delay period.
* @param delay - Delay after which to invoke callback.
*/
function throttle(callback, delay) {
var leadingCall = false,
trailingCall = false,
lastCallTime = 0;
/**
* Invokes the original callback function and schedules new invocation if
* the "proxy" was called during current request.
*/
function resolvePending() {
if (leadingCall) {
leadingCall = false;
callback();
}
if (trailingCall) {
proxy();
}
}
/**
* Callback invoked after the specified delay. It will further postpone
* invocation of the original function delegating it to the
* requestAnimationFrame.
*
* @returns {void}
*/
function timeoutCallback() {
requestAnimationFrame$1(resolvePending);
}
/**
* Schedules invocation of the original function.
*/
function proxy() {
var timeStamp = Date.now();
if (leadingCall) {
// Reject immediately following calls.
if (timeStamp - lastCallTime < trailingTimeout) {
return;
}
// Schedule new call to be in invoked when the pending one is resolved.
// This is important for "transitions" which never actually start
// immediately so there is a chance that we might miss one if change
// happens amids the pending invocation.
trailingCall = true;
} else {
leadingCall = true;
trailingCall = false;
setTimeout(timeoutCallback, delay);
}
lastCallTime = timeStamp;
}
return proxy;
}
// Minimum delay before invoking the update of observers.
var REFRESH_DELAY = 20;
// A list of substrings of CSS properties used to find transition events that
// might affect dimensions of observed elements.
var transitionKeys = [
"top",
"right",
"bottom",
"left",
"width",
"height",
"size",
"weight",
];
// Check if MutationObserver is available.
var mutationObserverSupported = typeof MutationObserver !== "undefined";
// Get the native implementation of HTMLElement.attachShadow
var origAttachShadow =
isBrowser &&
HTMLElement.prototype.attachShadow &&
HTMLElement.prototype.attachShadow.toString().indexOf("[native code]") !== -1
? HTMLElement.prototype.attachShadow
: null;
/**
* Singleton controller class which handles updates of ResizeObserver instances.
*/
var ResizeObserverController = /** @class */ (function () {
/**
* Creates a new instance of ResizeObserverController.
*/
function ResizeObserverController() {
/**
* Indicates whether DOM listeners have been added.
*/
this.connected_ = false;
/**
* Tells that controller has subscribed for Mutation Events.
*/
this.mutationEventsAdded_ = false;
/**
* Keeps reference to the instance of MutationObserver.
*/
this.mutationsObserver_ = null;
/**
* A list of connected observers.
*/
this.observers_ = [];
this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);
this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);
}
/**
* Adds observer to observers list.
*
* @param observer Observer to be added.
*/
ResizeObserverController.prototype.addObserver = function (observer) {
if (!~this.observers_.indexOf(observer)) {
this.observers_.push(observer);
}
// Add listeners if they haven't been added yet.
if (!this.connected_) {
this.connect_();
}
};
/**
* Removes observer from observers list.
*
* @param observer Observer to be removed.
*/
ResizeObserverController.prototype.removeObserver = function (observer) {
var observers = this.observers_;
var index = observers.indexOf(observer);
// Remove observer if it's present in registry.
if (~index) {
observers.splice(index, 1);
}
// Remove listeners if controller has no connected observers.
if (!observers.length && this.connected_) {
this.disconnect_();
}
};
/**
* Invokes the update of observers. It will continue running updates insofar
* it detects changes.
*/
ResizeObserverController.prototype.refresh = function () {
var changesDetected = this.updateObservers_();
// Continue running updates if changes have been detected as there might
// be future ones caused by CSS transitions.
if (changesDetected) {
this.refresh();
}
};
/**
* Updates every observer from observers list and notifies them of queued
* entries.
*
* @returns Returns "true" if any observer has detected changes in
* dimensions of it's elements.
*/
ResizeObserverController.prototype.updateObservers_ = function () {
// Collect observers that have active observations.
var activeObservers = this.observers_.filter(function (observer) {
return observer.gatherActive(), observer.hasActive();
});
// Deliver notifications in a separate cycle in order to avoid any
// collisions between observers, e.g. when multiple instances of
// ResizeObserver are tracking the same element and the callback of one
// of them changes content dimensions of the observed target. Sometimes
// this may result in notifications being blocked for the rest of observers.
activeObservers.forEach(function (observer) {
return observer.broadcastActive();
});
return activeObservers.length > 0;
};
/**
* Initializes DOM listeners.
*/
ResizeObserverController.prototype.connect_ = function () {
// Do nothing if running in a non-browser environment or if listeners
// have been already added.
if (!isBrowser || this.connected_) {
return;
}
// Subscription to the "Transitionend" event is used as a workaround for
// delayed transitions. This way it's possible to capture at least the
// final state of an element.
document.addEventListener("transitionend", this.onTransitionEnd_);
window.addEventListener("resize", this.refresh);
if (mutationObserverSupported) {
this.mutationsObserver_ = new MutationObserver(this.refresh);
var options_1 = {
attributes: true,
childList: true,
characterData: true,
subtree: true,
};
this.mutationsObserver_.observe(document, options_1);
if (origAttachShadow) {
var controller_1 = this;
(function observeExistingShadowRoots(node) {
var shadowRoot = node.shadowRoot;
if (shadowRoot) {
controller_1.mutationsObserver_.observe(shadowRoot, options_1);
observeExistingShadowRoots(shadowRoot);
}
var child = node.firstElementChild;
while (child) {
observeExistingShadowRoots(child);
child = child.nextElementSibling;
}
})(document);
HTMLElement.prototype.attachShadow = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var shadowRoot = origAttachShadow.apply(this, args);
controller_1.mutationsObserver_.observe(shadowRoot, options_1);
return shadowRoot;
};
}
} else {
document.addEventListener("DOMSubtreeModified", this.refresh);
this.mutationEventsAdded_ = true;
}
this.connected_ = true;
};
/**
* Removes DOM listeners.
*/
ResizeObserverController.prototype.disconnect_ = function () {
// Do nothing if running in a non-browser environment or if listeners
// have been already removed.
if (!isBrowser || !this.connected_) {
return;
}
document.removeEventListener("transitionend", this.onTransitionEnd_);
window.removeEventListener("resize", this.refresh);
if (this.mutationsObserver_) {
this.mutationsObserver_.disconnect();
if (origAttachShadow) {
HTMLElement.prototype.attachShadow = origAttachShadow;
}
}
if (this.mutationEventsAdded_) {
document.removeEventListener("DOMSubtreeModified", this.refresh);
}
this.mutationsObserver_ = null;
this.mutationEventsAdded_ = false;
this.connected_ = false;
};
/**
* "Transitionend" event handler.
*/
ResizeObserverController.prototype.onTransitionEnd_ = function (_a) {
var _b = _a.propertyName,
propertyName = _b === void 0 ? "" : _b;
// Detect whether transition may affect dimensions of an element.
var isReflowProperty = transitionKeys.some(function (key) {
return !!~propertyName.indexOf(key);
});
if (isReflowProperty) {
this.refresh();
}
};
/**
* Returns instance of the ResizeObserverController.
*/
ResizeObserverController.getInstance = function () {
if (!ResizeObserverController.instance_) {
ResizeObserverController.instance_ = new ResizeObserverController();
}
return ResizeObserverController.instance_;
};
/**
* Holds reference to the controller's instance.
*/
ResizeObserverController.instance_ = null;
return ResizeObserverController;
})();
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
function __values(o) {
var m = typeof Symbol === "function" && o[Symbol.iterator],
i = 0;
if (m) return m.call(o);
return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
},
};
}
/**
* Defines non-writable/enumerable properties of the provided target object.
*
* @param target Object for which to define properties.
* @param props Properties to be defined.
* @returns Target object.
*/
var defineConfigurable = function (target, props) {
var e_1, _a;
try {
for (
var _b = __values(Object.keys(props)), _c = _b.next();
!_c.done;
_c = _b.next()
) {
var key = _c.value;
Object.defineProperty(target, key, {
value: props[key],
enumerable: false,
writable: false,
configurable: true,
});
}
} catch (e_1_1) {
e_1 = { error: e_1_1 };
} finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
} finally {
if (e_1) throw e_1.error;
}
}
return target;
};
/**
* Returns the global object associated with provided element.
*
* @param {EventT} target
* @returns {Object}
*/
var getWindowOf = function (target) {
var _a;
// Assume that the element is an instance of Node, which means that it
// has the "ownerDocument" property from which we can retrieve a
// corresponding global object.
// Return the local global object if it's not possible extract one from
// provided element.
return (
((_a =
target === null || target === void 0 ? void 0 : target.ownerDocument) ===
null || _a === void 0
? void 0
: _a.defaultView) || global$1
);
};
// Placeholder of an empty content rectangle.
var emptyRect = createRectInit(0, 0, 0, 0);
/**
* Converts provided string to a number.
*/
function toFloat(value) {
if (typeof value === "number") {
return value;
}
return parseFloat(value) || 0;
}
/**
* Extracts borders size from provided styles.
*/
function getBordersSize(styles) {
var positions = [];
for (var _i = 1; _i < arguments.length; _i++) {
positions[_i - 1] = arguments[_i];
}
return positions.reduce(function (size, position) {
var value = styles["border-" + position + "-width"];
return size + toFloat(value);
}, 0);
}
/**
* Extracts paddings sizes from provided styles.
*/
function getPaddings(styles) {
var st = styles;
return {
top: toFloat(st["padding-top"]),
right: toFloat(st["padding-right"]),
bottom: toFloat(st["padding-bottom"]),
left: toFloat(st["padding-left"]),
};
}
/**
* Calculates content rectangle of provided SVG element.
*
* @param target Element content rectangle of which needs to be calculated.
*/
function getSVGContentRect(target) {
var bbox = target.getBBox();
return createRectInit(0, 0, bbox.width, bbox.height);
}
/**
* Calculates content rectangle of provided HTMLElement.
*
* @param target Element for which to calculate the content rectangle.
*/
function getHTMLElementContentRect(target) {
// Client width & height properties can't be
// used exclusively as they provide rounded values.
var clientWidth = target.clientWidth,
clientHeight = target.clientHeight;
// By this condition we can catch all non-replaced inline, hidden and
// detached elements. Though elements with width & height properties less
// than 0.5 will be discarded as well.
//
// Without it we would need to implement separate methods for each of
// those cases and it's not possible to perform a precise and performance
// effective test for hidden elements. E.g. even jQuery's ':visible' filter
// gives wrong results for elements with width & height less than 0.5.
if (!clientWidth && !clientHeight) {
return emptyRect;
}
var styles = getWindowOf(target).getComputedStyle(target);
var paddings = getPaddings(styles);
var horizPad = paddings.left + paddings.right;
var vertPad = paddings.top + paddings.bottom;
// Computed styles of width & height are being used because they are the
// only dimensions available to JS that contain non-rounded values. It could
// be possible to utilize the getBoundingClientRect if only it's data wasn't
// affected by CSS transformations let alone paddings, borders and scroll
// bars.
var width = toFloat(styles.width),
height = toFloat(styles.height);
// Width & height include paddings and borders when the 'border-box' box
// model is applied (except for IE).
if (styles.boxSizing === "border-box") {
// Following conditions are required to handle Internet Explorer which
// doesn't include paddings and borders to computed CSS dimensions.
//
// We can say that if CSS dimensions + paddings are equal to the "client"
// properties then it's either IE, and thus we don't need to subtract
// anything, or an element merely doesn't have paddings/borders styles.
if (Math.round(width + horizPad) !== clientWidth) {
width -= getBordersSize(styles, "left", "right") + horizPad;
}
if (Math.round(height + vertPad) !== clientHeight) {
height -= getBordersSize(styles, "top", "bottom") + vertPad;
}
}
// Following steps can't be applied to the document's root element as its
// client[Width/Height] properties represent viewport area of the window.
// Besides, it's as well not necessary as the <html> itself neither has
// rendered scroll bars nor it can be clipped.
if (!isDocumentElement(target)) {
// In some browsers (only in Firefox, actually) CSS width & height
// include scroll bars size which can be removed at this step as scroll
// bars are the only difference between rounded dimensions + paddings
// and "client" properties, though that is not always true in Chrome.
var vertScrollbar = Math.round(width + horizPad) - clientWidth;
var horizScrollbar = Math.round(height + vertPad) - clientHeight;
// Chrome has a rather weird rounding of "client" properties.
// E.g. for an element with content width of 314.2px it sometimes gives
// the client width of 315px and for the width of 314.7px it may give
// 314px. And it doesn't happen all the time. So just ignore this delta
// as a non-relevant.
if (Math.abs(vertScrollbar) !== 1) {
width -= vertScrollbar;
}
if (Math.abs(horizScrollbar) !== 1) {
height -= horizScrollbar;
}
}
return createRectInit(paddings.left, paddings.top, width, height);
}
/**
* Checks whether provided element is an instance of the SVGGraphicsElement.
*
* @param target Element to be checked.
*/
var isSVGGraphicsElement = (function () {
// Some browsers, namely IE and Edge, don't have the SVGGraphicsElement
// interface.
if (typeof SVGGraphicsElement !== "undefined") {
return function (target) {
return target instanceof getWindowOf(target).SVGGraphicsElement;
};
}
// If it's so, then check that element is at least an instance of the
// SVGElement and that it has the "getBBox" method.
// eslint-disable-next-line no-extra-parens
return function (target) {
return (
target instanceof getWindowOf(target).SVGElement &&
typeof target.getBBox === "function"
);
};
})();
/**
* Checks whether provided element is a document element (<html>).
*
* @param target Element to be checked.
*/
function isDocumentElement(target) {
return target === getWindowOf(target).document.documentElement;
}
/**
* Calculates an appropriate content rectangle for provided html or svg element.
*
* @param target Element content rectangle of which needs to be calculated.
*/
function getContentRect(target) {
if (!isBrowser) {
return emptyRect;
}
if (isSVGGraphicsElement(target)) {
return getSVGContentRect(target);
}
return getHTMLElementContentRect(target);
}
/**
* Creates rectangle with an interface of the DOMRectReadOnly.
* Spec: https://drafts.fxtf.org/geometry/#domrectreadonly
*
* @param rectInit Object with rectangle's x/y coordinates and
* dimensions.
*/
function createReadOnlyRect(_a) {
var x = _a.x,
y = _a.y,
width = _a.width,
height = _a.height;
// If DOMRectReadOnly is available use it as a prototype for the rectangle.
var Constr =
typeof DOMRectReadOnly !== "undefined" ? DOMRectReadOnly : Object;
var rect = Object.create(Constr.prototype);
// Rectangle's properties are not writable and non-enumerable.
defineConfigurable(rect, {
x: x,
y: y,
width: width,
height: height,
top: y,
right: x + width,
bottom: height + y,
left: x,
});
return rect;
}
/**
* Creates DOMRectInit object based on the provided dimensions and the x/y
* coordinates. Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit
*
* @param x X coordinate.
* @param y Y coordinate.
* @param width Rectangle's width.
* @param height Rectangle's height.
*/
function createRectInit(x, y, width, height) {
return { x: x, y: y, width: width, height: height };
}
/**
* Class that is responsible for computations of the content rectangle of
* provided DOM element and for keeping track of it's changes.
*/
var ResizeObservation = /** @class */ (function () {
/**
* Creates an instance of ResizeObservation.
*
* @param target Element to be observed.
*/
function ResizeObservation(target) {
/**
* Broadcasted width of content rectangle.
*/
this.broadcastWidth = 0;
/**
* Broadcasted height of content rectangle.
*/
this.broadcastHeight = 0;
/**
* Reference to the last observed content rectangle.
*/
this.contentRect_ = createRectInit(0, 0, 0, 0);
this.target = target;
}
/**
* Updates content rectangle and tells whether it's width or height properties
* have changed since the last broadcast.
*/
ResizeObservation.prototype.isActive = function () {
var rect = getContentRect(this.target);
this.contentRect_ = rect;
return (
rect.width !== this.broadcastWidth || rect.height !== this.broadcastHeight
);
};
/**
* Updates 'broadcastWidth' and 'broadcastHeight' properties with a data
* from the corresponding properties of the last observed content rectangle.
*
* @returns Last observed content rectangle.
*/
ResizeObservation.prototype.broadcastRect = function () {
var rect = this.contentRect_;
this.broadcastWidth = rect.width;
this.broadcastHeight = rect.height;
return rect;
};
return ResizeObservation;
})();
var ResizeObserverEntry = /** @class */ (function () {
/**
* Creates an instance of ResizeObserverEntry.
*
* @param target Element that is being observed.
* @param rectInit Data of the element's content rectangle.
*/
function ResizeObserverEntry(target, rectInit) {
var contentRect = createReadOnlyRect(rectInit);
// According to the specification following properties are not writable
// and are also not enumerable in the native implementation.
//
// Property accessors are not being used as they'd require to define a
// private WeakMap storage which may cause memory leaks in browsers that
// don't support this type of collections.
defineConfigurable(this, { target: target, contentRect: contentRect });
}
return ResizeObserverEntry;
})();
/**
* A collection of shims that provide minimal functionality of the ES6
* collections.
*
* These implementations are not meant to be used outside of the ResizeObserver
* modules as they cover only a limited range of use cases.
*/
/* eslint-disable require-jsdoc, valid-jsdoc */
var MapShim = (function () {
if (typeof Map !== "undefined") {
return Map;
}
/**
* Returns index in provided array that matches the specified key.
*/
function getIndex(arr, key) {
var result = -1;
arr.some(function (entry, index) {
if (entry[0] === key) {
result = index;
return true;
}
return false;
});
return result;
}
return /** @class */ (function () {
function class_1() {
this.__entries__ = [];
}
Object.defineProperty(class_1.prototype, "size", {
get: function () {
return this.__entries__.length;
},
enumerable: true,
configurable: true,
});
class_1.prototype.get = function (key) {
var _a;
var index = getIndex(this.__entries__, key);
return (_a = this.__entries__[index]) === null || _a === void 0
? void 0
: _a[1];
};
class_1.prototype.set = function (key, value) {
var index = getIndex(this.__entries__, key);
if (~index) {
this.__entries__[index][1] = value;
} else {
this.__entries__.push([key, value]);
}
};
class_1.prototype.delete = function (key) {
var entries = this.__entries__;
var index = getIndex(entries, key);
if (~index) {
entries.splice(index, 1);
}
};
class_1.prototype.has = function (key) {
return !!~getIndex(this.__entries__, key);
};
class_1.prototype.clear = function () {
this.__entries__.splice(0);
};
class_1.prototype.forEach = function (callback, ctx) {
var e_1, _a;
if (ctx === void 0) {
ctx = null;
}
try {
for (
var _b = __values(this.__entries__), _c = _b.next();
!_c.done;
_c = _b.next()
) {
var entry = _c.value;
callback.call(ctx, entry[1], entry[0]);
}
} catch (e_1_1) {
e_1 = { error: e_1_1 };
} finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
} finally {
if (e_1) throw e_1.error;
}
}
};
return class_1;
})();
})();
var ResizeObserverSPI = /** @class */ (function () {
/**
* Creates a new instance of ResizeObserver.
*
* @param callback Callback function that is invoked when one of the observed
* elements changes it's content dimensions.
* @param controller Controller instance which is responsible for the updates
* of observer.
* @param callbackCtx Reference to the public ResizeObserver instance which
* will be passed to callback function.
*/
function ResizeObserverSPI(callback, controller, callbackCtx) {
/**
* Collection of resize observations that have detected changes in dimensions
* of elements.
*/
this.activeObservations_ = [];
/**
* Registry of the ResizeObservation instances.
*/
this.observations_ = new MapShim();
if (typeof callback !== "function") {
throw new TypeError(
"The callback provided as parameter 1 is not a function."
);
}
this.callback_ = callback;
this.controller_ = controller;
this.callbackCtx_ = callbackCtx;
}
/**
* Starts observing provided element.
*
* @param target Element to be observed.
*/
ResizeObserverSPI.prototype.observe = function (target) {
if (target === undefined) {
throw new TypeError("1 argument required, but only 0 present.");
}
// Do nothing if current environment doesn't have the Element interface.
if (typeof Element === "undefined" || !(Element instanceof Object)) {
return;
}
if (!(target instanceof getWindowOf(target).Element)) {
throw new TypeError('parameter 1 is not of type "Element".');
}
var observations = this.observations_;
// Do nothing if element is already being observed.
if (observations.has(target)) {
return;
}
observations.set(target, new ResizeObservation(target));
this.controller_.addObserver(this);
// Force the update of observations.
this.controller_.refresh();
};
/**
* Stops observing provided element.
*
* @param target Element to stop observing.
*/
ResizeObserverSPI.prototype.unobserve = function (target) {
if (target === undefined) {
throw new TypeError("1 argument required, but only 0 present.");
}
// Do nothing if current environment doesn't have the Element interface.
if (typeof Element === "undefined" || !(Element instanceof Object)) {
return;
}
if (!(target instanceof getWindowOf(target).Element)) {
throw new TypeError('parameter 1 is not of type "Element".');
}
var observations = this.observations_;
// Do nothing if element is not being observed.
if (!observations.has(target)) {
return;
}
observations.delete(target);
if (!observations.size) {
this.controller_.removeObserver(this);
}
};
/**
* Stops observing all elements.
*/
ResizeObserverSPI.prototype.disconnect = function () {
this.clearActive();
this.observations_.clear();
this.controller_.removeObserver(this);
};
/**
* Collects observation instances the associated element of which has changed
* it's content rectangle.
*/
ResizeObserverSPI.prototype.gatherActive = function () {
var _this = this;
this.clearActive();
this.observations_.forEach(function (observation) {
if (observation.isActive()) {
_this.activeObservations_.push(observation);
}
});
};
/**
* Invokes initial callback function with a list of ResizeObserverEntry
* instances collected from active resize observations.
*/
ResizeObserverSPI.prototype.broadcastActive = function () {
// Do nothing if observer doesn't have active observations.
if (!this.hasActive()) {
return;
}
var ctx = this.callbackCtx_;
// Create ResizeObserverEntry instance for every active observation.
var entries = this.activeObservations_.map(function (observation) {
return new ResizeObserverEntry(
observation.target,
observation.broadcastRect()
);
});
this.callback_.call(ctx, entries, ctx);
this.clearActive();
};
/**
* Clears the collection of active observations.
*/
ResizeObserverSPI.prototype.clearActive = function () {
this.activeObservations_.splice(0);
};
/**
* Tells whether observer has active observations.
*/
ResizeObserverSPI.prototype.hasActive = function () {
return this.activeObservations_.length > 0;
};
return ResizeObserverSPI;
})();
/**
* ResizeObserver API. Encapsulates the ResizeObserver SPI implementation
* exposing only those methods and properties that are defined in the spec.
*/
var ResizeObserver = /** @class */ (function () {
/**
* Creates a new instance of ResizeObserver.
*
* @param callback Callback that is invoked when dimensions of the observed
* elements change.
*/
function ResizeObserver(callback) {
if (!(this instanceof ResizeObserver)) {
throw new TypeError("Cannot call a class as a function.");
}
if (!arguments.length) {
throw new TypeError("1 argument required, but only 0 present.");
}
var controller = ResizeObserverController.getInstance();
this.observer_ = new ResizeObserverSPI(callback, controller, this);
}
ResizeObserver.prototype.observe = function (target) {
this.observer_.observe(target);
};
ResizeObserver.prototype.unobserve = function (target) {
this.observer_.unobserve(target);
};
ResizeObserver.prototype.disconnect = function () {
this.observer_.disconnect();
};
return ResizeObserver;
})();
var index = (function () {
// Export existing implementation if available.
if (typeof global$1.ResizeObserver !== "undefined") {
return global$1.ResizeObserver;
}
return ResizeObserver;
})();
export default index;

View File

@ -0,0 +1,8 @@
export {
VirtualScroller,
RangeChangeEvent,
scrollerRef,
} from "./lib/VirtualScroller.js";
export { Layout1d, layout1d } from "./lib/layouts/Layout1d.js";
export { Layout1dGrid } from "./lib/layouts/Layout1dGrid.js";
//# sourceMappingURL=uni-virtualizer.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"uni-virtualizer.d.ts","sourceRoot":"","sources":["../../src/lib/uni-virtualizer/uni-virtualizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC"}

View File

@ -0,0 +1,4 @@
export { VirtualScroller, scrollerRef } from "./lib/VirtualScroller.js";
export { Layout1d, layout1d } from "./lib/layouts/Layout1d.js";
export { Layout1dGrid } from "./lib/layouts/Layout1dGrid.js";
//# sourceMappingURL=uni-virtualizer.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"uni-virtualizer.js","sourceRoot":"","sources":["../../src/lib/uni-virtualizer/uni-virtualizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAoB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC","sourcesContent":["export { VirtualScroller, RangeChangeEvent, scrollerRef } from './lib/VirtualScroller.js';\n\nexport { Layout1d, layout1d } from './lib/layouts/Layout1d.js';\nexport { Layout1dGrid } from './lib/layouts/Layout1dGrid.js';\n"]}

View File

@ -0,0 +1,10 @@
export { scroll } from "./lib/scroll.js";
export {
Layout1d,
layout1d,
Layout1dGrid,
RangeChangeEvent,
scrollerRef,
} from "./lib/uni-virtualizer/uni-virtualizer.js";
export { LitVirtualizer } from "./lib/lit-virtualizer.js";
//# sourceMappingURL=lit-virtualizer.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"lit-virtualizer.d.ts","sourceRoot":"","sources":["src/lit-virtualizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAC3H,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC"}

View File

@ -0,0 +1,10 @@
// export { repeat } from './lib/repeat.js';
export { scroll } from "./lib/scroll.js";
export {
Layout1d,
layout1d,
Layout1dGrid,
scrollerRef,
} from "./lib/uni-virtualizer/uni-virtualizer.js";
export { LitVirtualizer } from "./lib/lit-virtualizer.js";
//# sourceMappingURL=lit-virtualizer.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"lit-virtualizer.js","sourceRoot":"","sources":["src/lit-virtualizer.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAoB,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAC3H,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["// export { repeat } from './lib/repeat.js';\nexport { scroll } from './lib/scroll.js';\nexport { Layout1d, layout1d, Layout1dGrid, RangeChangeEvent, scrollerRef } from './lib/uni-virtualizer/uni-virtualizer.js';\nexport { LitVirtualizer } from './lib/lit-virtualizer.js';\n"]}

View File

@ -0,0 +1,55 @@
{
"name": "@lit-labs/virtualizer",
"version": "0.7.0",
"description": "Virtual scrolling for Lit",
"license": "BSD-3-Clause",
"main": "lit-virtualizer.js",
"files": [
"/lit-virtualizer.js",
"/lit-virtualizer.d.ts",
"/lit-virtualizer.d.ts.map",
"/lib/"
],
"module": "lit-virtualizer.js",
"scripts": {
"bench": "tach --root=../.. --browser=chrome-headless test/benchmarks/${BENCH:-basic}.html --measure=fcp",
"bench:debug": "tach --root=../.. test/benchmarks/${BENCH:-basic}.html --measure=fcp",
"bench:scroll": "tach --force-clean-npm-install --config=test/benchmarks/scrollingBenchmarks.json",
"build": "tsc & copyfiles -u 2 src/lib/uni-virtualizer/lib/polyfills/resize-observer-polyfill/ResizeObserver.js lib",
"checksize": "rollup -c; rm lit-virtualizer.bundled.js lit-virtualizer-with-polyfills.bundled.js",
"clean": "rm -r node_modules/",
"lint": "tslint --project ./",
"prepare": "npm run build",
"test": "karma start karma.conf.js",
"test:debug": "karma start karma.conf.js --single-run=false --debug",
"test:screenshot": "cd test/screenshot && rollup -c && mocha screenshot.js",
"generate-screenshots": "cd test/screenshot && rollup -c && mocha screenshot.js --generate-screenshots"
},
"author": "Google LLC",
"devDependencies": {
"chai": "^4.2.0",
"copyfiles": "^2.4.1",
"http-server": "^0.11.1",
"karma": "^4.1.0",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^2.2.0",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.5",
"karma-rollup-preprocessor": "^7.0.0",
"mocha": "^6.1.4",
"pixelmatch": "^4.0.2",
"pngjs": "^3.4.0",
"puppeteer": "^1.17.0",
"rollup": "^1.11.2",
"rollup-plugin-filesize": "^6.1.1",
"rollup-plugin-node-resolve": "^4.2.3",
"rollup-plugin-terser": "^5.0.0",
"tachometer": "^0.4.7",
"typescript": "^4.1.3"
},
"dependencies": {
"event-target-shim": "^5.0.1",
"lit": "^2.0.0-rc.1",
"tslib": "^1.10.0"
}
}

View File

@ -931,6 +931,9 @@
"config_entry_system_options": {
"enable_new_entities_description": "비활성화한 경우 새로 발견된 {integration} 구성요소는 Home Assistant에 자동으로 추가되지 않습니다.",
"enable_new_entities_label": "새로 추가된 구성요소를 활성화합니다.",
"enable_polling_description": "Home Assistant가 업데이트를 위해 {integration} 구성요소를 자동으로 폴링해야하는 경우.",
"enable_polling_label": "업데이트를 위해 폴링을 활성화합니다.",
"restart_home_assistant": "변경 사항을 적용하려면 Home Assistant를 다시 시작해야합니다.",
"title": "{integration} 시스템 옵션",
"update": "업데이트"
},
@ -2217,6 +2220,7 @@
},
"disabled_cause": "{cause}에 의해 비활성화되었습니다"
},
"disabled_polling": "데이터 업데이트를 위한 자동 폴링 비활성화",
"documentation": "관련 문서",
"enable_restart_confirm": "이 통합 구성요소를 활성화하려면 Home Assistant를 다시 시작해주세요",
"entities": "{count} {count, plural,\none{개의 구성요소}\nother{개의 구성요소}\n}",