diff --git a/examples/alpine-ts/.gitignore b/examples/alpine-ts/.gitignore new file mode 100644 index 0000000000..d547d8a265 --- /dev/null +++ b/examples/alpine-ts/.gitignore @@ -0,0 +1,8 @@ +node_modules +dist +.data +.nitro +.cache +.output +.env +.env.local diff --git a/examples/alpine-ts/README.md b/examples/alpine-ts/README.md new file mode 100644 index 0000000000..fddea78b59 --- /dev/null +++ b/examples/alpine-ts/README.md @@ -0,0 +1,19 @@ +# Nitro starter + +Create your full-stack apps and deploy it anywhere with this [Vite](https://vite.dev/) + [Nitro](https://v3.nitro.build/) starter. + +## Getting started + +```bash +npm install +npm run dev +``` + +## Deploying + +```bash +npm run build +npm run preview +``` + +Then checkout the [Nitro documentation](https://v3.nitro.build/deploy) to learn more about the different deployment presets. diff --git a/examples/alpine-ts/lib/bindable.ts b/examples/alpine-ts/lib/bindable.ts new file mode 100644 index 0000000000..9f5c30a34a --- /dev/null +++ b/examples/alpine-ts/lib/bindable.ts @@ -0,0 +1,68 @@ +import type { Bindable, BindableParams } from "@zag-js/core" +import { isFunction } from "@zag-js/utils" +import Alpine from "alpinejs" + +export function bindable(props: () => BindableParams): Bindable { + const initial = props().defaultValue ?? props().value + const eq = props().isEqual ?? Object.is + + const v = Alpine.reactive({ value: initial }) + const controlled = { + get value() { + return props().value !== undefined + }, + } + + const valueRef = { + get value() { + return controlled.value ? props().value : v.value + }, + } + + const setFn = (val: T | ((prev: T) => T)) => { + const prev = controlled.value ? props().value : v.value + const next = isFunction(val) ? val(prev as T) : val + + if (props().debug) { + console.log(`[bindable > ${props().debug}] setValue`, { next, prev }) + } + + if (!controlled.value) v.value = next + if (!eq(next, prev)) { + props().onChange?.(next, prev) + } + } + + function get(): T { + return (controlled.value ? props().value : v.value) as T + } + + return { + initial, + ref: valueRef, + get, + set(val: T | ((prev: T) => T)) { + setFn(val) + }, + invoke(nextValue: T, prevValue: T) { + props().onChange?.(nextValue, prevValue) + }, + hash(value: T) { + return props().hash?.(value) ?? String(value) + }, + } +} + +bindable.cleanup = (fn: VoidFunction) => { + Alpine.onElRemoved(() => fn()) +} + +bindable.ref = (defaultValue: T) => { + let value = defaultValue + return { + get: () => value, + set: (next: T) => { + value = next + }, + } +} diff --git a/examples/alpine-ts/lib/index.ts b/examples/alpine-ts/lib/index.ts new file mode 100644 index 0000000000..a03d003750 --- /dev/null +++ b/examples/alpine-ts/lib/index.ts @@ -0,0 +1 @@ +export { usePlugin } from "./plugin" diff --git a/examples/alpine-ts/lib/machine.ts b/examples/alpine-ts/lib/machine.ts new file mode 100644 index 0000000000..842a592769 --- /dev/null +++ b/examples/alpine-ts/lib/machine.ts @@ -0,0 +1,298 @@ +import type { + ActionsOrFn, + BindableContext, + ChooseFn, + ComputedFn, + EffectsOrFn, + GuardFn, + Machine, + MachineSchema, + Params, + Service, +} from "@zag-js/core" +import { createScope, INIT_STATE, MachineStatus } from "@zag-js/core" +import { compact, ensure, isFunction, isString, toArray, warn } from "@zag-js/utils" +import { bindable } from "./bindable" +import { useRefs } from "./refs" +import { track } from "./track" +import Alpine from "alpinejs" + +export function useMachine( + machine: Machine, + evaluateProps: (callback: (props: Partial) => void) => void, +): Service & { init: VoidFunction; destroy: VoidFunction } { + let initialProps = {} as T["props"] + evaluateProps((props) => { + initialProps = props + }) + // TODO: cache and update scope + const { id, ids, getRootNode } = initialProps as any + const scope = createScope({ id, ids, getRootNode }) + + const debug = (...args: any[]) => { + if (machine.debug) console.log(...args) + } + + const props = Alpine.reactive({ + value: machine.props?.({ props: compact(initialProps), scope }) ?? initialProps, + }) + Alpine.effect(() => { + let p = {} + evaluateProps((value) => (p = value)) + props.value = machine.props?.({ props: compact(p), scope }) ?? (p as any) + }) + const prop = useProp(props) + + const context: any = machine.context?.({ + prop, + bindable, + scope, + flush, + getContext() { + return ctx + }, + getComputed() { + return computed + }, + getRefs() { + return refs + }, + getEvent() { + return getEvent() + }, + }) + + 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) + }, + } + + let effects = new Map() + let transitionRef: any = null + + let previousEventRef: { current: any } = { current: null } + let eventRef: { current: any } = { current: { type: "" } } + + const getEvent = () => ({ + ...eventRef.current, + current() { + return eventRef.current + }, + previous() { + return previousEventRef.current + }, + }) + + const getState = () => ({ + ...state, + matches(...values: T["state"][]) { + const currentState = state.get() + return values.includes(currentState) + }, + hasTag(tag: T["tag"]) { + const currentState = state.get() + return !!machine.states[currentState]?.tags?.includes(tag) + }, + }) + + const refs = useRefs(machine.refs?.({ prop, context: ctx }) ?? {}) + + const getParams = (): Params => ({ + state: getState(), + context: ctx, + event: getEvent(), + prop, + send, + action, + guard, + track, + refs, + computed, + flush, + scope, + choose, + }) + + const action = (keys: ActionsOrFn | undefined) => { + const strs = isFunction(keys) ? keys(getParams()) : keys + if (!strs) return + const fns = strs.map((s) => { + const fn = 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?.(getParams()) + } + } + const guard = (str: T["guard"] | GuardFn) => { + if (isFunction(str)) return str(getParams()) + return machine.implementations?.guards?.[str](getParams()) + } + + const effect = (keys: EffectsOrFn | undefined) => { + const strs = isFunction(keys) ? keys(getParams()) : keys + if (!strs) return + const fns = strs.map((s) => { + const fn = 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?.(getParams()) + if (cleanup) cleanups.push(cleanup) + } + return () => cleanups.forEach((fn) => fn?.()) + } + + const choose: ChooseFn = (transitions) => { + return toArray(transitions).find((t) => { + let result = !t.guard + if (isString(t.guard)) result = !!guard(t.guard) + else if (isFunction(t.guard)) result = t.guard(getParams()) + return result + }) + } + + const computed: ComputedFn = (key) => { + ensure(machine.computed, () => `[zag-js] No computed object found on machine`) + const fn = machine.computed[key] + return fn({ + context: ctx, + event: getEvent(), + prop, + refs, + scope, + computed: computed as any, + }) + } + + const state = bindable(() => ({ + defaultValue: machine.initialState({ prop }), + onChange(nextState, prevState) { + // compute effects: exit -> transition -> enter + + queueMicrotask(() => { + // exit effects + if (prevState) { + const exitEffects = effects.get(prevState) + exitEffects?.() + effects.delete(prevState) + } + + // exit actions + if (prevState) { + action(machine.states[prevState]?.exit) + } + + // transition actions + action(transitionRef?.actions) + + // enter effect + const cleanup = effect(machine.states[nextState]?.effects) + if (cleanup) effects.set(nextState as string, cleanup) + + // root entry actions + if (prevState === INIT_STATE) { + action(machine.entry) + const cleanup = effect(machine.effects) + if (cleanup) effects.set(INIT_STATE, cleanup) + } + + // enter actions + action(machine.states[nextState]?.entry) + }) + }, + })) + + let status = MachineStatus.NotStarted + + const init = () => { + const started = status === MachineStatus.Started + status = MachineStatus.Started + debug(started ? "rehydrating..." : "initializing...") + state.invoke(state.initial!, INIT_STATE) + } + + const destroy = () => { + debug("unmounting...") + status = MachineStatus.Stopped + + effects.forEach((fn) => fn?.()) + effects = new Map() + transitionRef.current = null + + action(machine.exit) + } + + const send = (event: any) => { + if (status !== MachineStatus.Started) return + + previousEventRef.current = eventRef.current + eventRef.current = event + + let currentState = state.get() + + // @ts-ignore + const transitions = machine.states[currentState].on?.[event.type] ?? machine.on?.[event.type] + + const transition = choose(transitions) + if (!transition) return + + // save current transition + transitionRef = transition + const target = transition.target ?? currentState + + debug("transition", event.type, transition.target || currentState, `(${transition.actions})`) + + const changed = target !== currentState + if (changed) { + // state change is high priority + state.set(target) + } else if (transition.reenter && !changed) { + // reenter will re-invoke the current state + state.invoke(currentState, currentState) + } else { + // call transition actions + action(transition.actions) + } + } + + machine.watch?.(getParams()) + + return { + state: getState(), + send, + context: ctx, + prop, + scope, + refs, + computed, + event: getEvent(), + getStatus: () => status, + init, + destroy, + } +} + +function useProp(ref: { value: T }) { + return function get(key: K): T[K] { + return ref.value[key] + } +} + +function flush(fn: VoidFunction) { + queueMicrotask(() => fn()) +} diff --git a/examples/alpine-ts/lib/normalize-props.ts b/examples/alpine-ts/lib/normalize-props.ts new file mode 100644 index 0000000000..5c76b1adfe --- /dev/null +++ b/examples/alpine-ts/lib/normalize-props.ts @@ -0,0 +1,29 @@ +import { createNormalizer } from "@zag-js/types" + +const propMap: Record = { + htmlFor: "for", + className: "class", + onDoubleClick: "onDblclick", + onChange: "onInput", + onFocus: "onFocusin", + onBlur: "onFocusout", + defaultValue: "value", + defaultChecked: "checked", +} + +export const normalizeProps = createNormalizer((props) => { + const normalized: Record any> = {} + for (const key in props) { + const prop = key in propMap ? propMap[key] : key + const value = props[key] + + if (prop === "children") { + normalized["x-html"] = () => value + } else if (prop.startsWith("on")) { + normalized["@" + prop.substring(2).toLowerCase()] = value + } else { + normalized[":" + prop.toLowerCase()] = () => value + } + } + return normalized +}) diff --git a/examples/alpine-ts/lib/plugin.ts b/examples/alpine-ts/lib/plugin.ts new file mode 100644 index 0000000000..08d0d96238 --- /dev/null +++ b/examples/alpine-ts/lib/plugin.ts @@ -0,0 +1,108 @@ +import type { Machine, MachineSchema, Service } from "@zag-js/core" +import type { NormalizeProps, PropTypes } from "@zag-js/types" +import type { Alpine } from "alpinejs" +import { useMachine } from "./machine" +import { normalizeProps } from "./normalize-props" + +export function usePlugin( + name: string, + component: { + machine: Machine + connect: (service: Service, normalizeProps: NormalizeProps) => any + collection?: (options: any) => any + gridCollection?: (options: any) => any + }, +) { + const _x_snake_case = "_x_" + name.replaceAll("-", "_") + + return function (Alpine: Alpine) { + Alpine.directive(name, (el, { expression, value, modifiers }, { effect, evaluateLater }) => { + const _modifier = modifiers.at(0) ? "_" + modifiers.at(0) : "" + if (!value) { + const evaluateProps = evaluateLater(expression) as any + const service = useMachine(component.machine, evaluateProps) + Alpine.bind(el, { + "x-data"() { + return { + [_x_snake_case + _modifier + "_service"]: service, // dev only, for state visualization + [_x_snake_case + _modifier]: component.connect(service, normalizeProps), + init() { + queueMicrotask(() => { + effect(() => { + this[_x_snake_case + _modifier] = component.connect(service, normalizeProps) + }) + }) + service.init() + }, + destroy() { + service.destroy() + }, + } + }, + }) + } else if (value === "collection") { + const evaluateCollection = evaluateLater(expression) + Alpine.bind(el, { + "x-data"() { + return { + get collection() { + let options: any = {} + evaluateCollection((value) => (options = value)) + return component.collection?.(options) + }, + } + }, + }) + } else if (value === "grid-collection") { + const evaluateCollection = evaluateLater(expression) + Alpine.bind(el, { + "x-data"() { + return { + get collection() { + let options: any = {} + evaluateCollection((value) => (options = value)) + return component.gridCollection?.(options) + }, + } + }, + }) + } else { + const getProps = `get${value + .split("-") + .map((v) => v.at(0)?.toUpperCase() + v.substring(1).toLowerCase()) + .join("")}Props` + const evaluateProps = expression ? evaluateLater(expression) : null + + let props = {} + evaluateProps && evaluateProps((value: any) => (props = value)) + const ref = Alpine.reactive({ ...(Alpine.$data(el) as any)[_x_snake_case + _modifier][getProps](props) }) + + const binding: Record any> = {} + for (const prop in ref) { + binding[prop] = (...args: any[]) => ref[prop]?.(...args) + } + Alpine.bind(el, binding) + + effect(() => { + let props = {} + evaluateProps && evaluateProps((value: any) => (props = value)) + const next = (Alpine.$data(el) as any)[_x_snake_case + _modifier][getProps](props) + for (const prop in next) { + if (prop.startsWith("@") || next[prop]() !== ref[prop]()) { + ref[prop] = next[prop] + } + } + }) + } + }).before("bind") + Alpine.magic( + name + .split("-") + .map((str, i) => (i === 0 ? str : str.at(0)?.toUpperCase() + str.substring(1).toLowerCase())) + .join(""), + (el) => { + return (modifier?: string) => (Alpine.$data(el) as any)[_x_snake_case + (modifier ? "_" + modifier : "")] + }, + ) + } +} diff --git a/examples/alpine-ts/lib/refs.ts b/examples/alpine-ts/lib/refs.ts new file mode 100644 index 0000000000..07ee8cc2fa --- /dev/null +++ b/examples/alpine-ts/lib/refs.ts @@ -0,0 +1,11 @@ +export function useRefs(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/alpine-ts/lib/track.ts b/examples/alpine-ts/lib/track.ts new file mode 100644 index 0000000000..c0e3253a70 --- /dev/null +++ b/examples/alpine-ts/lib/track.ts @@ -0,0 +1,21 @@ +import { isEqual } from "@zag-js/utils" +import Alpine from "alpinejs" + +export const track = (deps: any[], effect: VoidFunction) => { + // @ts-ignore @types/alpinejs is outdated + Alpine.watch( + () => [...deps.map((d) => d())], + (current: any[], previous: any[]) => { + let changed = false + for (let i = 0; i < current.length; i++) { + if (!isEqual(previous[i], current[i])) { + changed = true + break + } + } + if (changed) { + effect() + } + }, + ) +} diff --git a/examples/alpine-ts/nitro.config.ts b/examples/alpine-ts/nitro.config.ts new file mode 100644 index 0000000000..c3d0bb9980 --- /dev/null +++ b/examples/alpine-ts/nitro.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from "nitro" + +export default defineConfig({ + serverDir: "./server", +}) diff --git a/examples/alpine-ts/package.json b/examples/alpine-ts/package.json new file mode 100644 index 0000000000..4355004d6b --- /dev/null +++ b/examples/alpine-ts/package.json @@ -0,0 +1,68 @@ +{ + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite dev", + "preview": "vite preview" + }, + "devDependencies": { + "@types/alpinejs": "^3.13.11", + "@types/form-serialize": "0.7.4", + "nitro": "latest", + "vite": "npm:rolldown-vite" + }, + "dependencies": { + "@zag-js/accordion": "workspace:*", + "@zag-js/anatomy-icons": "workspace:*", + "@zag-js/angle-slider": "workspace:*", + "@zag-js/async-list": "workspace:*", + "@zag-js/auto-resize": "workspace:*", + "@zag-js/avatar": "workspace:*", + "@zag-js/bottom-sheet": "workspace:*", + "@zag-js/carousel": "workspace:*", + "@zag-js/checkbox": "workspace:*", + "@zag-js/clipboard": "workspace:*", + "@zag-js/color-picker": "workspace:*", + "@zag-js/combobox": "workspace:*", + "@zag-js/core": "workspace:*", + "@zag-js/dialog": "workspace:*", + "@zag-js/dom-query": "workspace:*", + "@zag-js/editable": "workspace:*", + "@zag-js/file-upload": "workspace:*", + "@zag-js/floating-panel": "workspace:*", + "@zag-js/hover-card": "workspace:*", + "@zag-js/listbox": "workspace:*", + "@zag-js/marquee": "workspace:*", + "@zag-js/menu": "workspace:*", + "@zag-js/number-input": "workspace:*", + "@zag-js/pagination": "workspace:*", + "@zag-js/password-input": "workspace:*", + "@zag-js/pin-input": "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/scroll-area": "workspace:*", + "@zag-js/select": "workspace:*", + "@zag-js/shared": "workspace:*", + "@zag-js/slider": "workspace:*", + "@zag-js/steps": "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/toggle": "workspace:*", + "@zag-js/toggle-group": "workspace:*", + "@zag-js/tooltip": "workspace:*", + "@zag-js/types": "workspace:*", + "@zag-js/utils": "workspace:*", + "alpinejs": "^3.15.2", + "form-serialize": "0.7.2", + "lucide-static": "^0.554.0", + "match-sorter": "8.1.0", + "mono-jsx": "0.8.0-beta.4" + } +} diff --git a/examples/alpine-ts/public/nitro.svg b/examples/alpine-ts/public/nitro.svg new file mode 100644 index 0000000000..cdeec33d26 --- /dev/null +++ b/examples/alpine-ts/public/nitro.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/alpine-ts/public/robots.txt b/examples/alpine-ts/public/robots.txt new file mode 100644 index 0000000000..eb0536286f --- /dev/null +++ b/examples/alpine-ts/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/examples/alpine-ts/scripts/accordion.ts b/examples/alpine-ts/scripts/accordion.ts new file mode 100644 index 0000000000..cdf48aa4d9 --- /dev/null +++ b/examples/alpine-ts/scripts/accordion.ts @@ -0,0 +1,6 @@ +import * as accordion from "@zag-js/accordion" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("accordion", accordion)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/angle-slider.ts b/examples/alpine-ts/scripts/angle-slider.ts new file mode 100644 index 0000000000..e357b14b92 --- /dev/null +++ b/examples/alpine-ts/scripts/angle-slider.ts @@ -0,0 +1,6 @@ +import * as angleSlider from "@zag-js/angle-slider" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("angle-slider", angleSlider)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/async-list.ts b/examples/alpine-ts/scripts/async-list.ts new file mode 100644 index 0000000000..dd79e9f0fd --- /dev/null +++ b/examples/alpine-ts/scripts/async-list.ts @@ -0,0 +1,6 @@ +import * as asyncList from "@zag-js/async-list" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("async-list", asyncList)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/autoresize.ts b/examples/alpine-ts/scripts/autoresize.ts new file mode 100644 index 0000000000..c13dbe1150 --- /dev/null +++ b/examples/alpine-ts/scripts/autoresize.ts @@ -0,0 +1,5 @@ +import { autoresizeTextarea } from "@zag-js/auto-resize" +import Alpine from "alpinejs" + +Alpine.magic("autoresizeTextarea", () => autoresizeTextarea) +Alpine.start() diff --git a/examples/alpine-ts/scripts/avatar.ts b/examples/alpine-ts/scripts/avatar.ts new file mode 100644 index 0000000000..7c02263222 --- /dev/null +++ b/examples/alpine-ts/scripts/avatar.ts @@ -0,0 +1,13 @@ +import * as avatar from "@zag-js/avatar" +import { avatarData } from "@zag-js/shared" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +const images = avatarData.full + +Alpine.magic("broken", () => avatarData.broken) +Alpine.magic("getRandomImage", () => { + return () => images[Math.floor(Math.random() * images.length)] +}) +Alpine.plugin(usePlugin("avatar", avatar)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/carousel.ts b/examples/alpine-ts/scripts/carousel.ts new file mode 100644 index 0000000000..00818fc293 --- /dev/null +++ b/examples/alpine-ts/scripts/carousel.ts @@ -0,0 +1,24 @@ +import * as carousel from "@zag-js/carousel" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +// Sample data with variable width content (content-driven sizing) +const variableWidthData = [ + { content: "Short", color: "#ff6b6b", type: "text" }, + { + content: "This is a much longer slide with significantly more content that will naturally take up more space", + color: "#4ecdc4", + type: "text", + }, + { content: "Hi", color: "#45b7d1", type: "text" }, + { content: "Medium length slide content here", color: "#96ceb4", type: "text" }, + { content: "⭐ Star Rating Widget ⭐⭐⭐⭐⭐", color: "#ffeaa7", type: "widget" }, + { content: "X", color: "#dda0dd", type: "text" }, + { content: "🔥 Popular Item with Badge 🔥", color: "#98d8c8", type: "badge" }, + { content: "A", color: "#e74c3c", type: "text" }, + { content: "📊 Analytics Dashboard Component 📈📉", color: "#9b59b6", type: "component" }, +] + +Alpine.magic("variableWidthData", () => variableWidthData) +Alpine.plugin(usePlugin("carousel", carousel)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/checkbox.ts b/examples/alpine-ts/scripts/checkbox.ts new file mode 100644 index 0000000000..e04ee6694e --- /dev/null +++ b/examples/alpine-ts/scripts/checkbox.ts @@ -0,0 +1,8 @@ +import * as checkbox from "@zag-js/checkbox" +import serialize from "form-serialize" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("serialize", () => serialize) +Alpine.plugin(usePlugin("checkbox", checkbox)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/clipboard.ts b/examples/alpine-ts/scripts/clipboard.ts new file mode 100644 index 0000000000..2313d70543 --- /dev/null +++ b/examples/alpine-ts/scripts/clipboard.ts @@ -0,0 +1,9 @@ +import * as clipboard from "@zag-js/clipboard" +import { ClipboardCheck, ClipboardCopy } from "lucide-static" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("ClipboardCheck", () => ClipboardCheck) +Alpine.magic("ClipboardCopy", () => ClipboardCopy) +Alpine.plugin(usePlugin("clipboard", clipboard)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/color-picker.ts b/examples/alpine-ts/scripts/color-picker.ts new file mode 100644 index 0000000000..1bea3ab091 --- /dev/null +++ b/examples/alpine-ts/scripts/color-picker.ts @@ -0,0 +1,9 @@ +import * as colorPicker from "@zag-js/color-picker" +import serialize from "form-serialize" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("serialize", () => serialize) +Alpine.magic("parse", () => colorPicker.parse) +Alpine.plugin(usePlugin("color-picker", colorPicker)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/combobox.ts b/examples/alpine-ts/scripts/combobox.ts new file mode 100644 index 0000000000..9487d7bcd2 --- /dev/null +++ b/examples/alpine-ts/scripts/combobox.ts @@ -0,0 +1,10 @@ +import * as combobox from "@zag-js/combobox" +import { comboboxData } from "@zag-js/shared" +import { matchSorter } from "match-sorter" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("matchSorter", () => matchSorter) +Alpine.magic("comboboxData", () => comboboxData) +Alpine.plugin(usePlugin("combobox", combobox)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/dialog.ts b/examples/alpine-ts/scripts/dialog.ts new file mode 100644 index 0000000000..891b5f1b4c --- /dev/null +++ b/examples/alpine-ts/scripts/dialog.ts @@ -0,0 +1,6 @@ +import * as dialog from "@zag-js/dialog" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("dialog", dialog)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/editable.ts b/examples/alpine-ts/scripts/editable.ts new file mode 100644 index 0000000000..97cd95b5ca --- /dev/null +++ b/examples/alpine-ts/scripts/editable.ts @@ -0,0 +1,6 @@ +import * as editable from "@zag-js/editable" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("editable", editable)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/file-upload.ts b/examples/alpine-ts/scripts/file-upload.ts new file mode 100644 index 0000000000..2f993c3380 --- /dev/null +++ b/examples/alpine-ts/scripts/file-upload.ts @@ -0,0 +1,6 @@ +import * as fileUpload from "@zag-js/file-upload" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("file-upload", fileUpload)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/floating-panel.ts b/examples/alpine-ts/scripts/floating-panel.ts new file mode 100644 index 0000000000..701657a2dd --- /dev/null +++ b/examples/alpine-ts/scripts/floating-panel.ts @@ -0,0 +1,6 @@ +import * as floating from "@zag-js/floating-panel" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("floating", floating)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/hover-card.ts b/examples/alpine-ts/scripts/hover-card.ts new file mode 100644 index 0000000000..565e833b2f --- /dev/null +++ b/examples/alpine-ts/scripts/hover-card.ts @@ -0,0 +1,6 @@ +import * as hoverCard from "@zag-js/hover-card" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("hover-card", hoverCard)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/listbox.ts b/examples/alpine-ts/scripts/listbox.ts new file mode 100644 index 0000000000..41043bacbb --- /dev/null +++ b/examples/alpine-ts/scripts/listbox.ts @@ -0,0 +1,8 @@ +import * as listbox from "@zag-js/listbox" +import { selectData } from "@zag-js/shared" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("selectData", () => selectData) +Alpine.plugin(usePlugin("listbox", listbox)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/marquee.ts b/examples/alpine-ts/scripts/marquee.ts new file mode 100644 index 0000000000..55f0359bc5 --- /dev/null +++ b/examples/alpine-ts/scripts/marquee.ts @@ -0,0 +1,6 @@ +import * as marquee from "@zag-js/marquee" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("marquee", marquee)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/menu.ts b/examples/alpine-ts/scripts/menu.ts new file mode 100644 index 0000000000..a9e9a8806f --- /dev/null +++ b/examples/alpine-ts/scripts/menu.ts @@ -0,0 +1,45 @@ +import * as menu from "@zag-js/menu" +import { menuData, menuOptionData } from "@zag-js/shared" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +const [level1, level2, level3] = menuData +Alpine.magic("level1", () => level1) +Alpine.magic("level2", () => level2) +Alpine.magic("level3", () => level3) + +Alpine.data("radios", () => ({ + get radios() { + return menuOptionData.order.map((item) => ({ + type: "radio" as const, + name: "order", + value: item.value, + label: item.label, + // @ts-ignore + checked: this.$data.order === item.value, + onCheckedChange: (checked: boolean) => { + // @ts-ignore + this.$data.order = checked ? item.value : "" + }, + })) + }, +})) +Alpine.data("checkboxes", () => ({ + get checkboxes() { + return menuOptionData.type.map((item) => ({ + type: "checkbox" as const, + name: "type", + value: item.value, + label: item.label, + // @ts-ignore + checked: this.$data.type.includes(item.value), + onCheckedChange: (checked: boolean) => { + // @ts-ignore + this.$data.type = checked ? [...this.$data.type, item.value] : this.$data.type.filter((x) => x !== item.value) + }, + })) + }, +})) + +Alpine.plugin(usePlugin("menu", menu)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/number-input.ts b/examples/alpine-ts/scripts/number-input.ts new file mode 100644 index 0000000000..2341e4cf16 --- /dev/null +++ b/examples/alpine-ts/scripts/number-input.ts @@ -0,0 +1,6 @@ +import * as numberInput from "@zag-js/number-input" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("number-input", numberInput)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/pagination.ts b/examples/alpine-ts/scripts/pagination.ts new file mode 100644 index 0000000000..25defe3d17 --- /dev/null +++ b/examples/alpine-ts/scripts/pagination.ts @@ -0,0 +1,8 @@ +import * as pagination from "@zag-js/pagination" +import { paginationData } from "@zag-js/shared" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("paginationData", () => paginationData) +Alpine.plugin(usePlugin("pagination", pagination)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/password-input.ts b/examples/alpine-ts/scripts/password-input.ts new file mode 100644 index 0000000000..1bdb1f27f3 --- /dev/null +++ b/examples/alpine-ts/scripts/password-input.ts @@ -0,0 +1,9 @@ +import * as passwordInput from "@zag-js/password-input" +import { Eye, EyeOff } from "lucide-static" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("EyeIcon", () => Eye) +Alpine.magic("EyeOffIcon", () => EyeOff) +Alpine.plugin(usePlugin("password-input", passwordInput)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/pin-input.ts b/examples/alpine-ts/scripts/pin-input.ts new file mode 100644 index 0000000000..c910546487 --- /dev/null +++ b/examples/alpine-ts/scripts/pin-input.ts @@ -0,0 +1,8 @@ +import * as pinInput from "@zag-js/pin-input" +import serialize from "form-serialize" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("serialize", () => serialize) +Alpine.plugin(usePlugin("pin-input", pinInput)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/popper.ts b/examples/alpine-ts/scripts/popper.ts new file mode 100644 index 0000000000..eceea2a5fb --- /dev/null +++ b/examples/alpine-ts/scripts/popper.ts @@ -0,0 +1,6 @@ +import { getPlacement, getPlacementStyles } from "@zag-js/popper" +import Alpine from "alpinejs" + +Alpine.magic("getPlacement", () => getPlacement) +Alpine.magic("getPlacementStyles", () => getPlacementStyles) +Alpine.start() diff --git a/examples/alpine-ts/scripts/presence.ts b/examples/alpine-ts/scripts/presence.ts new file mode 100644 index 0000000000..08857ac2c6 --- /dev/null +++ b/examples/alpine-ts/scripts/presence.ts @@ -0,0 +1,6 @@ +import * as presence from "@zag-js/presence" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("presence", presence)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/progress.ts b/examples/alpine-ts/scripts/progress.ts new file mode 100644 index 0000000000..1a3d19ce95 --- /dev/null +++ b/examples/alpine-ts/scripts/progress.ts @@ -0,0 +1,6 @@ +import * as progress from "@zag-js/progress" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("progress", progress)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/qr-code.ts b/examples/alpine-ts/scripts/qr-code.ts new file mode 100644 index 0000000000..fa05dc74cc --- /dev/null +++ b/examples/alpine-ts/scripts/qr-code.ts @@ -0,0 +1,6 @@ +import * as qrCode from "@zag-js/qr-code" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("qr-code", qrCode)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/radio-group.ts b/examples/alpine-ts/scripts/radio-group.ts new file mode 100644 index 0000000000..9ccbd44634 --- /dev/null +++ b/examples/alpine-ts/scripts/radio-group.ts @@ -0,0 +1,8 @@ +import * as radio from "@zag-js/radio-group" +import serialize from "form-serialize" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("serialize", () => serialize) +Alpine.plugin(usePlugin("radio", radio)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/rating-group.ts b/examples/alpine-ts/scripts/rating-group.ts new file mode 100644 index 0000000000..50fb41862f --- /dev/null +++ b/examples/alpine-ts/scripts/rating-group.ts @@ -0,0 +1,6 @@ +import * as rating from "@zag-js/rating-group" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("rating", rating)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/scroll-area.ts b/examples/alpine-ts/scripts/scroll-area.ts new file mode 100644 index 0000000000..80f9ed1577 --- /dev/null +++ b/examples/alpine-ts/scripts/scroll-area.ts @@ -0,0 +1,6 @@ +import * as scrollArea from "@zag-js/scroll-area" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("scroll-area", scrollArea)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/select.ts b/examples/alpine-ts/scripts/select.ts new file mode 100644 index 0000000000..a8ee12ca41 --- /dev/null +++ b/examples/alpine-ts/scripts/select.ts @@ -0,0 +1,10 @@ +import * as select from "@zag-js/select" +import { selectData } from "@zag-js/shared" +import serialize from "form-serialize" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("serialize", () => serialize) +Alpine.magic("selectData", () => selectData) +Alpine.plugin(usePlugin("select", select)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/slider.ts b/examples/alpine-ts/scripts/slider.ts new file mode 100644 index 0000000000..fc79bc3a22 --- /dev/null +++ b/examples/alpine-ts/scripts/slider.ts @@ -0,0 +1,8 @@ +import * as slider from "@zag-js/slider" +import serialize from "form-serialize" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("serialize", () => serialize) +Alpine.plugin(usePlugin("slider", slider)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/state-visualizer.ts b/examples/alpine-ts/scripts/state-visualizer.ts new file mode 100644 index 0000000000..5b0322212d --- /dev/null +++ b/examples/alpine-ts/scripts/state-visualizer.ts @@ -0,0 +1,17 @@ +import { highlightState } from "@zag-js/stringify-state" +import Alpine from "alpinejs" + +Alpine.magic("highlightState", (el) => { + return ({ label, omit, context }: { label: string; omit?: string[]; context?: string[] }) => { + const { [`_x_${label.replaceAll("-", "_")}_service`]: service } = Alpine.$data(el) as any + return highlightState( + { + state: service.state.get(), + event: service.event.current(), + previouseEvent: service.event.previous(), + context: context ? Object.fromEntries(context.map((key) => [key, service.context.get(key)])) : undefined, + }, + omit, + ) + } +}) diff --git a/examples/alpine-ts/scripts/steps.ts b/examples/alpine-ts/scripts/steps.ts new file mode 100644 index 0000000000..8014e78221 --- /dev/null +++ b/examples/alpine-ts/scripts/steps.ts @@ -0,0 +1,6 @@ +import * as steps from "@zag-js/steps" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("steps", steps)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/style.ts b/examples/alpine-ts/scripts/style.ts new file mode 100644 index 0000000000..3b860d0815 --- /dev/null +++ b/examples/alpine-ts/scripts/style.ts @@ -0,0 +1 @@ +import "@zag-js/shared/src/style.css" diff --git a/examples/alpine-ts/scripts/switch.ts b/examples/alpine-ts/scripts/switch.ts new file mode 100644 index 0000000000..c8b5a867c6 --- /dev/null +++ b/examples/alpine-ts/scripts/switch.ts @@ -0,0 +1,6 @@ +import * as zagSwitch from "@zag-js/switch" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("switch", zagSwitch)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/tabs.ts b/examples/alpine-ts/scripts/tabs.ts new file mode 100644 index 0000000000..164d702cc4 --- /dev/null +++ b/examples/alpine-ts/scripts/tabs.ts @@ -0,0 +1,6 @@ +import * as tabs from "@zag-js/tabs" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("tabs", tabs)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/tags-input.ts b/examples/alpine-ts/scripts/tags-input.ts new file mode 100644 index 0000000000..fd70074cd7 --- /dev/null +++ b/examples/alpine-ts/scripts/tags-input.ts @@ -0,0 +1,9 @@ +import * as tagsInput from "@zag-js/tags-input" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("toDashCase", () => { + return (str: string) => str.replace(/\s+/g, "-").toLowerCase() +}) +Alpine.plugin(usePlugin("tags-input", tagsInput)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/timer.ts b/examples/alpine-ts/scripts/timer.ts new file mode 100644 index 0000000000..c5c37b3f62 --- /dev/null +++ b/examples/alpine-ts/scripts/timer.ts @@ -0,0 +1,7 @@ +import * as timer from "@zag-js/timer" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.magic("parse", () => timer.parse) +Alpine.plugin(usePlugin("timer", timer)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/toggle-group.ts b/examples/alpine-ts/scripts/toggle-group.ts new file mode 100644 index 0000000000..1c843b8734 --- /dev/null +++ b/examples/alpine-ts/scripts/toggle-group.ts @@ -0,0 +1,6 @@ +import * as toggle from "@zag-js/toggle-group" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("toggle", toggle)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/toggle.ts b/examples/alpine-ts/scripts/toggle.ts new file mode 100644 index 0000000000..0290f847f6 --- /dev/null +++ b/examples/alpine-ts/scripts/toggle.ts @@ -0,0 +1,6 @@ +import * as toggle from "@zag-js/toggle" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("toggle", toggle)) +Alpine.start() diff --git a/examples/alpine-ts/scripts/toolbar.ts b/examples/alpine-ts/scripts/toolbar.ts new file mode 100644 index 0000000000..124f6a705b --- /dev/null +++ b/examples/alpine-ts/scripts/toolbar.ts @@ -0,0 +1,6 @@ +import { dataAttr } from "@zag-js/dom-query" +import Alpine from "alpinejs" + +Alpine.magic("dataAttr", () => { + return (guard: boolean | undefined) => dataAttr(guard) +}) diff --git a/examples/alpine-ts/scripts/tooltip.ts b/examples/alpine-ts/scripts/tooltip.ts new file mode 100644 index 0000000000..54ab6c0183 --- /dev/null +++ b/examples/alpine-ts/scripts/tooltip.ts @@ -0,0 +1,6 @@ +import * as tooltip from "@zag-js/tooltip" +import Alpine from "alpinejs" +import { usePlugin } from "~/lib" + +Alpine.plugin(usePlugin("tooltip", tooltip)) +Alpine.start() diff --git a/examples/alpine-ts/server/components/controls.tsx b/examples/alpine-ts/server/components/controls.tsx new file mode 100644 index 0000000000..09232d370c --- /dev/null +++ b/examples/alpine-ts/server/components/controls.tsx @@ -0,0 +1,58 @@ +import { deepGet, type ControlRecord, type ControlValue } from "@zag-js/shared" + +interface ControlsProps { + config: T + state: ControlValue +} + +export function Controls({ config, state }: ControlsProps) { + return ( +
+ {Object.keys(config).map((key) => { + const { type, label = key, options, placeholder, min, max } = (config[key] ?? {}) as any + const value = deepGet(state, key) + switch (type) { + case "boolean": + return ( +
+ + +
+ ) + case "string": + return ( +
+ + +
+ ) + case "select": + return ( +
+ + +
+ ) + case "number": + return ( +
+ + +
+ ) + default: + return null + } + })} +
+ ) +} diff --git a/examples/alpine-ts/server/components/head.tsx b/examples/alpine-ts/server/components/head.tsx new file mode 100644 index 0000000000..9bb7b20397 --- /dev/null +++ b/examples/alpine-ts/server/components/head.tsx @@ -0,0 +1,14 @@ +export function Head() { + return ( + + + + + + + + Nitro + Vite + + + ) +} diff --git a/examples/alpine-ts/server/components/nav.tsx b/examples/alpine-ts/server/components/nav.tsx new file mode 100644 index 0000000000..9567109f0e --- /dev/null +++ b/examples/alpine-ts/server/components/nav.tsx @@ -0,0 +1,20 @@ +import { dataAttr } from "@zag-js/dom-query" +import { routesData } from "@zag-js/shared" + +export function Nav({ pathname }: { pathname: string }) { + return ( + + ) +} diff --git a/examples/alpine-ts/server/components/presence.tsx b/examples/alpine-ts/server/components/presence.tsx new file mode 100644 index 0000000000..cbfb717d2f --- /dev/null +++ b/examples/alpine-ts/server/components/presence.tsx @@ -0,0 +1,13 @@ +export function Presence(props: any) { + return ( +
+ +
+ ) +} diff --git a/examples/alpine-ts/server/components/state-visualizer.tsx b/examples/alpine-ts/server/components/state-visualizer.tsx new file mode 100644 index 0000000000..612401a9b9 --- /dev/null +++ b/examples/alpine-ts/server/components/state-visualizer.tsx @@ -0,0 +1,18 @@ +interface StateVisualizerProps { + label: string + omit?: string[] + context?: string[] +} + +export function StateVisualizer({ label, omit, context }: StateVisualizerProps) { + return ( +
+
+        
+ {label} Visualizer +
+
+
+
+ ) +} diff --git a/examples/alpine-ts/server/components/toolbar.tsx b/examples/alpine-ts/server/components/toolbar.tsx new file mode 100644 index 0000000000..259caa3874 --- /dev/null +++ b/examples/alpine-ts/server/components/toolbar.tsx @@ -0,0 +1,26 @@ +export function Toolbar({ viz = false, controls = true }: { viz?: boolean; controls?: boolean }) { + return ( +
+ +
+ {controls && ( +
+ +
+ )} +
+ +
+
+
+ ) +} diff --git a/examples/alpine-ts/server/routes/accordion.tsx b/examples/alpine-ts/server/routes/accordion.tsx new file mode 100644 index 0000000000..4b7019e93a --- /dev/null +++ b/examples/alpine-ts/server/routes/accordion.tsx @@ -0,0 +1,55 @@ +import { defineHandler } from "nitro/h3" +import { accordionControls, accordionData, getControlDefaults } from "@zag-js/shared" +import { ArrowRight } from "lucide-static" +import { Controls } from "../components/controls" +import { Head } from "../components/head" +import { Nav } from "../components/nav" +import { StateVisualizer } from "../components/state-visualizer" +import { Toolbar } from "../components/toolbar" + +export default defineHandler((event) => { + const state = getControlDefaults(accordionControls) + + return ( + + + + + + +
+
+ + + ) +}) diff --git a/examples/alpine-ts/server/routes/angle-slider.tsx b/examples/alpine-ts/server/routes/angle-slider.tsx new file mode 100644 index 0000000000..d4a15921ca --- /dev/null +++ b/examples/alpine-ts/server/routes/angle-slider.tsx @@ -0,0 +1,52 @@ +import { defineHandler } from "nitro/h3" +import { angleSliderControls, getControlDefaults } from "@zag-js/shared" +import { StateVisualizer } from "../components/state-visualizer" +import { Head } from "../components/head" +import { Nav } from "../components/nav" +import { Toolbar } from "../components/toolbar" +import { Controls } from "../components/controls" + +export default defineHandler((event) => { + const state = getControlDefaults(angleSliderControls) + + return ( + + + + + + +
+
+ + + ) +}) diff --git a/examples/alpine-ts/server/routes/async-list.tsx b/examples/alpine-ts/server/routes/async-list.tsx new file mode 100644 index 0000000000..2550ceecc1 --- /dev/null +++ b/examples/alpine-ts/server/routes/async-list.tsx @@ -0,0 +1,75 @@ +import { defineHandler } from "nitro/h3" +import { Head } from "../components/head" +import { Nav } from "../components/nav" +import { StateVisualizer } from "../components/state-visualizer" +import { Toolbar } from "../components/toolbar" + +export default defineHandler((event) => { + return ( + + + + + + +
+
+ + + ) +}) diff --git a/examples/alpine-ts/server/routes/autoresize.tsx b/examples/alpine-ts/server/routes/autoresize.tsx new file mode 100644 index 0000000000..c702e723b0 --- /dev/null +++ b/examples/alpine-ts/server/routes/autoresize.tsx @@ -0,0 +1,35 @@ +import { defineHandler } from "nitro/h3" +import { Head } from "../components/head" +import { Nav } from "../components/nav" + +export default defineHandler((event) => { + return ( + + + + + + +
+