@very-simple/ajax 0.4.0 → 0.5.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.
package/dist/index.d.ts CHANGED
@@ -10,10 +10,33 @@ type MergeStrategy = 'replace' | 'morph' | 'before' | 'after' | 'prepend' | 'app
10
10
  interface Config {
11
11
  watchHistory?: boolean;
12
12
  merge?: MergeStrategy;
13
+ morphHeads?: boolean;
14
+ }
15
+ type EventMap = {
16
+ visit: {
17
+ url: string;
18
+ prevUrl: string;
19
+ };
20
+ 'before-visit': {
21
+ url: string;
22
+ prevUrl?: string;
23
+ };
24
+ 'before-render': {
25
+ url: string;
26
+ prevUrl?: string;
27
+ newDocument: Document;
28
+ };
29
+ };
30
+ type DomEventMap = {
31
+ [K in keyof EventMap as `simple-ajax:${K & string}`]: CustomEvent<EventMap[K]>;
32
+ };
33
+ declare global {
34
+ interface DocumentEventMap extends DomEventMap {
35
+ }
13
36
  }
14
37
 
15
38
  declare const _default: {
16
- start: ({ watchHistory, merge, }?: Config) => void;
39
+ start: ({ watchHistory, merge, morphHeads, }?: Config) => void;
17
40
  visit: (url: string, { action, emitEvents, isBackForward, morphHeads, merge: mergeStrategy, autoFocus, }?: VisitOptions) => Promise<void>;
18
41
  };
19
42
 
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // src/config.ts
2
2
  var globalConfig = {
3
3
  watchHistory: true,
4
+ morphHeads: true,
4
5
  merge: "replace"
5
6
  };
6
7
 
7
8
  // src/load.ts
8
9
  var parser = new DOMParser();
9
- var progress = 0;
10
10
  var currentLoadController;
11
11
  var load = async (url, regionIds) => {
12
12
  let progressDelayTimeout;
@@ -23,18 +23,27 @@ var load = async (url, regionIds) => {
23
23
  signal: currentLoadController.signal
24
24
  });
25
25
  const reader = response.body?.getReader();
26
- const length = parseInt(response.headers.get("Content-Length") ?? "0");
26
+ const contentLengthHeader = response.headers.get("Content-Length");
27
+ const length = parseInt(contentLengthHeader ?? "0");
27
28
  let receivedBytes = 0;
