Skip to content

Commit

Permalink
Merge pull request #17803 from emberjs/input-manager
Browse files Browse the repository at this point in the history
Refactor #17788 to use ...@Args
  • Loading branch information
rwjblue authored Mar 26, 2019
2 parents 16bab5c + 305731c commit ed897e5
Show file tree
Hide file tree
Showing 17 changed files with 348 additions and 214 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,24 @@ 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>;

let prepared = {
positional: EMPTY_POSITIONAL_ARGS,
named: {
...args.named.capture().map,
...__args__,
},
};

if (DEBUG) {
delete prepared.named.__ARGS__;
}

return prepared;
}

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,
};
}
}
4 changes: 1 addition & 3 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,8 +650,7 @@ 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.tagName !== '' ||
!this.renderer._destinedForDOM ||
!(() => {
let eventDispatcher = getOwner(this).lookup<any | undefined>('event_dispatcher:main');
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 ed897e5

Please sign in to comment.