Skip to content

Commit

Permalink
✨ Allow assigning an element to a reference using :ref syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
skerit committed Apr 30, 2024
1 parent 765c458 commit 89680df
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* Prepare text nodes during compiling
* Clean up custom element `renderCustomTemplate` function
* Add initial reactive variable support
* Allow assigning an element to a reference using `:ref` syntax

## 2.3.19 (2024-04-13)

Expand Down
23 changes: 18 additions & 5 deletions lib/core/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2321,6 +2321,16 @@ Renderer.setMethod(function applyElementOptions(element, options, for_sync_rende
}
}

if (options.hooks?.length) {
for (let hook of options.hooks) {
if (hook.name === 'ref') {
if (hook.value) {
hook.value.value = element;
}
}
}
}

// If the element body is rendered using reference variables,
// the element has to be registered so the browser knows about it
if (options.reference_count > 0 && options.references?.length) {
Expand Down Expand Up @@ -2551,21 +2561,24 @@ Renderer.setMethod(function startExpression(name, options, vars, fnc) {
*
* @author Jelle De Loecker <[email protected]>
* @since 1.3.3
* @version 2.0.0
* @version 2.4.0
*/
Renderer.setMethod(function parseExpression(tokens, vars) {
return Hawkejs.Expression.Expression.parseExpression(this, tokens, vars);
Renderer.setMethod(function parseExpression(tokens, vars, unwrap_optionals = true) {
let expression = new Hawkejs.Expression.Expression(this);
expression.unwrap_optionals = unwrap_optionals;
return expression.parseExpression(tokens, vars);
});

/**
* Parse expression arguments
*
* @author Jelle De Loecker <[email protected]>
* @since 1.3.3
* @version 2.0.0
* @version 2.4.0
*/
Renderer.setMethod(function parseExpressionAsArguments(tokens, vars) {
Renderer.setMethod(function parseExpressionAsArguments(tokens, vars, unwrap_optionals = true) {
let expression = new Hawkejs.Expression.Expression(this);
expression.unwrap_optionals = unwrap_optionals;
return expression.getTokenValuesArray(tokens, vars);
});

Expand Down
18 changes: 15 additions & 3 deletions lib/expression/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ const Expression = Fn.inherits(null, 'Hawkejs.Expression', function Expression(v
// The renderer instance
this.view = view;

// Should optionals be unwrapped?
this.unwrap_optionals = true;

this.options = null;
this.vars = null;
this.fnc = null;
Expand Down Expand Up @@ -430,6 +433,15 @@ Expression.setMethod(function _getValueByPath(path, variables, main_variables) {
break;
}

// Always unwrap optionals that are not the last part of a path
if (current instanceof Classes.Develry.Optional) {
current = current.value;
}

if (!current) {
break;
}

if (current instanceof Classes.Hawkejs.Variables) {
current = current.get(piece);
} else {
Expand Down Expand Up @@ -766,7 +778,7 @@ const toLowerCase = (input) => input ? input.toLowerCase() : input;
*
* @author Jelle De Loecker <[email protected]>
* @since 1.2.9
* @version 1.4.0
* @version 2.4.0
*
* @param {Object} token
* @param {Object} vars An object of variables
Expand Down Expand Up @@ -796,7 +808,7 @@ Expression.setMethod(function getTokenValue(token, vars) {
}

// Unwrap optionals
if (result && result instanceof Classes.Develry.Optional) {
if (result && this.unwrap_optionals && result instanceof Classes.Develry.Optional) {
result = result.value;
}

Expand Down Expand Up @@ -1051,7 +1063,7 @@ Expression.setMethod(function callPathWithArgs(path, args, vars) {
*
* @author Jelle De Loecker <[email protected]>
* @since 1.2.9
* @version 2.2.0
* @version 2.4.0
*
* @param {Object} token
* @param {Object} vars An object of variables
Expand Down
13 changes: 10 additions & 3 deletions lib/parser/base_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,15 @@ Parser.setStatic(function rawString(text) {
*
* @author Jelle De Loecker <[email protected]>
* @since 2.2.0
* @version 2.2.0
* @version 2.4.0
*
* @param {Object} options
* @param {boolean} as_args
* @param {boolean} unwrap_optionals
*
* @return {Object}
*/
Parser.setStatic(function wrapExpression(options, as_args) {
Parser.setStatic(function wrapExpression(options, as_args, unwrap_optionals = true) {

options = Bound.Array.cast(options);

Expand All @@ -127,10 +128,16 @@ Parser.setStatic(function wrapExpression(options, as_args) {
code += 'AsArguments';
}

return {
let result = {
$wrap: '__render.' + code,
$args: [options, Parser.rawString('vars')],
};

if (!unwrap_optionals) {
result.$args.push(false);
}

return result;
});

/**
Expand Down
34 changes: 22 additions & 12 deletions lib/parser/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ Builder.setMethod(function _compileEntry(subroutine, entry) {
*
* @author Jelle De Loecker <[email protected]>
* @since 2.2.0
* @version 2.2.0
* @version 2.4.0
*
* @param {Object} entry
*
Expand All @@ -813,7 +813,7 @@ Builder.setMethod(function _compileElementOptions(entry) {

let has_attributes = !Obj.isEmpty(entry.attributes);

if (!has_attributes && !entry.directives && !entry.properties && !entry.variables && !entry.codes && !entry.body) {
if (!has_attributes && !entry.directives && !entry.properties && !entry.variables && !entry.codes && !entry.body && !entry.hooks) {
return;
}

Expand All @@ -825,6 +825,7 @@ Builder.setMethod(function _compileElementOptions(entry) {
directives : null,
properties : null,
variables : null,
hooks : null,
codes : null,
body : null,
};
Expand Down Expand Up @@ -852,6 +853,10 @@ Builder.setMethod(function _compileElementOptions(entry) {
data.variables = this._compileDirectiveValues(entry.variables);
}

if (entry.hooks) {
data.hooks = this._compileDirectiveValues(entry.hooks, false);
}

if (entry.codes) {
data.codes = this._compileCodes(entry.codes);
}
Expand All @@ -868,16 +873,17 @@ Builder.setMethod(function _compileElementOptions(entry) {
*
* @author Jelle De Loecker <[email protected]>
* @since 2.2.0
* @version 2.2.0
* @version 2.4.0
*
* @param {Object} entry
* @param {boolean} unwrap_optionals
*
* @return {Builder}
*/
Builder.setMethod(function _compileRawExpression(entry) {
Builder.setMethod(function _compileRawExpression(entry, unwrap_optionals = true) {
let tokens = new Hawkejs.Parser.Expressions(entry.value);
let expression = tokens.getExpression();
return Hawkejs.Parser.Parser.wrapExpression(expression);
return Hawkejs.Parser.Parser.wrapExpression(expression, null, unwrap_optionals);
});

/**
Expand All @@ -887,9 +893,12 @@ Builder.setMethod(function _compileRawExpression(entry) {
* @since 2.0.0
* @version 2.2.0
*
* @param {Object} val
* @param {boolean} unwrap_optionals
*
* @return {Object}
*/
Builder.setMethod(function _compileAttributeValue(val) {
Builder.setMethod(function _compileAttributeValue(val, unwrap_optionals = true) {

let result = '';

Expand Down Expand Up @@ -918,7 +927,7 @@ Builder.setMethod(function _compileAttributeValue(val) {
entry = val.value[i];

if (entry.type == 'expressions') {
result.push(this._compileRawExpression(entry));
result.push(this._compileRawExpression(entry, unwrap_optionals));
} else if (entry.type == 'code') {
let code;

Expand Down Expand Up @@ -947,7 +956,7 @@ Builder.setMethod(function _compileAttributeValue(val) {
val.value = val.value.slice(2, -2);
}

result = this._compileRawExpression(val);
result = this._compileRawExpression(val, unwrap_optionals);
} else {
result = R(val.value);
}
Expand All @@ -961,13 +970,14 @@ Builder.setMethod(function _compileAttributeValue(val) {
*
* @author Jelle De Loecker <[email protected]>
* @since 2.0.0
* @version 2.0.0
* @version 2.4.0
*
* @param {Array} values
* @param {Object[]} values
* @param {boolean} unwrap_optionals
*
* @return {String}
*/
Builder.setMethod(function _compileDirectiveValues(values) {
Builder.setMethod(function _compileDirectiveValues(values, unwrap_optionals = true) {

let result = [],
value,
Expand All @@ -981,7 +991,7 @@ Builder.setMethod(function _compileDirectiveValues(values) {
name : value.name,
context : value.context || null,
method : value.method || null,
value : this._compileAttributeValue(value.value)
value : this._compileAttributeValue(value.value, unwrap_optionals),
};

result.push(entry);
Expand Down
11 changes: 9 additions & 2 deletions lib/parser/directives_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ Dparser.setMethod(function parseCurrent() {
*
* @author Jelle De Loecker <[email protected]>
* @since 2.0.0
* @version 2.1.5
* @version 2.4.0
*
* @param {Object} result
*/
Expand Down Expand Up @@ -441,7 +441,7 @@ Dparser.setMethod(function parseAttributes(result) {
char = next.value[0];
name = next.value;

if (char === '!' || char === '#' || char === '+') {
if (char === '!' || char === '#' || char === '+' || char === ':') {
name = next.value.slice(1);
}

Expand Down Expand Up @@ -493,6 +493,13 @@ Dparser.setMethod(function parseAttributes(result) {
}

result.variables.push(entry);
} else if (char === ':') {

if (!result.hooks) {
result.hooks = [];
}

result.hooks.push(entry);
} else {
result.attributes[entry.name] = entry;
}
Expand Down
19 changes: 19 additions & 0 deletions test/10-expressions.js
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,25 @@ This should be a converted variable:
(vars) => {vars.get('ref_bool').value = null},
`<span>Ref bool is: FALSE</span>`,
],
[
(vars) => vars.set('ref_el', Optional()),
`<span :ref={% ref_el %}>INNER</span><div>{{ &ref_el.textContent }}</div>`,
`<span>INNER</span><div>INNER</div>`,
(vars) => {
// Get the reference again
let ref_el = vars.get('ref_el');

// Get the element it is referring
let el = ref_el.value;

// Change the content of the element directly
el.textContent = 'CHANGED';

// Trigger a change
ref_el.value = el;
},
`<span>CHANGED</span><div>CHANGED</div>`,
]
];

createReactiveTests(tests);
Expand Down

0 comments on commit 89680df

Please sign in to comment.