crelte 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/LICENSE.md +41 -0
  2. package/dist/Crelte.d.ts +55 -0
  3. package/dist/Crelte.d.ts.map +1 -0
  4. package/dist/Crelte.js +106 -0
  5. package/dist/CrelteBase.d.ts +16 -0
  6. package/dist/CrelteBase.d.ts.map +1 -0
  7. package/dist/CrelteBase.js +1 -0
  8. package/dist/CrelteRouted.d.ts +50 -0
  9. package/dist/CrelteRouted.d.ts.map +1 -0
  10. package/dist/CrelteRouted.js +88 -0
  11. package/dist/blocks/Blocks.d.ts +35 -0
  12. package/dist/blocks/Blocks.d.ts.map +1 -0
  13. package/dist/blocks/Blocks.js +100 -0
  14. package/dist/blocks/Blocks.svelte +21 -0
  15. package/dist/blocks/Blocks.svelte.d.ts +24 -0
  16. package/dist/blocks/Blocks.svelte.d.ts.map +1 -0
  17. package/dist/blocks/index.d.ts +5 -0
  18. package/dist/blocks/index.d.ts.map +1 -0
  19. package/dist/blocks/index.js +3 -0
  20. package/dist/cookies/ClientCookies.d.ts +9 -0
  21. package/dist/cookies/ClientCookies.d.ts.map +1 -0
  22. package/dist/cookies/ClientCookies.js +22 -0
  23. package/dist/cookies/ServerCookies.d.ts +13 -0
  24. package/dist/cookies/ServerCookies.d.ts.map +1 -0
  25. package/dist/cookies/ServerCookies.js +31 -0
  26. package/dist/cookies/index.d.ts +20 -0
  27. package/dist/cookies/index.d.ts.map +1 -0
  28. package/dist/cookies/index.js +1 -0
  29. package/dist/cookies/utils.d.ts +12 -0
  30. package/dist/cookies/utils.d.ts.map +1 -0
  31. package/dist/cookies/utils.js +32 -0
  32. package/dist/graphql/GraphQl.d.ts +60 -0
  33. package/dist/graphql/GraphQl.d.ts.map +1 -0
  34. package/dist/graphql/GraphQl.js +197 -0
  35. package/dist/graphql/gql.test.d.ts +2 -0
  36. package/dist/graphql/gql.test.d.ts.map +1 -0
  37. package/dist/graphql/gql.test.js +80 -0
  38. package/dist/graphql/index.d.ts +3 -0
  39. package/dist/graphql/index.d.ts.map +1 -0
  40. package/dist/graphql/index.js +2 -0
  41. package/dist/index.d.ts +67 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +83 -0
  44. package/dist/init/client.d.ts +13 -0
  45. package/dist/init/client.d.ts.map +1 -0
  46. package/dist/init/client.js +129 -0
  47. package/dist/init/server.d.ts +38 -0
  48. package/dist/init/server.d.ts.map +1 -0
  49. package/dist/init/server.js +95 -0
  50. package/dist/init/shared.d.ts +29 -0
  51. package/dist/init/shared.d.ts.map +1 -0
  52. package/dist/init/shared.js +154 -0
  53. package/dist/loadData/Globals.d.ts +33 -0
  54. package/dist/loadData/Globals.d.ts.map +1 -0
  55. package/dist/loadData/Globals.js +119 -0
  56. package/dist/loadData/index.d.ts +25 -0
  57. package/dist/loadData/index.d.ts.map +1 -0
  58. package/dist/loadData/index.js +39 -0
  59. package/dist/plugins/Events.d.ts +11 -0
  60. package/dist/plugins/Events.d.ts.map +1 -0
  61. package/dist/plugins/Events.js +29 -0
  62. package/dist/plugins/Plugins.d.ts +12 -0
  63. package/dist/plugins/Plugins.d.ts.map +1 -0
  64. package/dist/plugins/Plugins.js +12 -0
  65. package/dist/plugins/index.d.ts +5 -0
  66. package/dist/plugins/index.d.ts.map +1 -0
  67. package/dist/plugins/index.js +2 -0
  68. package/dist/routing/History.d.ts +22 -0
  69. package/dist/routing/History.d.ts.map +1 -0
  70. package/dist/routing/History.js +36 -0
  71. package/dist/routing/InnerRouter.d.ts +111 -0
  72. package/dist/routing/InnerRouter.d.ts.map +1 -0
  73. package/dist/routing/InnerRouter.js +397 -0
  74. package/dist/routing/PageLoader.d.ts +37 -0
  75. package/dist/routing/PageLoader.d.ts.map +1 -0
  76. package/dist/routing/PageLoader.js +72 -0
  77. package/dist/routing/Route.d.ts +82 -0
  78. package/dist/routing/Route.d.ts.map +1 -0
  79. package/dist/routing/Route.js +134 -0
  80. package/dist/routing/Router.d.ts +162 -0
  81. package/dist/routing/Router.d.ts.map +1 -0
  82. package/dist/routing/Router.js +333 -0
  83. package/dist/routing/Site.d.ts +47 -0
  84. package/dist/routing/Site.d.ts.map +1 -0
  85. package/dist/routing/Site.js +48 -0
  86. package/dist/routing/index.d.ts +5 -0
  87. package/dist/routing/index.d.ts.map +1 -0
  88. package/dist/routing/index.js +4 -0
  89. package/dist/ssr/SsrCache.d.ts +12 -0
  90. package/dist/ssr/SsrCache.d.ts.map +1 -0
  91. package/dist/ssr/SsrCache.js +50 -0
  92. package/dist/ssr/SsrComponents.d.ts +7 -0
  93. package/dist/ssr/SsrComponents.d.ts.map +1 -0
  94. package/dist/ssr/SsrComponents.js +30 -0
  95. package/dist/ssr/index.d.ts +4 -0
  96. package/dist/ssr/index.d.ts.map +1 -0
  97. package/dist/ssr/index.js +3 -0
  98. package/package.json +79 -0
  99. package/src/Crelte.ts +135 -0
  100. package/src/CrelteBase.ts +24 -0
  101. package/src/CrelteRouted.ts +128 -0
  102. package/src/blocks/Blocks.svelte +68 -0
  103. package/src/blocks/Blocks.ts +155 -0
  104. package/src/blocks/index.ts +14 -0
  105. package/src/cookies/ClientCookies.ts +30 -0
  106. package/src/cookies/ServerCookies.ts +42 -0
  107. package/src/cookies/index.ts +24 -0
  108. package/src/cookies/utils.ts +53 -0
  109. package/src/graphql/GraphQl.ts +281 -0
  110. package/src/graphql/gql.test.ts +123 -0
  111. package/src/graphql/index.ts +8 -0
  112. package/src/index.ts +109 -0
  113. package/src/init/client.ts +190 -0
  114. package/src/init/server.ts +177 -0
  115. package/src/init/shared.ts +221 -0
  116. package/src/loadData/Globals.ts +150 -0
  117. package/src/loadData/index.ts +67 -0
  118. package/src/plugins/Events.ts +50 -0
  119. package/src/plugins/Plugins.ts +23 -0
  120. package/src/plugins/index.ts +5 -0
  121. package/src/routing/History.ts +52 -0
  122. package/src/routing/InnerRouter.ts +469 -0
  123. package/src/routing/PageLoader.ts +112 -0
  124. package/src/routing/Route.ts +184 -0
  125. package/src/routing/Router.ts +476 -0
  126. package/src/routing/Site.ts +65 -0
  127. package/src/routing/index.ts +5 -0
  128. package/src/ssr/SsrCache.ts +61 -0
  129. package/src/ssr/SsrComponents.ts +34 -0
  130. package/src/ssr/index.ts +4 -0
