Skip to content

Instantly share code, notes, and snippets.

@julianrubisch
Last active July 5, 2022 09:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save julianrubisch/1b613a791aa025aa5f76f9f849a1e037 to your computer and use it in GitHub Desktop.
Save julianrubisch/1b613a791aa025aa5f76f9f849a1e037 to your computer and use it in GitHub Desktop.
Futurism

There's one thing that I felt was missing from the CableReady ecosystem and should be doable: lazy loading

I introduce:

Futurism

with a helper in your template

<%= futurize @posts %>

custom <futurism-elements> (in the form of a <div> or a <tr is="futurism-table-row"> are rendered. Those custom elements have an IntersectionObserver attached that will send the dom_id to an ActionCable channel (FuturismChannel) which will then replace the placeholders with the actual resource partial.

With that method, you could lazy load every class that has to_partial_path defined (ActiveModel has by default).

You can pass the placeholder as a block:

<%= futurize @posts do %>
  <td class="placeholder"></td>
<% end %>
<tr>
<td><%= post.title %></td>
</tr>
<table>
<tbody>
<%= futurize @posts do %>
<td class="placeholder"></td>
<% end %>
</tbody>
</table>
import consumer from "./consumer";
import CableReady from "cable_ready";
const debounceEvents = (callback, delay = 250) => {
let timeoutId;
let events = [];
return (...args) => {
clearTimeout(timeoutId);
events = [...events, ...args];
timeoutId = setTimeout(() => {
timeoutId = null;
callback(events);
events = [];
}, delay);
};
};
consumer.subscriptions.create("FuturismChannel", {
connected() {
window.Futurism = this;
document.addEventListener(
"futurism:appear",
debounceEvents(
(events => {
this.send({ sgids: events.map(e => e.target.dataset.sgid) });
}).bind(this)
)
);
},
received(data) {
if (data.cableReady) {
CableReady.perform(data.operations, {
emitMissingElementWarnings: false
});
}
}
});
class FuturismChannel < ApplicationCable::Channel
include CableReady::Broadcaster
def subscribed
stream_from "FuturismChannel"
end
def receive(data)
resources = data["sgids"].map { |sgid|
[sgid, GlobalID::Locator.locate_signed(sgid)]
}
resources.each do |sgid, resource|
cable_ready["FuturismChannel"].outer_html(
selector: "[data-sgid='#{sgid}']",
html: ApplicationController.render(resource)
)
end
cable_ready.broadcast
end
end
class FuturismElement extends HTMLElement {
connectedCallback() {
const options = {};
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach(
(entry => {
if (entry.isIntersecting) {
this.dispatchAppearEvent(entry, observer);
}
}).bind(this)
);
}, options);
this.observer.observe(this);
}
dispatchAppearEvent(entry, observer) {
if (window.Futurism) {
const evt = new CustomEvent("futurism:appear", {
bubbles: true,
detail: {
target: entry.target,
observer
}
});
this.dispatchEvent(evt);
} else {
setTimeout(
(() => {
this.dispatchAppearEvent(entry, observer);
}).bind(this),
1
);
}
}
}
customElements.define("futurism-element", FuturismElement);
module FuturismHelper
def futurize(records, extends: :tr, &block)
placeholder = capture(&block)
Array(records).map { |record|
case extends
when :tr
content_tag :tr, placeholder, data: { sgid: record.to_sgid.to_s }, is: "futurism-table-row"
else
content_tag :"futurism-element", placeholder, data: { sgid: record.to_sgid.to_s }
end
}.join.html_safe
end
end
class FuturismTableRow extends HTMLTableRowElement {
connectedCallback() {
const options = {};
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach(
(entry => {
if (entry.isIntersecting) {
this.dispatchAppearEvent(entry, observer);
}
}).bind(this)
);
}, options);
this.observer.observe(this);
}
dispatchAppearEvent(entry, observer) {
if (window.Futurism) {
const evt = new CustomEvent("futurism:appear", {
bubbles: true,
detail: {
target: entry.target,
observer
}
});
this.dispatchEvent(evt);
} else {
setTimeout(
(() => {
this.dispatchAppearEvent(entry, observer);
}).bind(this),
1
);
}
}
}
customElements.define("futurism-table-row", FuturismTableRow, {
extends: "tr"
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment