Skip to content

Commit

Permalink
Merge fc966ce into 8dd94e6
Browse files Browse the repository at this point in the history
  • Loading branch information
WebReflection committed Mar 27, 2020
2 parents 8dd94e6 + fc966ce commit b7f0ffd
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 159 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ These are the rules to follow for attributes:
* if the attribute name starts with `on`, as example, `onclick=${...}`, it will be set as listener. If the listener changes, the previous one will be automatically removed. If the listener is an `Array` like `[listener, {once:true}]`, the second entry of the array would be used as listener's options.
* if the attribute starts with a `.` dot, as in `.setter=${value}`, the value will be passed directly to the element per each update. If such value is a known setter, either native elements or defined via Custom Elements, the setter will be invoked per each update, even if the value is the same
* if the attribute name is `ref`, as in `ref=${object}`, the `object.current` property will be assigned to the node, once this is rendered, and per each update. If a callback is passed instead, the callback will receive the node right away, same way [React ref](https://reactjs.org/docs/refs-and-the-dom.html) does.
* if the attribute name is `aria`, as in `aria=${object}`, aria attributes are applied to the node, including the `role` one.
* if the attribute name is `data`, as in `data=${object}`, the `node.dataset` gets populated with all values.

</div>
</details>
Expand Down
65 changes: 14 additions & 51 deletions cjs/handlers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';
const {isArray, slice} = require('uarray');
const udomdiff = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('udomdiff'));
const {aria, attribute, data, event, ref, setter} = require('uhandlers');
const {diffable} = require('uwire');

