forked from hotwired/turbo
-
Notifications
You must be signed in to change notification settings - Fork 2
/
frame_element.ts
198 lines (175 loc) · 4.67 KB
/
frame_element.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import { FetchResponse } from "../http/fetch_response"
import { Snapshot } from "../core/snapshot"
import { LinkClickObserverDelegate } from "../observers/link_click_observer"
import { FormSubmitObserverDelegate } from "../observers/form_submit_observer"
export enum FrameLoadingStyle {
eager = "eager",
lazy = "lazy",
}
export type FrameElementObservedAttribute = keyof FrameElement & ("disabled" | "complete" | "loading" | "src")
export interface FrameElementDelegate extends LinkClickObserverDelegate, FormSubmitObserverDelegate {
connect(): void
disconnect(): void
completeChanged(): void
loadingStyleChanged(): void
sourceURLChanged(): void
disabledChanged(): void
loadResponse(response: FetchResponse): void
fetchResponseLoaded: (fetchResponse: FetchResponse) => void
visitCachedSnapshot: (snapshot: Snapshot) => void
isLoading: boolean
}
/**
* Contains a fragment of HTML which is updated based on navigation within
* it (e.g. via links or form submissions).
*
* @customElement turbo-frame
* @example
* <turbo-frame id="messages">
* <a href="/messages/expanded">
* Show all expanded messages in this frame.
* </a>
*
* <form action="/messages">
* Show response from this form within this frame.
* </form>
* </turbo-frame>
*/
export class FrameElement extends HTMLElement {
static delegateConstructor: new (element: FrameElement) => FrameElementDelegate
loaded: Promise<void> = Promise.resolve()
readonly delegate: FrameElementDelegate
static get observedAttributes(): FrameElementObservedAttribute[] {
return ["disabled", "complete", "loading", "src"]
}
constructor() {
super()
this.delegate = new FrameElement.delegateConstructor(this)
}
connectedCallback() {
this.delegate.connect()
}
disconnectedCallback() {
this.delegate.disconnect()
}
reload(): Promise<void> {
const { src } = this
this.removeAttribute("complete")
this.src = null
this.src = src
return this.loaded
}
attributeChangedCallback(name: string) {
if (name == "loading") {
this.delegate.loadingStyleChanged()
} else if (name == "complete") {
this.delegate.completeChanged()
} else if (name == "src") {
this.delegate.sourceURLChanged()
} else {
this.delegate.disabledChanged()
}
}
/**
* Gets the URL to lazily load source HTML from
*/
get src() {
return this.getAttribute("src")
}
/**
* Sets the URL to lazily load source HTML from
*/
set src(value: string | null) {
if (value) {
this.setAttribute("src", value)
} else {
this.removeAttribute("src")
}
}
/**
* Determines if the element is loading
*/
get loading(): FrameLoadingStyle {
return frameLoadingStyleFromString(this.getAttribute("loading") || "")
}
/**
* Sets the value of if the element is loading
*/
set loading(value: FrameLoadingStyle) {
if (value) {
this.setAttribute("loading", value)
} else {
this.removeAttribute("loading")
}
}
/**
* Gets the disabled state of the frame.
*
* If disabled, no requests will be intercepted by the frame.
*/
get disabled() {
return this.hasAttribute("disabled")
}
/**
* Sets the disabled state of the frame.
*
* If disabled, no requests will be intercepted by the frame.
*/
set disabled(value: boolean) {
if (value) {
this.setAttribute("disabled", "")
} else {
this.removeAttribute("disabled")
}
}
/**
* Gets the autoscroll state of the frame.
*
* If true, the frame will be scrolled into view automatically on update.
*/
get autoscroll() {
return this.hasAttribute("autoscroll")
}
/**
* Sets the autoscroll state of the frame.
*
* If true, the frame will be scrolled into view automatically on update.
*/
set autoscroll(value: boolean) {
if (value) {
this.setAttribute("autoscroll", "")
} else {
this.removeAttribute("autoscroll")
}
}
/**
* Determines if the element has finished loading
*/
get complete() {
return !this.delegate.isLoading
}
/**
* Gets the active state of the frame.
*
* If inactive, source changes will not be observed.
*/
get isActive() {
return this.ownerDocument === document && !this.isPreview
}
/**
* Sets the active state of the frame.
*
* If inactive, source changes will not be observed.
*/
get isPreview() {
return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview")
}
}
function frameLoadingStyleFromString(style: string) {
switch (style.toLowerCase()) {
case "lazy":
return FrameLoadingStyle.lazy
default:
return FrameLoadingStyle.eager
}
}