UNPKG

travels

Version:

A fast, framework-agnostic undo/redo core library powered by Mutative JSON Patch

3 lines (2 loc) 9.94 kB
"use strict";var t=require("mutative");"function"==typeof SuppressedError&&SuppressedError;const s=t=>"object"==typeof t&&null!==t,e=t=>{if(!s(t))return!1;const e=Object.getPrototypeOf(t);return null===e||e===Object.prototype},i=t=>({patches:t?t.patches.map(t=>[...t]):[],inversePatches:t?t.inversePatches.map(t=>[...t]):[]}),a=t=>{if(null===t||"object"!=typeof t)return t;if(Array.isArray(t))return t.map(a);const s={};for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(s[e]=a(t[e]));return s},h=(t,s)=>{if(s&&t&&"object"==typeof t){for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(s[e]=a(t[e]));return s}return a(t)},r=t=>!!Array.isArray(t)&&Reflect.ownKeys(t).every(t=>{if("length"===t)return!0;if("symbol"==typeof t)return!1;const s=Number(t);return Number.isInteger(s)&&s>=0&&String(s)===t});class n{constructor(t,s={}){this.listeners=new Set,this.pendingState=null,this.historyCache=null,this.historyVersion=0,this.mutableFallbackWarned=!1,this.subscribe=t=>(this.listeners.add(t),()=>{this.listeners.delete(t)}),this.getState=()=>this.state;const{maxHistory:e=10,initialPatches:a,initialPosition:r=0,autoArchive:n=!0,mutable:o=!1,patchesOptions:c}=s,l=function(t,s){var e={};for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&s.indexOf(i)<0&&(e[i]=t[i]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var a=0;for(i=Object.getOwnPropertySymbols(t);a<i.length;a++)s.indexOf(i[a])<0&&Object.prototype.propertyIsEnumerable.call(t,i[a])&&(e[i[a]]=t[i[a]])}return e}(s,["maxHistory","initialPatches","initialPosition","autoArchive","mutable","patchesOptions"]);if(e<0)throw new Error(`Travels: maxHistory must be non-negative, but got ${e}`);0===e&&"production"!==process.env.NODE_ENV&&console.warn("Travels: maxHistory is 0, which disables undo/redo history. This is rarely intended."),"production"!==process.env.NODE_ENV&&a&&(Array.isArray(a.patches)&&Array.isArray(a.inversePatches)?a.patches.length!==a.inversePatches.length&&console.error("Travels: initialPatches.patches and initialPatches.inversePatches must have the same length"):console.error("Travels: initialPatches must have 'patches' and 'inversePatches' arrays")),this.state=t,this.initialState=o?h(t):t,this.maxHistory=e,this.autoArchive=n,this.mutable=o,this.options=Object.assign(Object.assign({},l),{enablePatches:null==c||c});const{patches:p,position:P}=this.normalizeInitialHistory(a,r);this.allPatches=p,this.initialPatches=a?i(p):void 0,this.position=P,this.initialPosition=P,this.tempPatches=i()}normalizeInitialHistory(t,s){const e=i(t),a=e.patches.length,h=this.maxHistory>0?this.maxHistory:0,r="number"!=typeof s||!Number.isFinite(s);let n=r?0:s;const o=Math.max(0,Math.min(n,a));if("production"===process.env.NODE_ENV||!r&&o===n||console.warn(`Travels: initialPosition (${s}) is invalid for available patches (${a}). Using ${o} instead.`),n=o,0===a)return{patches:e,position:0};if(0===h)return"production"!==process.env.NODE_ENV&&console.warn(`Travels: maxHistory (${this.maxHistory}) discards persisted history.`),{patches:i(),position:0};if(h>=a)return{patches:e,position:n};const c=a-h,l={patches:e.patches.slice(-h),inversePatches:e.inversePatches.slice(-h)},p=i(l),P=Math.max(0,Math.min(h,n-c));return"production"!==process.env.NODE_ENV&&console.warn(`Travels: initialPatches length (${a}) exceeds maxHistory (${h}). Trimmed to last ${h} steps. Position adjusted to ${P}.`),{patches:p,position:P}}invalidateHistoryCache(){this.historyVersion+=1,this.historyCache=null}notify(){this.listeners.forEach(t=>t(this.state,this.getPatches(),this.position))}hasRootReplacement(t){return t.some(t=>Array.isArray(t.path)&&0===t.path.length&&"replace"===t.op)}setState(i){let a,h;const n=this.mutable&&s(this.state),o="function"==typeof i,c=Array.isArray(this.state),l=Array.isArray(i),p=!c&&!l&&e(this.state)&&e(i),P=c&&l&&r(this.state)&&r(i),y=o&&n||n&&!o&&(P||p);if(!this.mutable||n||this.mutableFallbackWarned||(this.mutableFallbackWarned=!0,"production"!==process.env.NODE_ENV&&console.warn("Travels: mutable mode requires the state root to be an object. Falling back to immutable updates.")),y)[,a,h]=t.create(this.state,o?i:t=>{((t,s)=>{const e=Array.isArray(t),i=Array.isArray(s),a=Reflect.ownKeys(t);for(const i of a)e&&"length"===i||Object.prototype.hasOwnProperty.call(s,i)||delete t[i];e&&i&&(t.length=s.length),Object.assign(t,s)})(t,i)},this.options),t.apply(this.state,a,{mutable:!0}),this.pendingState=this.state;else{const[e,r,n]="function"==typeof i?t.create(this.state,i,this.options):t.create(this.state,()=>s(i)?t.rawReturn(i):i,this.options);a=r,h=n,this.state=e,this.pendingState=e}Promise.resolve().then(()=>{this.pendingState=null});if(!(0===a.length&&0===h.length)){if(this.autoArchive){this.position<this.allPatches.patches.length&&(this.allPatches.patches.splice(this.position,this.allPatches.patches.length-this.position),this.allPatches.inversePatches.splice(this.position,this.allPatches.inversePatches.length-this.position)),this.allPatches.patches.push(a),this.allPatches.inversePatches.push(h),this.position=this.maxHistory<this.allPatches.patches.length?this.maxHistory:this.position+1,this.maxHistory<this.allPatches.patches.length&&(0===this.maxHistory?(this.allPatches.patches=[],this.allPatches.inversePatches=[]):(this.allPatches.patches=this.allPatches.patches.slice(-this.maxHistory),this.allPatches.inversePatches=this.allPatches.inversePatches.slice(-this.maxHistory)))}else{const t=this.position<this.allPatches.patches.length+Number(!!this.tempPatches.patches.length);t&&(this.allPatches.patches.splice(this.position,this.allPatches.patches.length-this.position),this.allPatches.inversePatches.splice(this.position,this.allPatches.inversePatches.length-this.position)),this.tempPatches.patches.length&&!t||(this.position=this.maxHistory<this.allPatches.patches.length+1?this.maxHistory:this.position+1),t&&(this.tempPatches.patches.length=0,this.tempPatches.inversePatches.length=0),this.tempPatches.patches.push(a),this.tempPatches.inversePatches.push(h)}this.invalidateHistoryCache(),this.notify()}}archive(){var s;if(this.autoArchive)return void console.warn("Auto archive is enabled, no need to archive manually");if(!this.tempPatches.patches.length)return;const e=null!==(s=this.pendingState)&&void 0!==s?s:this.state,[,i,a]=t.create(e,s=>t.apply(s,this.tempPatches.inversePatches.flat().reverse()),this.options);this.allPatches.patches.push(a),this.allPatches.inversePatches.push(i),this.maxHistory<this.allPatches.patches.length&&(0===this.maxHistory?(this.allPatches.patches=[],this.allPatches.inversePatches=[]):(this.allPatches.patches=this.allPatches.patches.slice(-this.maxHistory),this.allPatches.inversePatches=this.allPatches.inversePatches.slice(-this.maxHistory))),this.tempPatches.patches.length=0,this.tempPatches.inversePatches.length=0,this.invalidateHistoryCache(),this.notify()}getAllPatches(){return!this.autoArchive&&!!this.tempPatches.patches.length?{patches:this.allPatches.patches.concat([this.tempPatches.patches.flat()]),inversePatches:this.allPatches.inversePatches.concat([this.tempPatches.inversePatches.flat().reverse()])}:this.allPatches}getHistory(){if(this.historyCache&&this.historyCache.version===this.historyVersion)return this.historyCache.history;const s=[this.state];let e=this.state;const i=this.getAllPatches(),a=!this.autoArchive&&i.patches.length>this.maxHistory?i.patches.slice(i.patches.length-this.maxHistory):i.patches,h=!this.autoArchive&&i.inversePatches.length>this.maxHistory?i.inversePatches.slice(i.inversePatches.length-this.maxHistory):i.inversePatches;for(let i=this.position;i<a.length;i++)e=t.apply(e,a[i]),s.push(e);e=this.state;for(let i=this.position-1;i>-1;i--)e=t.apply(e,h[i]),s.unshift(e);return this.historyCache={version:this.historyVersion,history:s},"production"!==process.env.NODE_ENV&&Object.freeze(s),s}go(e){const i=!this.autoArchive&&!!this.tempPatches.patches.length;i&&this.archive();const a=this.getAllPatches(),h=e<this.position;if(e>a.patches.length&&(console.warn(`Can't go forward to position ${e}`),e=a.patches.length),e<0&&(console.warn(`Can't go back to position ${e}`),e=0),e===this.position)return;if(i){const t=a.inversePatches.slice(-1)[0];a.inversePatches[a.inversePatches.length-1]=[...t].reverse()}const r=h?a.inversePatches.slice(-this.maxHistory).slice(e,this.position).flat().reverse():a.patches.slice(-this.maxHistory).slice(this.position,e).flat();this.mutable&&s(this.state)&&!this.hasRootReplacement(r)?t.apply(this.state,r,{mutable:!0}):this.state=t.apply(this.state,r),this.position=e,this.invalidateHistoryCache(),this.notify()}back(t=1){this.go(this.position-t)}forward(t=1){this.go(this.position+t)}reset(){if(this.mutable&&s(this.state)&&s(this.initialState)){const[,s]=t.create(this.state,t=>{for(const s of Object.keys(t))delete t[s];h(this.initialState,t)},this.options);t.apply(this.state,s,{mutable:!0})}else this.state=this.initialState;this.position=this.initialPosition,this.allPatches=i(this.initialPatches),this.tempPatches=i(),this.invalidateHistoryCache(),this.notify()}canBack(){return this.position>0}canForward(){const t=!this.autoArchive&&!!this.tempPatches.patches.length,s=this.getAllPatches();return t?this.position<s.patches.length-1:this.position<s.patches.length}canArchive(){return!this.autoArchive&&!!this.tempPatches.patches.length}getPosition(){return this.position}getPatches(){return!this.autoArchive&&!!this.tempPatches.patches.length?this.getAllPatches():this.allPatches}getControls(){const t=this,s={get position(){return t.getPosition()},getHistory:()=>t.getHistory(),get patches(){return t.getPatches()},back:s=>t.back(s),forward:s=>t.forward(s),reset:()=>t.reset(),go:s=>t.go(s),canBack:()=>t.canBack(),canForward:()=>t.canForward()};return this.autoArchive||(s.archive=()=>t.archive(),s.canArchive=()=>t.canArchive()),s}}exports.Travels=n,exports.createTravels=function(t,s={}){return new n(t,s)}; //# sourceMappingURL=index.cjs.map