Skip to content

Commit

Permalink
Merge pull request #19155 from emberjs/refactor-input-component
Browse files Browse the repository at this point in the history
Refactor internal `<Input>` component implementation
  • Loading branch information
chancancode committed Sep 27, 2020
2 parents 931ac4e + 4789dc5 commit 74a99a2
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 170 deletions.
Expand Up @@ -218,7 +218,14 @@ export default class CurlyComponentManager
assign(named, args.named.capture());

for (let i = 0; i < count; i++) {
const name = positionalParams[i];
// As of TS 3.7, tsc is giving us the following error on this line without the type annotation
//
// TS7022: 'name' implicitly has type 'any' because it does not have a type annotation and is
// referenced directly or indirectly in its own initializer.
//
// This is almost certainly a TypeScript bug, feel free to try and remove the annotation after
// upgrading if it is not needed anymore.
const name: string = positionalParams[i];

assert(
`You cannot specify both a positional param (at position ${i}) and the hash argument \`${name}\`.`,
Expand Down
134 changes: 0 additions & 134 deletions packages/@ember/-internals/glimmer/lib/component-managers/input.ts

This file was deleted.

127 changes: 116 additions & 11 deletions packages/@ember/-internals/glimmer/lib/component-managers/internal.ts
@@ -1,35 +1,140 @@
import { Factory, Owner } from '@ember/-internals/owner';
import { ComponentDefinition, WithStaticLayout } from '@glimmer/interfaces';
import { ENV } from '@ember/-internals/environment';
import { Owner } from '@ember/-internals/owner';
import { assert } from '@ember/debug';
import {
Bounds,
ComponentCapabilities,
ComponentDefinition,
Destroyable,
DynamicScope,
VMArguments,
WithStaticLayout,
} from '@glimmer/interfaces';
import { createConstRef, isConstRef, Reference, valueForRef } from '@glimmer/reference';
import { registerDestructor } from '@glimmer/runtime';
import { unwrapTemplate } from '@glimmer/util';
import InternalComponent from '../components/internal';
import { EmberVMEnvironment } from '../environment';
import RuntimeResolver from '../resolver';
import { OwnedTemplate } from '../template';
import AbstractComponentManager from './abstract';

const CAPABILITIES: ComponentCapabilities = {
dynamicLayout: false,
dynamicTag: false,
prepareArgs: false,
createArgs: true,
attributeHook: false,
elementHook: false,
createCaller: true,
dynamicScope: false,
updateHook: true,
createInstance: true,
wrapped: false,
willDestroy: false,
};

export interface InternalDefinitionState {
ComponentClass: Factory<any, any>;
ComponentClass: typeof InternalComponent;
layout: OwnedTemplate;
}

export class InternalComponentDefinition<T>
implements ComponentDefinition<InternalDefinitionState, T, InternalManager<T>> {
export interface InternalComponentState {
env: EmberVMEnvironment;
instance: Destroyable;
}

export class InternalComponentDefinition
implements ComponentDefinition<InternalDefinitionState, InternalComponentState, InternalManager> {
public state: InternalDefinitionState;

constructor(
public manager: InternalManager<T>,
ComponentClass: Factory<any, any>,
public manager: InternalManager,
ComponentClass: typeof InternalComponent,
layout: OwnedTemplate
) {
this.state = { ComponentClass, layout };
}
}

export default abstract class InternalManager<T>
extends AbstractComponentManager<T, InternalDefinitionState>
implements WithStaticLayout<T, InternalDefinitionState, RuntimeResolver> {
constructor(protected owner: Owner) {
export default class InternalManager
extends AbstractComponentManager<InternalComponentState, InternalDefinitionState>
implements WithStaticLayout<InternalComponentState, InternalDefinitionState, RuntimeResolver> {
static for(name: string): (owner: Owner) => InternalManager {
return (owner: Owner) => new InternalManager(owner, name);
}

constructor(private owner: Owner, private name: string) {
super();
}

getCapabilities(): ComponentCapabilities {
return CAPABILITIES;
}

create(
env: EmberVMEnvironment,
{ ComponentClass, layout }: InternalDefinitionState,
args: VMArguments,
_dynamicScope: DynamicScope,
caller: Reference
): InternalComponentState {
assert('caller must be const', isConstRef(caller));

assert(
`The ${this.name} component does not take any positional arguments`,
args.positional.length === 0
);

let instance = new ComponentClass(this.owner, args.named.capture(), valueForRef(caller));

let state = { env, instance };

if (ENV._DEBUG_RENDER_TREE) {
env.extra.debugRenderTree.create(state, {
type: 'component',
name: this.getDebugName(),
args: args.capture(),
instance,
template: layout,
});

registerDestructor(instance, () => env.extra.debugRenderTree.willDestroy(state));
}

return state;
}

getDebugName(): string {
return this.name;
}

getSelf({ instance }: InternalComponentState): Reference {
return createConstRef(instance, 'this');
}

didRenderLayout(state: InternalComponentState, bounds: Bounds): void {
if (ENV._DEBUG_RENDER_TREE) {
state.env.extra.debugRenderTree.didRender(state, bounds);
}
}

update(state: InternalComponentState): void {
if (ENV._DEBUG_RENDER_TREE) {
state.env.extra.debugRenderTree.update(state);
}
}

didUpdateLayout(state: InternalComponentState, bounds: Bounds): void {
if (ENV._DEBUG_RENDER_TREE) {
state.env.extra.debugRenderTree.didRender(state, bounds);
}
}

getDestroyable(state: InternalComponentState): Destroyable {
return state.instance;
}

getStaticLayout({ layout: template }: InternalDefinitionState) {
return unwrapTemplate(template).asLayout();
}
Expand Down
19 changes: 8 additions & 11 deletions packages/@ember/-internals/glimmer/lib/components/input.ts
@@ -1,10 +1,9 @@
/**
@module @ember/component
*/
import { computed } from '@ember/-internals/metal';
import { Object as EmberObject } from '@ember/-internals/runtime';
import { InputComponentManagerFactory } from '../component-managers/input';
import InternalManager from '../component-managers/internal';
import { setManager } from '../utils/managers';
import InternalComponent from './internal';

/**
See [Ember.Templates.components.Input](/ember/release/classes/Ember.Templates.components/methods/Input?anchor=Input).
Expand Down Expand Up @@ -112,21 +111,19 @@ import { setManager } from '../utils/managers';
@param {Hash} options
@public
*/
const Input = EmberObject.extend({
isCheckbox: computed('type', function(this: { type?: unknown }) {
return this.type === 'checkbox';
}),
});
export default class Input extends InternalComponent {
get isCheckbox(): boolean {
return this.arg('type') === 'checkbox';
}
}

setManager(
{
factory: InputComponentManagerFactory,
factory: InternalManager.for('input'),
internal: true,
type: 'component',
},
Input
);

Input.toString = () => '@ember/component/input';

export default Input;
40 changes: 40 additions & 0 deletions packages/@ember/-internals/glimmer/lib/components/internal.ts
@@ -0,0 +1,40 @@
import { Owner, setOwner } from '@ember/-internals/owner';
import { guidFor } from '@ember/-internals/utils';
import { assert } from '@ember/debug';
import { Reference, valueForRef } from '@glimmer/reference';

export default class InternalComponent {
// Factory interface
static create(): never {
throw assert('Use constructor instead of create');
}

static get class(): typeof InternalComponent {
return this;
}

static get fullName(): string {
return this.name;
}

static get normalizedName(): string {
return this.name;
}

constructor(
protected owner: Owner,
public args: Record<string, Reference | undefined>,
public caller: unknown
) {
setOwner(this, owner);
}

protected arg(key: string): unknown {
let ref = this.args[key];
return ref ? valueForRef(ref) : undefined;
}

toString(): string {
return `<${this.constructor.toString()}:${guidFor(this)}>`;
}
}

0 comments on commit 74a99a2

Please sign in to comment.