28
29
  const stream = new ReadableStream({
29
30
  start(controller) {
30
31
  const read = async () => {
31
32
  const { done, value } = await reader.read();
32
33
  if (done) {
34
+ if (contentLengthHeader && receivedBytes > length) {
35
+ console.warn(
36
+ `Server sent more data (${receivedBytes} bytes) than specified in Content-Length header (${length} bytes).`
37
+ );
38
+ }
33
39
  controller.close();
34
40
  return;
35
41
  }
36
42
  receivedBytes += value.length;
37
- setProgress(receivedBytes / length);
43
+ if (contentLengthHeader) {
44
+ const progress = Math.min(receivedBytes / length, 1);
45
+ setProgress(progress);
46
+ }
38
47
  controller.enqueue(value);
39
48
  read();
40
49
  };
@@ -56,13 +65,10 @@ var load = async (url, regionIds) => {
56
65
  toggleLoading(false);
57
66
  }
58
67
  };
59
- var setProgress = (value) => {
60
- progress = value;
61
- document.documentElement.style.setProperty(
62
- "--simple-progress",
63
- `${value * 100}%`
64
- );
65
- };
68
+ var setProgress = (value) => document.documentElement.style.setProperty(
69
+ "--simple-progress",
70
+ `${Math.round(value * 1e4) / 100}%`
71
+ );
66
72
  var toggleLoading = (state) => document.documentElement.toggleAttribute("data-simple-loading", state);
67
73
 
68
74
  // src/merge.ts
@@ -100,22 +106,27 @@ var merge = (region, newRegion, strategy) => {
100
106
  var morph = (container, newContainer) => {
101
107
  const manualReplacements = [];
102
108
  let autoFocusEls = [];
109
+ let currentNodeKeepAttributes = [];
103
110
  Idiomorph.morph(container, newContainer, {
104
111
  callbacks: {
105
- afterNodeMorphed(_, newNode) {
106
- if (newNode instanceof HTMLElement && (newNode.autofocus || newNode.dataset.simpleAutofocus)) {
107
- autoFocusEls.push(newNode);
108
- }
109
- },
110
112
  beforeNodeMorphed(oldNode, newNode) {
111
113
  if (!(oldNode instanceof HTMLElement)) return true;
112
114
  if (!(newNode instanceof HTMLElement)) return true;
115
+ currentNodeKeepAttributes = oldNode.dataset.simpleKeepAttributes?.split(" ") ?? [];
113
116
  if (oldNode.hasAttribute("data-simple-permanent") && oldNode.id === newNode.id)
114
117
  return false;
115
118
  const oldComponent = oldNode.dataset.simpleComponent;
116
119
  const newComponent = newNode.dataset.simpleComponent;
117
120
  if (!oldComponent || oldComponent === newComponent) return true;
118
121
  manualReplacements.push([oldNode, newNode]);
122
+ },
123
+ beforeAttributeUpdated: (name) => {
124
+ return !currentNodeKeepAttributes.includes(name);
125
+ },
126
+ afterNodeMorphed(_, newNode) {
127
+ if (newNode instanceof HTMLElement && (newNode.autofocus || newNode.dataset.simpleAutofocus)) {
128
+ autoFocusEls.push(newNode);
129
+ }
119
130
  }
120
131
  }
121
132
  });
@@ -131,26 +142,31 @@ var emit = async (type, payload) => {
131
142
  const event = new CustomEvent(`simple-ajax:${type}`, { detail: payload });
132
143
  document.dispatchEvent(event);
133
144
  };
145
+ var currentUrl = window.location.pathname;
146
+ var prevUrl;
134
147
  var visit = async (url, {
135
148
  action = "none",
136
149
  emitEvents = true,
137
150
  isBackForward = false,
138
- morphHeads = false,
151
+ morphHeads = globalConfig.morphHeads,
139
152
  merge: mergeStrategy = globalConfig.merge,
140
153
  autoFocus = true
141
154
  } = {}) => {
142
- if (emitEvents) emit("before-visit", { url });
155
+ prevUrl = currentUrl;
156
+ currentUrl = url;
157
+ if (emitEvents) emit("before-visit", { url, prevUrl });
143
158
  let newDocument;
144
159
  if (isBackForward) {
145
160
  newDocument = cache.get(url)?.cloneNode(true);
146
- } else {
147
- const cacheId = window.location.pathname + window.location.search;
148
- cache.set(cacheId, document.cloneNode(true));
149
161
  }
150
162
  const regions = findRegions(document);
151
163
  newDocument ??= await load(url, Array.from(regions.keys()));
152
164
  if (!newDocument) return;
153
- if (emitEvents) emit("before-render", { url, newDocument });
165
+ if (emitEvents) emit("before-render", { url, prevUrl, newDocument });
166
+ if (!isBackForward) {
167
+ const cacheId = window.location.pathname + window.location.search;
168
+ cache.set(cacheId, document.cloneNode(true));
169
+ }
154
170
  if (morphHeads) Idiomorph2.morph(document.head, newDocument.head);
155
171
  const getMergeStrategy = (oldEl, newEl) => newEl.dataset.simpleAjax || oldEl.dataset.simpleAjax || mergeStrategy;
156
172
  let autoFocusEl;
@@ -177,7 +193,7 @@ var visit = async (url, {
177
193
  if (autoFocus) autoFocusEl?.focus();
178
194
  if (action === "replace") history.replaceState(null, "", url);
179
195
  else if (action === "push") history.pushState(null, "", url);
180
- if (emitEvents) emit("visit", { url });
196
+ if (emitEvents) emit("visit", { url, prevUrl });
181
197
  };
182
198
  var findRegions = (doc) => {
183
199
  let regions = /* @__PURE__ */ new Map();
@@ -191,13 +207,15 @@ var findRegions = (doc) => {
191
207
  // src/start.ts
192
208
  var start = ({
193
209
  watchHistory = true,
194
- merge: merge2 = "replace"
210
+ merge: merge2 = "replace",
211
+ morphHeads = true
195
212
  } = {}) => {
196
213
  globalConfig.merge = merge2;
197
214
  globalConfig.watchHistory = watchHistory;
215
+ globalConfig.morphHeads = morphHeads;
198
216
  if (watchHistory) {
199
- window.addEventListener("popstate", async () => {
200
- await visit(window.location.pathname + window.location.search, {
217
+ window.addEventListener("popstate", () => {
218
+ visit(window.location.pathname + window.location.search, {
201
219
  action: "none",
202
220
  isBackForward: true
203
221
  });
@@ -206,7 +224,7 @@ var start = ({
206
224
  };
207
225
 
208
226
  // src/index.ts
209
- var src_default = { start, visit };
227
+ var index_default = { start, visit };
210
228
  export {
211
- src_default as default
229
+ index_default as default
212
230
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@very-simple/ajax",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A very simple turbolinks inspired library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,7 +13,7 @@
13
13
  "url": "https://github.com/very-simple/ajax.git"
14
14
  },
15
15
  "scripts": {
16
- "build": "rm -rf ./dist && tsup src/index.ts --dts --format esm",
16
+ "build": "tsup src/index.ts --dts --format esm",
17
17
  "dev": "vite",
18
18
  "test": "playwright test",
19
19
  "release": "npm run build && npm run test && bumpp"
@@ -23,14 +23,14 @@
23
23
  "devDependencies": {
24
24
  "@picocss/pico": "^2.0.6",
25
25
  "@playwright/test": "^1.48.2",
26
- "@types/node": "^18.19.64",
26
+ "@types/node": "^22.0",
27
27
  "@very-simple/components": "^0.6.0",
28
- "bumpp": "^9.8.1",
28
+ "bumpp": "^10.0.0",
29
29
  "tsup": "8",
30
30
  "typescript": "^5",
31
- "vite": "^5"
31
+ "vite": "^6"
32
32
  },
33
33
  "dependencies": {
34
- "idiomorph": "^0.3.0"
34
+ "idiomorph": "^0.7.0"
35
35
  }
36
36
  }