Skip to content

Commit f7be1dd

Browse files
committed
Support custom properties.
1 parent 51d6aef commit f7be1dd

File tree

1 file changed

+127
-116
lines changed

1 file changed

+127
-116
lines changed

src/coconut/vdom/Html.hx

Lines changed: 127 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ private class Elt<Attr:{}> extends Factory<Attr, Node, Element> {
190190
ELEMENTS.update(target, nu, old);
191191

192192
static final ELEMENTS = new Updater<Element, {}>(
193-
// (target, field) -> '$target.removeAttribute("$field")',
194-
(target, field) -> '$target.$field = null',
193+
(target, field) -> '$target.removeAttribute("$field")',
194+
(target, field, value) -> '$target.$field = $value',
195195
{
196196
className: function (t:Element, _, v:String, _) if (!(cast v)) t.removeAttribute('class') else t.className = v,
197197
style: function (t:Element, _, nu, old) updateStyle(t.style, nu, old),
@@ -227,11 +227,12 @@ private class Elt<Attr:{}> extends Factory<Attr, Node, Element> {
227227
}
228228
}
229229

230-
static final STYLES = new Updater<CSSStyleDeclaration, tink.domspec.Style>(
231-
(target, field) -> '$target.$field = null',
232-
null,
233-
(_, _) -> null
234-
);
230+
static final STYLES = new Updater<CSSStyleDeclaration, tink.domspec.Style>(
231+
(target, field) -> if (field.contains('-')) '$target.removeProperty("$field")' else '$target.$field = null',
232+
(target, field, value) -> if (field.contains('-')) 'target.setProperty("$field", $value)' else '$target.$field = $value',
233+
null,
234+
(_, _) -> null
235+
);
235236

236237
static function updateStyle(target:CSSStyleDeclaration, newVal:tink.domspec.Style, ?oldVal:tink.domspec.Style)
237238
STYLES.update(target, newVal, oldVal);
@@ -242,117 +243,127 @@ private class Elt<Attr:{}> extends Factory<Attr, Node, Element> {
242243
private typedef Rules<Target> = haxe.DynamicAccess<(target:Target, field:String, nu:Dynamic, old:Null<Dynamic>)->Void>;
243244

244245
private class Updater<Target:{}, Value:{}> {//TODO: extract to coconut.diffing
245-
final unset:(target:String, field:String)->String;
246-
final rules:Rules<Target>;
247-
final getRule:(rules:Rules<Target>, field:String)->Null<String>;
248-
public function new(unset, rules, getRule) {
249-
this.unset = unset;
250-
this.rules = rules;
251-
this.getRule = getRule;
252-
}
253-
254-
public function update(target:Target, newVal:Value, ?oldVal:Value) {
255-
if (newVal != null)
256-
getApplicator(newVal)(target, newVal, oldVal);
257-
258-
if (oldVal != null)
259-
getDeleter(oldVal, newVal)(target);
260-
}
261-
262-
final applicators = new js.lib.Map<String, (target:Target, nu:Value, ?old:Value)->Void>();
263-
function getApplicator(obj:{}) {
264-
var props = getFields(obj);
265-
var key = props.toString();
266-
var apply = applicators.get(key);
267-
268-
if (apply == null) {
269-
var source = 'if (old) {';
270-
271-
function add(prefix) {
272-
for (p in props)
273-
source += '\n ${prefix(p)}' + switch getRule(rules, p) {
274-
case null: 'if (nu.$p == null) { ${unset('target', p)} } else target.$p = nu.$p;';
275-
case rule: 'this.$rule(target, "$p", nu.$p, old && old.$p);';
276-
}
277-
}
278-
279-
add(p -> 'if (nu.$p !== old.$p) ');
280-
281-
source += '\n} else {';
282-
283-
add(p -> '');
284-
285-
source += '\n}';
286-
apply = cast new js.lib.Function('target', 'nu', 'old', source).bind(rules);
287-
applicators.set(key, apply);
288-
}
289-
290-
return apply;
291-
}
292-
293-
function noop(target:Target) {}
294-
final deleters = new js.lib.Map<String, (target:Target)->Void>();
295-
function getDeleter(old:{}, ?nu:{}) {
296-
297-
function forFields(fields:haxe.ds.ReadOnlyArray<String>) {
298-
var key = fields.toString();
299-
var ret = deleters.get(key);
300-
if (ret == null) {
301-
var body = '';
302-
for (f in fields)
303-
body += '\n' + switch getRule(rules, f) {
246+
final unset:(target:String, field:String)->String;
247+
final set:(target:String, field:String, value:String)->String;
248+
final rules:Rules<Target>;
249+
final getRule:(rules:Rules<Target>, field:String)->Null<String>;
250+
public function new(unset, set, rules, getRule) {
251+
this.unset = unset;
252+
this.set = set;
253+
this.rules = rules;
254+
this.getRule = getRule;
255+
}
256+
257+
static public function get(target, property:String)
258+
return switch property.indexOf('-') {
259+
case -1: '$target.$property';
260+
default: '$target["$property"]';
261+
}
262+
263+
public function update(target:Target, newVal:Value, ?oldVal:Value) {
264+
if (newVal != null)
265+
getApplicator(newVal)(target, newVal, oldVal);
266+
267+
if (oldVal != null)
268+
getDeleter(oldVal, newVal)(target);
269+
}
270+
271+
final applicators = new js.lib.Map<String, (target:Target, nu:Value, ?old:Value)->Void>();
272+
function getApplicator(obj:{}) {
273+
var props = getFields(obj);
274+
var key = props.toString();
275+
var apply = applicators.get(key);
276+
277+
if (apply == null) {
278+
var source = 'if (old) {';
279+
280+
function add(prefix) {
281+
for (p in props)
282+
source += '\n ${prefix(p)}' + switch getRule(rules, p) {
283+
case null:
284+
var nu = get('nu', p);
285+
'if ($nu == null) { ${unset('target', p)} } else ${set('target', p, nu)}';
286+
case rule: 'this.$rule(target, "$p", ${get('nu', p)}, old && ${get('old', p)});';
287+
}
288+
}
289+
290+
add(p -> 'if (${get('nu', p)} !== ${get('old', p)}) ');
291+
292+
source += '\n} else {';
293+
294+
add(p -> '');
295+
296+
source += '\n}';
297+
apply = cast new js.lib.Function('target', 'nu', 'old', source).bind(rules);
298+
applicators.set(key, apply);
299+
}
300+
301+
return apply;
302+
}
303+
304+
function noop(target:Target) {}
305+
final deleters = new js.lib.Map<String, (target:Target)->Void>();
306+
function getDeleter(old:{}, ?nu:{}) {
307+
308+
function forFields(fields:haxe.ds.ReadOnlyArray<String>) {
309+
var key = fields.toString();
310+
var ret = deleters.get(key);
311+
if (ret == null) {
312+
var body = '';
313+
for (f in fields)
314+
body += '\n' + switch getRule(rules, f) {
304315
case null: unset('target', f);
305316
case rule: 'this.$rule(target, "$f", null);';
306317
}
307318
deleters.set(key, ret = cast new js.lib.Function('target', body).bind(rules));
308-
}
309-
return ret;
310-
}
311-
312-
return
313-
if (nu == null)
314-
forFields(getFields(old));
315-
else {
316-
var oldFields = getFields(old),
317-
nuFields = getFields(nu);
318-
319-
var nuKey = nuFields.toString(),
320-
oldKey = oldFields.toString();
321-
322-
if (nuKey == oldKey) noop;
323-
else {
324-
var key = '${nuKey}:${oldKey}';
325-
var ret = deleters.get(key);
326-
327-
if (ret == null)
328-
deleters.set(key, ret = forFields([for (f in oldFields) if (!nuFields.contains(f)) f]));
329-
330-
ret;
331-
}
332-
}
333-
}
334-
335-
static function getFields(o:{}) {
336-
var ret = js.lib.Object.getOwnPropertyNames(o);
337-
switch ret {
338-
case [], [_]:
339-
case [a, b]:
340-
if (a > b) {
341-
ret[0] = b;
342-
ret[1] = a;
343-
}
344-
default:
345-
(cast ret).sort();
346-
}
347-
return ret;
348-
// TODO: check the caching attempt below again. Thus far profiling suggested this causes a slow down.
349-
// var ret:haxe.ds.ReadOnlyArray<String> = untyped o._coco_keys;
350-
// if (ret == null) {
351-
// ret = untyped Object.getOwnPropertyNames(o).sort();
352-
// js.lib.Object.defineProperty(o, '_coco_keys', { value: ret, enumerable: false });
353-
// var joined = ret.toString();
354-
// untyped ret.toString = function () return joined;
355-
// }
356-
// return ret;
357-
}
319+
}
320+
return ret;
321+
}
322+
323+
return
324+
if (nu == null)
325+
forFields(getFields(old));
326+
else {
327+
var oldFields = getFields(old),
328+
nuFields = getFields(nu);
329+
330+
var nuKey = nuFields.toString(),
331+
oldKey = oldFields.toString();
332+
333+
if (nuKey == oldKey) noop;
334+
else {
335+
var key = '${nuKey}:${oldKey}';
336+
var ret = deleters.get(key);
337+
338+
if (ret == null)
339+
deleters.set(key, ret = forFields([for (f in oldFields) if (!nuFields.contains(f)) f]));
340+
341+
ret;
342+
}
343+
}
344+
}
345+
346+
static function getFields(o:{}) {
347+
var ret = js.lib.Object.getOwnPropertyNames(o);
348+
switch ret {
349+
case [], [_]:
350+
case [a, b]:
351+
if (a > b) {
352+
ret[0] = b;
353+
ret[1] = a;
354+
}
355+
default:
356+
(cast ret).sort();
357+
}
358+
return ret;
359+
// TODO: check the caching attempt below again. Thus far profiling suggested this causes a slow down.
360+
// var ret:haxe.ds.ReadOnlyArray<String> = untyped o._coco_keys;
361+
// if (ret == null) {
362+
// ret = untyped Object.getOwnPropertyNames(o).sort();
363+
// js.lib.Object.defineProperty(o, '_coco_keys', { value: ret, enumerable: false });
364+
// var joined = ret.toString();
365+
// untyped ret.toString = function () return joined;
366+
// }
367+
// return ret;
368+
}
358369
}

0 commit comments

Comments
 (0)