fix(lit-components): fix rendering and interaction issues in force-graph, html-table, and draggable-dom
Build and Deploy / build-and-deploy (push) Has been cancelled

- lit-force-graph: Refactored for instance reuse, better lifecycle management, and added dynamic imports for AR/VR modes.
- lit-html-table: Improved visual design, readability of headers, and fixed reactivity in editable mode.
- lit-draggable-dom: Fixed clipping issues by switching to relative/absolute positioning and resolved a panning speed bug caused by CSS variable inheritance.
This commit is contained in:
2026-05-17 00:41:14 -07:00
parent bbdbeb9788
commit b9e42c3ef4
253 changed files with 2555 additions and 4471 deletions
@@ -0,0 +1,37 @@
import { html, TemplateResult } from "lit";
import "@material/mwc-top-app-bar-fixed";
import "@material/mwc-icon-button";
import { iconButton } from "./app-button";
const TITLE = "TODOS";
export const appBar = (props: {
navigation?: TemplateResult;
title?: string;
content?: TemplateResult;
actions?: TemplateResult[];
}) => {
return html` <mwc-top-app-bar-fixed>
${props?.navigation ?? ""}
<div slot="title">${props?.title ?? TITLE}</div>
${props?.actions ?? ""}
<div>${props?.content ?? ""}</div>
</mwc-top-app-bar-fixed>`;
};
export const navigationIcon = (callback: () => void, icon: string = "menu") => {
return iconButton({
icon: icon,
slot: "navigationIcon",
callback: () => callback(),
});
};
export const actionButton = (icon: string, callback: () => void) => {
return iconButton({
icon: icon,
slot: "actionItems",
callback: () => callback(),
});
};
@@ -0,0 +1,38 @@
import { html } from "lit";
import "@material/mwc-button";
import "@material/mwc-icon-button";
interface ButtonOptions {
icon?: string;
label?: string;
action?: string;
slot?: string;
type?: string;
form?: string;
callback: () => void;
}
export const button = (options: ButtonOptions) => {
return html` <mwc-button
@click=${() => options.callback()}
slot=${options?.slot ?? ""}
label=${options?.label ?? ""}
action=${options?.action ?? ""}
type=${options?.type ?? ""}
form=${options?.form ?? ""}
icon=${options?.icon ?? ""}
>
</mwc-button>`;
};
export const iconButton = (options: ButtonOptions) => {
return html` <mwc-icon-button
@click=${() => options.callback()}
slot=${options?.slot ?? ""}
action=${options?.action ?? ""}
type=${options?.type ?? ""}
form=${options?.form ?? ""}
icon=${options.icon ?? ""}
>
</mwc-icon-button>`;
};
@@ -0,0 +1,36 @@
import "@material/mwc-dialog";
import "@material/mwc-button/mwc-button";
import { html, TemplateResult } from "lit";
interface DialogOptions {
open: boolean;
title: string;
content: () => TemplateResult;
dismiss: (target: HTMLElement) => void;
}
export const appDialog = (options: DialogOptions) => {
return html`<mwc-dialog
?open=${options?.open ?? false}
heading="${options.title}"
@closed=${(e: any) => options.dismiss(e.target)}
>
<div>${options.content()}</div>
</mwc-dialog>`;
};
interface DialogActionOptions {
label: string;
action?: string;
callback: () => void;
}
export const dialogAction = (options: DialogActionOptions) => {
return html` <mwc-button
slot="primaryAction"
dialogAction="${options?.action ?? "close"}"
@click=${() => options.callback()}
>
${options.label}
</mwc-button>`;
};
@@ -0,0 +1,28 @@
import { html, TemplateResult } from "lit";
import "@material/mwc-drawer";
import { Drawer } from "@material/mwc-drawer";
export const appDrawer = (props: {
title?: string;
subtitle?: string;
drawerContent?: TemplateResult;
appContent?: TemplateResult;
open?: boolean;
}) => {
return html`
<mwc-drawer hasHeader type="modal" ?open=${props?.open ?? false}>
${props?.title ? html` <span slot="title">${props.title}e</span>` : ""}
${props?.subtitle
? html` <span slot="subtitle">${props.subtitle}e</span>`
: ""}
<div>${props?.drawerContent ?? ""}</div>
<div slot="appContent">${props?.appContent ?? ""}</div>
</mwc-drawer>
`;
};
export const toggleDrawer = (root: HTMLElement | ShadowRoot) => {
const drawer = root.querySelector("mwc-drawer")! as Drawer;
drawer.open = !drawer.open;
};
@@ -0,0 +1,22 @@
import { TemplateResult } from "lit";
import { appBar, navigationIcon } from "./app-bar";
import { appDrawer, toggleDrawer } from "./app-drawer";
export interface ScaffoldOptions {
root: HTMLElement | ShadowRoot;
body: () => TemplateResult;
actions?: TemplateResult[];
title?: string;
}
export const appScaffold = (options: ScaffoldOptions) => {
return appDrawer({
appContent: appBar({
title: options.title,
navigation: navigationIcon(() => toggleDrawer(options.root)),
content: options.body(),
actions: options.actions ? options.actions : DEFAULT_ACTIONS,
}),
});
};
const DEFAULT_ACTIONS: TemplateResult[] = [];
@@ -0,0 +1,45 @@
import { html, TemplateResult } from "lit";
export const formBuilder = (
content: TemplateResult,
onSubmit: (form: HTMLFormElement) => void
) => {
return html`<form
@submit=${(e: any) => {
e.preventDefault();
const form = e.currentTarget as HTMLFormElement;
onSubmit(form);
}}
>
${content} <br />
<input type="submit" value="Submit" />
</form>`;
};
export const input = (
id: string,
label: string,
options?: { type?: "text" | "number"; required?: boolean }
) => {
return html`
<div>
<label for="${id}">${label}</label><br />
<input
id="${id}"
type="${options?.type ?? "text"}"
?required=${options?.required ?? false}
/>
</div>
`;
};
export const inputValue = (
form: HTMLFormElement,
id: string,
fallback: string = ""
) => {
const input = form.querySelector(`#${id}`) as HTMLInputElement;
const value = input.value;
if (value.length > 0) return value;
return fallback;
};
@@ -0,0 +1,12 @@
import { TemplateResult, html } from "lit";
import { until } from "lit/directives/until.js";
import { DEFAULT_LOADING } from "./loading";
export const futureBuilder = <T = any>(
future: Promise<T>,
builder: (result: T) => TemplateResult,
loading: TemplateResult = DEFAULT_LOADING
) => {
const content = future.then((result) => builder(result));
return html`${until(content, loading)}`;
};
@@ -0,0 +1,12 @@
import { html, TemplateResult } from "lit";
export const listViewBuilder = <T = any>(
items: T[],
builder: (item: T, index: number) => TemplateResult
) => {
return html` <ul>
${items?.map((n: any, i) => {
return html`<li>${builder(n, i)}</li>`;
})}
</ul>`;
};
@@ -0,0 +1,3 @@
import { html } from "lit";
export const DEFAULT_LOADING = html`<span>Loading...</span>`;