Skip to content

Commit

Permalink
[FEATURE ember-glimmer-angle-bracket-built-ins]
Browse files Browse the repository at this point in the history
Refactor #17788 to internally forward all the args (essentially `...@args`)
in the component manager layer instead.
  • Loading branch information
chancancode committed Mar 26, 2019
1 parent e9733ae commit c27336a
Show file tree
Hide file tree
Showing 15 changed files with 301 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { get } from '@ember/-internals/metal';
import { getOwner } from '@ember/-internals/owner';
import { guidFor } from '@ember/-internals/utils';
import { addChildView, OwnedTemplateMeta, setViewElement } from '@ember/-internals/views';
import { assert } from '@ember/debug';
import { assert, debugFreeze } from '@ember/debug';
import { _instrumentStart } from '@ember/instrumentation';
import { assign } from '@ember/polyfills';
import { DEBUG } from '@glimmer/env';
import {
ComponentCapabilities,
Dict,
Option,
ProgramSymbolTable,
Simple,
Expand Down Expand Up @@ -96,6 +97,9 @@ function applyAttributeBindings(
}

const DEFAULT_LAYOUT = P`template:components/-default`;
const EMPTY_POSITIONAL_ARGS: VersionedPathReference[] = [];

debugFreeze(EMPTY_POSITIONAL_ARGS);

export default class CurlyComponentManager
extends AbstractManager<ComponentStateBucket, DefinitionState>
Expand Down Expand Up @@ -157,6 +161,18 @@ export default class CurlyComponentManager
}

prepareArgs(state: DefinitionState, args: Arguments): Option<PreparedArguments> {
if (args.named.has('__ARGS__')) {
let __args__ = args.named.get('__ARGS__').value() as Dict<VersionedPathReference>;

return {
positional: EMPTY_POSITIONAL_ARGS,
named: {
...args.named.capture().map,
...__args__,
},
};
}

const { positionalParams } = state.ComponentClass.class!;

// early exits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { Destroyable } from '@glimmer/util';
import Environment from '../environment';
import RuntimeResolver from '../resolver';
import { OwnedTemplate } from '../template';
import { ManagerArgs, valueForCapturedArgs } from '../utils/managers';
import { RootReference } from '../utils/references';
import AbstractComponentManager from './abstract';

Expand Down Expand Up @@ -65,10 +64,16 @@ export interface Capabilities {
destructor: boolean;
}

// TODO: export ICapturedArgumentsValue from glimmer and replace this
export interface Args {
named: Dict<Opaque>;
positional: Opaque[];
}

export interface ManagerDelegate<ComponentInstance> {
capabilities: Capabilities;
createComponent(factory: Opaque, args: ManagerArgs): ComponentInstance;
updateComponent(instance: ComponentInstance, args: ManagerArgs): void;
createComponent(factory: Opaque, args: Args): ComponentInstance;
updateComponent(instance: ComponentInstance, args: Args): void;
getContext(instance: ComponentInstance): Opaque;
}

Expand Down Expand Up @@ -145,14 +150,16 @@ export default class CustomComponentManager<ComponentInstance>
const { delegate } = definition;
const capturedArgs = args.capture();

let invocationArgs = valueForCapturedArgs(capturedArgs);
const component = delegate.createComponent(definition.ComponentClass.class, invocationArgs);
const component = delegate.createComponent(
definition.ComponentClass.class,
capturedArgs.value()
);

return new CustomComponentState(delegate, component, capturedArgs);
}

update({ delegate, component, args }: CustomComponentState<ComponentInstance>) {
delegate.updateComponent(component, valueForCapturedArgs(args));
delegate.updateComponent(component, args.value());
}

