summaryrefslogtreecommitdiff
path: root/packages/integrations/lit/src
diff options
context:
space:
mode:
authorGravatar Elliott Marquez <5981958+e111077@users.noreply.github.com> 2023-02-06 17:01:21 -0800
committerGravatar GitHub <noreply@github.com> 2023-02-06 20:01:21 -0500
commitec38a8921f02a275949abcababe1b8afdf8184a2 (patch)
treed1634f74690af1e87d2bcaa1b8296b3f0642c151 /packages/integrations/lit/src
parent327a76c3dbd6945e1a98b0262d7f20edaf1914b1 (diff)
downloadastro-ec38a8921f02a275949abcababe1b8afdf8184a2.tar.gz
astro-ec38a8921f02a275949abcababe1b8afdf8184a2.tar.zst
astro-ec38a8921f02a275949abcababe1b8afdf8184a2.zip
[Lit] add `client:only` functionality to Lit integration (#6111)
* [Lit] add `client:only` functionality to Lit integration * add changeset * update lit changeset to minor
Diffstat (limited to 'packages/integrations/lit/src')
-rw-r--r--packages/integrations/lit/src/client.ts89
1 files changed, 70 insertions, 19 deletions
diff --git a/packages/integrations/lit/src/client.ts b/packages/integrations/lit/src/client.ts
index fb92ac3df..b63147182 100644
--- a/packages/integrations/lit/src/client.ts
+++ b/packages/integrations/lit/src/client.ts
@@ -1,21 +1,72 @@
-export default (element: HTMLElement) => async (Component: any, props: Record<string, any>) => {
- // Get the LitElement element instance (may or may not be upgraded).
- const component = element.children[0] as HTMLElement;
-
- // If there is no deferral of hydration, then all reactive properties are
- // already serialzied as reflected attributes, or no reactive props were set
- if (!component || !component.hasAttribute('defer-hydration')) {
- return;
- }
-
- // Set properties on the LitElement instance for resuming hydration.
- for (let [name, value] of Object.entries(props)) {
- // Check if reactive property or class property.
- if (name in Component.prototype) {
- (component as any)[name] = value;
+/**
+ * Adds the appropriate slot attribute to each top-level node in the given HTML
+ * string.
+ *
+ * @example
+ * addSlotAttrsToHtmlString('foo', '<div>bar</div><div>baz</div>');
+ * // '<div slot="foo">bar</div><div slot="foo">baz</div>'
+ *
+ * @param slotName Name of slot to apply to HTML string.
+ * @param html Stringified HTML that should be projected into the given slotname.
+ * @returns A stringified HTML string with the slot attribute applied to each top-level node.
+ */
+const addSlotAttrsToHtmlString = (slotName: string, html: string) => {
+ const templ = document.createElement('template');
+ templ.innerHTML = html;
+ Array.from(templ.content.children).forEach((node) => {
+ node.setAttribute('slot', slotName);
+ });
+ return templ.innerHTML;
+};
+
+export default (element: HTMLElement) =>
+ async (
+ Component: any,
+ props: Record<string, any>,
+ { default: defaultChildren, ...slotted }: { default: string; [slotName: string]: string }
+ ) => {
+ // Get the LitElement element instance.
+ let component = element.children[0];
+ // Check if hydration model is client:only
+ const isClientOnly = element.getAttribute('client') === 'only';
+
+ // We need to attach the element and it's children to the DOM since it's not
+ // SSR'd.
+ if (isClientOnly) {
+ component = new Component();
+
+ const otherSlottedChildren = Object.entries(slotted)
+ .map(([slotName, htmlStr]) => addSlotAttrsToHtmlString(slotName, htmlStr))
+ .join('');
+
+ // defaultChildren can actually be undefined, but TS will complain if we
+ // type it as so, make sure we don't render undefined.
+ component.innerHTML = `${defaultChildren ?? ''}${otherSlottedChildren}`;
+ element.appendChild(component);
+
+ // Set props bound to non-reactive properties as attributes.
+ for (let [name, value] of Object.entries(props)) {
+ if (!(name in Component.prototype)) {
+ component.setAttribute(name, value);
+ }
+ }
}
- }
- // Tell LitElement to resume hydration.
- component.removeAttribute('defer-hydration');
-};
+ // If there is no deferral of hydration, then all reactive properties are
+ // already serialzied as reflected attributes, or no reactive props were set
+ // Alternatively, if hydration is client:only proceed to set props.
+ if (!component || !(component.hasAttribute('defer-hydration') || isClientOnly)) {
+ return;
+ }
+
+ // Set properties on the LitElement instance for resuming hydration.
+ for (let [name, value] of Object.entries(props)) {
+ // Check if reactive property or class property.
+ if (name in Component.prototype) {
+ (component as any)[name] = value;
+ }
+ }
+
+ // Tell LitElement to resume hydration.
+ component.removeAttribute('defer-hydration');
+ };