react-qml
Version:
289 lines (210 loc) • 8.95 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.diffProps = diffProps;
exports.updateProps = updateProps;
var _StyleSheet = _interopRequireDefault(require("../common/StyleSheet"));
var _Anchor = require("../common/Anchor");
var _util = require("util");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
// QML signal handler convention
var qmlSignalRegex = /^on([A-Z][a-zA-Z]+)$/; // Calculate the diff between the two objects.
function diffProps(lastProps, nextProps) {
var updatePayload = []; // phase 1: look for deleted props
for (var propKey in lastProps) {
if (lastProps[propKey] == null || !lastProps.hasOwnProperty(propKey) || nextProps.hasOwnProperty(propKey)) {
continue;
}
updatePayload.push(propKey, undefined);
} // phase 2: look for actual changes
for (var _propKey in nextProps) {
var nextPropValue = nextProps[_propKey];
var lastPropValue = lastProps != null ? lastProps[_propKey] : undefined;
if (!nextProps.hasOwnProperty(_propKey) || nextPropValue === lastPropValue) {
continue;
} // event handling
var matches = _propKey.match(qmlSignalRegex);
var isEventProp = matches && lastPropValue !== nextPropValue;
var isAnchorsProp = _propKey === 'anchors';
var isAnchorRefProp = _propKey === 'anchorRef';
var isStyleProp = _propKey === 'style'; // attached property or group props
// eg: Layout, Material, layer etc.
var isObjectProp = _typeof(nextPropValue) === 'object';
if (isEventProp || isAnchorsProp || isAnchorRefProp || isStyleProp || isObjectProp) {
updatePayload.push(_propKey, [lastPropValue, nextPropValue]);
continue;
} // flat prop
updatePayload.push(_propKey, nextPropValue);
}
return updatePayload.length === 0 ? null : updatePayload;
}
function listenTo(qmlElement, eventName, nextHandler, lastHandler) {
eventName = eventName[0].toLowerCase() + eventName.substring(1);
if (!qmlElement[eventName]) {
console.warn("Event \"".concat(eventName, "\" not found in ").concat(qmlElement));
return;
}
if (lastHandler) {
qmlElement[eventName].disconnect(lastHandler);
}
if (nextHandler) {
qmlElement[eventName].connect(nextHandler);
}
} // handle connections
function handleAnchors(qmlElement, lastAnchors, nextAnchors) {
// last anchors
for (var propName in lastAnchors) {
if (lastAnchors.hasOwnProperty(propName)) {
if ((0, _Anchor.isAnchorProp)(propName)) {
var anchorRef = lastAnchors[propName];
if (typeof anchorRef === 'string') {
_Anchor.ParentAnchor.removeSubscription(qmlElement, propName);
qmlElement.anchors[propName] = undefined;
continue;
}
if (anchorRef) {
anchorRef.removeSubscription(qmlElement, propName);
} // unset anchor
qmlElement.anchors[propName] = undefined;
}
}
} // next anchors
for (var _propName in nextAnchors) {
if (nextAnchors.hasOwnProperty(_propName)) {
if ((0, _Anchor.isAnchorProp)(_propName)) {
var _anchorRef = nextAnchors[_propName];
if (typeof _anchorRef === 'string') {
_Anchor.ParentAnchor.addSubscription(qmlElement, _propName, _anchorRef);
continue;
}
if (_anchorRef) {
// anchor, set value when anchor ready
_anchorRef.addSubscription(qmlElement, _propName);
} else {
// unset anchor
qmlElement.anchors[_propName] = undefined;
}
} else {
// primitive, set values right away
var propValue = nextAnchors[_propName];
qmlElement.anchors[_propName] = propValue;
}
}
}
} // handle anchor ref
function handleAnchorRef(qmlElement, lastRef, nextRef) {
// TODO: handle last ref
if (nextRef) {
nextRef.setQmlElement(qmlElement);
}
}
function handleStyle(qmlElement, lastStyle, nextStyle) {
// last style
var nextFlatStyle = _StyleSheet.default.flattenStyle(nextStyle);
var finalStyle = nextFlatStyle;
if (lastStyle) {
var lastFlatStyle = _StyleSheet.default.flattenStyle(lastStyle);
for (var styleName in lastFlatStyle) {
if (!nextFlatStyle.hasOwnProperty(styleName)) {
// not every element/prop allow setting to undefined
try {
finalStyle[styleName] = undefined;
} catch (ex) {
console.warn("Cannot unset property '".concat(styleName, "' of ").concat(qmlElement));
}
}
}
}
_StyleSheet.default.setStyle(qmlElement, finalStyle);
}
function handleGroupProps(qmlElement, group, lastProps, nextProps) {
// phase 1: look for deleted props
for (var propKey in lastProps) {
if (lastProps[propKey] == null || !lastProps.hasOwnProperty(propKey) || nextProps.hasOwnProperty(propKey)) {
continue;
} // not every element/prop allow setting to undefined
try {
qmlElement[group][propKey] = undefined;
} catch (ex) {
console.warn("Cannot unset property '".concat(propKey, "' of ").concat(qmlElement, ".").concat(group));
}
} // phase 2: look for actual changes
for (var _propKey2 in nextProps) {
var nextPropValue = nextProps[_propKey2];
var lastPropValue = lastProps != null ? lastProps[_propKey2] : undefined;
if (!nextProps.hasOwnProperty(_propKey2) || nextPropValue === lastPropValue) {
continue;
}
qmlElement[group][_propKey2] = nextPropValue;
}
} // Apply the diff.
function updateProps(container, updatePayload) {
var qmlElement = container.element;
for (var i = 0; i < updatePayload.length; i += 2) {
var propKey = updatePayload[i];
var propValue = updatePayload[i + 1]; // ignore the children
if (propKey === 'children') {
continue;
} // event handling
var matches = propKey.match(qmlSignalRegex);
if (matches) {
var eventName = matches[1];
var _propValue = _slicedToArray(propValue, 2),
lastHandler = _propValue[0],
nextHandler = _propValue[1];
listenTo(qmlElement, eventName, nextHandler, lastHandler);
continue;
} // anchor ref
if (propKey === 'anchorRef') {
var _propValue2 = _slicedToArray(propValue, 2),
lastRef = _propValue2[0],
nextRef = _propValue2[1];
handleAnchorRef(qmlElement, lastRef, nextRef);
continue;
} // anchors handling
if (propKey === 'anchors') {
var _propValue3 = _slicedToArray(propValue, 2),
lastAnchors = _propValue3[0],
nextAnchors = _propValue3[1];
handleAnchors(qmlElement, lastAnchors, nextAnchors);
continue;
} // style handling
if (propKey === 'style') {
var _propValue4 = _slicedToArray(propValue, 2),
lastStyle = _propValue4[0],
nextStyle = _propValue4[1];
handleStyle(qmlElement, lastStyle, nextStyle);
continue;
} // attached property or group props
// eg: Layout, Material, layer etc.
var isObjectProp = _typeof(qmlElement[propKey]) === 'object';
if (Array.isArray(propValue) && isObjectProp) {
var _propValue5 = _slicedToArray(propValue, 2),
lastPropValue = _propValue5[0],
nextPropValue = _propValue5[1];
if (_typeof(nextPropValue) === 'object') {
handleGroupProps(qmlElement, propKey, lastPropValue, nextPropValue);
continue;
}
}
if (!qmlElement.hasOwnProperty(propKey)) {
console.warn("Cannot assign to non-existent property \"".concat(propKey, "\""));
continue;
}
if (propValue === undefined) {
try {
qmlElement[propKey] = propValue;
} catch (ex) {
console.warn("Cannot unset property \"".concat(propKey, "\" of object ").concat(qmlElement));
}
} else {
qmlElement[propKey] = propValue;
}
}
}