@studiometa/js-toolkit
Version:
A set of useful little bits of JavaScript to boost your project! 🚀
171 lines (170 loc) • 5.05 kB
JavaScript
import deepmerge from "deepmerge";
import { AbstractManager } from "./AbstractManager.js";
import {
isDev,
isFunction,
isDefined,
isBoolean,
isArray,
isObject,
memo
} from "../../utils/index.js";
import { features } from "../features.js";
const types = /* @__PURE__ */ new Set([String, Number, Boolean, Array, Object]);
const __defaultValues = {
String: "",
Number: 0,
Boolean: false,
Array: () => [],
Object: () => ({})
};
const UPPERCASE_REGEX = /([A-Z])/g;
const __getPropertyName = memo(function __getPropertyName2(name, prefix = "", optionAttribute = features.get("attributes").option) {
return `${optionAttribute}${prefix ? `-${prefix}` : ""}-${name.replaceAll(UPPERCASE_REGEX, "-$1")}`;
});
class OptionsManager extends AbstractManager {
/**
* An object to store Array and Object values for reference.
*
* @private
*/
__values = {};
/**
* Default props.
*/
__props = {
name: "Base",
debug: false,
log: false
};
/**
* Class constructor.
*/
constructor(base) {
super(base);
const schema = this.__config.options || {};
this.props.name = this.__config.name;
schema.debug = {
type: Boolean,
default: this.__config.debug ?? false
};
schema.log = {
type: Boolean,
default: this.__config.log ?? false
};
for (const [name, config] of Object.entries(schema)) {
this.__register(
name,
types.has(config) ? { type: config } : config
);
}
}
/**
* Get an option value.
*/
get(name, config) {
const { type } = config;
const defaultValue = isFunction(config.default) ? config.default(this.__base) : config.default;
const attributes = features.get("attributes");
const propertyName = __getPropertyName(name, "", attributes.option);
const hasProperty = this.__element.hasAttribute(propertyName);
if (type === Boolean) {
if (defaultValue) {
const negatedPropertyName = __getPropertyName(name, "no", attributes.option);
const hasNegatedProperty = this.__element.hasAttribute(negatedPropertyName);
return !hasNegatedProperty;
}
return hasProperty || defaultValue;
}
const value = this.__element.getAttribute(propertyName);
if (type === Number) {
return hasProperty ? Number(value) : defaultValue;
}
if (type === Array || type === Object) {
config = type === Array ? config : config;
if (!this.__values[name]) {
let val = hasProperty ? JSON.parse(value) : defaultValue;
if (isDefined(config.merge)) {
val = isBoolean(config.merge) ? deepmerge(defaultValue, val) : deepmerge(defaultValue, val, config.merge);
}
this.__values[name] = val;
}
return this.__values[name];
}
return hasProperty ? value : defaultValue;
}
/**
* Set an option value.
*/
set(name, value, config) {
const { type, default: defaultValue } = config;
const attributes = features.get("attributes");
const propertyName = __getPropertyName(name, "", attributes.option);
if (value.constructor.name !== type.name) {
if (isDev) {
const val = isArray(value) || isObject(value) ? JSON.stringify(value) : value;
throw new TypeError(
`The "${val}" value for the "${name}" option must be of type "${type.name}"`
);
}
return;
}
switch (type) {
case Boolean:
if (defaultValue) {
const negatedPropertyName = __getPropertyName(name, "no", attributes.option);
if (value) {
this.__element.removeAttribute(negatedPropertyName);
} else {
this.__element.setAttribute(negatedPropertyName, "");
}
} else if (value) {
this.__element.setAttribute(propertyName, "");
} else {
this.__element.removeAttribute(propertyName);
}
break;
case Array:
case Object:
this.__values[name] = value;
break;
default:
this.__element.setAttribute(propertyName, value);
}
}
/**
* Register an option.
* @private
*/
__register(name, config) {
if (!types.has(config.type)) {
if (isDev) {
throw new Error(
`The "${name}" option has an invalid type. The allowed types are: String, Number, Boolean, Array and Object.`
);
}
return;
}
config.default = config.default ?? __defaultValues[config.type.name];
if ((config.type === Array || config.type === Object) && !isFunction(config.default)) {
if (isDev) {
throw new Error(
`The default value for options of type "${config.type.name}" must be returned by a function.`
);
}
return;
}
Object.defineProperty(this.props, name, {
get: () => this.get(name, config),
set: (value) => {
this.set(name, value, config);
},
enumerable: true
});
}
}
export {
OptionsManager,
__getPropertyName
};
//# sourceMappingURL=OptionsManager.js.map