diff --git a/src/features/animation.ts b/src/features/animation.ts index 0c38ec1116..0ded7b339f 100644 --- a/src/features/animation.ts +++ b/src/features/animation.ts @@ -23,123 +23,131 @@ const MILLISECONDS_PER_SECOND = 1000.0 const $changeAnimation = Symbol('changeAnimation'); const $paused = Symbol('paused'); -export const AnimationMixin = - >(ModelViewerElement: T) => { - class AnimationModelViewerElement extends ModelViewerElement { - @property({type: Boolean}) autoplay: boolean = false; - @property({type: String, attribute: 'animation-name'}) - animationName: string|void = undefined; - @property({type: Number, attribute: 'animation-crossfade-duration'}) - animationCrossfadeDuration: number = 300; - - protected[$paused]: boolean = true; - - /** - * Returns an array - */ - get availableAnimations(): Array { - if (this.loaded) { - return (this as any)[$scene].model.animationNames; - } - - return []; - } +export interface AnimationInterface { + autoplay: boolean; + animationName: string|void; + animationCrossfadeDuration: number; + readonly availableAnimations: Array; + readonly paused: boolean; + currentTime: number; + pause(): void; + play(): void; +} + +export const AnimationMixin = >( + ModelViewerElement: T): Constructor&T => { + class AnimationModelViewerElement extends ModelViewerElement { + @property({type: Boolean}) autoplay: boolean = false; + @property({type: String, attribute: 'animation-name'}) + animationName: string|void = undefined; + @property({type: Number, attribute: 'animation-crossfade-duration'}) + animationCrossfadeDuration: number = 300; + + protected[$paused]: boolean = true; + + /** + * Returns an array + */ + get availableAnimations(): Array { + if (this.loaded) { + return (this as any)[$scene].model.animationNames; + } - get paused(): boolean { - return this[$paused]; - } + return []; + } - get currentTime(): number { - return (this as any)[$scene].model.animationTime; - } + get paused(): boolean { + return this[$paused]; + } - set currentTime(value: number) { - (this as any)[$scene].model.animationTime = value; - } + get currentTime(): number { + return (this as any)[$scene].model.animationTime; + } - pause() { - if (this[$paused]) { - return; - } + set currentTime(value: number) { + (this as any)[$scene].model.animationTime = value; + } - this[$paused] = true; - this.dispatchEvent(new CustomEvent('pause')); - } + pause() { + if (this[$paused]) { + return; + } - play() { - if (this[$paused] && this.availableAnimations.length > 0) { - this[$paused] = false; + this[$paused] = true; + this.dispatchEvent(new CustomEvent('pause')); + } - if (!(this as any)[$scene].model.hasActiveAnimation) { - this[$changeAnimation](); - } + play() { + if (this[$paused] && this.availableAnimations.length > 0) { + this[$paused] = false; - this.dispatchEvent(new CustomEvent('play')); - } + if (!(this as any)[$scene].model.hasActiveAnimation) { + this[$changeAnimation](); } - [$onModelLoad]() { - this[$paused] = true; - - if (this.autoplay) { - this[$changeAnimation](); - this.play(); - } - } - - [$tick](_time: number, delta: number) { - super[$tick](_time, delta); - - if (this[$paused]) { - return; - } - - const {model} = (this as any)[$scene]; - model.updateAnimation(delta / MILLISECONDS_PER_SECOND); - - this[$needsRender](); - } + this.dispatchEvent(new CustomEvent('play')); + } + } - updated(changedProperties: Map) { - super.updated(changedProperties); + [$onModelLoad]() { + this[$paused] = true; - if (changedProperties.has('autoplay') && this.autoplay) { - this.play(); - } + if (this.autoplay) { + this[$changeAnimation](); + this.play(); + } + } - if (changedProperties.has('animationName')) { - this[$changeAnimation](); - } - } + [$tick](_time: number, delta: number) { + super[$tick](_time, delta); - async[$updateSource]() { - // If we are loading a new model, we need to stop the animation of - // the current one (if any is playing). Otherwise, we might lose - // the reference to the scene root and running actions start to - // throw exceptions and/or behave in unexpected ways: - (this as any)[$scene].model.stopAnimation(); + if (this[$paused]) { + return; + } - return super[$updateSource](); - } + const {model} = (this as any)[$scene]; + model.updateAnimation(delta / MILLISECONDS_PER_SECOND); - [$changeAnimation]() { - const {model} = (this as any)[$scene]; + this[$needsRender](); + } - model.playAnimation( - this.animationName, - this.animationCrossfadeDuration / MILLISECONDS_PER_SECOND); + updated(changedProperties: Map) { + super.updated(changedProperties); - // If we are currently paused, we need to force a render so that - // the model updates to the first frame of the new animation - if (this[$paused]) { - model.updateAnimation(0); - this[$needsRender](); - } - } + if (changedProperties.has('autoplay') && this.autoplay) { + this.play(); } - return AnimationModelViewerElement; - }; + if (changedProperties.has('animationName')) { + this[$changeAnimation](); + } + } + + async[$updateSource]() { + // If we are loading a new model, we need to stop the animation of + // the current one (if any is playing). Otherwise, we might lose + // the reference to the scene root and running actions start to + // throw exceptions and/or behave in unexpected ways: + (this as any)[$scene].model.stopAnimation(); + + return super[$updateSource](); + } + + [$changeAnimation]() { + const {model} = (this as any)[$scene]; + + model.playAnimation( + this.animationName, + this.animationCrossfadeDuration / MILLISECONDS_PER_SECOND); + + // If we are currently paused, we need to force a render so that + // the model updates to the first frame of the new animation + if (this[$paused]) { + model.updateAnimation(0); + this[$needsRender](); + } + } + } -export type AnimationInterface = - InstanceType>; \ No newline at end of file + return AnimationModelViewerElement; +}; diff --git a/src/features/ar.ts b/src/features/ar.ts index 1ece3930d9..752e3a6a50 100644 --- a/src/features/ar.ts +++ b/src/features/ar.ts @@ -118,8 +118,17 @@ const $onExitFullscreenButtonClick = Symbol('onExitFullscreenButtonClick'); const $fullscreenchangeHandler = Symbol('fullscreenHandler'); const $onFullscreenchange = Symbol('onFullscreen'); +export interface ARInterface { + ar: boolean; + unstableWebxr: boolean; + iosSrc: string|null; + quickLookBrowsers: string; + readonly canActivateAR: boolean; + activateAR(): Promise; +} + export const ARMixin = >( - ModelViewerElement: T) => { + ModelViewerElement: T): Constructor&T => { class ARModelViewerElement extends ModelViewerElement { @property({type: Boolean, attribute: 'ar'}) ar: boolean = false; @@ -348,5 +357,3 @@ configuration or device capabilities'); return ARModelViewerElement; }; - -export type ARInterface = InstanceType>; \ No newline at end of file diff --git a/src/features/controls.ts b/src/features/controls.ts index 745a89c3bc..62e87c20c8 100644 --- a/src/features/controls.ts +++ b/src/features/controls.ts @@ -90,8 +90,22 @@ const $userPromptedOnce = Symbol('userPromptedOnce'); const $lastSpherical = Symbol('lastSpherical'); const $jumpCamera = Symbol('jumpCamera'); +export interface ControlsInterface { + cameraControls: boolean; + cameraOrbit: string; + cameraTarget: string; + fieldOfView: string; + interactionPrompt: InteractionPromptStrategy; + interactionPolicy: InteractionPolicy; + interactionPromptThreshold: number; + getCameraOrbit(): SphericalPosition; + getCameraTarget(): Vector3; + getFieldOfView(): number; + jumpCameraToGoal(): void; +} + export const ControlsMixin = >( - ModelViewerElement: T) => { + ModelViewerElement: T): Constructor&T => { class ControlsModelViewerElement extends ModelViewerElement { @property({type: Boolean, attribute: 'camera-controls'}) cameraControls: boolean = false; @@ -455,5 +469,3 @@ export const ControlsMixin = >( return ControlsModelViewerElement; }; - -export type ControlsInterface = InstanceType>; \ No newline at end of file diff --git a/src/features/environment.ts b/src/features/environment.ts index fcf0d1d15e..fad0860b86 100644 --- a/src/features/environment.ts +++ b/src/features/environment.ts @@ -30,8 +30,16 @@ const $updateShadow = Symbol('updateShadow'); const $updateEnvironment = Symbol('updateEnvironment'); const $cancelEnvironmentUpdate = Symbol('cancelEnvironmentUpdate'); +export interface EnvironmentInterface { + environmentImage: string|null; + backgroundImage: string|null; + backgroundColor: string; + shadowIntensity: number; + exposure: number; +} + export const EnvironmentMixin = >( - ModelViewerElement: T) => { + ModelViewerElement: T): Constructor&T => { class EnvironmentModelViewerElement extends ModelViewerElement { @property({ type: String, @@ -184,6 +192,3 @@ export const EnvironmentMixin = >( return EnvironmentModelViewerElement; }; - -export type EnvironmentInterface = - InstanceType>; \ No newline at end of file diff --git a/src/features/loading.ts b/src/features/loading.ts index dd1111295f..6ec6c81889 100644 --- a/src/features/loading.ts +++ b/src/features/loading.ts @@ -76,13 +76,22 @@ const $onClick = Symbol('onClick'); const $onKeydown = Symbol('onKeydown'); const $onProgress = Symbol('onProgress'); +export interface LoadingInterface { + poster: string|null; + reveal: RevealAttributeValue; + preload: boolean; + readonly loaded: boolean; + readonly modelIsVisible: boolean; + dismissPoster(): void; +} + /** * LoadingMixin implements features related to lazy loading, as well as * presentation details related to the pre-load / pre-render presentation of a * */ export const LoadingMixin = >( - ModelViewerElement: T) => { + ModelViewerElement: T): Constructor&T => { class LoadingModelViewerElement extends ModelViewerElement { /** * A URL pointing to the image to use as a poster in scenarios where the @@ -397,5 +406,3 @@ export const LoadingMixin = >( return LoadingModelViewerElement; }; - -export type LoadingInterface = InstanceType>; diff --git a/src/features/magic-leap.ts b/src/features/magic-leap.ts index 87c81cdebe..b89bfe808b 100644 --- a/src/features/magic-leap.ts +++ b/src/features/magic-leap.ts @@ -34,101 +34,101 @@ const DEFAULT_HOLOGRAM_INLINE_SCALE = 0.65; // should work: const DEFAULT_HOLOGRAM_Z_OFFSET = '500px'; +export interface MagicLeapInterface { + magicLeap: boolean; +} + /** * In order to use Magic Leap support, please include prismatic.js in your * page. If you do not include prismatic.js, Magic Leap support will not work. * * @see https://www.npmjs.com/package/@magicleap/prismatic */ -export const MagicLeapMixin = - >(ModelViewerElement: T) => { - class MagicLeapModelViewerElement extends ModelViewerElement { - @property({type: Boolean, attribute: 'magic-leap'}) - magicLeap: boolean = false; - - // NOTE(cdata): Check at construction time because the check is cheap - // and it makes testing easier - private[$isHeliosBrowser]: boolean = self.mlWorld != null; - - private[$mlModel]: HTMLElement|null = null; - - updated(changedProperties: Map) { - super.updated(changedProperties); - - if (!this[$isHeliosBrowser]) { - return; - } - - if (!(changedProperties.has('magicLeap') || - changedProperties.has('src'))) { - return; - } - - const scene = this[$scene]; - - if (this.magicLeap) { - const hasMlModel = !!customElements.get('ml-model'); - - if (!hasMlModel) { - console.warn( - ' is not registered. Is prismatic.js loaded?'); - } - - scene.pause(); - this[$container].setAttribute('style', 'display: none;'); - this[$showMlModel](); - - if (changedProperties.has('src') && this.src && - this.src !== this[$mlModel]!.getAttribute('src')) { - this[$mlModel]!.setAttribute('src', this.src); - } - } else { - this[$hideMlModel](); - scene.resume(); - this[$container].removeAttribute('style'); - } - } +export const MagicLeapMixin = >( + ModelViewerElement: T): Constructor&T => { + class MagicLeapModelViewerElement extends ModelViewerElement { + @property({type: Boolean, attribute: 'magic-leap'}) + magicLeap: boolean = false; + + // NOTE(cdata): Check at construction time because the check is cheap + // and it makes testing easier + private[$isHeliosBrowser]: boolean = self.mlWorld != null; + + private[$mlModel]: HTMLElement|null = null; + + updated(changedProperties: Map) { + super.updated(changedProperties); + + if (!this[$isHeliosBrowser]) { + return; + } + + if (!(changedProperties.has('magicLeap') || + changedProperties.has('src'))) { + return; + } + + const scene = this[$scene]; - private[$showMlModel]() { - if (this[$mlModel] == null) { - this[$mlModel] = document.createElement('ml-model'); - this[$mlModel]!.setAttribute( - 'style', - 'display: block; top: 0; left: 0; width: 100%; height: 100%'); - // @see https://creator.magicleap.com/learn/guides/prismatic-getting-started - this[$mlModel]!.setAttribute( - 'model-scale', - `${DEFAULT_HOLOGRAM_INLINE_SCALE} ${ - DEFAULT_HOLOGRAM_INLINE_SCALE} ${ - DEFAULT_HOLOGRAM_INLINE_SCALE}`); - this[$mlModel]!.setAttribute('scrollable', 'true'); - this[$mlModel]!.setAttribute('z-offset', DEFAULT_HOLOGRAM_Z_OFFSET); - this[$mlModel]!.setAttribute('extractable', 'true'); - this[$mlModel]!.setAttribute('extracted-scale', '1'); - this[$mlModel]!.setAttribute( - 'environment-lighting', 'color-intensity: 2;'); - - if (this.src != null) { - this[$mlModel]!.setAttribute('src', this.src); - } - } - - this.shadowRoot!.appendChild(this[$mlModel]!); + if (this.magicLeap) { + const hasMlModel = !!customElements.get('ml-model'); + + if (!hasMlModel) { + console.warn(' is not registered. Is prismatic.js loaded?'); } - private[$hideMlModel]() { - if (this[$mlModel] == null) { - return; - } + scene.pause(); + this[$container].setAttribute('style', 'display: none;'); + this[$showMlModel](); + + if (changedProperties.has('src') && this.src && + this.src !== this[$mlModel]!.getAttribute('src')) { + this[$mlModel]!.setAttribute('src', this.src); + } + } else { + this[$hideMlModel](); + scene.resume(); + this[$container].removeAttribute('style'); + } + } - if (this[$mlModel]!.parentNode != null) { - this[$mlModel]!.parentNode!.removeChild(this[$mlModel]!); - } + private[$showMlModel]() { + if (this[$mlModel] == null) { + this[$mlModel] = document.createElement('ml-model'); + this[$mlModel]!.setAttribute( + 'style', + 'display: block; top: 0; left: 0; width: 100%; height: 100%'); + // @see https://creator.magicleap.com/learn/guides/prismatic-getting-started + this[$mlModel]!.setAttribute( + 'model-scale', + `${DEFAULT_HOLOGRAM_INLINE_SCALE} ${ + DEFAULT_HOLOGRAM_INLINE_SCALE} ${ + DEFAULT_HOLOGRAM_INLINE_SCALE}`); + this[$mlModel]!.setAttribute('scrollable', 'true'); + this[$mlModel]!.setAttribute('z-offset', DEFAULT_HOLOGRAM_Z_OFFSET); + this[$mlModel]!.setAttribute('extractable', 'true'); + this[$mlModel]!.setAttribute('extracted-scale', '1'); + this[$mlModel]!.setAttribute( + 'environment-lighting', 'color-intensity: 2;'); + + if (this.src != null) { + this[$mlModel]!.setAttribute('src', this.src); } } - return MagicLeapModelViewerElement; + this.shadowRoot!.appendChild(this[$mlModel]!); + } + + private[$hideMlModel]() { + if (this[$mlModel] == null) { + return; + } + + if (this[$mlModel]!.parentNode != null) { + this[$mlModel]!.parentNode!.removeChild(this[$mlModel]!); + } } + } -export type MagicLeapInterface = - InstanceType>; \ No newline at end of file + return MagicLeapModelViewerElement; +} diff --git a/src/features/staging.ts b/src/features/staging.ts index e1f04c4999..6e333bed45 100644 --- a/src/features/staging.ts +++ b/src/features/staging.ts @@ -32,71 +32,72 @@ const $onCameraChange = Symbol('onCameraChange'); export {AUTO_ROTATE_DELAY_AFTER_USER_INTERACTION}; -export const StagingMixin = - >(ModelViewerElement: T) => { - class StagingModelViewerElement extends ModelViewerElement { - @property({type: Boolean, attribute: 'auto-rotate'}) - autoRotate: boolean = false; - - private[$autoRotateTimer]: Timer = - new Timer(AUTO_ROTATE_DELAY_AFTER_USER_INTERACTION); - private[$cameraChangeHandler] = - (event: CustomEvent) => - this[$onCameraChange](event); - - connectedCallback() { - super.connectedCallback(); - this.addEventListener( - 'camera-change', this[$cameraChangeHandler] as EventListener); - this[$autoRotateTimer].stop(); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.removeEventListener( - 'camera-change', this[$cameraChangeHandler] as EventListener); - this[$autoRotateTimer].stop(); - } - - updated(changedProperties: Map) { - super.updated(changedProperties); - - if (changedProperties.has('autoRotate')) { - (this as any)[$scene].pivot.rotation.set(0, 0, 0); - this[$needsRender](); - } - } - - [$tick](time: number, delta: number) { - super[$tick](time, delta); - - if (!this.autoRotate || !this.modelIsVisible) { - return; - } - - this[$autoRotateTimer].tick(delta); - - if (this[$autoRotateTimer].hasStopped) { - (this as any)[$scene].pivot.rotation.y += - ROTATION_SPEED * delta * 0.001; - this[$needsRender](); - } - } - - [$onCameraChange](_event: CustomEvent) { - if (!this.autoRotate) { - return; - } - - this[$autoRotateTimer].reset(); - } - - get turntableRotation(): number { - return (this as any)[$scene].pivot.rotation.y; - } +export interface StagingInterface { + autoRotate: boolean; +} + +export const StagingMixin = >( + ModelViewerElement: T): Constructor&T => { + class StagingModelViewerElement extends ModelViewerElement { + @property({type: Boolean, attribute: 'auto-rotate'}) + autoRotate: boolean = false; + + private[$autoRotateTimer]: Timer = + new Timer(AUTO_ROTATE_DELAY_AFTER_USER_INTERACTION); + private[$cameraChangeHandler] = (event: CustomEvent) => + this[$onCameraChange](event); + + connectedCallback() { + super.connectedCallback(); + this.addEventListener( + 'camera-change', this[$cameraChangeHandler] as EventListener); + this[$autoRotateTimer].stop(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.removeEventListener( + 'camera-change', this[$cameraChangeHandler] as EventListener); + this[$autoRotateTimer].stop(); + } + + updated(changedProperties: Map) { + super.updated(changedProperties); + + if (changedProperties.has('autoRotate')) { + (this as any)[$scene].pivot.rotation.set(0, 0, 0); + this[$needsRender](); } + } - return StagingModelViewerElement; - }; + [$tick](time: number, delta: number) { + super[$tick](time, delta); -export type StagingInterface = InstanceType>; \ No newline at end of file + if (!this.autoRotate || !this.modelIsVisible) { + return; + } + + this[$autoRotateTimer].tick(delta); + + if (this[$autoRotateTimer].hasStopped) { + (this as any)[$scene].pivot.rotation.y += + ROTATION_SPEED * delta * 0.001; + this[$needsRender](); + } + } + + [$onCameraChange](_event: CustomEvent) { + if (!this.autoRotate) { + return; + } + + this[$autoRotateTimer].reset(); + } + + get turntableRotation(): number { + return (this as any)[$scene].pivot.rotation.y; + } + } + + return StagingModelViewerElement; +}; diff --git a/src/test/features/ar-spec.ts b/src/test/features/ar-spec.ts index cc8cc754a6..d9ac98eb5e 100644 --- a/src/test/features/ar-spec.ts +++ b/src/test/features/ar-spec.ts @@ -26,7 +26,7 @@ suite('ModelViewerElementBase with ARMixin', () => { suite('when registered', () => { let nextId = 0; let tagName: string; - let ModelViewerElement: Constructor; + let ModelViewerElement: Constructor; setup(() => { tagName = `model-viewer-ar-${nextId++}`; @@ -49,7 +49,7 @@ suite('ModelViewerElementBase with ARMixin', () => { }); suite('with unstable-webxr', () => { - let element: ARInterface; + let element: ModelViewerElementBase&ARInterface; setup(async () => { element = new ModelViewerElement(); @@ -75,7 +75,7 @@ suite('ModelViewerElementBase with ARMixin', () => { }); suite('ios-src', () => { - let element: ARInterface; + let element: ModelViewerElementBase&ARInterface; setup(async () => { element = new ModelViewerElement(); diff --git a/src/test/features/controls-spec.ts b/src/test/features/controls-spec.ts index a15fcc0bff..9978a0eab5 100644 --- a/src/test/features/controls-spec.ts +++ b/src/test/features/controls-spec.ts @@ -53,7 +53,8 @@ suite('ModelViewerElementBase with ControlsMixin', () => { suite('when registered', () => { let nextId = 0; let tagName: string; - let ModelViewerElement: Constructor; + let ModelViewerElement: + Constructor; setup(() => { tagName = `model-viewer-controls-${nextId++}`; @@ -69,7 +70,7 @@ suite('ModelViewerElementBase with ControlsMixin', () => { BasicSpecTemplate(() => ModelViewerElement, () => tagName); suite('camera-orbit', () => { - let element: ControlsInterface; + let element: ModelViewerElementBase&ControlsInterface; let controls: SmoothControls; setup(async () => { @@ -243,7 +244,7 @@ suite('ModelViewerElementBase with ControlsMixin', () => { }); suite('camera-controls', () => { - let element: ControlsInterface; + let element: ModelViewerElementBase&ControlsInterface; let controls: SmoothControls; setup(async () => { diff --git a/src/test/features/environment-spec.ts b/src/test/features/environment-spec.ts index abcfa12380..9ad3e614b1 100644 --- a/src/test/features/environment-spec.ts +++ b/src/test/features/environment-spec.ts @@ -127,8 +127,9 @@ suite('ModelViewerElementBase with EnvironmentMixin', () => { let nextId = 0; let tagName: string; - let ModelViewerElement: Constructor; - let element: EnvironmentInterface; + let ModelViewerElement: + Constructor; + let element: ModelViewerElementBase&EnvironmentInterface; let scene: ModelScene; setup(() => { diff --git a/src/test/features/loading-spec.ts b/src/test/features/loading-spec.ts index 60478ddba3..178b327b06 100644 --- a/src/test/features/loading-spec.ts +++ b/src/test/features/loading-spec.ts @@ -27,7 +27,8 @@ suite('ModelViewerElementBase with LoadingMixin', () => { suite('when registered', () => { let nextId = 0; let tagName: string; - let ModelViewerElement: Constructor; + let ModelViewerElement: + Constructor; setup(() => { tagName = `model-viewer-loading-${nextId++}`; @@ -45,7 +46,7 @@ suite('ModelViewerElementBase with LoadingMixin', () => { // TODO: Elements must have loaded to hide poster... suite('loading', () => { - let element: LoadingInterface; + let element: ModelViewerElementBase&LoadingInterface; setup(async () => { element = new ModelViewerElement(); diff --git a/src/test/features/magic-leap-spec.ts b/src/test/features/magic-leap-spec.ts index cfa3ad3b83..37fb1803f0 100644 --- a/src/test/features/magic-leap-spec.ts +++ b/src/test/features/magic-leap-spec.ts @@ -25,7 +25,8 @@ suite('ModelViewerElementBase with MagicLeapMixin', () => { suite('when registered', () => { let nextId = 0; let tagName: string; - let ModelViewerElement: Constructor; + let ModelViewerElement: + Constructor; setup(() => { tagName = `model-viewer-magic-leap-${nextId++}`; @@ -42,7 +43,7 @@ suite('ModelViewerElementBase with MagicLeapMixin', () => { suite('magic-leap', () => { suite('in standard browser environments', () => { - let element: MagicLeapInterface; + let element: ModelViewerElementBase&MagicLeapInterface; setup(async () => { element = new ModelViewerElement(); @@ -68,7 +69,7 @@ suite('ModelViewerElementBase with MagicLeapMixin', () => { }); suite('in the Helios browser environment', () => { - let element: MagicLeapInterface; + let element: ModelViewerElementBase&MagicLeapInterface; setup(async () => { self.mlWorld = {}; diff --git a/src/utilities/focus-visible.ts b/src/utilities/focus-visible.ts index 77ae0db9a7..8238139b6f 100644 --- a/src/utilities/focus-visible.ts +++ b/src/utilities/focus-visible.ts @@ -36,7 +36,7 @@ type EndPolyfillCoordinationCallback = () => void; * implementation that coordinates with the :focus-visible polyfill */ export const FocusVisiblePolyfillMixin = - >(SuperClass: T) => { + >(SuperClass: T): T => { const coordinateWithPolyfill = (instance: MixableBaseClass): EndPolyfillCoordinationCallback => { // If there is no shadow root, there is no need to coordinate with diff --git a/tsconfig.json b/tsconfig.json index 60c273cb16..9f0f3ee716 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,9 +12,9 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "outDir": "./lib", - "allowJs": true, "experimentalDecorators": true, - "noErrorTruncation": true + "noErrorTruncation": true, + "declaration": true }, "include": [ "./src/**/*.ts",