htmx.org 2.0.1 → 2.0.2

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.
@@ -1,67 +1,6 @@
1
- declare namespace htmx {
2
- const onLoad: (callback: (elt: Node) => void) => EventListener;
3
- const process: (elt: string | Element) => void;
4
- const on: (arg1: string | EventTarget, arg2: string | EventListener, arg3?: EventListener) => EventListener;
5
- const off: (arg1: string | EventTarget, arg2: string | EventListener, arg3?: EventListener) => EventListener;
6
- const trigger: (elt: string | EventTarget, eventName: string, detail?: any) => boolean;
7
- const ajax: (verb: HttpVerb, path: string, context: string | Element | HtmxAjaxHelperContext) => Promise<void>;
8
- const find: (eltOrSelector: string | ParentNode, selector?: string) => Element;
9
- const findAll: (eltOrSelector: string | ParentNode, selector?: string) => NodeListOf<Element>;
10
- const closest: (elt: string | Element, selector: string) => Element;
11
- function values(elt: Element, type: HttpVerb): any;
12
- const remove: (elt: Node, delay?: number) => void;
13
- const addClass: (elt: string | Element, clazz: string, delay?: number) => void;
14
- const removeClass: (node: string | Node, clazz: string, delay?: number) => void;
15
- const toggleClass: (elt: string | Element, clazz: string) => void;
16
- const takeClass: (elt: string | Node, clazz: string) => void;
17
- const swap: (target: string | Element, content: string, swapSpec: HtmxSwapSpecification, swapOptions?: SwapOptions) => void;
18
- const defineExtension: (name: string, extension: any) => void;
19
- const removeExtension: (name: string) => void;
20
- const logAll: () => void;
21
- const logNone: () => void;
22
- const logger: any;
23
- namespace config {
24
- const historyEnabled: boolean;
25
- const historyCacheSize: number;
26
- const refreshOnHistoryMiss: boolean;
27
- const defaultSwapStyle: HtmxSwapStyle;
28
- const defaultSwapDelay: number;
29
- const defaultSettleDelay: number;
30
- const includeIndicatorStyles: boolean;
31
- const indicatorClass: string;
32
- const requestClass: string;
33
- const addedClass: string;
34
- const settlingClass: string;
35
- const swappingClass: string;
36
- const allowEval: boolean;
37
- const allowScriptTags: boolean;
38
- const inlineScriptNonce: string;
39
- const inlineStyleNonce: string;
40
- const attributesToSettle: string[];
41
- const withCredentials: boolean;
42
- const timeout: number;
43
- const wsReconnectDelay: "full-jitter" | ((retryCount: number) => number);
44
- const wsBinaryType: BinaryType;
45
- const disableSelector: string;
46
- const scrollBehavior: 'auto' | 'instant' | 'smooth';
47
- const defaultFocusScroll: boolean;
48
- const getCacheBusterParam: boolean;
49
- const globalViewTransitions: boolean;
50
- const methodsThatUseUrlParams: (HttpVerb)[];
51
- const selfRequestsOnly: boolean;
52
- const ignoreTitle: boolean;
53
- const scrollIntoViewOnBoost: boolean;
54
- const triggerSpecsCache: any | null;
55
- const disableInheritance: boolean;
56
- const responseHandling: HtmxResponseHandlingConfig[];
57
- const allowNestedOobSwaps: boolean;
58
- }
59
- const parseInterval: (str: string) => number;
60
- const _: (str: string) => any;
61
- const version: string;
62
- }
63
- type HttpVerb = 'get' | 'head' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | 'patch';
64
- type SwapOptions = {
1
+ export default htmx;
2
+ export type HttpVerb = 'get' | 'head' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | 'patch';
3
+ export type SwapOptions = {
65
4
  select?: string;
66
5
  selectOOB?: string;
67
6
  eventInfo?: any;
@@ -70,9 +9,9 @@ type SwapOptions = {
70
9
  afterSwapCallback?: swapCallback;
71
10
  afterSettleCallback?: swapCallback;
72
11
  };
73
- type swapCallback = () => any;
74
- type HtmxSwapStyle = 'innerHTML' | 'outerHTML' | 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend' | 'delete' | 'none' | string;
75
- type HtmxSwapSpecification = {
12
+ export type swapCallback = () => any;
13
+ export type HtmxSwapStyle = 'innerHTML' | 'outerHTML' | 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend' | 'delete' | 'none' | string;
14
+ export type HtmxSwapSpecification = {
76
15
  swapStyle: HtmxSwapStyle;
77
16
  swapDelay: number;
78
17
  settleDelay: number;
@@ -85,10 +24,10 @@ type HtmxSwapSpecification = {
85
24
  showTarget?: string;
86
25
  focusScroll?: boolean;
87
26
  };
88
- type ConditionalFunction = ((this: Node, evt: Event) => boolean) & {
27
+ export type ConditionalFunction = ((this: Node, evt: Event) => boolean) & {
89
28
  source: string;
90
29
  };
91
- type HtmxTriggerSpecification = {
30
+ export type HtmxTriggerSpecification = {
92
31
  trigger: string;
93
32
  pollInterval?: number;
94
33
  eventFilter?: ConditionalFunction;
@@ -103,13 +42,13 @@ type HtmxTriggerSpecification = {
103
42
  root?: string;
104
43
  threshold?: string;
105
44
  };
106
- type HtmxElementValidationError = {
45
+ export type HtmxElementValidationError = {
107
46
  elt: Element;
108
47
  message: string;
109
48
  validity: ValidityState;
110
49
  };
111
- type HtmxHeaderSpecification = Record<string, string>;
112
- type HtmxAjaxHelperContext = {
50
+ export type HtmxHeaderSpecification = Record<string, string>;
51
+ export type HtmxAjaxHelperContext = {
113
52
  source?: Element | string;
114
53
  event?: Event;
115
54
  handler?: HtmxAjaxHandler;
@@ -119,7 +58,7 @@ type HtmxAjaxHelperContext = {
119
58
  headers?: Record<string, string>;
120
59
  select?: string;
121
60
  };
122
- type HtmxRequestConfig = {
61
+ export type HtmxRequestConfig = {
123
62
  boosted: boolean;
124
63
  useUrlParams: boolean;
125
64
  formData: FormData;
@@ -141,7 +80,7 @@ type HtmxRequestConfig = {
141
80
  path: string;
142
81
  triggeringEvent: Event;
143
82
  };
144
- type HtmxResponseInfo = {
83
+ export type HtmxResponseInfo = {
145
84
  xhr: XMLHttpRequest;
146
85
  target: Element;
147
86
  requestConfig: HtmxRequestConfig;
@@ -156,8 +95,9 @@ type HtmxResponseInfo = {
156
95
  };
157
96
  failed?: boolean;
158
97
  successful?: boolean;
98
+ keepIndicators?: boolean;
159
99
  };
160
- type HtmxAjaxEtc = {
100
+ export type HtmxAjaxEtc = {
161
101
  returnPromise?: boolean;
162
102
  handler?: HtmxAjaxHandler;
163
103
  select?: string;
@@ -168,7 +108,7 @@ type HtmxAjaxEtc = {
168
108
  credentials?: boolean;
169
109
  timeout?: number;
170
110
  };
171
- type HtmxResponseHandlingConfig = {
111
+ export type HtmxResponseHandlingConfig = {
172
112
  code?: string;
173
113
  swap: boolean;
174
114
  error?: boolean;
@@ -178,18 +118,88 @@ type HtmxResponseHandlingConfig = {
178
118
  swapOverride?: string;
179
119
  event?: string;
180
120
  };
181
- type HtmxBeforeSwapDetails = HtmxResponseInfo & {
121
+ export type HtmxBeforeSwapDetails = HtmxResponseInfo & {
182
122
  shouldSwap: boolean;
183
123
  serverResponse: any;
184
124
  isError: boolean;
185
125
  ignoreTitle: boolean;
186
126
  selectOverride: string;
187
127
  };
188
- type HtmxAjaxHandler = (elt: Element, responseInfo: HtmxResponseInfo) => any;
189
- type HtmxSettleTask = (() => void);
190
- type HtmxSettleInfo = {
128
+ export type HtmxAjaxHandler = (elt: Element, responseInfo: HtmxResponseInfo) => any;
129
+ export type HtmxSettleTask = (() => void);
130
+ export type HtmxSettleInfo = {
191
131
  tasks: HtmxSettleTask[];
192
132
  elts: Element[];
193
133
  title?: string;
194
134
  };
195
- type HtmxExtension = any;
135
+ export type HtmxExtension = {
136
+ init: (api: any) => void;
137
+ onEvent: (name: string, event: Event | CustomEvent) => boolean;
138
+ transformResponse: (text: string, xhr: XMLHttpRequest, elt: Element) => string;
139
+ isInlineSwap: (swapStyle: HtmxSwapStyle) => boolean;
140
+ handleSwap: (swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean | Node[];
141
+ encodeParameters: (xhr: XMLHttpRequest, parameters: FormData, elt: Node) => any | string | null;
142
+ getSelectors: () => string[] | null;
143
+ };
144
+ declare namespace htmx {
145
+ const onLoad: (callback: (elt: Node) => void) => EventListener;
146
+ const process: (elt: string | Element) => void;
147
+ const on: (arg1: string | EventTarget, arg2: string | EventListener, arg3?: EventListener) => EventListener;
148
+ const off: (arg1: string | EventTarget, arg2: string | EventListener, arg3?: EventListener) => EventListener;
149
+ const trigger: (elt: string | EventTarget, eventName: string, detail?: any) => boolean;
150
+ const ajax: (verb: HttpVerb, path: string, context: string | Element | HtmxAjaxHelperContext) => Promise<void>;
151
+ const find: (eltOrSelector: string | ParentNode, selector?: string) => Element;
152
+ const findAll: (eltOrSelector: string | ParentNode, selector?: string) => NodeListOf<Element>;
153
+ const closest: (elt: string | Element, selector: string) => Element;
154
+ function values(elt: Element, type: HttpVerb): any;
155
+ const remove: (elt: Node, delay?: number) => void;
156
+ const addClass: (elt: string | Element, clazz: string, delay?: number) => void;
157
+ const removeClass: (node: string | Node, clazz: string, delay?: number) => void;
158
+ const toggleClass: (elt: string | Element, clazz: string) => void;
159
+ const takeClass: (elt: string | Node, clazz: string) => void;
160
+ const swap: (target: string | Element, content: string, swapSpec: HtmxSwapSpecification, swapOptions?: SwapOptions) => void;
161
+ const defineExtension: (name: string, extension: HtmxExtension) => void;
162
+ const removeExtension: (name: string) => void;
163
+ const logAll: () => void;
164
+ const logNone: () => void;
165
+ const logger: any;
166
+ namespace config {
167
+ const historyEnabled: boolean;
168
+ const historyCacheSize: number;
169
+ const refreshOnHistoryMiss: boolean;
170
+ const defaultSwapStyle: HtmxSwapStyle;
171
+ const defaultSwapDelay: number;
172
+ const defaultSettleDelay: number;
173
+ const includeIndicatorStyles: boolean;
174
+ const indicatorClass: string;
175
+ const requestClass: string;
176
+ const addedClass: string;
177
+ const settlingClass: string;
178
+ const swappingClass: string;
179
+ const allowEval: boolean;
180
+ const allowScriptTags: boolean;
181
+ const inlineScriptNonce: string;
182
+ const inlineStyleNonce: string;
183
+ const attributesToSettle: string[];
184
+ const withCredentials: boolean;
185
+ const timeout: number;
186
+ const wsReconnectDelay: "full-jitter" | ((retryCount: number) => number);
187
+ const wsBinaryType: BinaryType;
188
+ const disableSelector: string;
189
+ const scrollBehavior: 'auto' | 'instant' | 'smooth';
190
+ const defaultFocusScroll: boolean;
191
+ const getCacheBusterParam: boolean;
192
+ const globalViewTransitions: boolean;
193
+ const methodsThatUseUrlParams: (HttpVerb)[];
194
+ const selfRequestsOnly: boolean;
195
+ const ignoreTitle: boolean;
196
+ const scrollIntoViewOnBoost: boolean;
197
+ const triggerSpecsCache: any | null;
198
+ const disableInheritance: boolean;
199
+ const responseHandling: HtmxResponseHandlingConfig[];
200
+ const allowNestedOobSwaps: boolean;
201
+ }
202
+ const parseInterval: (str: string) => number;
203
+ const _: (str: string) => any;
204
+ const version: string;
205
+ }
package/dist/htmx.esm.js CHANGED
@@ -206,7 +206,7 @@ var htmx = (function() {
206
206
  disableSelector: '[hx-disable], [data-hx-disable]',
207
207
  /**
208
208
  * @type {'auto' | 'instant' | 'smooth'}
209
- * @default 'smooth'
209
+ * @default 'instant'
210
210
  */
211
211
  scrollBehavior: 'instant',
212
212
  /**
@@ -277,7 +277,7 @@ var htmx = (function() {
277
277
  parseInterval: null,
278
278
  /** @type {typeof internalEval} */
279
279
  _: null,
280
- version: '2.0.1'
280
+ version: '2.0.2'
281
281
  }
282
282
  // Tsc madness part 2
283
283
  htmx.onLoad = onLoadHelper
@@ -1639,13 +1639,13 @@ var htmx = (function() {
1639
1639
  newElt = eltBeforeNewContent.nextSibling
1640
1640
  }
1641
1641
  settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
1642
+ // scan through all newly added content and add all elements to the settle info so we trigger
1643
+ // events properly on them
1642
1644
  while (newElt && newElt !== target) {
1643
1645
  if (newElt instanceof Element) {
1644
1646
  settleInfo.elts.push(newElt)
1645
- newElt = newElt.nextElementSibling
1646
- } else {
1647
- newElt = null
1648
1647
  }
1648
+ newElt = newElt.nextSibling
1649
1649
  }
1650
1650
  cleanUpElement(target)
1651
1651
  if (target instanceof Element) {
@@ -1753,7 +1753,7 @@ var htmx = (function() {
1753
1753
  try {
1754
1754
  const newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo)
1755
1755
  if (newElements) {
1756
- if (typeof newElements.length !== 'undefined') {
1756
+ if (Array.isArray(newElements)) {
1757
1757
  // if handleSwap returns an array (like) of elements, we handle them
1758
1758
  for (let j = 0; j < newElements.length; j++) {
1759
1759
  const child = newElements[j]
@@ -1781,7 +1781,8 @@ var htmx = (function() {
1781
1781
  * @param {HtmxSettleInfo} settleInfo
1782
1782
  */
1783
1783
  function findAndSwapOobElements(fragment, settleInfo) {
1784
- forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
1784
+ var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
1785
+ forEach(oobElts, function(oobElement) {
1785
1786
  if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
1786
1787
  const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
1787
1788
  if (oobValue != null) {
@@ -1792,6 +1793,7 @@ var htmx = (function() {
1792
1793
  oobElement.removeAttribute('data-hx-swap-oob')
1793
1794
  }
1794
1795
  })
1796
+ return oobElts.length > 0
1795
1797
  }
1796
1798
 
1797
1799
  /**
@@ -1853,9 +1855,8 @@ var htmx = (function() {
1853
1855
  // oob swaps
1854
1856
  findAndSwapOobElements(fragment, settleInfo)
1855
1857
  forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
1856
- findAndSwapOobElements(template.content, settleInfo)
1857
- if (template.content.childElementCount === 0 && template.content.textContent.trim() === '') {
1858
- // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1858
+ if (findAndSwapOobElements(template.content, settleInfo)) {
1859
+ // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1859
1860
  template.remove()
1860
1861
  }
1861
1862
  })
@@ -1952,7 +1953,10 @@ var htmx = (function() {
1952
1953
  for (const eventName in triggers) {
1953
1954
  if (triggers.hasOwnProperty(eventName)) {
1954
1955
  let detail = triggers[eventName]
1955
- if (!isRawObject(detail)) {
1956
+ if (isRawObject(detail)) {
1957
+ // @ts-ignore
1958
+ elt = detail.target !== undefined ? detail.target : elt
1959
+ } else {
1956
1960
  detail = { value: detail }
1957
1961
  }
1958
1962
  triggerEvent(elt, eventName, detail)
@@ -2273,7 +2277,7 @@ var htmx = (function() {
2273
2277
  * @param {HtmxTriggerSpecification[]} triggerSpecs
2274
2278
  */
2275
2279
  function boostElement(elt, nodeData, triggerSpecs) {
2276
- if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
2280
+ if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || (elt.tagName === 'FORM' && String(getRawAttribute(elt, 'method')).toLowerCase() !== 'dialog')) {
2277
2281
  nodeData.boosted = true
2278
2282
  let verb, path
2279
2283
  if (elt.tagName === 'A') {
@@ -2435,13 +2439,17 @@ var htmx = (function() {
2435
2439
 
2436
2440
  if (triggerSpec.throttle > 0) {
2437
2441
  if (!elementData.throttle) {
2442
+ triggerEvent(elt, 'htmx:trigger')
2438
2443
  handler(elt, evt)
2439
2444
  elementData.throttle = getWindow().setTimeout(function() {
2440
2445
  elementData.throttle = null
2441
2446
  }, triggerSpec.throttle)
2442
2447
  }
2443
2448
  } else if (triggerSpec.delay > 0) {
2444
- elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
2449
+ elementData.delayed = getWindow().setTimeout(function() {
2450
+ triggerEvent(elt, 'htmx:trigger')
2451
+ handler(elt, evt)
2452
+ }, triggerSpec.delay)
2445
2453
  } else {
2446
2454
  triggerEvent(elt, 'htmx:trigger')
2447
2455
  handler(elt, evt)
@@ -3059,6 +3067,10 @@ var htmx = (function() {
3059
3067
  forEach(findAll(clone, '.' + className), function(child) {
3060
3068
  removeClassFromElement(child, className)
3061
3069
  })
3070
+ // remove the disabled attribute for any element disabled due to an htmx request
3071
+ forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
3072
+ child.removeAttribute('disabled')
3073
+ })
3062
3074
  return clone.innerHTML
3063
3075
  }
3064
3076
 
@@ -3212,6 +3224,7 @@ var htmx = (function() {
3212
3224
  const internalData = getInternalData(disabledElement)
3213
3225
  internalData.requestCount = (internalData.requestCount || 0) + 1
3214
3226
  disabledElement.setAttribute('disabled', '')
3227
+ disabledElement.setAttribute('data-disabled-by-htmx', '')
3215
3228
  })
3216
3229
  return disabledElts
3217
3230
  }
@@ -3233,6 +3246,7 @@ var htmx = (function() {
3233
3246
  internalData.requestCount = (internalData.requestCount || 0) - 1
3234
3247
  if (internalData.requestCount === 0) {
3235
3248
  disabledElement.removeAttribute('disabled')
3249
+ disabledElement.removeAttribute('data-disabled-by-htmx')
3236
3250
  }
3237
3251
  })
3238
3252
  }
@@ -3382,10 +3396,10 @@ var htmx = (function() {
3382
3396
  function overrideFormData(receiver, donor) {
3383
3397
  for (const key of donor.keys()) {
3384
3398
  receiver.delete(key)
3385
- donor.getAll(key).forEach(function(value) {
3386
- receiver.append(key, value)
3387
- })
3388
3399
  }
3400
+ donor.forEach(function(value, key) {
3401
+ receiver.append(key, value)
3402
+ })
3389
3403
  return receiver
3390
3404
  }
3391
3405
 
@@ -3915,7 +3929,7 @@ var htmx = (function() {
3915
3929
  if (obj.hasOwnProperty(key)) {
3916
3930
  if (typeof obj[key].forEach === 'function') {
3917
3931
  obj[key].forEach(function(v) { formData.append(key, v) })
3918
- } else if (typeof obj[key] === 'object') {
3932
+ } else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
3919
3933
  formData.append(key, JSON.stringify(obj[key]))
3920
3934
  } else {
3921
3935
  formData.append(key, obj[key])
@@ -4008,6 +4022,8 @@ var htmx = (function() {
4008
4022
  target.delete(name)
4009
4023
  if (typeof value.forEach === 'function') {
4010
4024
  value.forEach(function(v) { target.append(name, v) })
4025
+ } else if (typeof value === 'object' && !(value instanceof Blob)) {
4026
+ target.append(name, JSON.stringify(value))
4011
4027
  } else {
4012
4028
  target.append(name, value)
4013
4029
  }
@@ -4343,7 +4359,9 @@ var htmx = (function() {
4343
4359
  const hierarchy = hierarchyForElt(elt)
4344
4360
  responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
4345
4361
  responseHandler(elt, responseInfo)
4346
- removeRequestIndicators(indicators, disableElts)
4362
+ if (responseInfo.keepIndicators !== true) {
4363
+ removeRequestIndicators(indicators, disableElts)
4364
+ }
4347
4365
  triggerEvent(elt, 'htmx:afterRequest', responseInfo)
4348
4366
  triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
4349
4367
  // if the body no longer contains the element, trigger the event on the closest parent
@@ -4583,12 +4601,14 @@ var htmx = (function() {
4583
4601
  const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
4584
4602
 
4585
4603
  if (hasHeader(xhr, /HX-Redirect:/i)) {
4604
+ responseInfo.keepIndicators = true
4586
4605
  location.href = xhr.getResponseHeader('HX-Redirect')
4587
4606
  shouldRefresh && location.reload()
4588
4607
  return
4589
4608
  }
4590
4609
 
4591
4610
  if (shouldRefresh) {
4611
+ responseInfo.keepIndicators = true
4592
4612
  location.reload()
4593
4613
  return
4594
4614
  }
@@ -5072,6 +5092,7 @@ var htmx = (function() {
5072
5092
  * @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
5073
5093
  * @property {boolean} [failed]
5074
5094
  * @property {boolean} [successful]
5095
+ * @property {boolean} [keepIndicators]
5075
5096
  */
5076
5097
 
5077
5098
  /**
@@ -5121,13 +5142,14 @@ var htmx = (function() {
5121
5142
  */
5122
5143
 
5123
5144
  /**
5145
+ * @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
5124
5146
  * @typedef {Object} HtmxExtension
5125
- * @see https://htmx.org/extensions/#defining
5126
5147
  * @property {(api: any) => void} init
5127
5148
  * @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
5128
5149
  * @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
5129
5150
  * @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
5130
- * @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
5131
- * @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
5151
+ * @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap
5152
+ * @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Node) => *|string|null} encodeParameters
5153
+ * @property {() => string[]|null} getSelectors
5132
5154
  */
5133
5155
  export default htmx
package/dist/htmx.js CHANGED
@@ -206,7 +206,7 @@ var htmx = (function() {
206
206
  disableSelector: '[hx-disable], [data-hx-disable]',
207
207
  /**
208
208
  * @type {'auto' | 'instant' | 'smooth'}
209
- * @default 'smooth'
209
+ * @default 'instant'
210
210
  */
211
211
  scrollBehavior: 'instant',
212
212
  /**
@@ -277,7 +277,7 @@ var htmx = (function() {
277
277
  parseInterval: null,
278
278
  /** @type {typeof internalEval} */
279
279
  _: null,
280
- version: '2.0.1'
280
+ version: '2.0.2'
281
281
  }
282
282
  // Tsc madness part 2
283
283
  htmx.onLoad = onLoadHelper
@@ -1639,13 +1639,13 @@ var htmx = (function() {
1639
1639
  newElt = eltBeforeNewContent.nextSibling
1640
1640
  }
1641
1641
  settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
1642
+ // scan through all newly added content and add all elements to the settle info so we trigger
1643
+ // events properly on them
1642
1644
  while (newElt && newElt !== target) {
1643
1645
  if (newElt instanceof Element) {
1644
1646
  settleInfo.elts.push(newElt)
1645
- newElt = newElt.nextElementSibling
1646
- } else {
1647
- newElt = null
1648
1647
  }
1648
+ newElt = newElt.nextSibling
1649
1649
  }
1650
1650
  cleanUpElement(target)
1651
1651
  if (target instanceof Element) {
@@ -1753,7 +1753,7 @@ var htmx = (function() {
1753
1753
  try {
1754
1754
  const newElements = ext.handleSwap(swapStyle, target, fragment, settleInfo)
1755
1755
  if (newElements) {
1756
- if (typeof newElements.length !== 'undefined') {
1756
+ if (Array.isArray(newElements)) {
1757
1757
  // if handleSwap returns an array (like) of elements, we handle them
1758
1758
  for (let j = 0; j < newElements.length; j++) {
1759
1759
  const child = newElements[j]
@@ -1781,7 +1781,8 @@ var htmx = (function() {
1781
1781
  * @param {HtmxSettleInfo} settleInfo
1782
1782
  */
1783
1783
  function findAndSwapOobElements(fragment, settleInfo) {
1784
- forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
1784
+ var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
1785
+ forEach(oobElts, function(oobElement) {
1785
1786
  if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
1786
1787
  const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
1787
1788
  if (oobValue != null) {
@@ -1792,6 +1793,7 @@ var htmx = (function() {
1792
1793
  oobElement.removeAttribute('data-hx-swap-oob')
1793
1794
  }
1794
1795
  })
1796
+ return oobElts.length > 0
1795
1797
  }
1796
1798
 
1797
1799
  /**
@@ -1853,9 +1855,8 @@ var htmx = (function() {
1853
1855
  // oob swaps
1854
1856
  findAndSwapOobElements(fragment, settleInfo)
1855
1857
  forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
1856
- findAndSwapOobElements(template.content, settleInfo)
1857
- if (template.content.childElementCount === 0 && template.content.textContent.trim() === '') {
1858
- // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1858
+ if (findAndSwapOobElements(template.content, settleInfo)) {
1859
+ // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1859
1860
  template.remove()
1860
1861
  }
1861
1862
  })
@@ -1952,7 +1953,10 @@ var htmx = (function() {
1952
1953
  for (const eventName in triggers) {
1953
1954
  if (triggers.hasOwnProperty(eventName)) {
1954
1955
  let detail = triggers[eventName]
1955
- if (!isRawObject(detail)) {
1956
+ if (isRawObject(detail)) {
1957
+ // @ts-ignore
1958
+ elt = detail.target !== undefined ? detail.target : elt
1959
+ } else {
1956
1960
  detail = { value: detail }
1957
1961
  }
1958
1962
  triggerEvent(elt, eventName, detail)
@@ -2273,7 +2277,7 @@ var htmx = (function() {
2273
2277
  * @param {HtmxTriggerSpecification[]} triggerSpecs
2274
2278
  */
2275
2279
  function boostElement(elt, nodeData, triggerSpecs) {
2276
- if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
2280
+ if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || (elt.tagName === 'FORM' && String(getRawAttribute(elt, 'method')).toLowerCase() !== 'dialog')) {
2277
2281
  nodeData.boosted = true
2278
2282
  let verb, path
2279
2283
  if (elt.tagName === 'A') {
@@ -2435,13 +2439,17 @@ var htmx = (function() {
2435
2439
 
2436
2440
  if (triggerSpec.throttle > 0) {
2437
2441
  if (!elementData.throttle) {
2442
+ triggerEvent(elt, 'htmx:trigger')
2438
2443
  handler(elt, evt)
2439
2444
  elementData.throttle = getWindow().setTimeout(function() {
2440
2445
  elementData.throttle = null
2441
2446
  }, triggerSpec.throttle)
2442
2447
  }
2443
2448
  } else if (triggerSpec.delay > 0) {
2444
- elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
2449
+ elementData.delayed = getWindow().setTimeout(function() {
2450
+ triggerEvent(elt, 'htmx:trigger')
2451
+ handler(elt, evt)
2452
+ }, triggerSpec.delay)
2445
2453
  } else {
2446
2454
  triggerEvent(elt, 'htmx:trigger')
2447
2455
  handler(elt, evt)
@@ -3059,6 +3067,10 @@ var htmx = (function() {
3059
3067
  forEach(findAll(clone, '.' + className), function(child) {
3060
3068
  removeClassFromElement(child, className)
3061
3069
  })
3070
+ // remove the disabled attribute for any element disabled due to an htmx request
3071
+ forEach(findAll(clone, '[data-disabled-by-htmx]'), function(child) {
3072
+ child.removeAttribute('disabled')
3073
+ })
3062
3074
  return clone.innerHTML
3063
3075
  }
3064
3076
 
@@ -3212,6 +3224,7 @@ var htmx = (function() {
3212
3224
  const internalData = getInternalData(disabledElement)
3213
3225
  internalData.requestCount = (internalData.requestCount || 0) + 1
3214
3226
  disabledElement.setAttribute('disabled', '')
3227
+ disabledElement.setAttribute('data-disabled-by-htmx', '')
3215
3228
  })
3216
3229
  return disabledElts
3217
3230
  }
@@ -3233,6 +3246,7 @@ var htmx = (function() {
3233
3246
  internalData.requestCount = (internalData.requestCount || 0) - 1
3234
3247
  if (internalData.requestCount === 0) {
3235
3248
  disabledElement.removeAttribute('disabled')
3249
+ disabledElement.removeAttribute('data-disabled-by-htmx')
3236
3250
  }
3237
3251
  })
3238
3252
  }
@@ -3382,10 +3396,10 @@ var htmx = (function() {
3382
3396
  function overrideFormData(receiver, donor) {
3383
3397
  for (const key of donor.keys()) {
3384
3398
  receiver.delete(key)
3385
- donor.getAll(key).forEach(function(value) {
3386
- receiver.append(key, value)
3387
- })
3388
3399
  }
3400
+ donor.forEach(function(value, key) {
3401
+ receiver.append(key, value)
3402
+ })
3389
3403
  return receiver
3390
3404
  }
3391
3405
 
@@ -3915,7 +3929,7 @@ var htmx = (function() {
3915
3929
  if (obj.hasOwnProperty(key)) {
3916
3930
  if (typeof obj[key].forEach === 'function') {
3917
3931
  obj[key].forEach(function(v) { formData.append(key, v) })
3918
- } else if (typeof obj[key] === 'object') {
3932
+ } else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
3919
3933
  formData.append(key, JSON.stringify(obj[key]))
3920
3934
  } else {
3921
3935
  formData.append(key, obj[key])
@@ -4008,6 +4022,8 @@ var htmx = (function() {
4008
4022
  target.delete(name)
4009
4023
  if (typeof value.forEach === 'function') {
4010
4024
  value.forEach(function(v) { target.append(name, v) })
4025
+ } else if (typeof value === 'object' && !(value instanceof Blob)) {
4026
+ target.append(name, JSON.stringify(value))
4011
4027
  } else {
4012
4028
  target.append(name, value)
4013
4029
  }
@@ -4343,7 +4359,9 @@ var htmx = (function() {
4343
4359
  const hierarchy = hierarchyForElt(elt)
4344
4360
  responseInfo.pathInfo.responsePath = getPathFromResponse(xhr)
4345
4361
  responseHandler(elt, responseInfo)
4346
- removeRequestIndicators(indicators, disableElts)
4362
+ if (responseInfo.keepIndicators !== true) {
4363
+ removeRequestIndicators(indicators, disableElts)
4364
+ }
4347
4365
  triggerEvent(elt, 'htmx:afterRequest', responseInfo)
4348
4366
  triggerEvent(elt, 'htmx:afterOnLoad', responseInfo)
4349
4367
  // if the body no longer contains the element, trigger the event on the closest parent
@@ -4583,12 +4601,14 @@ var htmx = (function() {
4583
4601
  const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
4584
4602
 
4585
4603
  if (hasHeader(xhr, /HX-Redirect:/i)) {
4604
+ responseInfo.keepIndicators = true
4586
4605
  location.href = xhr.getResponseHeader('HX-Redirect')
4587
4606
  shouldRefresh && location.reload()
4588
4607
  return
4589
4608
  }
4590
4609
 
4591
4610
  if (shouldRefresh) {
4611
+ responseInfo.keepIndicators = true
4592
4612
  location.reload()
4593
4613
  return
4594
4614
  }
@@ -5072,6 +5092,7 @@ var htmx = (function() {
5072
5092
  * @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
5073
5093
  * @property {boolean} [failed]
5074
5094
  * @property {boolean} [successful]
5095
+ * @property {boolean} [keepIndicators]
5075
5096
  */
5076
5097
 
5077
5098
  /**
@@ -5121,12 +5142,13 @@ var htmx = (function() {
5121
5142
  */
5122
5143
 
5123
5144
  /**
5145
+ * @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
5124
5146
  * @typedef {Object} HtmxExtension
5125
- * @see https://htmx.org/extensions/#defining
5126
5147
  * @property {(api: any) => void} init
5127
5148
  * @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
5128
5149
  * @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
5129
5150
  * @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
5130
- * @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
5131
- * @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
5151
+ * @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap
5152
+ * @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Node) => *|string|null} encodeParameters
5153
+ * @property {() => string[]|null} getSelectors
5132
5154
  */