Skip to content

Commit eccb363

Browse files
authored
Merge pull request #13615 from asakusuma/input-dynamic
[Glimmer2] Implement input
2 parents 39b606f + 22e1b13 commit eccb363

File tree

6 files changed

+143
-7
lines changed

6 files changed

+143
-7
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"express": "^4.5.0",
3838
"finalhandler": "^0.4.0",
3939
"github": "^0.2.3",
40-
"glimmer-engine": "tildeio/glimmer#bf8213",
40+
"glimmer-engine": "tildeio/glimmer#d4d1e3a",
4141
"glob": "^5.0.13",
4242
"htmlbars": "0.14.23",
4343
"mocha": "^2.4.5",
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
@module ember
3+
@submodule ember-glimmer
4+
*/
5+
6+
/**
7+
A Glimmer2 AST transformation that replaces all instances of
8+
9+
```handlebars
10+
{{input type=boundType}}
11+
```
12+
13+
with
14+
15+
```handlebars
16+
{{input (-input-type boundType) type=boundType}}
17+
```
18+
19+
Note that the type parameters is not removed as the -input-type helpers
20+
is only used to select the component class. The component still needs
21+
the type parameter to function.
22+
23+
@private
24+
@class TransformInputTypeSyntax
25+
*/
26+
27+
export default function TransformInputTypeSyntax() {
28+
// set later within Glimmer2 to the syntax package
29+
this.syntax = null;
30+
}
31+
32+
/**
33+
@private
34+
@method transform
35+
@param {AST} ast The AST to be transformed.
36+
*/
37+
TransformInputTypeSyntax.prototype.transform = function TransformInputTypeSyntax_transform(ast) {
38+
let { traverse, builders: b } = this.syntax;
39+
40+
traverse(ast, {
41+
MustacheStatement(node) {
42+
if (isInput(node)) {
43+
insertTypeHelperParameter(node, b);
44+
}
45+
}
46+
});
47+
48+
return ast;
49+
};
50+
51+
function isInput(node) {
52+
return node.path.original === 'input';
53+
}
54+
55+
function insertTypeHelperParameter(node, builders) {
56+
let pairs = node.hash.pairs;
57+
let pair = null;
58+
for (let i = 0; i < pairs.length; i++) {
59+
if (pairs[i].key === 'type') {
60+
pair = pairs[i];
61+
break;
62+
}
63+
}
64+
if (pair && pair.value.type !== 'StringLiteral') {
65+
node.params.unshift(builders.sexpr('-input-type', [builders.path(pair.value.original, pair.loc)], null, pair.loc));
66+
}
67+
}

