1- import { dispatch } from 'codesandbox-api' ;
1+ import { dispatch , isStandalone } from 'codesandbox-api' ;
22
33function sendUrlChange ( url : string ) {
44 dispatch ( {
@@ -7,40 +7,123 @@ function sendUrlChange(url: string) {
77 } ) ;
88}
99
10+ /* eslint-disable no-console */
11+
12+ const origHistoryProto = window . history . __proto__ ; // eslint-disable-line no-proto
13+ const historyList = [ ] ;
14+ let historyPosition = - 1 ;
15+ let disableNextHashChange = false ;
16+
17+ function pushHistory ( url , state ) {
18+ if ( historyPosition === - 1 || historyList [ historyPosition ] . url !== url ) {
19+ historyPosition += 1 ;
20+ historyList . length = historyPosition + 1 ;
21+ historyList [ historyPosition ] = { url, state } ;
22+ }
23+ }
24+
25+ function pathWithHash ( location ) {
26+ return `${ location . pathname } ${ location . hash } ` ;
27+ }
28+
1029export default function setupHistoryListeners ( ) {
11- const pushState = window . history . pushState ;
12- window . history . pushState = function ( state ) {
13- if ( typeof history . onpushstate === 'function' ) {
14- window . history . onpushstate ( { state } ) ;
15- }
16- // ... whatever else you want to do
17- // maybe call onhashchange e.handler
18- return pushState . apply ( window . history , arguments ) ;
19- } ;
20-
21- const replaceState = window . history . replaceState ;
22- window . history . replaceState = function ( state ) {
23- if ( typeof history . onpushstate === 'function' ) {
24- window . history . onpushstate ( { state } ) ;
25- }
26- // ... whatever else you want to do
27- // maybe call onhashchange e.handler
28- return replaceState . apply ( window . history , arguments ) ;
29- } ;
30-
31- history . onpushstate = e => {
32- setTimeout ( ( ) => {
33- sendUrlChange ( document . location . href ) ;
30+ if ( ! isStandalone ) {
31+ Object . assign ( window . history , {
32+ go ( delta ) {
33+ console . log ( `go(${ delta } )` ) ;
34+ const newPos = historyPosition + delta ;
35+ if ( newPos >= 0 && newPos <= historyList . length - 1 ) {
36+ historyPosition = newPos ;
37+ const { url, state } = historyList [ historyPosition ] ;
38+ const oldURL = document . location . href ;
39+ origHistoryProto . replaceState . call ( window . history , state , '' , url ) ;
40+ const newURL = document . location . href ;
41+ if ( newURL . indexOf ( '#' ) === - 1 ) {
42+ window . dispatchEvent ( new PopStateEvent ( 'popstate' , { state } ) ) ;
43+ } else {
44+ disableNextHashChange = true ;
45+ window . dispatchEvent (
46+ new HashChangeEvent ( 'hashchange' , { oldURL, newURL } )
47+ ) ;
48+ }
49+ }
50+ } ,
51+
52+ back ( ) {
53+ console . log ( 'back()' ) ;
54+ window . history . go ( - 1 ) ;
55+ } ,
56+
57+ forward ( ) {
58+ console . log ( 'forward()' ) ;
59+ window . history . go ( 1 ) ;
60+ } ,
61+
62+ pushState ( state , title , url ) {
63+ origHistoryProto . replaceState . call ( window . history , state , title , url ) ;
64+ pushHistory ( url , state ) ;
65+ sendUrlChange ( document . location . href ) ;
66+ } ,
67+
68+ replaceState ( state , title , url ) {
69+ origHistoryProto . replaceState . call ( window . history , state , title , url ) ;
70+ historyList [ historyPosition ] = { state, url } ;
71+ sendUrlChange ( document . location . href ) ;
72+ } ,
73+ } ) ;
74+
75+ Object . defineProperties ( window . history , {
76+ length : {
77+ get ( ) {
78+ return historyList . length ;
79+ } ,
80+ } ,
81+
82+ state : {
83+ get ( ) {
84+ return historyList [ historyPosition ] . state ;
85+ } ,
86+ } ,
87+ } ) ;
88+
89+ window . addEventListener ( 'hashchange' , ( ) => {
90+ if ( ! disableNextHashChange ) {
91+ const url = pathWithHash ( document . location ) ;
92+ pushHistory ( url , null ) ;
93+ sendUrlChange ( document . location . href ) ;
94+ } else {
95+ disableNextHashChange = false ;
96+ }
3497 } ) ;
35- } ;
3698
37- history . onreplacestate = e => {
99+ document . addEventListener (
100+ 'click' ,
101+ ev => {
102+ const el = ev . target ;
103+ if ( el . nodeName === 'A' && el . href . indexOf ( '#' ) !== - 1 ) {
104+ const url = el . href ;
105+ const oldURL = document . location . href ;
106+ origHistoryProto . replaceState . call ( window . history , null , '' , url ) ;
107+ const newURL = document . location . href ;
108+ if ( oldURL !== newURL ) {
109+ disableNextHashChange = true ;
110+ window . dispatchEvent (
111+ new HashChangeEvent ( 'hashchange' , { oldURL, newURL } )
112+ ) ;
113+ pushHistory ( pathWithHash ( document . location ) , null ) ;
114+ sendUrlChange ( document . location . href ) ;
115+ }
116+ ev . preventDefault ( ) ;
117+ ev . stopPropagation ( ) ;
118+ }
119+ } ,
120+ true
121+ ) ;
122+
123+ pushHistory ( pathWithHash ( document . location ) , null ) ;
124+
38125 setTimeout ( ( ) => {
39126 sendUrlChange ( document . location . href ) ;
40127 } ) ;
41- } ;
42-
43- setTimeout ( ( ) => {
44- sendUrlChange ( document . location . href ) ;
45- } ) ;
128+ }
46129}
0 commit comments