didCreate({ delegate, component }: CustomComponentState<ComponentInstance>) {
Expand Down
93 changes: 93 additions & 0 deletions packages/@ember/-internals/glimmer/lib/component-managers/input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { set } from '@ember/-internals/metal';
import { Owner } from '@ember/-internals/owner';
import { assert, debugFreeze } from '@ember/debug';
import { ComponentCapabilities, Dict } from '@glimmer/interfaces';
import { CONSTANT_TAG, isConst, VersionedPathReference } from '@glimmer/reference';
import { Arguments, DynamicScope, Environment, PreparedArguments } from '@glimmer/runtime';
import { Destroyable } from '@glimmer/util';
import { RootReference } from '../utils/references';
import InternalComponentManager, { InternalDefinitionState } from './internal';

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

export interface InputComponentState {
type: VersionedPathReference;
instance: Destroyable;
}

const EMPTY_POSITIONAL_ARGS: VersionedPathReference[] = [];

debugFreeze(EMPTY_POSITIONAL_ARGS);

export default class InputComponentManager extends InternalComponentManager<InputComponentState> {
getCapabilities(): ComponentCapabilities {
return CAPABILITIES;
}

prepareArgs(_state: InternalDefinitionState, args: Arguments): PreparedArguments {
assert(
'The `<Input />` component does not take any positional arguments',
args.positional.length === 0
);

let __ARGS__: Dict<VersionedPathReference> = args.named.capture().map;

return {
positional: EMPTY_POSITIONAL_ARGS,
named: {
__ARGS__: new RootReference(__ARGS__),
type: args.named.get('type'),
},
};
}

create(
_env: Environment,
{ ComponentClass }: InternalDefinitionState,
args: Arguments,
_dynamicScope: DynamicScope,
caller: VersionedPathReference
): InputComponentState {
assert('caller must be const', isConst(caller));

let type = args.named.get('type');

let instance = ComponentClass.create({
caller: caller.value(),
type: type.value(),
});

return { type, instance };
}

getSelf({ instance }: InputComponentState): VersionedPathReference {
return new RootReference(instance);
}

getTag() {
return CONSTANT_TAG;
}

update({ type, instance }: InputComponentState): void {
set(instance, 'type', type.value());
}

getDestructor({ instance }: InputComponentState): Destroyable {
return instance;
}
}

export const InputComponentManagerFactory = (owner: Owner) => {
return new InputComponentManager(owner);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Factory, Owner } from '@ember/-internals/owner';
import { OwnedTemplateMeta } from '@ember/-internals/views';
import { ComponentDefinition, Invocation, WithStaticLayout } from '@glimmer/runtime';
import RuntimeResolver from '../resolver';
import { OwnedTemplate } from '../template';
import AbstractComponentManager from './abstract';

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

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

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

export default abstract class InternalManager<T>
extends AbstractComponentManager<T, InternalDefinitionState>
implements WithStaticLayout<T, InternalDefinitionState, OwnedTemplateMeta, RuntimeResolver> {
constructor(protected owner: Owner) {
super();
}

getLayout({ layout: _layout }: InternalDefinitionState): Invocation {
let layout = _layout.asLayout();

return {
handle: layout.compile(),
symbolTable: layout.symbolTable,
};
}
}
2 changes: 0 additions & 2 deletions packages/@ember/-internals/glimmer/lib/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const ROOT_REF = symbol('ROOT_REF');
export const IS_DISPATCHING_ATTRS = symbol('IS_DISPATCHING_ATTRS');
export const HAS_BLOCK = symbol('HAS_BLOCK');
export const BOUNDS = symbol('BOUNDS');
export const DISABLE_TAGLESS_EVENT_CHECK = symbol('DISABLE_TAGLESS_EVENT_CHECK');

/**
@module @ember/component
Expand Down Expand Up @@ -651,7 +650,6 @@ const Component = CoreView.extend(
assert(
// tslint:disable-next-line:max-line-length
`You can not define a function that handles DOM events in the \`${this}\` tagless component since it doesn't have any DOM element.`,
this[DISABLE_TAGLESS_EVENT_CHECK] ||
this.tagName !== '' ||
!this.renderer._destinedForDOM ||
!(() => {
Expand Down
27 changes: 19 additions & 8 deletions packages/@ember/-internals/glimmer/lib/components/checkbox.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { get, set } from '@ember/-internals/metal';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import EmberComponent from '../component';
import layout from '../templates/empty';

Expand Down Expand Up @@ -59,16 +61,25 @@ const Checkbox = EmberComponent.extend({
change() {
set(this, 'checked', this.element.checked);
},
});

__sourceInput: null,
if (DEBUG) {
const UNSET = {};

init() {
if (this.__sourceInput) {
this.__sourceInput.__injectEvents(this);
}
this._super(...arguments);
},
});
Checkbox.reopen({
value: UNSET,

didReceiveAttrs() {
this._super();

assert(
"`<Input @type='checkbox' @value={{...}} />` is not supported; " +
"please use `<Input @type='checkbox' @checked={{...}} />` instead.",
!(this.type === 'checkbox' && this.value !== UNSET)
);
},
});
}

Checkbox.toString = () => '@ember/component/checkbox';

Expand Down
54 changes: 12 additions & 42 deletions packages/@ember/-internals/glimmer/lib/components/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
@module @ember/component
*/
import { computed } from '@ember/-internals/metal';
import { getOwner } from '@ember/-internals/owner';
import { Object as EmberObject } from '@ember/-internals/runtime';
import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import { Dict } from '@glimmer/interfaces';
import Component, { DISABLE_TAGLESS_EVENT_CHECK } from '../component';
import { InputComponentManagerFactory } from '../component-managers/input';
import { setManager } from '../utils/managers';

let Input: any;

Expand Down Expand Up @@ -147,50 +145,22 @@ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
@param {Hash} options
@public
*/
Input = Component.extend({
tagName: '',

init() {
if (DEBUG) {
this[DISABLE_TAGLESS_EVENT_CHECK] = true;
}
this._super(...arguments);
},

Input = EmberObject.extend({
isCheckbox: computed('type', function(this: { type?: unknown }) {
return this.type === 'checkbox';
}),
});

__injectEvents(target: any) {
let eventDispatcher = getOwner(this).lookup<any | undefined>('event_dispatcher:main');
let events: Dict<string> = (eventDispatcher && eventDispatcher._finalEvents) || {};
Object.values(events).forEach(key => {
if (this[key]) {
target[key] = this[key];
}
});
setManager(
{
factory: InputComponentManagerFactory,
internal: true,
type: 'component',
},
});
Input
);

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

if (DEBUG) {
const UNSET = {};

Input.reopen({
value: UNSET,

didReceiveAttrs() {
this._super();

assert(
"`<Input @type='checkbox' @value={{...}} />` is not supported; " +
"please use `<Input @type='checkbox' @checked={{...}} />` instead.",
!(this.type === 'checkbox' && this.value !== UNSET)
);
},
});
}
}

export default Input;
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,6 @@ const TextField = Component.extend(TextSupport, {
@public
*/
max: null,

__sourceInput: null,

init() {
if (this.__sourceInput) {
this.__sourceInput.__injectEvents(this);
}
this._super(...arguments);
},
});

TextField.toString = () => '@ember/component/text-field';
Expand Down
Loading

0 comments on commit c27336a

Please sign in to comment.