@itsjonq/controls
Version:
A control panel to develop React UI
232 lines (189 loc) • 6.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
store: true,
addField: true,
updateField: true,
updateFields: true,
unsafeOverrideFields: true,
resetFields: true
};
exports.addField = addField;
exports.updateField = updateField;
exports.updateFields = updateFields;
exports.unsafeOverrideFields = unsafeOverrideFields;
exports.resetFields = resetFields;
exports.store = void 0;
var _unistore = _interopRequireDefault(require("unistore"));
var _is = require("@itsjonq/is");
var _selectors = require("./selectors");
Object.keys(_selectors).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _selectors[key];
}
});
});
var _transformValue = require("../knobs/transformValue");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* The store for the Controls fields. Using a dedicated store allows for updates
* and re-renders to happen outside of the React render cycle. Effectively,
* creating an alternate stream. This is the key to enabling the knobs like
* text(), color(), and date() to work without them being React hooks.
*
* This alternative state stream is a technique used by libraries like
* React Redux or Emotion.
*
* We're using unistore to create to store due to it's simplicity and
* (light) weight.
*/
var store = (0, _unistore.default)({
fields: []
});
/**
* Creates a specific "field" object (shape) to be added to the store.
* @param {string} prop The unique key for the field.
* @param {any} value The value for the field.
* @param {Object} props Additional props for the field. e.g. Options for a select()
* @returns {Object} A field to be added to the store.
*/
exports.store = store;
function createField(prop, value, props) {
var transformValue = props.transformValue || _transformValue.toValue;
return _objectSpread({
prop: prop,
transformValue: transformValue
}, props, {
value: transformValue(value)
});
}
/**
* Adds a field to the store. If the field already exists, it would be
* updated instead. Fields within the store have to have a unique key (prop).
* @param {Object} props Data to be parsed and added to the store as a field.
* @returns {any} The value of the field.
*/
function addField(props) {
var prop = props.prop,
value = props.value;
var prevField = (0, _selectors.getField)(prop);
if (prevField) {
return;
}
var prevFields = (0, _selectors.getFields)();
var nextField = createField(prop, value, props);
var nextFields = [].concat(prevFields, [nextField]);
store.setState({
fields: nextFields
});
return value;
}
/**
* Updates a field to the store.
* @param {string} prop The id (prop) of the field to update.
* @param {any} value The next value for the field.
* @returns {any} The next value of the field.
*/
function updateField(prop, value) {
var _updateFields;
var prevField = (0, _selectors.getField)(prop);
if (!prevField) {
return value;
}
var prevValue = prevField.value,
transformValueProp = prevField.transformValue;
var nextValue = transformValueProp(value);
if (prevValue === nextValue) {
return nextValue;
}
updateFields((_updateFields = {}, _updateFields[prop] = value, _updateFields));
return value;
}
/**
* Updates several fields in the store.
* @param {Object} fields Fields to be updated. Key is the id, value is the next value.
* @returns {Array} The updated fields.
*/
function updateFields(fields) {
if (fields === void 0) {
fields = {};
}
if (!_is.is.plainObject(fields)) {
return [];
}
var props = Object.keys(fields);
var prevFields = (0, _selectors.getFields)();
var diffs = 0;
var nextFields = prevFields.map(function (field) {
var prop = field.prop,
value = field.value;
if (props.includes(prop)) {
var nextValue = fields[prop];
if (value !== nextValue) {
diffs++;
}
return _objectSpread({}, field, {
value: nextValue
});
}
return field;
}); // Prevents unnecessary re-renders if there are no changes.
if (diffs) {
store.setState({
fields: nextFields
});
}
return nextFields;
}
/**
* A forceful way to override the fields. Hope you know what you're doing!
* @param {Array|Object} fields The next fields to forcefully update to.
* @returns {any} The next fields, if successful.
*/
function unsafeOverrideFields(fields) {
if (fields === void 0) {
fields = {};
}
var nextFields;
if (_is.is.plainObject(fields)) {
nextFields = Object.keys(fields).reduce(function (next, prop) {
var value = fields[prop];
return [].concat(next, [{
prop: prop,
value: value
}]);
}, []);
}
if (_is.is.array) {
nextFields = fields;
}
if (nextFields) {
store.setState({
fields: nextFields
});
}
return nextFields;
}
/**
* Resets (empties) the all of the fields within the store. This enables
* the default behaviour of removing fields when the component that
* triggers the control via useControl is unmounted.
*/
function resetFields() {
var prevFields = (0, _selectors.getFields)();
var nextFields = prevFields.filter(function () {
return false;
});
store.setState({
fields: nextFields
});
}