wj-elements
Version:
WebJET Elements is a modern set of user interface tools harnessing the power of web components designed to simplify web application development.
401 lines (400 loc) • 14.6 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
const addAction = (stateValueName) => {
return (payload2) => {
return {
type: `${stateValueName}/ADD`,
payload: structuredClone(payload2),
actionType: "ADD"
};
};
};
const addManyAction = (stateValueName) => {
return (payload2) => {
return {
type: `${stateValueName}/ADD_MANY`,
payload: structuredClone(payload2),
actionType: "ADD_MANY"
};
};
};
const updateAction = (stateValueName) => {
return (payload2) => {
return {
type: `${stateValueName}/UPDATE`,
payload: structuredClone(payload2),
actionType: "UPDATE"
};
};
};
const deleteAction = (stateValueName) => {
return (payload2) => {
return {
type: `${stateValueName}/DELETE`,
payload: structuredClone(payload2),
actionType: "DELETE"
};
};
};
const loadAction = (stateValueName) => {
return (payload2) => {
return {
type: `${stateValueName}/LOAD`,
payload: structuredClone(payload2),
actionType: "LOAD"
};
};
};
const defaultStoreActions = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
__proto__: null,
addAction,
addManyAction,
deleteAction,
loadAction,
updateAction
}, Symbol.toStringTag, { value: "Module" }));
class PubSub {
constructor() {
this.events = {};
}
/**
* Either create a new event instance for passed `event` name.
* or push a new callback into the existing collection.
* @param {string} event The event name to subscribe to
* @param {Function} callback The callback function to subscribe to the event
* @returns {number} A count of callbacks for this event
* @memberof PubSub
*/
subscribe(event, callback) {
let self = this;
if (!self.events.hasOwnProperty(event)) {
self.events[event] = [];
}
self.events[event].push(callback) - 1;
return {
unsubscribe() {
self.events[event].splice(self.events[event].indexOf(callback), 1);
}
};
}
/**
* If the passed event has callbacks attached to it, loop through each one and call it.
* @param {string} event The name of the event to publish
* @param {any} state The current state to pass to the callbacks
* @param {object} [newData] The new data to pass to the callbacks
* @param {object} [oldData] The old data to pass to the callbacks
* @returns {Array} The results of the callbacks for this event, or an empty array if no event exists
* @memberof PubSub
*/
publish(event, state, newData = {}, oldData = {}) {
let self = this;
if (!self.events.hasOwnProperty(event)) {
return [];
}
return self.events[event].map((callback) => callback(state, oldData, newData));
}
}
class Store {
/**
* Initializes the store with optional reducer and state.
* @param {object} [params] Configuration for the store.
* @param {Function} [params.reducer] Initial reducer function for handling state updates.
* @param {object} [params.state] Initial state of the store.
*/
constructor(params = {}) {
__publicField(this, "_state");
__publicField(this, "_reducer");
__publicField(this, "events");
__publicField(this, "status");
this._isPause = false;
this._state = {};
this._reducer = () => {
return {};
};
this.status = "resting";
this.events = new PubSub();
if (params == null ? void 0 : params.hasOwnProperty("reducer")) {
this._reducer = params.reducer;
}
this.refreshProxy(params == null ? void 0 : params.state);
}
/**
* Dispatches an action to update the state by invoking the reducer function.
* @param {object} action The action object containing the type and any associated payload.
* @param {string} action.type The type of the action being dispatched.
* @returns {boolean} Returns `true` after the state has been successfully updated.
* @example
* const action = { type: 'INCREMENT', payload: { amount: 1 } };
* store.dispatch(action);
*/
dispatch(action) {
this.status = "action";
let newState = this._reducer(this._state, action);
this.status = "mutation";
this._state = Object.assign(this._state, newState);
return true;
}
/**
* Retrieves a deep copy of the current state to ensure immutability.
* @returns {object} A deep copy of the current state.
* @example
* const currentState = store.getState();
* console.log(currentState);
*/
getState() {
return JSON.parse(JSON.stringify(this._state));
}
/**
* Subscribes to a specific event with a provided callback function.
* @param {string} eventName The name of the event to subscribe to.
* @param {Function} callbackFn The function to execute when the event is triggered.
* @returns {Function} - A function to unsubscribe from the event.
* @example
* const unsubscribe = store.subscribe('stateChange', (newState) => {
* console.log('State changed:', newState);
* });
* // Later, to unsubscribe
* unsubscribe();
*/
subscribe(eventName, callbackFn) {
return this.events.subscribe(eventName, callbackFn);
}
/**
* Unsubscribes from a specific event by removing all associated listeners.
* @param {string} eventName The name of the event to unsubscribe from.
* @returns {void}
* @example
* store.unsubscribe('stateChange');
*/
unsubscribe(eventName) {
delete this.events[eventName];
}
/**
* Pauses event handling or other operations.
* @returns {this} Returns the current instance for method chaining.
* @example
* store.pause().doSomething();
*/
pause() {
this._isPause = true;
return this;
}
/**
* Resumes event handling or other operations.
* @param {*} [val] Optional value to pass while resuming.
* @returns {this} Returns the current instance for method chaining.
* @example
* store.play().doSomething();
*/
play(val) {
this._isPause = false;
return this;
}
/**
* Merges a new reducer function into the existing reducer for a specific state property.
* @param {string} stateValueName The key in the state object that the new reducer will manage.
* @param {Function} newReducer The reducer function to handle updates for the specified state property.
* @returns {void}
* @example
* const newReducer = (newState, currentState) => ({ ...currentState, ...newState });
* store.mergeReducers('user', newReducer);
*/
mergeReducers(stateValueName, newReducer) {
let reducerCopy = this._reducer;
this._reducer = (state, newState) => {
let preState = reducerCopy(state, newState);
return {
...preState,
[stateValueName]: newReducer(newState, state[stateValueName])
};
};
}
/**
* Synchronizes each entry in an array with the store by defining or updating state entries.
* @param {string} storeKey The key prefix used for defining or updating store entries.
* @param {Array<object>} [array] The array of entries to be synchronized with the store.
* @param {string} [identificator] The property name used as a unique identifier for each entry.
* @returns {void}
* @example
* const data = [{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }];
* store.makeEveryArrayEntryAsStoreState('items', data, 'id');
*/
makeEveryArrayEntryAsStoreState(storeKey, array = [], identificator = "id") {
array.forEach((entry) => {
if (this.getState().hasOwnProperty(`${storeKey}-${entry[identificator]}`)) {
this.dispatch(updateAction(`${storeKey}-${entry[identificator]}`)(entry));
} else {
this.define(
`${storeKey}-${entry.id || entry.source || entry[identificator]}`,
entry,
null,
identificator
);
}
});
}
/**
* Defines a new state variable and associates it with a reducer.
* @param {string} stateValueName The name of the state variable to define.
* @param {*} defaultValue The initial value of the state variable.
* @param {Function|null} [reducer] An optional reducer function to manage updates for the state variable.
* @param {string} [key] The key used to identify individual entries if the state value is an array or object.
* @returns {void}
* @example
* // Define a new state with a custom reducer
* store.define('user', { id: 1, name: 'John Doe' }, (newState, currentState) => ({ ...currentState, ...newState }));
* @example
* // Define a new state with default array reducer
* store.define('items', [], null, 'itemId');
*/
define(stateValueName, defaultValue, reducer, key = "id") {
if (this._state.hasOwnProperty(stateValueName)) {
console.warn(`STATE už obsahuje premennú ${stateValueName},ktorú sa pokúšate pridať`);
return;
}
if (reducer instanceof Function) {
this.mergeReducers(stateValueName, reducer);
} else {
if (defaultValue instanceof Array) {
this.mergeReducers(stateValueName, this.createArrayReducer(stateValueName, key));
} else {
this.mergeReducers(stateValueName, this.createObjectReducer(stateValueName, key));
}
}
this.refreshProxy({
...this._state,
[stateValueName]: defaultValue
});
}
/**
* Refreshes the state by wrapping it in a Proxy to track changes and notify subscribers.
* @param {object} newState The new state object to be set. Defaults to an empty object if not provided.
* @returns {void}
* @example
* store.refreshProxy({ user: { id: 1, name: 'John Doe' } });
*/
refreshProxy(newState) {
this._state = new Proxy(newState || {}, {
set: (state, key, value) => {
if (JSON.stringify(state[key]) === JSON.stringify(value)) {
return true;
}
let oldState = state[key];
state[key] = value;
if (!this._isPause) this.events.publish(key, this._state, state[key], oldState);
if (this.status !== "mutation") {
console.warn(`You should use a mutation to set ${key}`);
}
this.status = "resting";
return true;
}
});
}
/**
* Creates a reducer function to manage an object state.
* @param {string} stateValueName The name of the state property this reducer manages.
* @returns {Function} A reducer function that handles `ADD`, `UPDATE`, and `DELETE` actions for the specified state property.
* @throws {Error} If the payload is an array, an error is logged since the reducer is designed for object state management.
* @example
* const userReducer = store.createObjectReducer('user');
* const newState = userReducer({ type: 'user/ADD', payload: { id: 1, name: 'John Doe' } });
*/
createObjectReducer(stateValueName) {
return (action, state = {}) => {
if (Array.isArray(action.payload) && (action.type === `${stateValueName}/ADD` || action.type === `${stateValueName}/UPDATE`)) {
console.error(`Nemôžete pridať do objektu ${stateValueName} hodnotu, ktorá je pole.`);
}
const actionType = action.type.split("/")[1];
if (!["ADD", "UPDATE", "DELETE"].includes(actionType)) {
console.error(
`Nemôžete použiť akciu ${actionType} na objekt. Správne akcie pre objekt sú: ADD, UPDATE, DELETE`
);
}
switch (action.type) {
case `${stateValueName}/ADD`:
return {
...action.payload
};
case `${stateValueName}/UPDATE`:
return {
...state,
...action.payload
};
case `${stateValueName}/DELETE`:
return {};
default:
return state;
}
};
}
/**
* Creates a reducer function to manage an array state.
* @param {string} stateValueName The name of the state property this reducer manages.
* @param {string} key The unique key used to identify items in the array for updates and deletions.
* @returns {Function} A reducer function that handles `ADD`, `ADD_MANY`, `UPDATE`, `DELETE`, and `LOAD` actions for the specified state property.
* @throws {Error} If `action.payload` is not an array when required.
* @example
* const itemsReducer = store.createArrayReducer('items', 'id');
* const newState = itemsReducer({ type: 'items/ADD', payload: { id: 1, name: 'Item 1' } });
*/
createArrayReducer(stateValueName, key) {
return (action, state = []) => {
var _a;
if (action.actionType === "LOAD" && ((_a = action.type) == null ? void 0 : _a.includes(stateValueName))) {
if (!Array.isArray(action.payload)) {
console.error(`Snažíte sa použiť "LOAD" akciu na pole, ale payload nie je pole.`);
return [...state];
}
}
switch (action.type) {
case `${stateValueName}/ADD`:
if (Array.isArray(action.payload)) {
return [...state, ...action.payload];
} else {
return [...state, action.payload];
}
case `${stateValueName}/ADD_MANY`:
return [...state, ...action.payload];
case `${stateValueName}/UPDATE`:
if (state.some((obj) => obj[key] === action.payload[key])) {
return [
...state.map((obj) => {
if (obj[key] === action.payload[key]) {
return action.payload;
}
return obj;
})
];
} else {
return [...state, action.payload];
}
case `${stateValueName}/DELETE`:
if (Array.isArray(action.payload)) {
return [
...state.filter(
(obj) => !action.payload.some(
(item) => obj.hasOwnProperty(key) && obj[key] !== item[key] || !obj.hasOwnProperty(key) && obj !== item
)
)
];
}
return [
...state.filter(
(obj) => obj.hasOwnProperty(key) && obj[key] !== action.payload[key] || !obj.hasOwnProperty(key) && obj !== action.payload
)
];
case `${stateValueName}/LOAD`:
return [...action.payload];
default:
return state;
}
};
}
}
let store = new Store();
export {
defaultStoreActions,
store
};
//# sourceMappingURL=wje-store.js.map