const {reducePath} = require('./node.js');
Expand Down Expand Up @@ -94,67 +95,29 @@ const handleAnything = (comment, nodes) => {

// attributes can be:
// * ref=${...} for hooks and other purposes
// * aria=${...} for aria attributes
// * data=${...} for dataset related attributes
// * .setter=${...} for Custom Elements setters or nodes with setters
// such as buttons, details, options, select, etc
// * onevent=${...} to automatically handle event listeners
// * generic=${...} to handle an attribute just like an attribute
const handleAttribute = (node, name) => {
// hooks and ref
if (name === 'ref')
return ref => {
if (typeof ref === 'function')
ref(node);
else
ref.current = node;
};
return ref(node);

// direct setters
if (name.slice(0, 1) === '.') {
const setter = name.slice(1);
return value => { node[setter] = value; }
}
if (name === 'aria')
return aria(node);

let oldValue;
if (name === 'data')
return data(node);

// events
if (name.slice(0, 2) === 'on') {
let type = name.slice(2);
if (!(name in node) && name.toLowerCase() in node)
type = type.toLowerCase();
return newValue => {
const info = isArray(newValue) ? newValue : [newValue, false];
if (oldValue !== info[0]) {
if (oldValue)
node.removeEventListener(type, oldValue, info[1]);
if (oldValue = info[0])
node.addEventListener(type, oldValue, info[1]);
}
};
}
if (name.slice(0, 1) === '.')
return setter(node, name.slice(1));

// all other cases
let noOwner = true;
const attribute = document.createAttribute(name);
return newValue => {
if (oldValue !== newValue) {
oldValue = newValue;
if (oldValue == null) {
if (!noOwner) {
node.removeAttributeNode(attribute);
noOwner = true;
}
}
else {
attribute.value = newValue;
// There is no else case here.
// If the attribute has no owner, it's set back.
if (noOwner) {
node.setAttributeNode(attribute);
noOwner = false;
}
}
}
};
if (name.slice(0, 2) === 'on')
return event(node, name);

return attribute(node, name);
};

// style and textarea nodes can change only their text
Expand Down
65 changes: 14 additions & 51 deletions esm/handlers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {isArray, slice} from 'uarray';
import udomdiff from 'udomdiff';
import {aria, attribute, data, event, ref, setter} from 'uhandlers';
import {diffable} from 'uwire';

import {reducePath} from './node.js';
Expand Down Expand Up @@ -93,67 +94,29 @@ const handleAnything = (comment, nodes) => {

// attributes can be:
// * ref=${...} for hooks and other purposes
// * aria=${...} for aria attributes
// * data=${...} for dataset related attributes
// * .setter=${...} for Custom Elements setters or nodes with setters
// such as buttons, details, options, select, etc
// * onevent=${...} to automatically handle event listeners
// * generic=${...} to handle an attribute just like an attribute
const handleAttribute = (node, name) => {
// hooks and ref
if (name === 'ref')
return ref => {
if (typeof ref === 'function')
ref(node);
else
ref.current = node;
};
return ref(node);

// direct setters
if (name.slice(0, 1) === '.') {
const setter = name.slice(1);
return value => { node[setter] = value; }
}
if (name === 'aria')
return aria(node);

let oldValue;
if (name === 'data')
return data(node);

// events
if (name.slice(0, 2) === 'on') {
let type = name.slice(2);
if (!(name in node) && name.toLowerCase() in node)
type = type.toLowerCase();
return newValue => {
const info = isArray(newValue) ? newValue : [newValue, false];
if (oldValue !== info[0]) {
if (oldValue)
node.removeEventListener(type, oldValue, info[1]);
if (oldValue = info[0])
node.addEventListener(type, oldValue, info[1]);
}
};
}
if (name.slice(0, 1) === '.')
return setter(node, name.slice(1));

// all other cases
let noOwner = true;
const attribute = document.createAttribute(name);
return newValue => {
if (oldValue !== newValue) {
oldValue = newValue;
if (oldValue == null) {
if (!noOwner) {
node.removeAttributeNode(attribute);
noOwner = true;
}
}
else {
attribute.value = newValue;
// There is no else case here.
// If the attribute has no owner, it's set back.
if (noOwner) {
node.setAttributeNode(attribute);
noOwner = false;
}
}
}
};
if (name.slice(0, 2) === 'on')
return event(node, name);

return attribute(node, name);
};

// style and textarea nodes can change only their text
Expand Down
123 changes: 72 additions & 51 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,75 @@ var uhtml = (function (exports) {
return b;
});

var aria = function aria(node) {
return function (value) {
for (var key in value) {
node.setAttribute(key === 'role' ? key : "aria-".concat(key), value[key]);
}
};
};
var attribute = function attribute(node, name) {
var oldValue,
orphan = true;
var attributeNode = document.createAttribute(name);
return function (newValue) {
if (oldValue !== newValue) {
oldValue = newValue;

if (oldValue == null) {
if (!orphan) {
node.removeAttributeNode(attributeNode);
orphan = true;
}
} else {
attributeNode.value = newValue;

if (orphan) {
node.setAttributeNode(attributeNode);
orphan = false;
}
}
}
};
};
var data = function data(_ref) {
var dataset = _ref.dataset;
return function (value) {
for (var key in value) {
dataset[key] = value[key];
}
};
};
var event = function event(node, name) {
var oldValue,
type = name.slice(2);
if (!(name in node) && name.toLowerCase() in node) type = type.toLowerCase();
return function (newValue) {
var info = isArray(newValue) ? newValue : [newValue, false];

if (oldValue !== info[0]) {
if (oldValue) node.removeEventListener(type, oldValue, info[1]);
if (oldValue = info[0]) node.addEventListener(type, oldValue, info[1]);
}
};
};
var ref = function ref(node) {
return function (value) {
if (typeof value === 'function') value(node);else value.current = node;
};
};
var setter = function setter(node, key) {
return function (value) {
node[key] = value;
};
};

/*! (c) Andrea Giammarchi - ISC */
var createContent = function (document) {

var FRAGMENT = 'fragment';
var TEMPLATE = 'template';
var HAS_CONTENT = 'content' in create(TEMPLATE);
var HAS_CONTENT = ('content' in create(TEMPLATE));
var createHTML = HAS_CONTENT ? function (html) {
var template = create(TEMPLATE);
template.innerHTML = html;
Expand Down Expand Up @@ -432,63 +495,21 @@ var uhtml = (function (exports) {
return anyContent;
}; // attributes can be:
// * ref=${...} for hooks and other purposes
// * aria=${...} for aria attributes
// * data=${...} for dataset related attributes
// * .setter=${...} for Custom Elements setters or nodes with setters
// such as buttons, details, options, select, etc
// * onevent=${...} to automatically handle event listeners
// * generic=${...} to handle an attribute just like an attribute


var handleAttribute = function handleAttribute(node, name) {
// hooks and ref
if (name === 'ref') return function (ref) {
if (typeof ref === 'function') ref(node);else ref.current = node;
}; // direct setters

if (name.slice(0, 1) === '.') {
var setter = name.slice(1);
return function (value) {
node[setter] = value;
};
}

var oldValue; // events

if (name.slice(0, 2) === 'on') {
var type = name.slice(2);
if (!(name in node) && name.toLowerCase() in node) type = type.toLowerCase();
return function (newValue) {
var info = isArray(newValue) ? newValue : [newValue, false];

if (oldValue !== info[0]) {
if (oldValue) node.removeEventListener(type, oldValue, info[1]);
if (oldValue = info[0]) node.addEventListener(type, oldValue, info[1]);
}
};
} // all other cases


var noOwner = true;
var attribute = document.createAttribute(name);
return function (newValue) {
if (oldValue !== newValue) {
oldValue = newValue;

if (oldValue == null) {
if (!noOwner) {
node.removeAttributeNode(attribute);
noOwner = true;
}
} else {
attribute.value = newValue; // There is no else case here.
// If the attribute has no owner, it's set back.

if (noOwner) {
node.setAttributeNode(attribute);
noOwner = false;
}
}
}
};
if (name === 'ref') return ref(node);
if (name === 'aria') return aria(node);
if (name === 'data') return data(node);
if (name.slice(0, 1) === '.') return setter(node, name.slice(1));
if (name.slice(0, 2) === 'on') return event(node, name);
return attribute(node, name);
}; // style and textarea nodes can change only their text
// without any possibility to accept child nodes.
// in these two cases the content is simply updated, or cleaned,
Expand Down

0 comments on commit b7f0ffd

Please sign in to comment.