@@ -0,0 +1,11 @@
1
+ import { CrelteRouted } from '../index.js';
2
+ export default class Events {
3
+ inner: Map<string, Set<any>>;
4
+ constructor();
5
+ on(ev: 'loadGlobalData', fn: (cr: CrelteRouted) => Promise<any>): () => void;
6
+ on(ev: 'loadData', fn: (cr: CrelteRouted, entry: any, data: any) => Promise<any>): () => void;
7
+ on(ev: 'beforeRender', fn: (cr: CrelteRouted) => void): () => void;
8
+ remove(ev: string, fn: any): void;
9
+ trigger(ev: string, ...args: any[]): any[];
10
+ }
11
+ //# sourceMappingURL=Events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Events.d.ts","sourceRoot":"","sources":["../../../../src/plugins/Events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,CAAC,OAAO,OAAO,MAAM;IAC1B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;;IAU7B,EAAE,CACD,EAAE,EAAE,gBAAgB,EACpB,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,GACpC,MAAM,IAAI;IACb,EAAE,CACD,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAC3D,MAAM,IAAI;IACb,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI;IAelE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG;IAO1B,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE;CAM1C"}
@@ -0,0 +1,29 @@
1
+ export default class Events {
2
+ inner;
3
+ constructor() {
4
+ this.inner = new Map();
5
+ }
6
+ on(ev, fn) {
7
+ let set = this.inner.get(ev);
8
+ if (!set) {
9
+ set = new Set();
10
+ this.inner.set(ev, set);
11
+ }
12
+ set.add(fn);
13
+ return () => {
14
+ set.delete(fn);
15
+ };
16
+ }
17
+ remove(ev, fn) {
18
+ const set = this.inner.get(ev);
19
+ if (!set)
20
+ return;
21
+ set.delete(fn);
22
+ }
23
+ trigger(ev, ...args) {
24
+ const set = this.inner.get(ev);
25
+ if (!set)
26
+ return [];
27
+ return Array.from(set).map(fn => fn(...args));
28
+ }
29
+ }
@@ -0,0 +1,12 @@
1
+ import Crelte from '../Crelte.js';
2
+ export interface Plugin {
3
+ name: string;
4
+ }
5
+ export type PluginCreator = (crelte: Crelte) => Plugin;
6
+ export default class Plugins {
7
+ plugins: Map<string, Plugin>;
8
+ constructor();
9
+ add(plugin: Plugin): void;
10
+ get(name: string): Plugin | null;
11
+ }
12
+ //# sourceMappingURL=Plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugins.d.ts","sourceRoot":"","sources":["../../../../src/plugins/Plugins.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,cAAc,CAAC;AAElC,MAAM,WAAW,MAAM;IACtB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;AAEvD,MAAM,CAAC,OAAO,OAAO,OAAO;IAC3B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;IAM7B,GAAG,CAAC,MAAM,EAAE,MAAM;IAIlB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGhC"}
@@ -0,0 +1,12 @@
1
+ export default class Plugins {
2
+ plugins;
3
+ constructor() {
4
+ this.plugins = new Map();
5
+ }
6
+ add(plugin) {
7
+ this.plugins.set(plugin.name, plugin);
8
+ }
9
+ get(name) {
10
+ return this.plugins.get(name) ?? null;
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import Events from './Events.js';
2
+ import { Plugin, PluginCreator } from './Plugins.js';
3
+ export type { Plugin, PluginCreator };
4
+ export { Events };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAErD,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import Events from './Events.js';
2
+ export { Events };
@@ -0,0 +1,22 @@
1
+ export default interface History {
2
+ scrollY(): number;
3
+ replaceState(data: any, url?: string): void;
4
+ pushState(data: any, url: string): void;
5
+ open(url: string): void;
6
+ }
7
+ export declare class ClientHistory implements History {
8
+ scrollY(): number;
9
+ replaceState(data: any, url?: string): void;
10
+ pushState(data: any, url: string): void;
11
+ open(url: string): void;
12
+ }
13
+ export declare class ServerHistory implements History {
14
+ state: any | null;
15
+ url: string | null;
16
+ constructor();
17
+ scrollY(): number;
18
+ replaceState(data: any, url?: string): void;
19
+ pushState(data: any, url: string): void;
20
+ open(url: string): void;
21
+ }
22
+ //# sourceMappingURL=History.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"History.d.ts","sourceRoot":"","sources":["../../../../src/routing/History.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,WAAW,OAAO;IAC/B,OAAO,IAAI,MAAM,CAAC;IAClB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,qBAAa,aAAc,YAAW,OAAO;IAC5C,OAAO,IAAI,MAAM;IAIjB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAI3C,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAIvC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAGvB;AAED,qBAAa,aAAc,YAAW,OAAO;IAC5C,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;;IAOnB,OAAO,IAAI,MAAM;IAIjB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAK3C,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKvC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAGvB"}
@@ -0,0 +1,36 @@
1
+ export class ClientHistory {
2
+ scrollY() {
3
+ return window.scrollY;
4
+ }
5
+ replaceState(data, url) {
6
+ history.replaceState(data, '', url);
7
+ }
8
+ pushState(data, url) {
9
+ history.pushState(data, '', url);
10
+ }
11
+ open(url) {
12
+ window.location.href = url;
13
+ }
14
+ }
15
+ export class ServerHistory {
16
+ state;
17
+ url;
18
+ constructor() {
19
+ this.state = null;
20
+ this.url = null;
21
+ }
22
+ scrollY() {
23
+ return 0;
24
+ }
25
+ replaceState(data, url) {
26
+ this.state = data;
27
+ this.url = url ?? null;
28
+ }
29
+ pushState(data, url) {
30
+ this.state = data;
31
+ this.url = url;
32
+ }
33
+ open(url) {
34
+ this.url = url;
35
+ }
36
+ }
@@ -0,0 +1,111 @@
1
+ import Route from './Route.js';
2
+ import Site, { SiteFromGraphQl } from './Site.js';
3
+ import History from './History.js';
4
+ export type InnerRouterOpts = {
5
+ preloadOnMouseOver: boolean;
6
+ };
7
+ /**
8
+ * Manages event listeners or functions.
9
+ */
10
+ export default class InnerRouter {
11
+ sites: Site[];
12
+ route: Route | null;
13
+ site: Site;
14
+ history: History;
15
+ preloadOnMouseOver: boolean;
16
+ /**
17
+ * @param changeHistory returns a function you need to call when you are ready to
18
+ update the window history (note do not call this after another onRoute call was made)
19
+ */
20
+ onRoute: (route: Route, site: Site, changeHistory: () => void) => void;
21
+ onPreload: (route: Route, site: Site) => void;
22
+ private scrollDebounceTimeout;
23
+ /**
24
+ * Creates a new Router
25
+ *
26
+ * @param sites - sites needs to be from craft-graphql-sites plugin
27
+ * @param opts - Options for the router
28
+ */
29
+ constructor(sites: SiteFromGraphQl[], opts: InnerRouterOpts);
30
+ /**
31
+ * Initializes the router when running on the client.
32
+ */
33
+ initClient(): void;
34
+ /**
35
+ * Initializes the router when running on the server.
36
+ */
37
+ initServer(): void;
38
+ /**
39
+ * Get a site and if possible use the accept lang header.
40
+ *
41
+ * @param {(string|null)} [acceptLang=null] Accept Language header.
42
+ * @return {Site}
43
+ */
44
+ siteByAcceptLang(acceptLang?: string | null): Site;
45
+ /**
46
+ * Get the default site
47
+ */
48
+ defaultSite(): Site;
49
+ /**
50
+ * Tries to get a site by it's id
51
+ */
52
+ siteById(id: number): Site | null;
53
+ /**
54
+ * Resolve a url or Route and convert it to a Route
55
+ *
56
+ * @param target
57
+ * @return Returns null if the url does not match our host (the protocol get's ignored)
58
+ */
59
+ targetToRoute(target: string | URL | Route): Route;
60
+ /**
61
+ * Resolve a url and convert it to a Route
62
+ *
63
+ * @param url
64
+ * @return Returns null if the url does not match our host (the protocol get's ignored)
65
+ */
66
+ routeFromUrl(fullUrl: URL): Route;
67
+ listen(): void;
68
+ /**
69
+ * Open's a route
70
+ *
71
+ * @param route a route object or an url or uri, never input the same route object again
72
+ * @param pushState if true pushed the state to the window.history
73
+ */
74
+ open(target: string | URL | Route, pushState?: boolean): void;
75
+ /**
76
+ * Sets a route
77
+ *
78
+ * Will trigger an onRoute event but will not store any scroll progress
79
+ * or modify the history
80
+ *
81
+ * @param route
82
+ */
83
+ setRoute(route: Route): void;
84
+ /**
85
+ * This pushes the state of the route without triggering a currentRoute
86
+ * or currentSiteId change
87
+ *
88
+ * You can use when using pagination for example change the route object
89
+ * (search argument) and then call pushState
90
+ *
91
+ * @param route, never input the same route object again
92
+ */
93
+ pushState(route: Route): void;
94
+ /**
95
+ * This replaces the state of the route without triggering a currentRoute
96
+ * or currentSiteId change
97
+ *
98
+ * @param route, never input the same route object again
99
+ */
100
+ replaceState(route: Route): void;
101
+ /**
102
+ * Preload a url
103
+ *
104
+ * This will only work if the origin of the url matches the current site
105
+ *
106
+ * @param url
107
+ */
108
+ preload(target: string | URL | Route): void;
109
+ domReady(route: Route): void;
110
+ }
111
+ //# sourceMappingURL=InnerRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InnerRouter.d.ts","sourceRoot":"","sources":["../../../../src/routing/InnerRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,IAAI,EAAE,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,OAAyC,MAAM,cAAc,CAAC;AAErE,MAAM,MAAM,eAAe,GAAG;IAC7B,kBAAkB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACvE,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAE9C,OAAO,CAAC,qBAAqB,CAAa;IAE1C;;;;;OAKG;gBACS,KAAK,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,eAAe;IAmB3D;;OAEG;IACH,UAAU;IAkBV;;OAEG;IACH,UAAU;IAEV;;;;;OAKG;IACH,gBAAgB,CAAC,UAAU,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI;IAsCxD;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIjC;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,KAAK;IAiBlD;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,GAAG,GAAG,KAAK;IA8BjC,MAAM;IA0FN;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK,EAAE,SAAS,GAAE,OAAc;IAkC5D;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK;IAOrB;;;;;;;;OAQG;IACH,SAAS,CAAC,KAAK,EAAE,KAAK;IAYtB;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK;IAYzB;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK;IAmBpC,QAAQ,CAAC,KAAK,EAAE,KAAK;CAgErB"}
@@ -0,0 +1,397 @@
1
+ import Route from './Route.js';
2
+ import Site from './Site.js';
3
+ import { ClientHistory, ServerHistory } from './History.js';
4
+ /**
5
+ * Manages event listeners or functions.
6
+ */
7
+ export default class InnerRouter {
8
+ sites;
9
+ route;
10
+ site;
11
+ history;
12
+ preloadOnMouseOver;
13
+ /**
14
+ * @param changeHistory returns a function you need to call when you are ready to
15
+ update the window history (note do not call this after another onRoute call was made)
16
+ */
17
+ onRoute;
18
+ onPreload;
19
+ scrollDebounceTimeout;
20
+ /**
21
+ * Creates a new Router
22
+ *
23
+ * @param sites - sites needs to be from craft-graphql-sites plugin
24
+ * @param opts - Options for the router
25
+ */
26
+ constructor(sites, opts) {
27
+ this.sites = sites.map(s => new Site(s));
28
+ this.route = null;
29
+ this.site = this.defaultSite();
30
+ // @ts-ignore
31
+ this.history = import.meta.env.SSR
32
+ ? new ServerHistory()
33
+ : new ClientHistory();
34
+ this.preloadOnMouseOver = opts.preloadOnMouseOver;
35
+ // this.preloadListeners = new Listeners();
36
+ this.onRoute = () => { };
37
+ this.onPreload = () => { };
38
+ this.scrollDebounceTimeout = null;
39
+ }
40
+ /**
41
+ * Initializes the router when running on the client.
42
+ */
43
+ initClient() {
44
+ this.listen();
45
+ // let's first try to load from the state
46
+ const route = this.targetToRoute(window.location.href);
47
+ route._fillFromState(window.history.state);
48
+ route.origin = 'init';
49
+ if (route.search.get('x-craft-live-preview')) {
50
+ route.origin = 'live-preview-init';
51
+ }
52
+ window.history.scrollRestoration = 'manual';
53
+ this.open(route, false);
54
+ }
55
+ /**
56
+ * Initializes the router when running on the server.
57
+ */
58
+ initServer() { }
59
+ /**
60
+ * Get a site and if possible use the accept lang header.
61
+ *
62
+ * @param {(string|null)} [acceptLang=null] Accept Language header.
63
+ * @return {Site}
64
+ */
65
+ siteByAcceptLang(acceptLang = null) {
66
+ if (!acceptLang)
67
+ return this.defaultSite();
68
+ const directives = acceptLang
69
+ .split(',')
70
+ .map(d => d.trim())
71
+ .filter(d => !!d);
72
+ // let's expect that weights are correctly ordered
73
+ const languages = directives
74
+ .map(d => {
75
+ const lang = d.split(';');
76
+ return lang[0].trim();
77
+ })
78
+ .filter(d => !!d);
79
+ // find a site which matches the language
80
+ // first try to match the full language
81
+ for (const lang of languages) {
82
+ const site = this.sites.find(s => s.language === lang);
83
+ if (site)
84
+ return site;
85
+ }
86
+ // if we don't find any language which matches
87
+ // try to match languages without the -
88
+ for (let lang of languages) {
89
+ lang = lang.split('-')[0];
90
+ const site = this.sites.find(s => {
91
+ const sLang = s.language.split('-')[0];
92
+ return sLang === lang;
93
+ });
94
+ if (site)
95
+ return site;
96
+ }
97
+ // we did not find a match then just return the first site
98
+ return this.defaultSite();
99
+ }
100
+ /**
101
+ * Get the default site
102
+ */
103
+ defaultSite() {
104
+ return this.sites[0];
105
+ }
106
+ /**
107
+ * Tries to get a site by it's id
108
+ */
109
+ siteById(id) {
110
+ return this.sites.find(s => s.id === id) ?? null;
111
+ }
112
+ /**
113
+ * Resolve a url or Route and convert it to a Route
114
+ *
115
+ * @param target
116
+ * @return Returns null if the url does not match our host (the protocol get's ignored)
117
+ */
118
+ targetToRoute(target) {
119
+ if (typeof target === 'string') {
120
+ if (target.startsWith('/')) {
121
+ const site = this.site;
122
+ target = new URL(site.uri + target, site.url);
123
+ }
124
+ else {
125
+ target = new URL(target);
126
+ }
127
+ }
128
+ if (target instanceof URL) {
129
+ return this.routeFromUrl(target);
130
+ }
131
+ return target;
132
+ }
133
+ /**
134
+ * Resolve a url and convert it to a Route
135
+ *
136
+ * @param url
137
+ * @return Returns null if the url does not match our host (the protocol get's ignored)
138
+ */
139
+ routeFromUrl(fullUrl) {
140
+ // strip stuff we dont need from url
141
+ const route = new Route(fullUrl, null);
142
+ const url = route.url;
143
+ let site = null;
144
+ // get the site which matches the url the most
145
+ for (const s of this.sites) {
146
+ const siteUri = s.uri;
147
+ // make sure the start of the url matches
148
+ if (url.host !== s.url.host || !url.pathname.startsWith(siteUri)) {
149
+ continue;
150
+ }
151
+ // make sure that after the base url a slash follows or nothing
152
+ const uri = url.pathname.substring(siteUri.length);
153
+ if (uri.length > 0 && !uri.startsWith('/'))
154
+ continue;
155
+ /// make sure we get the most matched site
156
+ if (site && site.uri.length > siteUri.length)
157
+ continue;
158
+ site = s;
159
+ }
160
+ route.site = site;
161
+ return route;
162
+ }
163
+ listen() {
164
+ window.addEventListener('click', async (e) => {
165
+ // @ts-ignore
166
+ const link = e.target.closest('a');
167
+ const openInNewTab = e.metaKey || e.ctrlKey || e.shiftKey;
168
+ const saveLink = e.altKey;
169
+ if (!link || !link.href || openInNewTab || saveLink)
170
+ return;
171
+ if (link.target.toLowerCase() === '_blank')
172
+ return;
173
+ if (!link.href.startsWith('http'))
174
+ return;
175
+ e.preventDefault();
176
+ const route = this.routeFromUrl(link.href);
177
+ if (this.route?.eq(route))
178
+ return;
179
+ route.origin = 'click';
180
+ this.open(route);
181
+ });
182
+ if (this.preloadOnMouseOver) {
183
+ let currentMouseOver = null;
184
+ window.addEventListener('mouseover', e => {
185
+ // @ts-ignore
186
+ const link = e.target.closest('a');
187
+ if (currentMouseOver && link === currentMouseOver)
188
+ return;
189
+ if (link && link.target.toLowerCase() === '_blank')
190
+ return;
191
+ if (link && !link.hasAttribute('data-no-preload')) {
192
+ this.preload(link.href);
193
+ }
194
+ currentMouseOver = link;
195
+ });
196
+ }
197
+ // store the scrollY position every 200ms
198
+ // we can't do this at the time of the open call since the pop event
199
+ // has already changed to a new history state so we can't update our
200
+ // current/previous state
201
+ // eslint-disable-next-line no-constant-condition
202
+ if (true) {
203
+ window.addEventListener('scroll', () => {
204
+ const current = this.route;
205
+ if (!current)
206
+ return;
207
+ // store the scroll position
208
+ current.scrollY = window.scrollY;
209
+ if (this.scrollDebounceTimeout)
210
+ return;
211
+ // this might cause `Attempt to use history.replaceState() more than
212
+ // 100 times per 30 seconds` in safari
213
+ // since we wait a moment we should almost ever be fine
214
+ this.scrollDebounceTimeout = setTimeout(() => {
215
+ if (!this.route || !current.eq(this.route))
216
+ return;
217
+ // use the latest state
218
+ this.history.replaceState(this.route._toState());
219
+ if (current.origin === 'live-preview-init') {
220
+ sessionStorage.setItem('live-preview-scroll',
221
+ // use the latest scrollY
222
+ this.route.scrollY + '');
223
+ }
224
+ this.scrollDebounceTimeout = null;
225
+ }, 280);
226
+ });
227
+ }
228
+ window.addEventListener('popstate', async (e) => {
229
+ if (!('route' in e.state))
230
+ return;
231
+ const route = this.targetToRoute(window.location.href);
232
+ route._fillFromState(e.state);
233
+ route.origin = 'pop';
234
+ // since the pop event replaced our state we can't replace the state
235
+ // for the scrollY in our open call so we just clear the current
236
+ // route since it is now already the new route
237
+ this.route = null;
238
+ this.open(route, false);
239
+ });
240
+ }
241
+ /**
242
+ * Open's a route
243
+ *
244
+ * @param route a route object or an url or uri, never input the same route object again
245
+ * @param pushState if true pushed the state to the window.history
246
+ */
247
+ open(target, pushState = true) {
248
+ const route = this.targetToRoute(target);
249
+ const current = this.route;
250
+ if (current) {
251
+ // if the scrollY will still be updated we clear the timeout
252
+ // since we should have the latest scrollY
253
+ if (this.scrollDebounceTimeout) {
254
+ clearTimeout(this.scrollDebounceTimeout);
255
+ this.scrollDebounceTimeout = null;
256
+ }
257
+ // store the scroll position
258
+ current.scrollY = this.history.scrollY();
259
+ this.history.replaceState(current._toState());
260
+ }
261
+ // if the domain of the current site is different than the domain of the
262
+ // new site we need to do a window.location.href call
263
+ if (current && current.url.origin !== route.url.origin) {
264
+ this.history.open(route.url.href);
265
+ return;
266
+ }
267
+ if (pushState) {
268
+ route.index = (current?.index ?? 0) + 1;
269
+ this.onRoute(route, route.site ?? this.site, () => {
270
+ this.pushState(route);
271
+ });
272
+ }
273
+ else {
274
+ this.setRoute(route);
275
+ }
276
+ }
277
+ /**
278
+ * Sets a route
279
+ *
280
+ * Will trigger an onRoute event but will not store any scroll progress
281
+ * or modify the history
282
+ *
283
+ * @param route
284
+ */
285
+ setRoute(route) {
286
+ this.route = route;
287
+ if (route.site)
288
+ this.site = route.site;
289
+ this.onRoute(route, this.site, () => { });
290
+ }
291
+ /**
292
+ * This pushes the state of the route without triggering a currentRoute
293
+ * or currentSiteId change
294
+ *
295
+ * You can use when using pagination for example change the route object
296
+ * (search argument) and then call pushState
297
+ *
298
+ * @param route, never input the same route object again
299
+ */
300
+ pushState(route) {
301
+ const url = route.url;
302
+ this.history.pushState(route._toState(), url.pathname + url.search + url.hash);
303
+ this.route = route;
304
+ if (route.site)
305
+ this.site = route.site;
306
+ }
307
+ /**
308
+ * This replaces the state of the route without triggering a currentRoute
309
+ * or currentSiteId change
310
+ *
311
+ * @param route, never input the same route object again
312
+ */
313
+ replaceState(route) {
314
+ const url = route.url;
315
+ this.history.replaceState(route._toState(), url.pathname + url.search + url.hash);
316
+ this.route = route;
317
+ if (route.site)
318
+ this.site = route.site;
319
+ }
320
+ /**
321
+ * Preload a url
322
+ *
323
+ * This will only work if the origin of the url matches the current site
324
+ *
325
+ * @param url
326
+ */
327
+ preload(target) {
328
+ const route = this.targetToRoute(target);
329
+ // if the domain of the current site is different than the domain of the
330
+ // new site id does not make sense to preload
331
+ if (this.site.url.origin !== route.url.origin) {
332
+ return;
333
+ }
334
+ const current = this.route;
335
+ const site = route.site ?? this.site;
336
+ // if the origin matches, the route will be able to be load
337
+ // so let's preload it
338
+ if (current && current.url.origin === route.url.origin) {
339
+ this.onPreload(route, site);
340
+ }
341
+ }
342
+ domReady(route) {
343
+ // scroll to target
344
+ let scrollTo = null;
345
+ // if the route is a live preview init and we have a scrollY stored
346
+ // scroll to that
347
+ if (route.origin === 'live-preview-init') {
348
+ const scrollY = sessionStorage.getItem('live-preview-scroll');
349
+ if (scrollY) {
350
+ scrollTo = {
351
+ top: parseInt(scrollY),
352
+ behavior: 'instant',
353
+ };
354
+ }
355
+ // if we have a hash and the route was not visited
356
+ }
357
+ else if (route.hash &&
358
+ ((route.origin === 'init' && typeof route.scrollY !== 'number') ||
359
+ route.origin === 'click')) {
360
+ const el = document.getElementById(route.hash.substring(1));
361
+ if (el) {
362
+ scrollTo = {
363
+ intoView: el,
364
+ behavior: 'smooth',
365
+ };
366
+ }
367
+ }
368
+ // restore scroll position
369
+ if (!scrollTo &&
370
+ route.origin !== 'click' &&
371
+ typeof route.scrollY === 'number') {
372
+ scrollTo = {
373
+ top: route.scrollY,
374
+ behavior: 'instant',
375
+ };
376
+ }
377
+ // scroll to the top if nothing else matches
378
+ if (!scrollTo) {
379
+ scrollTo = {
380
+ top: 0,
381
+ behavior: 'instant',
382
+ };
383
+ }
384
+ if ('top' in scrollTo) {
385
+ window.scrollTo({
386
+ top: scrollTo.top,
387
+ behavior: scrollTo.behavior,
388
+ });
389
+ }
390
+ else {
391
+ scrollTo.intoView.scrollIntoView({
392
+ behavior: scrollTo.behavior,
393
+ block: 'start',
394
+ });
395
+ }
396
+ }
397
+ }