packages/ember-glimmer-template-compiler/lib/system/compile-options.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import defaultPlugins from 'ember-template-compiler/plugins';
22
import TransformActionSyntax from '../plugins/transform-action-syntax';
3+
import TransformInputTypeSyntax from '../plugins/transform-input-type-syntax';
34
import TransformAttrsIntoProps from '../plugins/transform-attrs-into-props';
45
import TransformEachInIntoEach from '../plugins/transform-each-in-into-each';
56
import TransformHasBlockSyntax from '../plugins/transform-has-block-syntax';
@@ -9,6 +10,7 @@ export const PLUGINS = [
910
...defaultPlugins,
1011
// the following are ember-glimmer specific
1112
TransformActionSyntax,
13+
TransformInputTypeSyntax,
1214
TransformAttrsIntoProps,
1315
TransformEachInIntoEach,
1416
TransformHasBlockSyntax

packages/ember-glimmer/lib/environment.js

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { default as mut } from './helpers/mut';
3131
import { default as readonly } from './helpers/readonly';
3232
import { default as unbound } from './helpers/unbound';
3333
import { default as classHelper } from './helpers/-class';
34+
import { default as inputTypeHelper } from './helpers/-input-type';
3435
import { default as queryParams } from './helpers/query-param';
3536
import { default as eachIn } from './helpers/each-in';
3637
import { default as normalizeClassHelper } from './helpers/-normalize-class';
@@ -40,6 +41,42 @@ const builtInComponents = {
4041
textarea: '-text-area'
4142
};
4243

44+
function createCurly(args, templates, definition) {
45+
wrapClassAttribute(args);
46+
return new CurlyComponentSyntax({ args, definition, templates });
47+
}
48+
49+
function buildTextFieldSyntax({ args, templates }, getDefinition) {
50+
let definition = getDefinition('-text-field');
51+
wrapClassAttribute(args);
52+
return new CurlyComponentSyntax({ args, definition, templates });
53+
}
54+
55+
const builtInDynamicComponents = {
56+
input({ key, args, templates }, getDefinition) {
57+
if (args.named.has('type')) {
58+
let typeArg = args.named.at('type');
59+
if (typeArg.type === 'value') {
60+
if (typeArg.value === 'checkbox') {
61+
assert(
62+
'{{input type=\'checkbox\'}} does not support setting `value=someBooleanValue`; ' +
63+
'you must use `checked=someBooleanValue` instead.',
64+
!args.named.has('value')
65+
);
66+
67+
return createCurly(args, templates, getDefinition('-checkbox'));
68+
} else {
69+
return buildTextFieldSyntax({ args, templates }, getDefinition);
70+
}
71+
}
72+
} else {
73+
return buildTextFieldSyntax({ args, templates }, getDefinition);
74+
}
75+
76+
return new DynamicComponentSyntax({ args, templates });
77+
}
78+
};
79+
4380
const builtInHelpers = {
4481
if: inlineIf,
4582
action,
@@ -55,6 +92,7 @@ const builtInHelpers = {
5592
unless: inlineUnless,
5693
'-class': classHelper,
5794
'-each-in': eachIn,
95+
'-input-type': inputTypeHelper,
5896
'-normalize-class': normalizeClassHelper
5997
};
6098

@@ -143,6 +181,11 @@ export default class Environment extends GlimmerEnvironment {
143181

144182
if (isSimple && (isInline || isBlock)) {
145183
// 2. built-in syntax
184+
185+
let generateBuiltInSyntax = builtInDynamicComponents[key];
186+
// Check if it's a keyword
187+
let mappedKey = builtInComponents[key];
188+
146189
if (key === 'component') {
147190
return new DynamicComponentSyntax({ args, templates });
148191
} else if (key === 'outlet') {
@@ -156,11 +199,14 @@ export default class Environment extends GlimmerEnvironment {
156199
definition = this.getComponentDefinition([internalKey]);
157200
} else if (key.indexOf('-') >= 0) {
158201
definition = this.getComponentDefinition(path);
202+
} else if (mappedKey) {
203+
definition = this.getComponentDefinition([mappedKey]);
159204
}
160205

161206
if (definition) {
162-
wrapClassAttribute(args);
163-
return new CurlyComponentSyntax({ args, definition, templates });
207+
return createCurly(args, templates, definition);
208+
} else if (generateBuiltInSyntax) {
209+
return generateBuiltInSyntax(statement, (path) => this.getComponentDefinition([path]));
164210
}
165211

166212
assert(`Could not find component named "${key}" (no component or template with that name was found)`, !isBlock || this.hasHelper(key));
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { InternalHelperReference } from '../utils/references';
2+
3+
function inputTypeHelper({ positional, named }) {
4+
let type = positional.at(0).value();
5+
if (type === 'checkbox') {
6+
return '-checkbox';
7+
}
8+
return '-text-field';
9+
}
10+
11+
export default {
12+
isInternalHelper: true,
13+
toReference(args) {
14+
return new InternalHelperReference(inputTypeHelper, args);
15+
}
16+
};

packages/ember-glimmer/tests/integration/helpers/input-test.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { set } from 'ember-metal/property_set';
22
import { TextField, Checkbox } from '../../utils/helpers';
33
import { RenderingTest, moduleFor } from '../../utils/test-case';
4+
import { runDestroy } from 'ember-runtime/tests/utils';
45

56
class InputRenderingTest extends RenderingTest {
67
constructor() {
78
super();
89

10+
// Modifying input.selectionStart, which is utilized in the cursor tests,
11+
// causes an event in Safari.
12+
runDestroy(this.owner.lookup('event_dispatcher:main'));
13+
914
this.registerComponent('-text-field', { ComponentClass: TextField });
1015
this.registerComponent('-checkbox', { ComponentClass: Checkbox });
1116
}
@@ -65,7 +70,7 @@ class InputRenderingTest extends RenderingTest {
6570
}
6671
}
6772

68-
moduleFor('@htmlbars Helpers test: {{input}}', class extends InputRenderingTest {
73+
moduleFor('Helpers test: {{input}}', class extends InputRenderingTest {
6974

7075
['@test a single text field is inserted into the DOM'](assert) {
7176
this.render(`{{input type="text" value=value}}`, { value: 'hello' });
@@ -252,7 +257,7 @@ moduleFor('@htmlbars Helpers test: {{input}}', class extends InputRenderingTest
252257

253258
});
254259

255-
moduleFor('@htmlbars Helpers test: {{input}} with dynamic type', class extends InputRenderingTest {
260+
moduleFor('Helpers test: {{input}} with dynamic type', class extends InputRenderingTest {
256261

257262
['@test a bound property can be used to determine type']() {
258263
this.render(`{{input type=type}}`, { type: 'password' });
@@ -274,7 +279,7 @@ moduleFor('@htmlbars Helpers test: {{input}} with dynamic type', class extends I
274279

275280
});
276281

277-
moduleFor(`@htmlbars Helpers test: {{input type='checkbox'}}`, class extends InputRenderingTest {
282+
moduleFor(`Helpers test: {{input type='checkbox'}}`, class extends InputRenderingTest {
278283

279284
['@test dynamic attributes']() {
280285
this.render(`{{input
@@ -370,7 +375,7 @@ moduleFor(`@htmlbars Helpers test: {{input type='checkbox'}}`, class extends Inp
370375

371376
});
372377

373-
moduleFor(`@htmlbars Helpers test: {{input type='text'}}`, class extends InputRenderingTest {
378+
moduleFor(`Helpers test: {{input type='text'}}`, class extends InputRenderingTest {
374379

375380
['@test null values'](assert) {
376381
let attributes = ['disabled', 'placeholder', 'name', 'maxlength', 'size', 'tabindex'];

0 commit comments

Comments
 (0)