chore: complete update
This commit is contained in:
213
resources/js/core/state.js
Normal file
213
resources/js/core/state.js
Normal file
@@ -0,0 +1,213 @@
|
||||
// src/core/state.js
|
||||
|
||||
const globalState = new Map();
|
||||
|
||||
/**
|
||||
* Normale State-Erstellung (nicht persistent)
|
||||
*/
|
||||
export function createState(key, initialValue) {
|
||||
if (!globalState.has(key)) globalState.set(key, initialValue);
|
||||
|
||||
return {
|
||||
get() {
|
||||
return globalState.get(key);
|
||||
},
|
||||
set(value) {
|
||||
globalState.set(key, value);
|
||||
dispatchEvent(new CustomEvent('statechange', {
|
||||
detail: { key, value }
|
||||
}));
|
||||
},
|
||||
subscribe(callback) {
|
||||
const handler = (event) => {
|
||||
if (event.detail.key === key) {
|
||||
callback(event.detail.value);
|
||||
}
|
||||
};
|
||||
addEventListener('statechange', handler);
|
||||
return () => removeEventListener('statechange', handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Persistent State via localStorage
|
||||
*/
|
||||
export function createPersistentState(key, defaultValue) {
|
||||
const stored = localStorage.getItem(`state:${key}`);
|
||||
const initial = stored !== null ? JSON.parse(stored) : defaultValue;
|
||||
|
||||
const state = createState(key, initial);
|
||||
|
||||
state.subscribe((val) => {
|
||||
localStorage.setItem(`state:${key}`, JSON.stringify(val));
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zugriff auf alle States (intern)
|
||||
*/
|
||||
export function getRawState() {
|
||||
return globalState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementbindung: Text, Attribut, Klasse oder Custom-Callback
|
||||
*/
|
||||
export function bindStateToElement({ state, element, property = 'text', attributeName = null }) {
|
||||
const apply = (value) => {
|
||||
if (property === 'text') {
|
||||
element.textContent = value;
|
||||
} else if (property === 'attr' && attributeName) {
|
||||
element.setAttribute(attributeName, value);
|
||||
} else if (property === 'class') {
|
||||
element.className = value;
|
||||
} else if (typeof property === 'function') {
|
||||
property(element, value);
|
||||
}
|
||||
};
|
||||
|
||||
apply(state.get());
|
||||
return state.subscribe(apply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mehrere Elemente gleichzeitig binden
|
||||
*/
|
||||
export function bindStateToElements({ state, elements, ...rest }) {
|
||||
return elements.map(el => bindStateToElement({ state, element: el, ...rest }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Declarative Auto-Bindings via data-bind Attribute
|
||||
*/
|
||||
export function initStateBindings() {
|
||||
document.querySelectorAll('[data-bind]').forEach(el => {
|
||||
const key = el.getAttribute('data-bind');
|
||||
const state = createState(key, '');
|
||||
bindStateToElement({ state, element: el, property: 'text' });
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-bind-attr]').forEach(el => {
|
||||
const key = el.getAttribute('data-bind-attr');
|
||||
const attr = el.getAttribute('data-bind-attr-name') || 'value';
|
||||
const state = createState(key, '');
|
||||
bindStateToElement({ state, element: el, property: 'attr', attributeName: attr });
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-bind-class]').forEach(el => {
|
||||
const key = el.getAttribute('data-bind-class');
|
||||
const state = createState(key, '');
|
||||
bindStateToElement({ state, element: el, property: 'class' });
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-bind-input]').forEach(el => {
|
||||
const key = el.getAttribute('data-bind-input');
|
||||
const persistent = el.hasAttribute('data-persistent');
|
||||
const state = persistent ? createPersistentState(key, '') : createState(key, '');
|
||||
|
||||
bindInputToState(el, state);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwei-Wege-Bindung für Formulareingaben
|
||||
*/
|
||||
export function bindInputToState(inputEl, state) {
|
||||
// Initialwert setzen
|
||||
if (inputEl.type === 'checkbox') {
|
||||
inputEl.checked = !!state.get();
|
||||
} else {
|
||||
inputEl.value = state.get();
|
||||
}
|
||||
|
||||
// DOM → State
|
||||
inputEl.addEventListener('input', () => {
|
||||
if (inputEl.type === 'checkbox') {
|
||||
state.set(inputEl.checked);
|
||||
} else {
|
||||
state.set(inputEl.value);
|
||||
}
|
||||
});
|
||||
|
||||
// State → DOM
|
||||
return state.subscribe((val) => {
|
||||
if (inputEl.type === 'checkbox') {
|
||||
inputEl.checked = !!val;
|
||||
} else {
|
||||
inputEl.value = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Berechneter Zustand auf Basis anderer States
|
||||
*/
|
||||
export function createComputedState(dependencies, computeFn) {
|
||||
let value = computeFn(...dependencies.map(d => d.get()));
|
||||
const key = `computed:${Math.random().toString(36).slice(2)}`;
|
||||
const state = createState(key, value);
|
||||
|
||||
dependencies.forEach(dep => {
|
||||
dep.subscribe(() => {
|
||||
const newValue = computeFn(...dependencies.map(d => d.get()));
|
||||
state.set(newValue);
|
||||
});
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktiviert Undo/Redo für einen State
|
||||
*/
|
||||
export function enableUndoRedoForState(state) {
|
||||
const history = [];
|
||||
let index = -1;
|
||||
let lock = false;
|
||||
|
||||
const record = (val) => {
|
||||
if (lock) return;
|
||||
history.splice(index + 1); // zukünftige States verwerfen
|
||||
history.push(val);
|
||||
index++;
|
||||
};
|
||||
|
||||
record(state.get());
|
||||
|
||||
const unsubscribe = state.subscribe((val) => {
|
||||
if (!lock) record(val);
|
||||
});
|
||||
|
||||
return {
|
||||
undo() {
|
||||
if (index > 0) {
|
||||
lock = true;
|
||||
index--;
|
||||
state.set(history[index]);
|
||||
lock = false;
|
||||
}
|
||||
},
|
||||
redo() {
|
||||
if (index < history.length - 1) {
|
||||
lock = true;
|
||||
index++;
|
||||
state.set(history[index]);
|
||||
lock = false;
|
||||
}
|
||||
},
|
||||
destroy: unsubscribe
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev-Konsole zur Nachverfolgung von State-Änderungen
|
||||
*/
|
||||
export function enableStateLogging(state, label = 'state') {
|
||||
state.subscribe((val) => {
|
||||
console.debug(`[State Change: ${label}]`, val);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user