@@ -34,51 +34,69 @@ type HxSync = ":drop" | ":abort" | ":replace" | ":queue" | ":queue first" | ":qu
3434/**
3535 * Any of the standard DOM events, or htmx-specific events.
3636 */
37- type HxTriggers = keyof GlobalEventHandlersEventMap | HtmxEvents ;
37+ type HxTriggers = keyof GlobalEventHandlersEventMap | HtmxUtils . HtmxEvents ;
3838
39- type HtmxEvents =
40- | "htmx:abort"
41- | "htmx:afterOnLoad"
42- | "htmx:afterProcessNode"
43- | "htmx:afterRequest"
44- | "htmx:afterSettle"
45- | "htmx:afterSwap"
46- | "htmx:beforeCleanupElement"
47- | "htmx:beforeOnLoad"
48- | "htmx:beforeProcessNode"
49- | "htmx:beforeRequest"
50- | "htmx:beforeSwap"
51- | "htmx:beforeSend"
52- | "htmx:configRequest"
53- | "htmx:confirm"
54- | "htmx:historyCacheError"
55- | "htmx:historyCacheMiss"
56- | "htmx:historyCacheMissError"
57- | "htmx:historyCacheMissLoad"
58- | "htmx:historyRestore"
59- | "htmx:beforeHistorySave"
60- | "htmx:load"
61- | "htmx:noSSESourceError"
62- | "htmx:onLoadError"
63- | "htmx:oobAfterSwap"
64- | "htmx:oobBeforeSwap"
65- | "htmx:oobErrorNoTarget"
66- | "htmx:prompt"
67- | "htmx:pushedIntoHistory"
68- | "htmx:responseError"
69- | "htmx:sendError"
70- | "htmx:sseError"
71- | "htmx:sseOpen"
72- | "htmx:swapError"
73- | "htmx:targetError"
74- | "htmx:timeout"
75- | "htmx:validation:validate"
76- | "htmx:vaildation:failed"
77- | "htmx:validation:halted"
78- | "htmx:xhr:abort"
79- | "htmx:xhr:loadend"
80- | "htmx:xhr:loadstart"
81- | "htmx:xhr:progress"
39+ /** @ignore */
40+ declare namespace HtmxUtils {
41+ type HxOnMap =
42+ { [ K in keyof GlobalEventHandlersEventMap as `hx-on-${K } `] ?: string ; } &
43+ { [ K in HxOnHtmxEvents as `hx-on--${K } `] ?: string ; }
44+
45+ type HxOnHtmxEvents =
46+ | JsxHtmxEvents
47+ | keyof { [ K in keyof HxSubevents as `${K } -${HxSubevents [ K ] } `] : never }
48+
49+ type JsxHtmxEvents =
50+ | "abort"
51+ | "after-on-load"
52+ | "after-process-node"
53+ | "after-request"
54+ | "after-settle"
55+ | "after-swap"
56+ | "before-cleanup-element"
57+ | "before-on-load"
58+ | "before-process-node"
59+ | "before-request"
60+ | "before-swap"
61+ | "before-send"
62+ | "config-request"
63+ | "confirm"
64+ | "history-cache-error"
65+ | "history-cache-miss"
66+ | "history-cache-miss-error"
67+ | "history-cache-miss-load"
68+ | "history-restore"
69+ | "before-history-save"
70+ | "load"
71+ | "no-sse-source-error"
72+ | "on-load-error"
73+ | "oob-after-swap"
74+ | "oob-before-swap"
75+ | "oob-error-no-target"
76+ | "prompt"
77+ | "pushed-into-history"
78+ | "response-error"
79+ | "send-error"
80+ | "sse-error"
81+ | "sse-open"
82+ | "swap-error"
83+ | "target-error"
84+ | "timeout"
85+
86+ type HxSubevents = {
87+ validation : 'validate' | 'failed' | 'halted' ;
88+ xhr : 'abort' | 'loadend' | 'loadstart' | 'progress' ;
89+ }
90+
91+ type KebabToCamel < T extends string > =
92+ T extends `${infer Head } -${infer Rest } `
93+ ? `${Head } ${KebabToCamel < Capitalize < Rest > > } `
94+ : T
95+
96+ type HtmxEvents =
97+ | `htmx:${KebabToCamel < JsxHtmxEvents > } `
98+ | keyof { [ K in keyof HxSubevents as `htmx:${K } :${HxSubevents [ K ] } `] : never }
99+ }
82100
83101/**
84102 * An event followed by one of these modifiers, e.g. `click once`.
@@ -111,14 +129,15 @@ type HxTriggerModifier =
111129 * }
112130 * interface HtmlTag {
113131 * /** Describe your attribute *\/
114- * ["my-extension-attr"]?: string ;
132+ * ["my-extension-attr"]?: "true" | "false" ;
115133 * // Add any other attributes your extension uses here
116134 * }
117135 * }
118136 * }
119137 *
120138 * <div hx-ext="my-extension">
121- * <span my-extension-attr="foo">Hello</span>
139+ * <span my-extension-attr="true">Hello</span>
140+ * // ^?
122141 * </div>
123142 * ```
124143 */
@@ -298,12 +317,12 @@ interface HtmxBuiltinExtensions {
298317 * `hx-get`, `hx-post` and other request attributes can include path variables by
299318 * using the {@linkcode HtmxBuiltinExtensions.pathParams path-params} extension.
300319 * Once used as a path variable, it will not be included in the request body.
301- * ```jsx
302- * <button hx-post="/api/user/{id}" hx-vals="{'id': 1,'foo':true}" hx-ext="path-params">...</a >
320+ * ```jsx twoslash
321+ * <button hx-post="/api/user/{id}" hx-vals="{'id': 1,'foo':true}" hx-ext="path-params">...</button >
303322 * // Only 'foo' will be included in the request body
304323 * ```
305324 */
306- interface HtmxAttributes {
325+ interface HtmxAttributes extends HtmxUtils . HxOnMap {
307326 /** @ignore For React compatibility only. */
308327 children ?: { } | null ;
309328 /** @ignore For React compatibility only. */
@@ -766,11 +785,11 @@ interface HtmxAttributes {
766785
767786/** @ignore */
768787declare namespace JSX {
769- interface HtmxExtensions extends HtmxBuiltinExtensions { }
788+ interface HtmxExtensions extends HtmxBuiltinExtensions { }
770789
771790 // typed-html
772- interface HtmlTag extends HtmxAttributes { }
791+ interface HtmlTag extends HtmxAttributes { }
773792}
774793
775794/** @ignore */
776- interface HTMLElement extends HtmxAttributes { }
795+ interface HTMLElement extends HtmxAttributes { }
0 commit comments