diff --git a/.changeset/config.json b/.changeset/config.json index e13042f0c2..7c3d4c9697 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -24,6 +24,7 @@ "preact-ts", "svelte-ts", "vanilla-ts", + "lit-ts", "website" ] } diff --git a/examples/lit-ts/.gitignore b/examples/lit-ts/.gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/examples/lit-ts/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/lit-ts/index.html b/examples/lit-ts/index.html new file mode 100644 index 0000000000..c7931fe365 --- /dev/null +++ b/examples/lit-ts/index.html @@ -0,0 +1,19 @@ + + + + + + + Vite + TS + + +

Lit + Zag

+ + Accordion + Avatar + Avatar with Controller + Popover + Checkbox + Combobox + + diff --git a/examples/lit-ts/package.json b/examples/lit-ts/package.json new file mode 100644 index 0000000000..58913513f0 --- /dev/null +++ b/examples/lit-ts/package.json @@ -0,0 +1,95 @@ +{ + "name": "lit-ts", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^5.8.3", + "vite": "^7.0.5" + }, + "dependencies": { + "@internationalized/date": "3.8.2", + "@open-wc/lit-helpers": "0.7.0", + "@zag-js/accordion": "workspace:*", + "@zag-js/anatomy": "workspace:*", + "@zag-js/anatomy-icons": "workspace:*", + "@zag-js/angle-slider": "workspace:*", + "@zag-js/aria-hidden": "workspace:*", + "@zag-js/async-list": "workspace:*", + "@zag-js/auto-resize": "workspace:*", + "@zag-js/avatar": "workspace:*", + "@zag-js/carousel": "workspace:*", + "@zag-js/checkbox": "workspace:*", + "@zag-js/clipboard": "workspace:*", + "@zag-js/collapsible": "workspace:*", + "@zag-js/collection": "workspace:*", + "@zag-js/color-picker": "workspace:*", + "@zag-js/color-utils": "workspace:*", + "@zag-js/combobox": "workspace:*", + "@zag-js/core": "workspace:*", + "@zag-js/date-picker": "workspace:*", + "@zag-js/date-utils": "workspace:*", + "@zag-js/dialog": "workspace:*", + "@zag-js/dismissable": "workspace:*", + "@zag-js/docs": "workspace:*", + "@zag-js/dom-query": "workspace:*", + "@zag-js/editable": "workspace:*", + "@zag-js/file-upload": "workspace:*", + "@zag-js/file-utils": "workspace:*", + "@zag-js/floating-panel": "workspace:*", + "@zag-js/focus-trap": "workspace:*", + "@zag-js/focus-visible": "workspace:*", + "@zag-js/highlight-word": "workspace:*", + "@zag-js/hover-card": "workspace:*", + "@zag-js/i18n-utils": "workspace:*", + "@zag-js/interact-outside": "workspace:*", + "@zag-js/json-tree-utils": "workspace:*", + "@zag-js/listbox": "workspace:*", + "@zag-js/live-region": "workspace:*", + "@zag-js/menu": "workspace:*", + "@zag-js/navigation-menu": "workspace:*", + "@zag-js/number-input": "workspace:*", + "@zag-js/pagination": "workspace:*", + "@zag-js/password-input": "workspace:*", + "@zag-js/pin-input": "workspace:*", + "@zag-js/popover": "workspace:*", + "@zag-js/popper": "workspace:*", + "@zag-js/presence": "workspace:*", + "@zag-js/progress": "workspace:*", + "@zag-js/qr-code": "workspace:*", + "@zag-js/radio-group": "workspace:*", + "@zag-js/rating-group": "workspace:*", + "@zag-js/rect-utils": "workspace:*", + "@zag-js/remove-scroll": "workspace:*", + "@zag-js/scroll-snap": "workspace:*", + "@zag-js/select": "workspace:*", + "@zag-js/shared": "workspace:*", + "@zag-js/signature-pad": "workspace:*", + "@zag-js/slider": "workspace:*", + "@zag-js/splitter": "workspace:*", + "@zag-js/steps": "workspace:*", + "@zag-js/store": "workspace:*", + "@zag-js/stringify-state": "workspace:*", + "@zag-js/switch": "workspace:*", + "@zag-js/tabs": "workspace:*", + "@zag-js/tags-input": "workspace:*", + "@zag-js/timer": "workspace:*", + "@zag-js/toast": "workspace:*", + "@zag-js/toggle": "workspace:*", + "@zag-js/toggle-group": "workspace:*", + "@zag-js/tooltip": "workspace:*", + "@zag-js/tour": "workspace:*", + "@zag-js/tree-view": "workspace:*", + "@zag-js/types": "workspace:*", + "@zag-js/utils": "workspace:*", + "form-serialize": "0.7.2", + "lit": "3.3.1", + "match-sorter": "8.0.3", + "nanoid": "^5.1.5" + } +} diff --git a/examples/lit-ts/pages/accordion.html b/examples/lit-ts/pages/accordion.html new file mode 100644 index 0000000000..313d365ebd --- /dev/null +++ b/examples/lit-ts/pages/accordion.html @@ -0,0 +1,17 @@ + + + + + + + + Vite + TS + + +

Accordion

+ + + + + + diff --git a/examples/lit-ts/pages/avatar-controller.html b/examples/lit-ts/pages/avatar-controller.html new file mode 100644 index 0000000000..dd5bb2e25f --- /dev/null +++ b/examples/lit-ts/pages/avatar-controller.html @@ -0,0 +1,17 @@ + + + + + + + + Vite + TS + + +

Avatar Element with Reactive Controller

+ + + + + + diff --git a/examples/lit-ts/pages/avatar.html b/examples/lit-ts/pages/avatar.html new file mode 100644 index 0000000000..a9ce7d4f4a --- /dev/null +++ b/examples/lit-ts/pages/avatar.html @@ -0,0 +1,17 @@ + + + + + + + + Vite + TS + + +

Avatar Element

+ + + + + + diff --git a/examples/lit-ts/pages/checkbox.html b/examples/lit-ts/pages/checkbox.html new file mode 100644 index 0000000000..fe4213bc11 --- /dev/null +++ b/examples/lit-ts/pages/checkbox.html @@ -0,0 +1,17 @@ + + + + + + + + Vite + TS + + +

Checkbox

+ + + + + + diff --git a/examples/lit-ts/pages/combobox.html b/examples/lit-ts/pages/combobox.html new file mode 100644 index 0000000000..308ea1f43a --- /dev/null +++ b/examples/lit-ts/pages/combobox.html @@ -0,0 +1,17 @@ + + + + + + + + Vite + TS + + +

Combobox

+ + + + + + diff --git a/examples/lit-ts/pages/popover.html b/examples/lit-ts/pages/popover.html new file mode 100644 index 0000000000..61dccf7c18 --- /dev/null +++ b/examples/lit-ts/pages/popover.html @@ -0,0 +1,17 @@ + + + + + + + + Vite + TS + + +

Popover

+ + + + + + diff --git a/examples/lit-ts/public/layout.css b/examples/lit-ts/public/layout.css new file mode 100644 index 0000000000..df9e5dee71 --- /dev/null +++ b/examples/lit-ts/public/layout.css @@ -0,0 +1,235 @@ +:root { + --ring-color: blue; +} + +body { + position: relative; +} + +* { + box-sizing: border-box; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", + sans-serif; +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; +} + +*:focus { + outline: 2px solid var(--ring-color); + outline-offset: 2px; +} + +button { + flex-shrink: 0; +} + +.root { + margin: 24px; +} + +ul { + padding-inline-start: 0; +} + +[hidden] { + display: none !important; +} + +.pre { + background: rgba(0, 0, 0, 0.05); + padding: 20px; + margin-bottom: 40px; + border-radius: 10px; + font-family: monospace; +} + +.index-nav { + line-height: 1em; + flex: 1; + padding: 40px; + overflow-y: auto; +} + +.index-nav ul { + list-style-type: none; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + grid-gap: 20px; + padding: 0; +} + +.index-nav a { + display: block; + border: 1px solid rgba(0, 0, 0, 0.1); + padding: 20px; + text-transform: capitalize; + text-decoration: none; +} + +.backlink { + margin: 16px 12px; +} + +body { + margin: 0; + font-size: 14px; + overflow: hidden; +} + +.page { + display: flex; + height: 100vh; + overflow: hidden; +} + +.page main { + flex: auto; + display: flex; + gap: 10px; + position: relative; + flex-direction: column; + align-items: flex-start; + padding: 40px; + overflow-y: auto; +} + +.nav { + display: flex; + flex-direction: column; + width: 292px; + border-right: solid 1px #d7e0da; + overflow-y: scroll; +} + +.nav header { + padding: 8px; + font-size: larger; + font-weight: 600; +} + +.nav a { + padding: 6px 14px; + border-left: solid 4px transparent; + text-decoration: none; + color: black; +} + +.nav a:hover { + background-color: rgba(124, 128, 125, 0.4); +} + +.nav a:focus { + outline: none; +} + +.nav a[data-active], +a[aria-current="page"] { + border-color: green; + font-weight: 500; +} + +.controls-container { + display: flex; + flex-direction: column; + gap: 14px; + order: 1; + padding: 12px 22px; +} +.controls-container .text { + display: flex; + flex-direction: column; + gap: 8px; +} +.controls-container .text label { + font-weight: 500; +} +.controls-container .text input, +.controls-container .text select { + padding: 4px; + border: solid 1px black; + border-radius: 4px; +} + +.toolbar { + border-left: solid 1px #d7e0da; + width: 400px; + display: flex; + flex-direction: column; +} + +.toolbar nav { + padding: 16px; + display: flex; + gap: 24px; + border-bottom: solid 1px #d7e0da; +} + +.toolbar nav button { + all: unset; + font-weight: 500; +} + +.toolbar nav button[data-active] { + color: green; +} + +.toolbar > div { + max-height: 100%; + overflow: hidden; +} + +.toolbar [data-content] { + display: none; +} + +.toolbar [data-content][data-active] { + display: flex; + flex-direction: column; + max-height: 100%; +} + +.toolbar .viz { + font-size: 13px; + --viz-font: SF Mono, Menlo, monospace; + padding-left: 14px; + flex: 1; + overflow: auto; + border-bottom: solid 1px #d7e0da; +} + +.toolbar .viz summary { + margin-bottom: 24; + font-family: var(--viz-font); + font-weight: bold; +} + +.toolbar .viz summary + div > * { + font-family: var(--viz-font); +} + +.output { + border: 2px solid black; + padding-inline: 40px; + padding-block: 10px; + padding-bottom: 32px; +} + +.output p { + font-weight: 600; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} diff --git a/examples/lit-ts/public/vite.svg b/examples/lit-ts/public/vite.svg new file mode 100644 index 0000000000..e7b8dfb1b2 --- /dev/null +++ b/examples/lit-ts/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/lit-ts/src/accordion.ts b/examples/lit-ts/src/accordion.ts new file mode 100644 index 0000000000..494d9fcf3d --- /dev/null +++ b/examples/lit-ts/src/accordion.ts @@ -0,0 +1,45 @@ +import { spread } from "@open-wc/lit-helpers" +import * as accordion from "@zag-js/accordion" +import style from "@zag-js/shared/src/css/accordion.css?inline" +import { html, unsafeCSS } from "lit" +import { customElement } from "lit/decorators.js" +import { Component } from "./component" +import { normalizeProps, VanillaMachine } from "./lib" + +@customElement("accordion-element") +export class Accordion extends Component { + initMachine() { + return new VanillaMachine(accordion.machine, { id: this.id, multiple: true }) + } + + initApi() { + return accordion.connect(this.machine.service, normalizeProps) + } + + static styles = unsafeCSS(style) + + override render() { + return html`
+
+ +
First Content
+
+ +
+ +
Second Content
+
+ +
+ +
Third Content
+
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + "accordion-element": Accordion + } +} diff --git a/examples/lit-ts/src/avatar.ts b/examples/lit-ts/src/avatar.ts new file mode 100644 index 0000000000..5617888f60 --- /dev/null +++ b/examples/lit-ts/src/avatar.ts @@ -0,0 +1,39 @@ +import { spread } from "@open-wc/lit-helpers" +import * as avatar from "@zag-js/avatar" +import style from "@zag-js/shared/src/css/avatar.css?inline" +import { html, unsafeCSS } from "lit" +import { customElement } from "lit/decorators.js" +import { Component } from "./component" +import { normalizeProps, VanillaMachine } from "./lib" + +@customElement("avatar-element") +export class Avatar extends Component { + initMachine() { + return new VanillaMachine(avatar.machine, { id: this.id }) + } + + initApi() { + return avatar.connect(this.machine.service, normalizeProps) + } + + static styles = unsafeCSS(style) + + override render() { + return html`
+ PA + Naruto +
` + } +} + +declare global { + interface HTMLElementTagNameMap { + "avatar-element": Avatar + } +} diff --git a/examples/lit-ts/src/avatar2.ts b/examples/lit-ts/src/avatar2.ts new file mode 100644 index 0000000000..885c062005 --- /dev/null +++ b/examples/lit-ts/src/avatar2.ts @@ -0,0 +1,43 @@ +import { spread } from "@open-wc/lit-helpers" +import * as avatar from "@zag-js/avatar" +import style from "@zag-js/shared/src/css/avatar.css?inline" +import { html, LitElement, unsafeCSS } from "lit" +import { customElement } from "lit/decorators.js" +import { createId } from "./lib/create-id" +import { ZagController } from "./zag-controller" + +@customElement("avatar-ctrl-element") +export class Avatar extends LitElement { + zag: ZagController + + constructor() { + super() + this.zag = new ZagController(this, avatar.connect, avatar.machine, { + id: createId(), + onStatusChange: (details) => { + console.log("Avatar status changed:", details.status, details) + }, + }) + } + + static styles = unsafeCSS(style) + + override render() { + return html`
+ PA + Naruto +
` + } +} + +declare global { + interface HTMLElementTagNameMap { + "avatar-ctrl-element": Avatar + } +} diff --git a/examples/lit-ts/src/checkbox.ts b/examples/lit-ts/src/checkbox.ts new file mode 100644 index 0000000000..0d9c9a9ddf --- /dev/null +++ b/examples/lit-ts/src/checkbox.ts @@ -0,0 +1,36 @@ +import { spread } from "@open-wc/lit-helpers" +import * as checkbox from "@zag-js/checkbox" +import style from "@zag-js/shared/src/css/checkbox.css?inline" +import { html, unsafeCSS } from "lit" +import { customElement } from "lit/decorators.js" +import { Component } from "./component" +import { normalizeProps, VanillaMachine } from "./lib" + +@customElement("checkbox-element") +export class Checkbox extends Component { + ctx = checkbox.machine.context + + initMachine() { + return new VanillaMachine(checkbox.machine, { id: this.id, defaultChecked: true }) + } + + initApi() { + return checkbox.connect(this.machine.service, normalizeProps) + } + + static styles = unsafeCSS(style) + + override render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + "checkbox-element": Checkbox + } +} diff --git a/examples/lit-ts/src/combobox.ts b/examples/lit-ts/src/combobox.ts new file mode 100644 index 0000000000..8eb9a45047 --- /dev/null +++ b/examples/lit-ts/src/combobox.ts @@ -0,0 +1,79 @@ +import { spread } from "@open-wc/lit-helpers" +import * as combobox from "@zag-js/combobox" +import { comboboxData } from "@zag-js/shared" +import style from "@zag-js/shared/src/css/combobox.css?inline" +import { html, unsafeCSS } from "lit" +import { customElement } from "lit/decorators.js" +import { matchSorter } from "match-sorter" +import { Component } from "./component" +import { normalizeProps, VanillaMachine } from "./lib" + +interface Item { + code: string + label: string +} + +@customElement("combobox-element") +export class Combobox extends Component { + options: Item[] = comboboxData.slice() + + getCollection(items: Item[]) { + return combobox.collection({ + items, + itemToValue: (item) => item.code, + itemToString: (item) => item.label, + }) + } + + initMachine() { + const self = this + return new VanillaMachine(combobox.machine, { + id: this.id, + getRootNode: () => this.renderRoot, + get collection() { + return self.getCollection(self.options || comboboxData) + }, + onOpenChange: () => { + self.options = comboboxData + }, + onInputValueChange: ({ inputValue }) => { + const filtered = matchSorter(comboboxData, inputValue, { keys: ["label"] }) + self.options = filtered.length > 0 ? filtered : comboboxData + }, + }) + } + + initApi() { + return combobox.connect(this.machine.service, normalizeProps) + } + + static styles = unsafeCSS(style) + + override render() { + return html` +
+ +
+ + + +
+ ${this.options.length > 0 && + html`
+ ${this.options.map( + (item) => + html`
+ ${item.label} +
`, + )} +
`} +
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + "combobox-element": Combobox + } +} diff --git a/examples/lit-ts/src/component.ts b/examples/lit-ts/src/component.ts new file mode 100644 index 0000000000..c0669e0f76 --- /dev/null +++ b/examples/lit-ts/src/component.ts @@ -0,0 +1,39 @@ +import { LitElement } from "lit" +import { VanillaMachine } from "./lib" +import { createId } from "./lib/create-id" + +interface ComponentInterface { + machine: VanillaMachine + api: Api +} + +export abstract class Component extends LitElement implements ComponentInterface { + id: string = createId() + machine: VanillaMachine + api: Api + + constructor() { + super() + this.machine = this.initMachine() + this.api = this.initApi() + } + + abstract initMachine(): VanillaMachine + abstract initApi(): Api + + override connectedCallback(): void { + super.connectedCallback() + + this.machine.subscribe(() => { + this.api = this.initApi() + this.requestUpdate() + }) + this.machine.start() + } + + override disconnectedCallback(): void { + super.disconnectedCallback() + + this.machine.stop() + } +} diff --git a/examples/lit-ts/src/lib/bindable.ts b/examples/lit-ts/src/lib/bindable.ts new file mode 100644 index 0000000000..34347acefc --- /dev/null +++ b/examples/lit-ts/src/lib/bindable.ts @@ -0,0 +1,58 @@ +import type { Bindable, BindableParams } from "@zag-js/core" +import { proxy } from "@zag-js/store" +import { isFunction } from "@zag-js/utils" + +export function bindable(props: () => BindableParams): Bindable { + const initial = props().value ?? props().defaultValue + + if (props().debug) { + console.log(`[bindable > ${props().debug}] initial`, initial) + } + + const eq = props().isEqual ?? Object.is + + const store = proxy({ value: initial as T }) + + const controlled = () => props().value !== undefined + + return { + initial, + ref: store, + get() { + return controlled() ? (props().value as T) : store.value + }, + set(nextValue: T | ((prev: T) => T)) { + const prev = store.value + const next = isFunction(nextValue) ? nextValue(prev as T) : nextValue + + if (props().debug) { + console.log(`[bindable > ${props().debug}] setValue`, { next, prev }) + } + + if (!controlled()) store.value = next + if (!eq(next, prev)) { + props().onChange?.(next, prev) + } + }, + invoke(nextValue: T, prevValue: T) { + props().onChange?.(nextValue, prevValue) + }, + hash(value: T) { + return props().hash?.(value) ?? String(value) + }, + } +} + +bindable.cleanup = (_fn: VoidFunction) => { + // No-op in vanilla implementation +} + +bindable.ref = (defaultValue: T) => { + let value = defaultValue + return { + get: () => value, + set: (next: T) => { + value = next + }, + } +} diff --git a/examples/lit-ts/src/lib/create-id.ts b/examples/lit-ts/src/lib/create-id.ts new file mode 100644 index 0000000000..8bbec5b951 --- /dev/null +++ b/examples/lit-ts/src/lib/create-id.ts @@ -0,0 +1,5 @@ +let id = 0 + +export const createId = () => { + return (++id).toString() +} diff --git a/examples/lit-ts/src/lib/index.ts b/examples/lit-ts/src/lib/index.ts new file mode 100644 index 0000000000..c3bc1eda3f --- /dev/null +++ b/examples/lit-ts/src/lib/index.ts @@ -0,0 +1,2 @@ +export { VanillaMachine } from "./machine" +export { normalizeProps } from "./normalize-props" diff --git a/examples/lit-ts/src/lib/machine.ts b/examples/lit-ts/src/lib/machine.ts new file mode 100644 index 0000000000..b6f6c2e131 --- /dev/null +++ b/examples/lit-ts/src/lib/machine.ts @@ -0,0 +1,343 @@ +import type { + ActionsOrFn, + Bindable, + BindableContext, + BindableRefs, + ChooseFn, + ComputedFn, + EffectsOrFn, + GuardFn, + Machine, + MachineSchema, + Params, + PropFn, + Scope, + Service, +} from "@zag-js/core" +import { createScope, INIT_STATE, MachineStatus } from "@zag-js/core" +import { subscribe } from "@zag-js/store" +import { compact, identity, isEqual, isFunction, isString, runIfFn, toArray, warn } from "@zag-js/utils" +import { bindable } from "./bindable" +import { createRefs } from "./refs" + +export class VanillaMachine { + scope: Scope + ctx: BindableContext + prop: PropFn + state: Bindable + refs: BindableRefs + computed: ComputedFn + + private event: any = { type: "" } + private previousEvent: any + + private effects = new Map() + private transition: any = null + + private cleanups: VoidFunction[] = [] + private subscriptions: Array<(service: Service) => void> = [] + + private getEvent = () => ({ + ...this.event, + current: () => this.event, + previous: () => this.previousEvent, + }) + + private getState = () => ({ + ...this.state, + matches: (...values: T["state"][]) => values.includes(this.state.get()), + hasTag: (tag: T["tag"]) => !!this.machine.states[this.state.get() as T["state"]]?.tags?.includes(tag), + }) + + debug = (...args: any[]) => { + if (this.machine.debug) console.log(...args) + } + + notify = () => { + this.publish() + } + + constructor( + private machine: Machine, + userProps: Partial | (() => Partial) = {}, + ) { + // create scope + const { id, ids, getRootNode } = runIfFn(userProps) as any + this.scope = createScope({ id, ids, getRootNode }) + + // create prop + const prop: PropFn = (key) => { + const __props = runIfFn(userProps) + const props: any = machine.props?.({ props: compact(__props), scope: this.scope }) ?? __props + return props[key] as any + } + this.prop = prop + + // create context + const context: any = machine.context?.({ + prop, + bindable, + scope: this.scope, + flush(fn: VoidFunction) { + queueMicrotask(fn) + }, + getContext() { + return ctx as any + }, + getComputed() { + return computed as any + }, + getRefs() { + return refs as any + }, + getEvent: this.getEvent.bind(this), + }) + + // subscribe to context changes + if (context) { + Object.values(context).forEach((item: any) => { + const unsub = subscribe(item.ref, () => this.notify()) + this.cleanups.push(unsub) + }) + } + + // context function + const ctx: BindableContext = { + get(key) { + return context?.[key].get() + }, + set(key, value) { + context?.[key].set(value) + }, + initial(key) { + return context?.[key].initial + }, + hash(key) { + const current = context?.[key].get() + return context?.[key].hash(current) + }, + } + this.ctx = ctx + + const computed: ComputedFn = (key) => { + return ( + machine.computed?.[key]({ + context: ctx as any, + event: this.getEvent(), + prop, + refs: this.refs, + scope: this.scope, + computed: computed as any, + }) ?? ({} as any) + ) + } + this.computed = computed + + const refs: BindableRefs = createRefs(machine.refs?.({ prop, context: ctx }) ?? {}) + this.refs = refs + + // state + const state = bindable(() => ({ + defaultValue: machine.initialState({ prop }), + onChange: (nextState, prevState) => { + // compute effects: exit -> transition -> enter + + // exit effects + if (prevState) { + const exitEffects = this.effects.get(prevState) + exitEffects?.() + this.effects.delete(prevState) + } + + // exit actions + if (prevState) { + // @ts-ignore + this.action(machine.states[prevState]?.exit) + } + + // transition actions + this.action(this.transition?.actions) + + // enter effect + // @ts-ignore + const cleanup = this.effect(machine.states[nextState]?.effects) + if (cleanup) this.effects.set(nextState as string, cleanup) + + // root entry actions + if (prevState === INIT_STATE) { + this.action(machine.entry) + const cleanup = this.effect(machine.effects) + if (cleanup) this.effects.set(INIT_STATE, cleanup) + } + + // enter actions + // @ts-ignore + this.action(machine.states[nextState]?.entry) + }, + })) + this.state = state + this.cleanups.push(subscribe(this.state.ref, () => this.notify())) + } + + send = (event: any) => { + if (this.status !== MachineStatus.Started) return + + queueMicrotask(() => { + this.previousEvent = this.event + this.event = event + + this.debug("send", event) + + let currentState = this.state.get() + + const transitions = + // @ts-ignore + this.machine.states[currentState].on?.[event.type] ?? + // @ts-ignore + this.machine.on?.[event.type] + + const transition = this.choose(transitions) + if (!transition) return + + // save current transition + this.transition = transition + const target = transition.target ?? currentState + + this.debug("transition", transition) + + const changed = target !== currentState + if (changed) { + // state change is high priority + this.state.set(target) + } else { + // call transition actions + this.action(transition.actions) + } + }) + } + + private action = (keys: ActionsOrFn | undefined) => { + const strs = isFunction(keys) ? keys(this.getParams()) : keys + if (!strs) return + const fns = strs.map((s) => { + const fn = this.machine.implementations?.actions?.[s] + if (!fn) warn(`[zag-js] No implementation found for action "${JSON.stringify(s)}"`) + return fn + }) + for (const fn of fns) { + fn?.(this.getParams()) + } + } + + private guard = (str: T["guard"] | GuardFn) => { + if (isFunction(str)) return str(this.getParams()) + return this.machine.implementations?.guards?.[str](this.getParams()) + } + + private effect = (keys: EffectsOrFn | undefined) => { + const strs = isFunction(keys) ? keys(this.getParams()) : keys + if (!strs) return + const fns = strs.map((s) => { + const fn = this.machine.implementations?.effects?.[s] + if (!fn) warn(`[zag-js] No implementation found for effect "${JSON.stringify(s)}"`) + return fn + }) + const cleanups: VoidFunction[] = [] + for (const fn of fns) { + const cleanup = fn?.(this.getParams()) + if (cleanup) cleanups.push(cleanup) + } + return () => cleanups.forEach((fn) => fn?.()) + } + + private choose: ChooseFn = (transitions) => { + return toArray(transitions).find((t: any) => { + let result = !t.guard + if (isString(t.guard)) result = !!this.guard(t.guard) + else if (isFunction(t.guard)) result = t.guard(this.getParams()) + return result + }) + } + + start() { + this.status = MachineStatus.Started + this.debug("initializing...") + this.state.invoke(this.state.initial!, INIT_STATE) + this.setupTrackers() + } + + stop() { + // run exit effects + this.effects.forEach((fn) => fn?.()) + this.effects.clear() + this.transition = null + this.action(this.machine.exit) + + // unsubscribe from all subscriptions + this.cleanups.forEach((unsub) => unsub()) + this.cleanups = [] + + this.status = MachineStatus.Stopped + this.debug("unmounting...") + } + + subscribe = (fn: (service: Service) => void) => { + this.subscriptions.push(fn) + } + + private status = MachineStatus.NotStarted + + get service(): Service { + return { + state: this.getState(), + send: this.send, + context: this.ctx, + prop: this.prop, + scope: this.scope, + refs: this.refs, + computed: this.computed, + event: this.getEvent(), + getStatus: () => this.status, + } + } + + private publish = () => { + this.callTrackers() + this.subscriptions.forEach((fn) => fn(this.service)) + } + + private trackers: { deps: any[]; fn: any }[] = [] + + private setupTrackers = () => { + this.machine.watch?.(this.getParams()) + } + + private callTrackers = () => { + this.trackers.forEach(({ deps, fn }) => { + const next = deps.map((dep) => dep()) + if (!isEqual(fn.prev, next)) { + fn() + fn.prev = next + } + }) + } + + getParams = (): Params => ({ + state: this.getState(), + context: this.ctx, + event: this.getEvent(), + prop: this.prop, + send: this.send, + action: this.action, + guard: this.guard, + track: (deps: any[], fn: any) => { + fn.prev = deps.map((dep) => dep()) + this.trackers.push({ deps, fn }) + }, + refs: this.refs, + computed: this.computed, + flush: identity, + scope: this.scope, + choose: this.choose, + }) +} diff --git a/examples/lit-ts/src/lib/normalize-props.ts b/examples/lit-ts/src/lib/normalize-props.ts new file mode 100644 index 0000000000..f73226221a --- /dev/null +++ b/examples/lit-ts/src/lib/normalize-props.ts @@ -0,0 +1,56 @@ +import { createNormalizer } from "@zag-js/types" + +export interface AttrMap { + [key: string]: string +} + +export const propMap: AttrMap = { + onFocus: "onFocusin", + onBlur: "onFocusout", + onChange: "onInput", + onDoubleClick: "onDblclick", + htmlFor: "for", + className: "class", + defaultValue: "value", + defaultChecked: "checked", +} + +const toStyleString = (style: any) => { + let string = "" + for (let key in style) { + const value = style[key] + if (value === null || value === undefined) continue + if (!key.startsWith("--")) key = key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`) + string += `${key}:${value};` + } + return string +} + +export const normalizeProps = createNormalizer((props: any) => { + return Object.entries(props).reduce((acc, [key, value]) => { + if (value === undefined) return acc + + if (key in propMap) { + key = propMap[key] + } + + if (key === "style" && typeof value === "object") { + acc.style = toStyleString(value) + return acc + } + + // Convert event handlers to the format expected by Lit + if (key.startsWith("on")) { + key = "@" + key.slice(2) + } + + // Convert boolean attributes to the format expected by Lit + if (typeof value === "boolean") { + key = "?" + key + } + + acc[key.toLowerCase()] = value + + return acc + }, {}) +}) diff --git a/examples/lit-ts/src/lib/refs.ts b/examples/lit-ts/src/lib/refs.ts new file mode 100644 index 0000000000..81057207f3 --- /dev/null +++ b/examples/lit-ts/src/lib/refs.ts @@ -0,0 +1,11 @@ +export function createRefs(refs: T) { + const ref = { current: refs } + return { + get(key: K): T[K] { + return ref.current[key] + }, + set(key: K, value: T[K]) { + ref.current[key] = value + }, + } +} diff --git a/examples/lit-ts/src/popover.ts b/examples/lit-ts/src/popover.ts new file mode 100644 index 0000000000..1c48a40477 --- /dev/null +++ b/examples/lit-ts/src/popover.ts @@ -0,0 +1,54 @@ +import { spread } from "@open-wc/lit-helpers" +import * as popover from "@zag-js/popover" +import style from "@zag-js/shared/src/css/popover.css?inline" +import { html, unsafeCSS } from "lit" +import { customElement } from "lit/decorators.js" +import { Component } from "./component" +import { normalizeProps, VanillaMachine } from "./lib" + +@customElement("popover-element") +export class Popover extends Component { + initMachine() { + return new VanillaMachine(popover.machine, { + id: this.id, + getRootNode: () => this.renderRoot, + positioning: { + placement: "right", + }, + }) + } + + initApi() { + return popover.connect(this.machine.service, normalizeProps) + } + + static styles = unsafeCSS(style) + + override render() { + return html`
+ +
+
+
+
+
+
+
+ About Tabs +
+
+ Tabs are used to organize and group content into sections that the user can navigate between. +
+ +
+
+
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + "popover-element": Popover + } +} diff --git a/examples/lit-ts/src/typescript.svg b/examples/lit-ts/src/typescript.svg new file mode 100644 index 0000000000..d91c910cc3 --- /dev/null +++ b/examples/lit-ts/src/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/lit-ts/src/vite-env.d.ts b/examples/lit-ts/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/lit-ts/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/lit-ts/src/zag-controller.ts b/examples/lit-ts/src/zag-controller.ts new file mode 100644 index 0000000000..c030fea239 --- /dev/null +++ b/examples/lit-ts/src/zag-controller.ts @@ -0,0 +1,45 @@ +import type { Machine, MachineSchema, Service } from "@zag-js/core" +import type { NormalizeProps } from "@zag-js/types" +import type { ReactiveController, ReactiveControllerHost } from "lit" +import { normalizeProps, VanillaMachine } from "./lib" +import { createId } from "./lib/create-id" + +export class ZagController implements ReactiveController { + host: ReactiveControllerHost + + private id = createId() + private connect: (service: Service, normalize: NormalizeProps) => Api + + machine: VanillaMachine + api: Api + + constructor( + host: ReactiveControllerHost, + connect: (service: Service, normalize: NormalizeProps) => Api, + machine: Machine, + userProps: Partial = {}, + ) { + // Store a reference to the host + this.host = host + // Register for lifecycle updates + host.addController(this) + + this.connect = connect + + this.machine = new VanillaMachine(machine, { ...userProps, id: this.id }) + this.api = this.connect(this.machine.service, normalizeProps) + } + + hostConnected() { + // Start the machine when the host is connected + this.machine.subscribe(() => { + this.api = this.connect(this.machine.service, normalizeProps) + this.host.requestUpdate() + }) + this.machine.start() + } + hostDisconnected() { + // Stop the machine when the host is disconnected + this.machine.stop() + } +} diff --git a/examples/lit-ts/tsconfig.json b/examples/lit-ts/tsconfig.json new file mode 100644 index 0000000000..4ab6f23891 --- /dev/null +++ b/examples/lit-ts/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/package.json b/package.json index 26afdea8b1..d59edc1c65 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,12 @@ "vue": "pnpm --filter \"./examples/nuxt-ts\"", "solid": "pnpm --filter \"./examples/solid-ts\"", "svelte": "pnpm --filter \"./examples/svelte-ts\"", + "lit": "pnpm --filter \"./examples/lit-ts\"", "start-react": "pnpm react dev", "start-vue": "pnpm vue dev", "start-solid": "pnpm solid dev", "start-svelte": "pnpm svelte dev", + "start-lit": "pnpm lit dev", "start-website": "pnpm website dev", "pw-report": "playwright show-report e2e/report", "pw-test": "cross-env FRAMEWORK=react playwright test", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72dfcf8cbb..3066522dd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,250 @@ importers: specifier: 3.2.4 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1) + examples/lit-ts: + dependencies: + '@internationalized/date': + specifier: 3.8.2 + version: 3.8.2 + '@open-wc/lit-helpers': + specifier: 0.7.0 + version: 0.7.0(lit@3.3.1) + '@zag-js/accordion': + specifier: workspace:* + version: link:../../packages/machines/accordion + '@zag-js/anatomy': + specifier: workspace:* + version: link:../../packages/anatomy + '@zag-js/anatomy-icons': + specifier: workspace:* + version: link:../../packages/anatomy-icons + '@zag-js/angle-slider': + specifier: workspace:* + version: link:../../packages/machines/angle-slider + '@zag-js/aria-hidden': + specifier: workspace:* + version: link:../../packages/utilities/aria-hidden + '@zag-js/async-list': + specifier: workspace:* + version: link:../../packages/machines/async-list + '@zag-js/auto-resize': + specifier: workspace:* + version: link:../../packages/utilities/auto-resize + '@zag-js/avatar': + specifier: workspace:* + version: link:../../packages/machines/avatar + '@zag-js/carousel': + specifier: workspace:* + version: link:../../packages/machines/carousel + '@zag-js/checkbox': + specifier: workspace:* + version: link:../../packages/machines/checkbox + '@zag-js/clipboard': + specifier: workspace:* + version: link:../../packages/machines/clipboard + '@zag-js/collapsible': + specifier: workspace:* + version: link:../../packages/machines/collapsible + '@zag-js/collection': + specifier: workspace:* + version: link:../../packages/utilities/collection + '@zag-js/color-picker': + specifier: workspace:* + version: link:../../packages/machines/color-picker + '@zag-js/color-utils': + specifier: workspace:* + version: link:../../packages/utilities/color-utils + '@zag-js/combobox': + specifier: workspace:* + version: link:../../packages/machines/combobox + '@zag-js/core': + specifier: workspace:* + version: link:../../packages/core + '@zag-js/date-picker': + specifier: workspace:* + version: link:../../packages/machines/date-picker + '@zag-js/date-utils': + specifier: workspace:* + version: link:../../packages/utilities/date-utils + '@zag-js/dialog': + specifier: workspace:* + version: link:../../packages/machines/dialog + '@zag-js/dismissable': + specifier: workspace:* + version: link:../../packages/utilities/dismissable + '@zag-js/docs': + specifier: workspace:* + version: link:../../packages/docs + '@zag-js/dom-query': + specifier: workspace:* + version: link:../../packages/utilities/dom-query + '@zag-js/editable': + specifier: workspace:* + version: link:../../packages/machines/editable + '@zag-js/file-upload': + specifier: workspace:* + version: link:../../packages/machines/file-upload + '@zag-js/file-utils': + specifier: workspace:* + version: link:../../packages/utilities/file-utils + '@zag-js/floating-panel': + specifier: workspace:* + version: link:../../packages/machines/floating-panel + '@zag-js/focus-trap': + specifier: workspace:* + version: link:../../packages/utilities/focus-trap + '@zag-js/focus-visible': + specifier: workspace:* + version: link:../../packages/utilities/focus-visible + '@zag-js/highlight-word': + specifier: workspace:* + version: link:../../packages/utilities/highlight-word + '@zag-js/hover-card': + specifier: workspace:* + version: link:../../packages/machines/hover-card + '@zag-js/i18n-utils': + specifier: workspace:* + version: link:../../packages/utilities/i18n-utils + '@zag-js/interact-outside': + specifier: workspace:* + version: link:../../packages/utilities/interact-outside + '@zag-js/json-tree-utils': + specifier: workspace:* + version: link:../../packages/utilities/json-tree-utils + '@zag-js/listbox': + specifier: workspace:* + version: link:../../packages/machines/listbox + '@zag-js/live-region': + specifier: workspace:* + version: link:../../packages/utilities/live-region + '@zag-js/menu': + specifier: workspace:* + version: link:../../packages/machines/menu + '@zag-js/navigation-menu': + specifier: workspace:* + version: link:../../packages/machines/navigation-menu + '@zag-js/number-input': + specifier: workspace:* + version: link:../../packages/machines/number-input + '@zag-js/pagination': + specifier: workspace:* + version: link:../../packages/machines/pagination + '@zag-js/password-input': + specifier: workspace:* + version: link:../../packages/machines/password-input + '@zag-js/pin-input': + specifier: workspace:* + version: link:../../packages/machines/pin-input + '@zag-js/popover': + specifier: workspace:* + version: link:../../packages/machines/popover + '@zag-js/popper': + specifier: workspace:* + version: link:../../packages/utilities/popper + '@zag-js/presence': + specifier: workspace:* + version: link:../../packages/machines/presence + '@zag-js/progress': + specifier: workspace:* + version: link:../../packages/machines/progress + '@zag-js/qr-code': + specifier: workspace:* + version: link:../../packages/machines/qr-code + '@zag-js/radio-group': + specifier: workspace:* + version: link:../../packages/machines/radio-group + '@zag-js/rating-group': + specifier: workspace:* + version: link:../../packages/machines/rating-group + '@zag-js/rect-utils': + specifier: workspace:* + version: link:../../packages/utilities/rect + '@zag-js/remove-scroll': + specifier: workspace:* + version: link:../../packages/utilities/remove-scroll + '@zag-js/scroll-snap': + specifier: workspace:* + version: link:../../packages/utilities/scroll-snap + '@zag-js/select': + specifier: workspace:* + version: link:../../packages/machines/select + '@zag-js/shared': + specifier: workspace:* + version: link:../../shared + '@zag-js/signature-pad': + specifier: workspace:* + version: link:../../packages/machines/signature-pad + '@zag-js/slider': + specifier: workspace:* + version: link:../../packages/machines/slider + '@zag-js/splitter': + specifier: workspace:* + version: link:../../packages/machines/splitter + '@zag-js/steps': + specifier: workspace:* + version: link:../../packages/machines/steps + '@zag-js/store': + specifier: workspace:* + version: link:../../packages/store + '@zag-js/stringify-state': + specifier: workspace:* + version: link:../../packages/utilities/stringify-state + '@zag-js/switch': + specifier: workspace:* + version: link:../../packages/machines/switch + '@zag-js/tabs': + specifier: workspace:* + version: link:../../packages/machines/tabs + '@zag-js/tags-input': + specifier: workspace:* + version: link:../../packages/machines/tags-input + '@zag-js/timer': + specifier: workspace:* + version: link:../../packages/machines/timer + '@zag-js/toast': + specifier: workspace:* + version: link:../../packages/machines/toast + '@zag-js/toggle': + specifier: workspace:* + version: link:../../packages/machines/toggle + '@zag-js/toggle-group': + specifier: workspace:* + version: link:../../packages/machines/toggle-group + '@zag-js/tooltip': + specifier: workspace:* + version: link:../../packages/machines/tooltip + '@zag-js/tour': + specifier: workspace:* + version: link:../../packages/machines/tour + '@zag-js/tree-view': + specifier: workspace:* + version: link:../../packages/machines/tree-view + '@zag-js/types': + specifier: workspace:* + version: link:../../packages/types + '@zag-js/utils': + specifier: workspace:* + version: link:../../packages/utilities/core + form-serialize: + specifier: 0.7.2 + version: 0.7.2 + lit: + specifier: 3.3.1 + version: 3.3.1 + match-sorter: + specifier: 8.0.3 + version: 8.0.3 + nanoid: + specifier: ^5.1.5 + version: 5.1.5 + devDependencies: + typescript: + specifier: ^5.8.3 + version: 5.9.2 + vite: + specifier: ^7.0.5 + version: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1) + examples/next-ts: dependencies: '@internationalized/date': @@ -5058,6 +5302,9 @@ packages: resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} engines: {node: '>=18'} + '@internationalized/date@3.8.2': + resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==} + '@internationalized/date@3.9.0': resolution: {integrity: sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg==} @@ -5115,6 +5362,12 @@ packages: '@kwsites/promise-deferred@1.1.1': resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + '@lit-labs/ssr-dom-shim@1.4.0': + resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} + + '@lit/reactive-element@2.1.1': + resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -5363,6 +5616,11 @@ packages: '@octokit/types@14.1.0': resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} + '@open-wc/lit-helpers@0.7.0': + resolution: {integrity: sha512-4NBlx5ve0EvZplCRJbESm0MdMbRCw16alP2y76KAAAwzmFFXXrUj5hFwhw55+sSg5qaRRx6sY+s7usKgnNo3TQ==} + peerDependencies: + lit: ^2.0.0 || ^3.0.0 + '@opentelemetry/api-logs@0.39.1': resolution: {integrity: sha512-9BJ8lMcOzEN0lu+Qji801y707oFO4xT3db6cosPvl+k7ItUHKN5ofWqtSbM9gbt1H4JJ/4/2TVrqI9Rq7hNv6Q==} engines: {node: '>=14'} @@ -6622,6 +6880,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -9867,6 +10128,15 @@ packages: resolution: {integrity: sha512-VVd7cS6W+vLJu2wmq4QmfVj14Iep7cz4r/OWNk36Aq5ZOY7G8/BfCrQFexcwB1OIxB3yERiePfE/REBjEFulag==} engines: {node: '>=20.0.0'} + lit-element@4.2.1: + resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} + + lit-html@3.3.1: + resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} + + lit@3.3.1: + resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==} + load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -10079,6 +10349,9 @@ packages: engines: {node: '>=0.10.0'} hasBin: true + match-sorter@8.0.3: + resolution: {integrity: sha512-BnR/YBS1iO94R3EcseKkb7kJ2w2su5CWmywHvFYv70HQ2syqca79JefoGocMK+G1NOdpV2kt0G3uQb5hcdFtIA==} + match-sorter@8.1.0: resolution: {integrity: sha512-0HX3BHPixkbECX+Vt7nS1vJ6P2twPgGTU3PMXjWrl1eyVCL24tFHeyYN1FN5RKLzve0TyzNI9qntqQGbebnfPQ==} @@ -14668,6 +14941,10 @@ snapshots: '@inquirer/figures@1.0.13': {} + '@internationalized/date@3.8.2': + dependencies: + '@swc/helpers': 0.5.17 + '@internationalized/date@3.9.0': dependencies: '@swc/helpers': 0.5.17 @@ -14736,6 +15013,12 @@ snapshots: '@kwsites/promise-deferred@1.1.1': {} + '@lit-labs/ssr-dom-shim@1.4.0': {} + + '@lit/reactive-element@2.1.1': + dependencies: + '@lit-labs/ssr-dom-shim': 1.4.0 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.28.3 @@ -15274,6 +15557,10 @@ snapshots: dependencies: '@octokit/openapi-types': 25.1.0 + '@open-wc/lit-helpers@0.7.0(lit@3.3.1)': + dependencies: + lit: 3.3.1 + '@opentelemetry/api-logs@0.39.1': dependencies: '@opentelemetry/api': 1.9.0 @@ -16417,6 +16704,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/trusted-types@2.0.7': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -20486,6 +20775,22 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.0 + lit-element@4.2.1: + dependencies: + '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit/reactive-element': 2.1.1 + lit-html: 3.3.1 + + lit-html@3.3.1: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.1: + dependencies: + '@lit/reactive-element': 2.1.1 + lit-element: 4.2.1 + lit-html: 3.3.1 + load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 @@ -20701,6 +21006,11 @@ snapshots: repeat-string: 1.6.1 strip-color: 0.1.0 + match-sorter@8.0.3: + dependencies: + '@babel/runtime': 7.28.3 + remove-accents: 0.5.0 + match-sorter@8.1.0: dependencies: '@babel/runtime': 7.28.3