From 17ada739ad1d35f1641efeabd3427b141c79836f Mon Sep 17 00:00:00 2001 From: karimsemmoud Date: Sat, 14 Feb 2026 17:13:34 +0700 Subject: [PATCH 01/11] add component and hooks --- assets/components/angle-slider.ts | 61 + assets/components/avatar.ts | 28 + assets/components/carousel.ts | 73 + assets/components/editable.ts | 58 + assets/components/floating-panel.ts | 83 + assets/components/listbox.ts | 143 + assets/components/number-input.ts | 56 + assets/components/password-input.ts | 46 + assets/components/pin-input.ts | 42 + assets/components/radio-group.ts | 73 + assets/components/timer.ts | 62 + assets/hooks/angle-slider.ts | 82 + assets/hooks/avatar.ts | 58 + assets/hooks/carousel.ts | 97 + assets/hooks/components.d.ts | 33 + assets/hooks/corex.ts | 11 + assets/hooks/editable.ts | 84 + assets/hooks/floating-panel.ts | 138 + assets/hooks/listbox.ts | 128 + assets/hooks/number-input.ts | 92 + assets/hooks/password-input.ts | 76 + assets/hooks/pin-input.ts | 109 + assets/hooks/radio-group.ts | 86 + assets/hooks/timer.ts | 64 + config/config.exs | 13 +- e2e/mix.exs | 2 +- package.json | 24 +- pnpm-lock.yaml | 147 + priv/static/accordion.mjs | 2 +- priv/static/angle-slider.mjs | 524 + priv/static/avatar.mjs | 229 + priv/static/carousel.mjs | 1129 ++ priv/static/checkbox.mjs | 4 +- priv/static/chunk-2PO3TGCF.mjs | 1322 ++ priv/static/chunk-BMVNROAE.mjs | 432 + priv/static/chunk-EENFWNGI.mjs | 1957 ++ priv/static/chunk-ER3INIAI.mjs | 237 + priv/static/chunk-IXOYOLUJ.mjs | 2664 +++ priv/static/chunk-RR7TJIQ5.mjs | 295 + priv/static/chunk-TEV2GE3U.mjs | 167 + priv/static/clipboard.mjs | 2 +- priv/static/collapsible.mjs | 2 +- priv/static/combobox.mjs | 9 +- priv/static/corex.cjs.js | 11 + priv/static/corex.cjs.js.map | 4 +- priv/static/corex.js | 26831 +++++++++++++++++--------- priv/static/corex.min.js | 12 +- priv/static/corex.mjs | 11 + priv/static/corex.mjs.map | 4 +- priv/static/date-picker.mjs | 7 +- priv/static/dialog.mjs | 5 +- priv/static/editable.mjs | 648 + priv/static/floating-panel.mjs | 1182 ++ priv/static/listbox.mjs | 998 + priv/static/menu.mjs | 68 +- priv/static/number-input.mjs | 1437 ++ priv/static/password-input.mjs | 340 + priv/static/pin-input.mjs | 689 + priv/static/radio-group.mjs | 576 + priv/static/select.mjs | 40 +- priv/static/signature-pad.mjs | 2 +- priv/static/switch.mjs | 4 +- priv/static/tabs.mjs | 2 +- priv/static/timer.mjs | 467 + priv/static/toast.mjs | 5 +- priv/static/toggle-group.mjs | 2 +- priv/static/tree-view.mjs | 4 +- 67 files changed, 35243 insertions(+), 9050 deletions(-) create mode 100644 assets/components/angle-slider.ts create mode 100644 assets/components/avatar.ts create mode 100644 assets/components/carousel.ts create mode 100644 assets/components/editable.ts create mode 100644 assets/components/floating-panel.ts create mode 100644 assets/components/listbox.ts create mode 100644 assets/components/number-input.ts create mode 100644 assets/components/password-input.ts create mode 100644 assets/components/pin-input.ts create mode 100644 assets/components/radio-group.ts create mode 100644 assets/components/timer.ts create mode 100644 assets/hooks/angle-slider.ts create mode 100644 assets/hooks/avatar.ts create mode 100644 assets/hooks/carousel.ts create mode 100644 assets/hooks/editable.ts create mode 100644 assets/hooks/floating-panel.ts create mode 100644 assets/hooks/listbox.ts create mode 100644 assets/hooks/number-input.ts create mode 100644 assets/hooks/password-input.ts create mode 100644 assets/hooks/pin-input.ts create mode 100644 assets/hooks/radio-group.ts create mode 100644 assets/hooks/timer.ts create mode 100644 priv/static/angle-slider.mjs create mode 100644 priv/static/avatar.mjs create mode 100644 priv/static/carousel.mjs create mode 100644 priv/static/chunk-2PO3TGCF.mjs create mode 100644 priv/static/chunk-BMVNROAE.mjs create mode 100644 priv/static/chunk-EENFWNGI.mjs create mode 100644 priv/static/chunk-ER3INIAI.mjs create mode 100644 priv/static/chunk-IXOYOLUJ.mjs create mode 100644 priv/static/chunk-RR7TJIQ5.mjs create mode 100644 priv/static/chunk-TEV2GE3U.mjs create mode 100644 priv/static/editable.mjs create mode 100644 priv/static/floating-panel.mjs create mode 100644 priv/static/listbox.mjs create mode 100644 priv/static/number-input.mjs create mode 100644 priv/static/password-input.mjs create mode 100644 priv/static/pin-input.mjs create mode 100644 priv/static/radio-group.mjs create mode 100644 priv/static/timer.mjs diff --git a/assets/components/angle-slider.ts b/assets/components/angle-slider.ts new file mode 100644 index 0000000..cf3de17 --- /dev/null +++ b/assets/components/angle-slider.ts @@ -0,0 +1,61 @@ +import { connect, machine, type Props, type Api } from "@zag-js/angle-slider"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class AngleSlider extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="angle-slider"][data-part="root"]') ?? + this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const labelEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const hiddenInputEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="hidden-input"]' + ); + if (hiddenInputEl) this.spreadProps(hiddenInputEl, this.api.getHiddenInputProps()); + + const controlEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const thumbEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="thumb"]' + ); + if (thumbEl) this.spreadProps(thumbEl, this.api.getThumbProps()); + + const valueTextEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="value-text"]' + ); + if (valueTextEl) this.spreadProps(valueTextEl, this.api.getValueTextProps()); + + const markerGroupEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="marker-group"]' + ); + if (markerGroupEl) this.spreadProps(markerGroupEl, this.api.getMarkerGroupProps()); + + this.el + .querySelectorAll('[data-scope="angle-slider"][data-part="marker"]') + .forEach((markerEl) => { + const valueStr = markerEl.dataset.value; + if (valueStr == null) return; + const value = Number(valueStr); + if (Number.isNaN(value)) return; + this.spreadProps(markerEl, this.api.getMarkerProps({ value })); + }); + } +} diff --git a/assets/components/avatar.ts b/assets/components/avatar.ts new file mode 100644 index 0000000..7b565fb --- /dev/null +++ b/assets/components/avatar.ts @@ -0,0 +1,28 @@ +import { connect, machine, type Props, type Api } from "@zag-js/avatar"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class Avatar extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="avatar"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const imageEl = this.el.querySelector('[data-scope="avatar"][data-part="image"]'); + if (imageEl) this.spreadProps(imageEl, this.api.getImageProps()); + + const fallbackEl = this.el.querySelector( + '[data-scope="avatar"][data-part="fallback"]' + ); + if (fallbackEl) this.spreadProps(fallbackEl, this.api.getFallbackProps()); + } +} diff --git a/assets/components/carousel.ts b/assets/components/carousel.ts new file mode 100644 index 0000000..c6821a7 --- /dev/null +++ b/assets/components/carousel.ts @@ -0,0 +1,73 @@ +import { connect, machine, type Props, type Api } from "@zag-js/carousel"; +import type { ItemProps, IndicatorProps } from "@zag-js/carousel"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class Carousel extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="carousel"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const controlEl = this.el.querySelector( + '[data-scope="carousel"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const itemGroupEl = this.el.querySelector( + '[data-scope="carousel"][data-part="item-group"]' + ); + if (itemGroupEl) this.spreadProps(itemGroupEl, this.api.getItemGroupProps()); + + const slideCount = Number(this.el.dataset.slideCount) || 0; + for (let i = 0; i < slideCount; i++) { + const itemEl = this.el.querySelector( + `[data-scope="carousel"][data-part="item"][data-index="${i}"]` + ); + if (itemEl) this.spreadProps(itemEl, this.api.getItemProps({ index: i } as ItemProps)); + } + + const prevTriggerEl = this.el.querySelector( + '[data-scope="carousel"][data-part="prev-trigger"]' + ); + if (prevTriggerEl) this.spreadProps(prevTriggerEl, this.api.getPrevTriggerProps()); + + const nextTriggerEl = this.el.querySelector( + '[data-scope="carousel"][data-part="next-trigger"]' + ); + if (nextTriggerEl) this.spreadProps(nextTriggerEl, this.api.getNextTriggerProps()); + + const autoplayTriggerEl = this.el.querySelector( + '[data-scope="carousel"][data-part="autoplay-trigger"]' + ); + if (autoplayTriggerEl) this.spreadProps(autoplayTriggerEl, this.api.getAutoplayTriggerProps()); + + const indicatorGroupEl = this.el.querySelector( + '[data-scope="carousel"][data-part="indicator-group"]' + ); + if (indicatorGroupEl) this.spreadProps(indicatorGroupEl, this.api.getIndicatorGroupProps()); + + const indicatorCount = this.api.pageSnapPoints.length; + for (let i = 0; i < indicatorCount; i++) { + const indicatorEl = this.el.querySelector( + `[data-scope="carousel"][data-part="indicator"][data-index="${i}"]` + ); + if (indicatorEl) + this.spreadProps(indicatorEl, this.api.getIndicatorProps({ index: i } as IndicatorProps)); + } + + const progressTextEl = this.el.querySelector( + '[data-scope="carousel"][data-part="progress-text"]' + ); + if (progressTextEl) this.spreadProps(progressTextEl, this.api.getProgressTextProps()); + } +} diff --git a/assets/components/editable.ts b/assets/components/editable.ts new file mode 100644 index 0000000..6267be7 --- /dev/null +++ b/assets/components/editable.ts @@ -0,0 +1,58 @@ +import { connect, machine, type Props, type Api } from "@zag-js/editable"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class Editable extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="editable"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const areaEl = this.el.querySelector('[data-scope="editable"][data-part="area"]'); + if (areaEl) this.spreadProps(areaEl, this.api.getAreaProps()); + + const labelEl = this.el.querySelector( + '[data-scope="editable"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const inputEl = this.el.querySelector( + '[data-scope="editable"][data-part="input"]' + ); + if (inputEl) this.spreadProps(inputEl, this.api.getInputProps()); + + const previewEl = this.el.querySelector( + '[data-scope="editable"][data-part="preview"]' + ); + if (previewEl) this.spreadProps(previewEl, this.api.getPreviewProps()); + + const editTriggerEl = this.el.querySelector( + '[data-scope="editable"][data-part="edit-trigger"]' + ); + if (editTriggerEl) this.spreadProps(editTriggerEl, this.api.getEditTriggerProps()); + + const controlEl = this.el.querySelector( + '[data-scope="editable"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const submitTriggerEl = this.el.querySelector( + '[data-scope="editable"][data-part="submit-trigger"]' + ); + if (submitTriggerEl) this.spreadProps(submitTriggerEl, this.api.getSubmitTriggerProps()); + + const cancelTriggerEl = this.el.querySelector( + '[data-scope="editable"][data-part="cancel-trigger"]' + ); + if (cancelTriggerEl) this.spreadProps(cancelTriggerEl, this.api.getCancelTriggerProps()); + } +} diff --git a/assets/components/floating-panel.ts b/assets/components/floating-panel.ts new file mode 100644 index 0000000..3f57b7d --- /dev/null +++ b/assets/components/floating-panel.ts @@ -0,0 +1,83 @@ +import { connect, machine, type Props, type Api } from "@zag-js/floating-panel"; +import type { ResizeTriggerProps, StageTriggerProps } from "@zag-js/floating-panel"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class FloatingPanel extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const triggerEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="trigger"]' + ); + if (triggerEl) this.spreadProps(triggerEl, this.api.getTriggerProps()); + + const positionerEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="positioner"]' + ); + if (positionerEl) this.spreadProps(positionerEl, this.api.getPositionerProps()); + + const contentEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="content"]' + ); + if (contentEl) this.spreadProps(contentEl, this.api.getContentProps()); + + const titleEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="title"]' + ); + if (titleEl) this.spreadProps(titleEl, this.api.getTitleProps()); + + const headerEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="header"]' + ); + if (headerEl) this.spreadProps(headerEl, this.api.getHeaderProps()); + + const bodyEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="body"]' + ); + if (bodyEl) this.spreadProps(bodyEl, this.api.getBodyProps()); + + const dragTriggerEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="drag-trigger"]' + ); + if (dragTriggerEl) this.spreadProps(dragTriggerEl, this.api.getDragTriggerProps()); + + const resizeAxes = ["s", "w", "e", "n", "sw", "nw", "se", "ne"] as const; + resizeAxes.forEach((axis) => { + const resizeEl = this.el.querySelector( + `[data-scope="floating-panel"][data-part="resize-trigger"][data-axis="${axis}"]` + ); + if (resizeEl) + this.spreadProps(resizeEl, this.api.getResizeTriggerProps({ axis } as ResizeTriggerProps)); + }); + + const closeTriggerEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="close-trigger"]' + ); + if (closeTriggerEl) this.spreadProps(closeTriggerEl, this.api.getCloseTriggerProps()); + + const controlEl = this.el.querySelector( + '[data-scope="floating-panel"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const stages = ["minimized", "maximized", "default"] as const; + stages.forEach((stage) => { + const stageTriggerEl = this.el.querySelector( + `[data-scope="floating-panel"][data-part="stage-trigger"][data-stage="${stage}"]` + ); + if (stageTriggerEl) + this.spreadProps( + stageTriggerEl, + this.api.getStageTriggerProps({ stage } as StageTriggerProps) + ); + }); + } +} diff --git a/assets/components/listbox.ts b/assets/components/listbox.ts new file mode 100644 index 0000000..de4edb8 --- /dev/null +++ b/assets/components/listbox.ts @@ -0,0 +1,143 @@ +import { connect, machine, collection, type Props, type Api } from "@zag-js/listbox"; +import type { ListCollection } from "@zag-js/collection"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +type Item = { + id?: string; + value?: string; + label: string; + disabled?: boolean; + group?: string; +}; + +export class Listbox extends Component, Api> { + private _options: Item[] = []; + hasGroups: boolean = false; + + constructor(el: HTMLElement | null, props: Props) { + super(el, props); + const collectionFromProps = (props as Props & { collection?: { items: Item[] } }) + .collection; + this._options = collectionFromProps?.items ?? []; + } + + get options(): Item[] { + return Array.isArray(this._options) ? this._options : []; + } + + setOptions(options: Item[]) { + this._options = Array.isArray(options) ? options : []; + } + + getCollection(): ListCollection { + const items = this.options; + if (this.hasGroups) { + return collection({ + items, + itemToValue: (item) => item.id ?? item.value ?? "", + itemToString: (item) => item.label, + isItemDisabled: (item) => !!item.disabled, + groupBy: (item: Item) => item.group ?? "", + }); + } + return collection({ + items, + itemToValue: (item) => item.id ?? item.value ?? "", + itemToString: (item) => item.label, + isItemDisabled: (item) => !!item.disabled, + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + const getCollection = this.getCollection.bind(this); + const collectionFromProps = (props as Props & { collection?: ListCollection }) + .collection; + return new VanillaMachine(machine, { + ...props, + get collection() { + return collectionFromProps ?? getCollection(); + }, + }); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + init = (): void => { + this.machine.start(); + this.render(); + this.machine.subscribe(() => { + this.api = this.initApi(); + this.render(); + }); + }; + + applyItemProps(): void { + const contentEl = this.el.querySelector( + '[data-scope="listbox"][data-part="content"]' + ); + if (!contentEl) return; + + contentEl + .querySelectorAll('[data-scope="listbox"][data-part="item-group"]') + .forEach((groupEl) => { + const groupId = groupEl.dataset.id ?? ""; + this.spreadProps(groupEl, this.api.getItemGroupProps({ id: groupId })); + const labelEl = groupEl.querySelector( + '[data-scope="listbox"][data-part="item-group-label"]' + ); + if (labelEl) { + this.spreadProps(labelEl, this.api.getItemGroupLabelProps({ htmlFor: groupId })); + } + }); + + contentEl + .querySelectorAll('[data-scope="listbox"][data-part="item"]') + .forEach((itemEl) => { + const value = itemEl.dataset.value ?? ""; + const item = this.options.find((i) => String(i.id ?? i.value ?? "") === String(value)); + if (!item) return; + this.spreadProps(itemEl, this.api.getItemProps({ item })); + const textEl = itemEl.querySelector( + '[data-scope="listbox"][data-part="item-text"]' + ); + if (textEl) { + this.spreadProps(textEl, this.api.getItemTextProps({ item })); + } + const indicatorEl = itemEl.querySelector( + '[data-scope="listbox"][data-part="item-indicator"]' + ); + if (indicatorEl) { + this.spreadProps(indicatorEl, this.api.getItemIndicatorProps({ item })); + } + }); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="listbox"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const labelEl = this.el.querySelector('[data-scope="listbox"][data-part="label"]'); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const valueTextEl = this.el.querySelector( + '[data-scope="listbox"][data-part="value-text"]' + ); + if (valueTextEl) this.spreadProps(valueTextEl, this.api.getValueTextProps()); + + const inputEl = this.el.querySelector('[data-scope="listbox"][data-part="input"]'); + if (inputEl) this.spreadProps(inputEl, this.api.getInputProps()); + + const contentEl = this.el.querySelector( + '[data-scope="listbox"][data-part="content"]' + ); + if (contentEl) { + this.spreadProps(contentEl, this.api.getContentProps()); + this.applyItemProps(); + } + } +} diff --git a/assets/components/number-input.ts b/assets/components/number-input.ts new file mode 100644 index 0000000..aac97fe --- /dev/null +++ b/assets/components/number-input.ts @@ -0,0 +1,56 @@ +import { connect, machine, type Props, type Api } from "@zag-js/number-input"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class NumberInput extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="number-input"][data-part="root"]') ?? + this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const labelEl = this.el.querySelector( + '[data-scope="number-input"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const controlEl = this.el.querySelector( + '[data-scope="number-input"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const valueTextEl = this.el.querySelector( + '[data-scope="number-input"][data-part="value-text"]' + ); + if (valueTextEl) this.spreadProps(valueTextEl, this.api.getValueTextProps()); + + const inputEl = this.el.querySelector( + '[data-scope="number-input"][data-part="input"]' + ); + if (inputEl) this.spreadProps(inputEl, this.api.getInputProps()); + + const decrementEl = this.el.querySelector( + '[data-scope="number-input"][data-part="decrement-trigger"]' + ); + if (decrementEl) this.spreadProps(decrementEl, this.api.getDecrementTriggerProps()); + + const incrementEl = this.el.querySelector( + '[data-scope="number-input"][data-part="increment-trigger"]' + ); + if (incrementEl) this.spreadProps(incrementEl, this.api.getIncrementTriggerProps()); + + const scrubberEl = this.el.querySelector( + '[data-scope="number-input"][data-part="scrubber"]' + ); + if (scrubberEl) this.spreadProps(scrubberEl, this.api.getScrubberProps()); + } +} diff --git a/assets/components/password-input.ts b/assets/components/password-input.ts new file mode 100644 index 0000000..179e0c4 --- /dev/null +++ b/assets/components/password-input.ts @@ -0,0 +1,46 @@ +import { connect, machine, type Props, type Api } from "@zag-js/password-input"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class PasswordInput extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="password-input"][data-part="root"]') ?? + this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const labelEl = this.el.querySelector( + '[data-scope="password-input"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const controlEl = this.el.querySelector( + '[data-scope="password-input"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const inputEl = this.el.querySelector( + '[data-scope="password-input"][data-part="input"]' + ); + if (inputEl) this.spreadProps(inputEl, this.api.getInputProps()); + + const triggerEl = this.el.querySelector( + '[data-scope="password-input"][data-part="visibility-trigger"]' + ); + if (triggerEl) this.spreadProps(triggerEl, this.api.getVisibilityTriggerProps()); + + const indicatorEl = this.el.querySelector( + '[data-scope="password-input"][data-part="indicator"]' + ); + if (indicatorEl) this.spreadProps(indicatorEl, this.api.getIndicatorProps()); + } +} diff --git a/assets/components/pin-input.ts b/assets/components/pin-input.ts new file mode 100644 index 0000000..f1d93dc --- /dev/null +++ b/assets/components/pin-input.ts @@ -0,0 +1,42 @@ +import { connect, machine, type Props, type Api } from "@zag-js/pin-input"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class PinInput extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="pin-input"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const labelEl = this.el.querySelector( + '[data-scope="pin-input"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const hiddenInputEl = this.el.querySelector( + '[data-scope="pin-input"][data-part="hidden-input"]' + ); + if (hiddenInputEl) this.spreadProps(hiddenInputEl, this.api.getHiddenInputProps()); + + const controlEl = this.el.querySelector( + '[data-scope="pin-input"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + this.api.items.forEach((i) => { + const inputEl = this.el.querySelector( + `[data-scope="pin-input"][data-part="input"][data-index="${i}"]` + ); + if (inputEl) this.spreadProps(inputEl, this.api.getInputProps({ index: i })); + }); + } +} diff --git a/assets/components/radio-group.ts b/assets/components/radio-group.ts new file mode 100644 index 0000000..8a785e2 --- /dev/null +++ b/assets/components/radio-group.ts @@ -0,0 +1,73 @@ +import { connect, machine, type Props, type Api } from "@zag-js/radio-group"; +import type { ItemProps } from "@zag-js/radio-group"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class RadioGroup extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="radio-group"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const labelEl = this.el.querySelector( + '[data-scope="radio-group"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + + const indicatorEl = this.el.querySelector( + '[data-scope="radio-group"][data-part="indicator"]' + ); + if (indicatorEl) this.spreadProps(indicatorEl, this.api.getIndicatorProps()); + + this.el + .querySelectorAll('[data-scope="radio-group"][data-part="item"]') + .forEach((itemEl) => { + const value = itemEl.dataset.value; + if (value == null) return; + const disabled = itemEl.dataset.disabled === "true"; + const invalid = itemEl.dataset.invalid === "true"; + this.spreadProps(itemEl, this.api.getItemProps({ value, disabled, invalid })); + const textEl = itemEl.querySelector( + '[data-scope="radio-group"][data-part="item-text"]' + ); + if (textEl) + this.spreadProps( + textEl, + this.api.getItemTextProps({ value, disabled, invalid } as ItemProps) + ); + const controlEl = itemEl.querySelector( + '[data-scope="radio-group"][data-part="item-control"]' + ); + if (controlEl) + this.spreadProps( + controlEl, + this.api.getItemControlProps({ + value, + disabled, + invalid, + } as ItemProps) + ); + const hiddenInputEl = itemEl.querySelector( + '[data-scope="radio-group"][data-part="item-hidden-input"]' + ); + if (hiddenInputEl) + this.spreadProps( + hiddenInputEl, + this.api.getItemHiddenInputProps({ + value, + disabled, + invalid, + } as ItemProps) + ); + }); + } +} diff --git a/assets/components/timer.ts b/assets/components/timer.ts new file mode 100644 index 0000000..a1d0b6f --- /dev/null +++ b/assets/components/timer.ts @@ -0,0 +1,62 @@ +import { connect, machine, type Props, type Api } from "@zag-js/timer"; +import type { ItemProps, ActionTriggerProps } from "@zag-js/timer"; +import { VanillaMachine, normalizeProps } from "@zag-js/vanilla"; +import { Component } from "../lib/core"; + +export class Timer extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props: Props): VanillaMachine { + return new VanillaMachine(machine, props); + } + + initApi(): Api { + return connect(this.machine.service, normalizeProps); + } + + render(): void { + const rootEl = + this.el.querySelector('[data-scope="timer"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + + const areaEl = this.el.querySelector('[data-scope="timer"][data-part="area"]'); + if (areaEl) this.spreadProps(areaEl, this.api.getAreaProps()); + + const controlEl = this.el.querySelector( + '[data-scope="timer"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + + const timeParts = ["days", "hours", "minutes", "seconds", "milliseconds"] as const; + timeParts.forEach((type) => { + const itemEl = this.el.querySelector( + `[data-scope="timer"][data-part="item"][data-type="${type}"]` + ); + if (itemEl) this.spreadProps(itemEl, this.api.getItemProps({ type } as ItemProps)); + const valueEl = this.el.querySelector( + `[data-scope="timer"][data-part="item-value"][data-type="${type}"]` + ); + if (valueEl) this.spreadProps(valueEl, this.api.getItemValueProps({ type } as ItemProps)); + const labelEl = this.el.querySelector( + `[data-scope="timer"][data-part="item-label"][data-type="${type}"]` + ); + if (labelEl) this.spreadProps(labelEl, this.api.getItemLabelProps({ type } as ItemProps)); + }); + + const separatorEl = this.el.querySelector( + '[data-scope="timer"][data-part="separator"]' + ); + if (separatorEl) this.spreadProps(separatorEl, this.api.getSeparatorProps()); + + const actions = ["start", "pause", "resume", "reset", "restart"] as const; + actions.forEach((action) => { + const triggerEl = this.el.querySelector( + `[data-scope="timer"][data-part="action-trigger"][data-action="${action}"]` + ); + if (triggerEl) + this.spreadProps( + triggerEl, + this.api.getActionTriggerProps({ action } as ActionTriggerProps) + ); + }); + } +} diff --git a/assets/hooks/angle-slider.ts b/assets/hooks/angle-slider.ts new file mode 100644 index 0000000..62298b7 --- /dev/null +++ b/assets/hooks/angle-slider.ts @@ -0,0 +1,82 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { AngleSlider } from "../components/angle-slider"; +import type { Props, ValueChangeDetails } from "@zag-js/angle-slider"; +import { getString, getBoolean, getNumber } from "../lib/util"; + +type AngleSliderHookState = { + angleSlider?: AngleSlider; + handlers?: Array; +}; + +const AngleSliderHook: Hook = { + mounted(this: object & HookInterface & AngleSliderHookState) { + const el = this.el; + const value = getNumber(el, "value"); + const defaultValue = getNumber(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new AngleSlider(el, { + id: el.id, + ...(controlled && value !== undefined ? { value } : { defaultValue: defaultValue ?? 0 }), + step: getNumber(el, "step") ?? 1, + disabled: getBoolean(el, "disabled"), + readOnly: getBoolean(el, "readOnly"), + invalid: getBoolean(el, "invalid"), + name: getString(el, "name"), + dir: getString<"ltr" | "rtl">(el, "dir", ["ltr", "rtl"]), + onValueChange: (details: ValueChangeDetails) => { + const hiddenInput = el.querySelector( + '[data-scope="angle-slider"][data-part="hidden-input"]' + ); + if (hiddenInput) { + hiddenInput.value = String(details.value); + hiddenInput.dispatchEvent(new Event("input", { bubbles: true })); + hiddenInput.dispatchEvent(new Event("change", { bubbles: true })); + } + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + valueAsDegree: details.valueAsDegree, + id: el.id, + }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.init(); + this.angleSlider = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & AngleSliderHookState) { + const value = getNumber(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + this.angleSlider?.updateProps({ + id: this.el.id, + ...(controlled && value !== undefined ? { value } : {}), + step: getNumber(this.el, "step") ?? 1, + disabled: getBoolean(this.el, "disabled"), + readOnly: getBoolean(this.el, "readOnly"), + invalid: getBoolean(this.el, "invalid"), + name: getString(this.el, "name"), + } as Partial); + }, + + destroyed(this: object & HookInterface & AngleSliderHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.angleSlider?.destroy(); + }, +}; + +export { AngleSliderHook as AngleSlider }; diff --git a/assets/hooks/avatar.ts b/assets/hooks/avatar.ts new file mode 100644 index 0000000..8750463 --- /dev/null +++ b/assets/hooks/avatar.ts @@ -0,0 +1,58 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { Avatar } from "../components/avatar"; +import type { Props, StatusChangeDetails } from "@zag-js/avatar"; +import { getString } from "../lib/util"; + +type AvatarHookState = { + avatar?: Avatar; + handlers?: Array; +}; + +const AvatarHook: Hook = { + mounted(this: object & HookInterface & AvatarHookState) { + const el = this.el; + const src = getString(el, "src"); + const zag = new Avatar(el, { + id: el.id, + onStatusChange: (details: StatusChangeDetails) => { + const eventName = getString(el, "onStatusChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { status: details.status, id: el.id }); + } + const clientName = getString(el, "onStatusChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + ...(src !== undefined ? {} : {}), + } as Props); + zag.init(); + this.avatar = zag; + if (src !== undefined) { + zag.api.setSrc(src); + } + this.handlers = []; + }, + + updated(this: object & HookInterface & AvatarHookState) { + const src = getString(this.el, "src"); + if (src !== undefined && this.avatar) { + this.avatar.api.setSrc(src); + } + }, + + destroyed(this: object & HookInterface & AvatarHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.avatar?.destroy(); + }, +}; + +export { AvatarHook as Avatar }; diff --git a/assets/hooks/carousel.ts b/assets/hooks/carousel.ts new file mode 100644 index 0000000..337b889 --- /dev/null +++ b/assets/hooks/carousel.ts @@ -0,0 +1,97 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { Carousel } from "../components/carousel"; +import type { Props, PageChangeDetails } from "@zag-js/carousel"; +import { getString, getBoolean, getNumber, getDir } from "../lib/util"; + +type CarouselHookState = { + carousel?: Carousel; + handlers?: Array; +}; + +const CarouselHook: Hook = { + mounted(this: object & HookInterface & CarouselHookState) { + const el = this.el; + const page = getNumber(el, "page"); + const defaultPage = getNumber(el, "defaultPage"); + const controlled = getBoolean(el, "controlled"); + const slideCount = getNumber(el, "slideCount"); + if (slideCount == null || slideCount < 1) { + return; + } + const zag = new Carousel(el, { + id: el.id, + slideCount, + ...(controlled && page !== undefined ? { page } : { defaultPage: defaultPage ?? 0 }), + dir: getDir(el), + orientation: getString<"horizontal" | "vertical">(el, "orientation", [ + "horizontal", + "vertical", + ]), + slidesPerPage: getNumber(el, "slidesPerPage") ?? 1, + slidesPerMove: + getString(el, "slidesPerMove") === "auto" ? "auto" : getNumber(el, "slidesPerMove"), + loop: getBoolean(el, "loop"), + autoplay: getBoolean(el, "autoplay") + ? { delay: getNumber(el, "autoplayDelay") ?? 4000 } + : false, + allowMouseDrag: getBoolean(el, "allowMouseDrag"), + spacing: getString(el, "spacing") ?? "0px", + padding: getString(el, "padding"), + inViewThreshold: getNumber(el, "inViewThreshold") ?? 0.6, + snapType: getString<"proximity" | "mandatory">(el, "snapType", ["proximity", "mandatory"]), + autoSize: getBoolean(el, "autoSize"), + onPageChange: (details: PageChangeDetails) => { + const eventName = getString(el, "onPageChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + page: details.page, + pageSnapPoint: details.pageSnapPoint, + id: el.id, + }); + } + const clientName = getString(el, "onPageChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.init(); + this.carousel = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & CarouselHookState) { + const slideCount = getNumber(this.el, "slideCount"); + if (slideCount == null || slideCount < 1) return; + const page = getNumber(this.el, "page"); + const controlled = getBoolean(this.el, "controlled"); + this.carousel?.updateProps({ + id: this.el.id, + slideCount, + ...(controlled && page !== undefined ? { page } : {}), + dir: getDir(this.el), + orientation: getString<"horizontal" | "vertical">(this.el, "orientation", [ + "horizontal", + "vertical", + ]), + slidesPerPage: getNumber(this.el, "slidesPerPage") ?? 1, + loop: getBoolean(this.el, "loop"), + allowMouseDrag: getBoolean(this.el, "allowMouseDrag"), + } as Partial); + }, + + destroyed(this: object & HookInterface & CarouselHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.carousel?.destroy(); + }, +}; + +export { CarouselHook as Carousel }; diff --git a/assets/hooks/components.d.ts b/assets/hooks/components.d.ts index 7235e71..ac6222f 100644 --- a/assets/hooks/components.d.ts +++ b/assets/hooks/components.d.ts @@ -7,6 +7,15 @@ type CorexHook = import("phoenix_live_view").Hook; declare module "corex/accordion" { export const Accordion: CorexHook; } +declare module "corex/angle-slider" { + export const AngleSlider: CorexHook; +} +declare module "corex/avatar" { + export const Avatar: CorexHook; +} +declare module "corex/carousel" { + export const Carousel: CorexHook; +} declare module "corex/checkbox" { export const Checkbox: CorexHook; } @@ -25,9 +34,30 @@ declare module "corex/date-picker" { declare module "corex/dialog" { export const Dialog: CorexHook; } +declare module "corex/editable" { + export const Editable: CorexHook; +} +declare module "corex/floating-panel" { + export const FloatingPanel: CorexHook; +} +declare module "corex/listbox" { + export const Listbox: CorexHook; +} declare module "corex/menu" { export const Menu: CorexHook; } +declare module "corex/number-input" { + export const NumberInput: CorexHook; +} +declare module "corex/password-input" { + export const PasswordInput: CorexHook; +} +declare module "corex/pin-input" { + export const PinInput: CorexHook; +} +declare module "corex/radio-group" { + export const RadioGroup: CorexHook; +} declare module "corex/select" { export const Select: CorexHook; } @@ -40,6 +70,9 @@ declare module "corex/switch" { declare module "corex/tabs" { export const Tabs: CorexHook; } +declare module "corex/timer" { + export const Timer: CorexHook; +} declare module "corex/toast" { export const Toast: CorexHook; } diff --git a/assets/hooks/corex.ts b/assets/hooks/corex.ts index 4a37a3f..d172d79 100644 --- a/assets/hooks/corex.ts +++ b/assets/hooks/corex.ts @@ -30,17 +30,28 @@ function hooks(importFn: () => Promise, exportName: string): Hook { export const Hooks = { Accordion: hooks(() => import("corex/accordion"), "Accordion"), + AngleSlider: hooks(() => import("corex/angle-slider"), "AngleSlider"), + Avatar: hooks(() => import("corex/avatar"), "Avatar"), + Carousel: hooks(() => import("corex/carousel"), "Carousel"), Checkbox: hooks(() => import("corex/checkbox"), "Checkbox"), Clipboard: hooks(() => import("corex/clipboard"), "Clipboard"), Collapsible: hooks(() => import("corex/collapsible"), "Collapsible"), Combobox: hooks(() => import("corex/combobox"), "Combobox"), DatePicker: hooks(() => import("corex/date-picker"), "DatePicker"), Dialog: hooks(() => import("corex/dialog"), "Dialog"), + Editable: hooks(() => import("corex/editable"), "Editable"), + FloatingPanel: hooks(() => import("corex/floating-panel"), "FloatingPanel"), + Listbox: hooks(() => import("corex/listbox"), "Listbox"), Menu: hooks(() => import("corex/menu"), "Menu"), + NumberInput: hooks(() => import("corex/number-input"), "NumberInput"), + PasswordInput: hooks(() => import("corex/password-input"), "PasswordInput"), + PinInput: hooks(() => import("corex/pin-input"), "PinInput"), + RadioGroup: hooks(() => import("corex/radio-group"), "RadioGroup"), Select: hooks(() => import("corex/select"), "Select"), SignaturePad: hooks(() => import("corex/signature-pad"), "SignaturePad"), Switch: hooks(() => import("corex/switch"), "Switch"), Tabs: hooks(() => import("corex/tabs"), "Tabs"), + Timer: hooks(() => import("corex/timer"), "Timer"), Toast: hooks(() => import("corex/toast"), "Toast"), ToggleGroup: hooks(() => import("corex/toggle-group"), "ToggleGroup"), TreeView: hooks(() => import("corex/tree-view"), "TreeView"), diff --git a/assets/hooks/editable.ts b/assets/hooks/editable.ts new file mode 100644 index 0000000..ad3910c --- /dev/null +++ b/assets/hooks/editable.ts @@ -0,0 +1,84 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { Editable } from "../components/editable"; +import type { Props, ValueChangeDetails } from "@zag-js/editable"; +import { getString, getBoolean, getDir } from "../lib/util"; + +type EditableHookState = { + editable?: Editable; + handlers?: Array; +}; + +const EditableHook: Hook = { + mounted(this: object & HookInterface & EditableHookState) { + const el = this.el; + const value = getString(el, "value"); + const defaultValue = getString(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new Editable(el, { + id: el.id, + ...(controlled && value !== undefined ? { value } : { defaultValue: defaultValue ?? "" }), + disabled: getBoolean(el, "disabled"), + readOnly: getBoolean(el, "readOnly"), + required: getBoolean(el, "required"), + invalid: getBoolean(el, "invalid"), + name: getString(el, "name"), + form: getString(el, "form"), + dir: getDir(el), + ...(getBoolean(el, "controlledEdit") + ? { edit: getBoolean(el, "edit") } + : { defaultEdit: getBoolean(el, "defaultEdit") }), + onValueChange: (details: ValueChangeDetails) => { + const inputEl = el.querySelector('[data-scope="editable"][data-part="input"]') as { + value: string; + dispatchEvent: (e: Event) => boolean; + } | null; + if (inputEl) { + inputEl.value = details.value; + inputEl.dispatchEvent(new Event("input", { bubbles: true })); + inputEl.dispatchEvent(new Event("change", { bubbles: true })); + } + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { value: details.value, id: el.id }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.init(); + this.editable = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & EditableHookState) { + const value = getString(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + this.editable?.updateProps({ + id: this.el.id, + ...(controlled && value !== undefined ? { value } : {}), + disabled: getBoolean(this.el, "disabled"), + readOnly: getBoolean(this.el, "readOnly"), + required: getBoolean(this.el, "required"), + invalid: getBoolean(this.el, "invalid"), + name: getString(this.el, "name"), + form: getString(this.el, "form"), + } as Partial); + }, + + destroyed(this: object & HookInterface & EditableHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.editable?.destroy(); + }, +}; + +export { EditableHook as Editable }; diff --git a/assets/hooks/floating-panel.ts b/assets/hooks/floating-panel.ts new file mode 100644 index 0000000..e374c3d --- /dev/null +++ b/assets/hooks/floating-panel.ts @@ -0,0 +1,138 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { FloatingPanel } from "../components/floating-panel"; +import type { + Props, + OpenChangeDetails, + PositionChangeDetails, + SizeChangeDetails, + StageChangeDetails, +} from "@zag-js/floating-panel"; +import { getString, getBoolean, getDir } from "../lib/util"; + +type FloatingPanelHookState = { + floatingPanel?: FloatingPanel; + handlers?: Array; +}; + +function parseSize(val: string | undefined): { width: number; height: number } | undefined { + if (!val) return undefined; + try { + const parsed = JSON.parse(val) as { width?: number; height?: number }; + if (typeof parsed.width === "number" && typeof parsed.height === "number") { + return { width: parsed.width, height: parsed.height }; + } + } catch { + // + } + return undefined; +} + +function parsePoint(val: string | undefined): { x: number; y: number } | undefined { + if (!val) return undefined; + try { + const parsed = JSON.parse(val) as { x?: number; y?: number }; + if (typeof parsed.x === "number" && typeof parsed.y === "number") { + return { x: parsed.x, y: parsed.y }; + } + } catch { + // + } + return undefined; +} + +const FloatingPanelHook: Hook = { + mounted(this: object & HookInterface & FloatingPanelHookState) { + const el = this.el; + const open = getBoolean(el, "open"); + const defaultOpen = getBoolean(el, "defaultOpen"); + const controlled = getBoolean(el, "controlled"); + const size = parseSize(el.dataset.size); + const defaultSize = parseSize(el.dataset.defaultSize); + const position = parsePoint(el.dataset.position); + const defaultPosition = parsePoint(el.dataset.defaultPosition); + const zag = new FloatingPanel(el, { + id: el.id, + ...(controlled ? { open } : { defaultOpen }), + draggable: getBoolean(el, "draggable") !== false, + resizable: getBoolean(el, "resizable") !== false, + allowOverflow: getBoolean(el, "allowOverflow") !== false, + closeOnEscape: getBoolean(el, "closeOnEscape") !== false, + disabled: getBoolean(el, "disabled"), + dir: getDir(el), + size, + defaultSize, + position, + defaultPosition, + minSize: parseSize(el.dataset.minSize), + maxSize: parseSize(el.dataset.maxSize), + persistRect: getBoolean(el, "persistRect"), + gridSize: Number(el.dataset.gridSize) || 1, + onOpenChange: (details: OpenChangeDetails) => { + const eventName = getString(el, "onOpenChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { open: details.open, id: el.id }); + } + const clientName = getString(el, "onOpenChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + onPositionChange: (details: PositionChangeDetails) => { + const eventName = getString(el, "onPositionChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + position: details.position, + id: el.id, + }); + } + }, + onSizeChange: (details: SizeChangeDetails) => { + const eventName = getString(el, "onSizeChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + size: details.size, + id: el.id, + }); + } + }, + onStageChange: (details: StageChangeDetails) => { + const eventName = getString(el, "onStageChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + stage: details.stage, + id: el.id, + }); + } + }, + } as Props); + zag.init(); + this.floatingPanel = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & FloatingPanelHookState) { + const open = getBoolean(this.el, "open"); + const controlled = getBoolean(this.el, "controlled"); + this.floatingPanel?.updateProps({ + id: this.el.id, + ...(controlled ? { open } : {}), + disabled: getBoolean(this.el, "disabled"), + dir: getDir(this.el), + } as Partial); + }, + + destroyed(this: object & HookInterface & FloatingPanelHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.floatingPanel?.destroy(); + }, +}; + +export { FloatingPanelHook as FloatingPanel }; diff --git a/assets/hooks/listbox.ts b/assets/hooks/listbox.ts new file mode 100644 index 0000000..211b0e4 --- /dev/null +++ b/assets/hooks/listbox.ts @@ -0,0 +1,128 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { collection } from "@zag-js/listbox"; +import { Listbox } from "../components/listbox"; +import type { Props, ValueChangeDetails } from "@zag-js/listbox"; +import type { Direction } from "@zag-js/types"; +import { getString, getBoolean, getStringList } from "../lib/util"; + +type ListboxItem = { + id?: string; + value?: string; + label: string; + disabled?: boolean; + group?: string; +}; + +function buildCollection(items: ListboxItem[], hasGroups: boolean) { + if (hasGroups) { + return collection({ + items, + itemToValue: (item) => item.id ?? item.value ?? "", + itemToString: (item) => item.label, + isItemDisabled: (item) => !!item.disabled, + groupBy: (item) => item.group ?? "", + }); + } + return collection({ + items, + itemToValue: (item) => item.id ?? item.value ?? "", + itemToString: (item) => item.label, + isItemDisabled: (item) => !!item.disabled, + }); +} + +type ListboxHookState = { + listbox?: Listbox; + handlers?: Array; +}; + +const ListboxHook: Hook = { + mounted(this: object & HookInterface & ListboxHookState) { + const el = this.el; + const allItems = JSON.parse(el.dataset.collection ?? "[]") as ListboxItem[]; + const hasGroups = allItems.some((item) => item.group !== undefined); + const valueList = getStringList(el, "value"); + const defaultValueList = getStringList(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new Listbox(el, { + id: el.id, + collection: buildCollection(allItems, hasGroups), + ...(controlled && valueList + ? { value: valueList } + : { defaultValue: defaultValueList ?? [] }), + disabled: getBoolean(el, "disabled"), + dir: getString(el, "dir", ["ltr", "rtl"]), + orientation: getString<"horizontal" | "vertical">(el, "orientation", [ + "horizontal", + "vertical", + ]), + loopFocus: getBoolean(el, "loopFocus"), + selectionMode: getString<"single" | "multiple" | "extended">(el, "selectionMode", [ + "single", + "multiple", + "extended", + ]), + selectOnHighlight: getBoolean(el, "selectOnHighlight"), + deselectable: getBoolean(el, "deselectable"), + typeahead: getBoolean(el, "typeahead"), + onValueChange: (details: ValueChangeDetails) => { + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + items: details.items, + id: el.id, + }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.hasGroups = hasGroups; + zag.setOptions(allItems); + zag.init(); + + this.listbox = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & ListboxHookState) { + const newItems = JSON.parse(this.el.dataset.collection ?? "[]") as ListboxItem[]; + const hasGroups = newItems.some((item) => item.group !== undefined); + const valueList = getStringList(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + + if (this.listbox) { + this.listbox.hasGroups = hasGroups; + this.listbox.setOptions(newItems); + this.listbox.updateProps({ + collection: buildCollection(newItems, hasGroups), + id: this.el.id, + ...(controlled && valueList ? { value: valueList } : {}), + disabled: getBoolean(this.el, "disabled"), + dir: getString(this.el, "dir", ["ltr", "rtl"]), + orientation: getString<"horizontal" | "vertical">(this.el, "orientation", [ + "horizontal", + "vertical", + ]), + } as Partial>); + } + }, + + destroyed(this: object & HookInterface & ListboxHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.listbox?.destroy(); + }, +}; + +export { ListboxHook as Listbox }; diff --git a/assets/hooks/number-input.ts b/assets/hooks/number-input.ts new file mode 100644 index 0000000..1d38dd1 --- /dev/null +++ b/assets/hooks/number-input.ts @@ -0,0 +1,92 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { NumberInput } from "../components/number-input"; +import type { Props, ValueChangeDetails } from "@zag-js/number-input"; +import { getString, getBoolean, getNumber } from "../lib/util"; + +type NumberInputHookState = { + numberInput?: NumberInput; + handlers?: Array; +}; + +const NumberInputHook: Hook = { + mounted(this: object & HookInterface & NumberInputHookState) { + const el = this.el; + const valueStr = getString(el, "value"); + const defaultValueStr = getString(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new NumberInput(el, { + id: el.id, + ...(controlled && valueStr !== undefined + ? { value: valueStr } + : { defaultValue: defaultValueStr }), + min: getNumber(el, "min"), + max: getNumber(el, "max"), + step: getNumber(el, "step"), + disabled: getBoolean(el, "disabled"), + readOnly: getBoolean(el, "readOnly"), + invalid: getBoolean(el, "invalid"), + required: getBoolean(el, "required"), + allowMouseWheel: getBoolean(el, "allowMouseWheel"), + name: getString(el, "name"), + form: getString(el, "form"), + onValueChange: (details: ValueChangeDetails) => { + const inputEl = el.querySelector( + '[data-scope="number-input"][data-part="input"]' + ); + if (inputEl) { + inputEl.value = details.value; + inputEl.dispatchEvent(new Event("input", { bubbles: true })); + inputEl.dispatchEvent(new Event("change", { bubbles: true })); + } + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + valueAsNumber: details.valueAsNumber, + id: el.id, + }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.init(); + this.numberInput = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & NumberInputHookState) { + const valueStr = getString(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + this.numberInput?.updateProps({ + id: this.el.id, + ...(controlled && valueStr !== undefined ? { value: valueStr } : {}), + min: getNumber(this.el, "min"), + max: getNumber(this.el, "max"), + step: getNumber(this.el, "step"), + disabled: getBoolean(this.el, "disabled"), + readOnly: getBoolean(this.el, "readOnly"), + invalid: getBoolean(this.el, "invalid"), + required: getBoolean(this.el, "required"), + name: getString(this.el, "name"), + form: getString(this.el, "form"), + } as Partial); + }, + + destroyed(this: object & HookInterface & NumberInputHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.numberInput?.destroy(); + }, +}; + +export { NumberInputHook as NumberInput }; diff --git a/assets/hooks/password-input.ts b/assets/hooks/password-input.ts new file mode 100644 index 0000000..e9c733e --- /dev/null +++ b/assets/hooks/password-input.ts @@ -0,0 +1,76 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { PasswordInput } from "../components/password-input"; +import type { Props, VisibilityChangeDetails } from "@zag-js/password-input"; +import { getString, getBoolean, getDir } from "../lib/util"; + +type PasswordInputHookState = { + passwordInput?: PasswordInput; + handlers?: Array; +}; + +const PasswordInputHook: Hook = { + mounted(this: object & HookInterface & PasswordInputHookState) { + const el = this.el; + const zag = new PasswordInput(el, { + id: el.id, + ...(getBoolean(el, "controlledVisible") + ? { visible: getBoolean(el, "visible") } + : { defaultVisible: getBoolean(el, "defaultVisible") }), + disabled: getBoolean(el, "disabled"), + invalid: getBoolean(el, "invalid"), + readOnly: getBoolean(el, "readOnly"), + required: getBoolean(el, "required"), + ignorePasswordManagers: getBoolean(el, "ignorePasswordManagers"), + name: getString(el, "name"), + dir: getDir(el), + autoComplete: getString<"current-password" | "new-password">(el, "autoComplete", [ + "current-password", + "new-password", + ]), + onVisibilityChange: (details: VisibilityChangeDetails) => { + const eventName = getString(el, "onVisibilityChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { visible: details.visible, id: el.id }); + } + const clientName = getString(el, "onVisibilityChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.init(); + this.passwordInput = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & PasswordInputHookState) { + this.passwordInput?.updateProps({ + id: this.el.id, + ...(getBoolean(this.el, "controlledVisible") + ? { visible: getBoolean(this.el, "visible") } + : {}), + disabled: getBoolean(this.el, "disabled"), + invalid: getBoolean(this.el, "invalid"), + readOnly: getBoolean(this.el, "readOnly"), + required: getBoolean(this.el, "required"), + name: getString(this.el, "name"), + form: getString(this.el, "form"), + dir: getDir(this.el), + } as Partial); + }, + + destroyed(this: object & HookInterface & PasswordInputHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.passwordInput?.destroy(); + }, +}; + +export { PasswordInputHook as PasswordInput }; diff --git a/assets/hooks/pin-input.ts b/assets/hooks/pin-input.ts new file mode 100644 index 0000000..88e7a6b --- /dev/null +++ b/assets/hooks/pin-input.ts @@ -0,0 +1,109 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { PinInput } from "../components/pin-input"; +import type { Props, ValueChangeDetails } from "@zag-js/pin-input"; +import type { Direction } from "@zag-js/types"; +import { getString, getBoolean, getStringList, getNumber } from "../lib/util"; + +type PinInputHookState = { + pinInput?: PinInput; + handlers?: Array; +}; + +const PinInputHook: Hook = { + mounted(this: object & HookInterface & PinInputHookState) { + const el = this.el; + const valueList = getStringList(el, "value"); + const defaultValueList = getStringList(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new PinInput(el, { + id: el.id, + count: getNumber(el, "count") ?? 4, + ...(controlled && valueList + ? { value: valueList } + : { defaultValue: defaultValueList ?? [] }), + disabled: getBoolean(el, "disabled"), + invalid: getBoolean(el, "invalid"), + required: getBoolean(el, "required"), + readOnly: getBoolean(el, "readOnly"), + mask: getBoolean(el, "mask"), + otp: getBoolean(el, "otp"), + blurOnComplete: getBoolean(el, "blurOnComplete"), + selectOnFocus: getBoolean(el, "selectOnFocus"), + name: getString(el, "name"), + form: getString(el, "form"), + dir: getString(el, "dir", ["ltr", "rtl"]), + type: getString<"alphanumeric" | "numeric" | "alphabetic">(el, "type", [ + "alphanumeric", + "numeric", + "alphabetic", + ]), + placeholder: getString(el, "placeholder"), + onValueChange: (details: ValueChangeDetails) => { + const hiddenInput = el.querySelector( + '[data-scope="pin-input"][data-part="hidden-input"]' + ); + if (hiddenInput) { + hiddenInput.value = details.valueAsString; + hiddenInput.dispatchEvent(new Event("input", { bubbles: true })); + hiddenInput.dispatchEvent(new Event("change", { bubbles: true })); + } + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + valueAsString: details.valueAsString, + id: el.id, + }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + onValueComplete: (details: ValueChangeDetails) => { + const eventName = getString(el, "onValueComplete"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + valueAsString: details.valueAsString, + id: el.id, + }); + } + }, + } as Props); + zag.init(); + this.pinInput = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & PinInputHookState) { + const valueList = getStringList(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + this.pinInput?.updateProps({ + id: this.el.id, + count: getNumber(this.el, "count") ?? this.pinInput?.api.count ?? 4, + ...(controlled && valueList ? { value: valueList } : {}), + disabled: getBoolean(this.el, "disabled"), + invalid: getBoolean(this.el, "invalid"), + required: getBoolean(this.el, "required"), + readOnly: getBoolean(this.el, "readOnly"), + name: getString(this.el, "name"), + form: getString(this.el, "form"), + } as Partial); + }, + + destroyed(this: object & HookInterface & PinInputHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.pinInput?.destroy(); + }, +}; + +export { PinInputHook as PinInput }; diff --git a/assets/hooks/radio-group.ts b/assets/hooks/radio-group.ts new file mode 100644 index 0000000..f3a2fb8 --- /dev/null +++ b/assets/hooks/radio-group.ts @@ -0,0 +1,86 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { RadioGroup } from "../components/radio-group"; +import type { Props, ValueChangeDetails } from "@zag-js/radio-group"; +import type { Direction } from "@zag-js/types"; +import { getString, getBoolean } from "../lib/util"; + +type RadioGroupHookState = { + radioGroup?: RadioGroup; + handlers?: Array; +}; + +const RadioGroupHook: Hook = { + mounted(this: object & HookInterface & RadioGroupHookState) { + const el = this.el; + const value = getString(el, "value"); + const defaultValue = getString(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new RadioGroup(el, { + id: el.id, + ...(controlled && value !== undefined + ? { value: value ?? null } + : { defaultValue: defaultValue ?? null }), + name: getString(el, "name"), + form: getString(el, "form"), + disabled: getBoolean(el, "disabled"), + invalid: getBoolean(el, "invalid"), + required: getBoolean(el, "required"), + readOnly: getBoolean(el, "readOnly"), + dir: getString(el, "dir", ["ltr", "rtl"]), + orientation: getString<"horizontal" | "vertical">(el, "orientation", [ + "horizontal", + "vertical", + ]), + onValueChange: (details: ValueChangeDetails) => { + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + id: el.id, + }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id }, + }) + ); + } + }, + } as Props); + zag.init(); + this.radioGroup = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & RadioGroupHookState) { + const value = getString(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + this.radioGroup?.updateProps({ + id: this.el.id, + ...(controlled && value !== undefined ? { value: value ?? null } : {}), + name: getString(this.el, "name"), + form: getString(this.el, "form"), + disabled: getBoolean(this.el, "disabled"), + invalid: getBoolean(this.el, "invalid"), + required: getBoolean(this.el, "required"), + readOnly: getBoolean(this.el, "readOnly"), + orientation: getString<"horizontal" | "vertical">(this.el, "orientation", [ + "horizontal", + "vertical", + ]), + } as Partial); + }, + + destroyed(this: object & HookInterface & RadioGroupHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.radioGroup?.destroy(); + }, +}; + +export { RadioGroupHook as RadioGroup }; diff --git a/assets/hooks/timer.ts b/assets/hooks/timer.ts new file mode 100644 index 0000000..94af14e --- /dev/null +++ b/assets/hooks/timer.ts @@ -0,0 +1,64 @@ +import type { Hook } from "phoenix_live_view"; +import type { HookInterface, CallbackRef } from "phoenix_live_view/assets/js/types/view_hook"; +import { Timer } from "../components/timer"; +import type { Props, TickDetails } from "@zag-js/timer"; +import { getString, getBoolean, getNumber } from "../lib/util"; + +type TimerHookState = { + timer?: Timer; + handlers?: Array; +}; + +const TimerHook: Hook = { + mounted(this: object & HookInterface & TimerHookState) { + const el = this.el; + const zag = new Timer(el, { + id: el.id, + countdown: getBoolean(el, "countdown"), + startMs: getNumber(el, "startMs"), + targetMs: getNumber(el, "targetMs"), + autoStart: getBoolean(el, "autoStart"), + interval: getNumber(el, "interval") ?? 1000, + onTick: (details: TickDetails) => { + const eventName = getString(el, "onTick"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + time: details.time, + formattedTime: details.formattedTime, + id: el.id, + }); + } + }, + onComplete: () => { + const eventName = getString(el, "onComplete"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { id: el.id }); + } + }, + } as Props); + zag.init(); + this.timer = zag; + this.handlers = []; + }, + + updated(this: object & HookInterface & TimerHookState) { + this.timer?.updateProps({ + id: this.el.id, + countdown: getBoolean(this.el, "countdown"), + startMs: getNumber(this.el, "startMs"), + targetMs: getNumber(this.el, "targetMs"), + autoStart: getBoolean(this.el, "autoStart"), + interval: getNumber(this.el, "interval") ?? 1000, + } as Partial); + }, + + destroyed(this: object & HookInterface & TimerHookState) { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.timer?.destroy(); + }, +}; + +export { TimerHook as Timer }; diff --git a/config/config.exs b/config/config.exs index 8970681..1416e5d 100644 --- a/config/config.exs +++ b/config/config.exs @@ -12,7 +12,7 @@ config :phoenix, if Mix.env() == :dev do corex_externals = - ~w(accordion checkbox clipboard collapsible combobox date-picker dialog menu select signature-pad switch tabs toast toggle-group tree-view) + ~w(accordion angle-slider avatar carousel checkbox clipboard collapsible combobox date-picker dialog editable floating-panel listbox menu number-input password-input pin-input radio-group select signature-pad switch tabs timer toast toggle-group tree-view) |> Enum.map(fn name -> "--external:corex/#{name}" end) esbuild = fn args -> @@ -26,17 +26,28 @@ if Mix.env() == :dev do hooks_entries = ~w( ./hooks/accordion.ts + ./hooks/angle-slider.ts + ./hooks/avatar.ts + ./hooks/carousel.ts ./hooks/checkbox.ts ./hooks/clipboard.ts ./hooks/collapsible.ts ./hooks/combobox.ts ./hooks/date-picker.ts ./hooks/dialog.ts + ./hooks/editable.ts + ./hooks/floating-panel.ts + ./hooks/listbox.ts ./hooks/menu.ts + ./hooks/number-input.ts + ./hooks/password-input.ts + ./hooks/pin-input.ts + ./hooks/radio-group.ts ./hooks/select.ts ./hooks/signature-pad.ts ./hooks/switch.ts ./hooks/tabs.ts + ./hooks/timer.ts ./hooks/toast.ts ./hooks/toggle-group.ts ./hooks/tree-view.ts diff --git a/e2e/mix.exs b/e2e/mix.exs index a7e3d41..4a0db12 100644 --- a/e2e/mix.exs +++ b/e2e/mix.exs @@ -67,7 +67,7 @@ defmodule E2e.MixProject do {:jason, "~> 1.2"}, {:dns_cluster, "~> 0.2.0"}, {:bandit, "~> 1.5"}, - {:corex, "~> 0.1.0-alpha.22"}, + {:corex, path: "../../corex"}, {:wallaby, "~> 0.30", only: :test}, {:a11y_audit, "~> 0.3.1", only: :test}, {:flagpack, "~> 0.6.0"} diff --git a/package.json b/package.json index 3c1c3dc..6d13864 100644 --- a/package.json +++ b/package.json @@ -13,17 +13,28 @@ "require": "./priv/static/corex.cjs.js" }, "./accordion": "./priv/static/accordion.mjs", + "./angle-slider": "./priv/static/angle-slider.mjs", + "./avatar": "./priv/static/avatar.mjs", + "./carousel": "./priv/static/carousel.mjs", "./checkbox": "./priv/static/checkbox.mjs", "./clipboard": "./priv/static/clipboard.mjs", "./collapsible": "./priv/static/collapsible.mjs", "./combobox": "./priv/static/combobox.mjs", "./date-picker": "./priv/static/date-picker.mjs", "./dialog": "./priv/static/dialog.mjs", + "./editable": "./priv/static/editable.mjs", + "./floating-panel": "./priv/static/floating-panel.mjs", + "./listbox": "./priv/static/listbox.mjs", "./menu": "./priv/static/menu.mjs", + "./number-input": "./priv/static/number-input.mjs", + "./password-input": "./priv/static/password-input.mjs", + "./pin-input": "./priv/static/pin-input.mjs", + "./radio-group": "./priv/static/radio-group.mjs", "./select": "./priv/static/select.mjs", "./signature-pad": "./priv/static/signature-pad.mjs", "./switch": "./priv/static/switch.mjs", "./tabs": "./priv/static/tabs.mjs", + "./timer": "./priv/static/timer.mjs", "./toast": "./priv/static/toast.mjs", "./toggle-group": "./priv/static/toggle-group.mjs", "./tree-view": "./priv/static/tree-view.mjs" @@ -52,8 +63,10 @@ "@eslint/js": "^9.39.2", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", - "@zag-js/accordion": "^1.33.1", +"@zag-js/accordion": "^1.33.1", + "@zag-js/angle-slider": "^1.33.1", "@zag-js/avatar": "^1.33.1", + "@zag-js/carousel": "^1.33.1", "@zag-js/collection": "^1.33.1", "@zag-js/checkbox": "^1.33.1", "@zag-js/clipboard": "^1.33.1", @@ -61,13 +74,20 @@ "@zag-js/combobox": "^1.33.1", "@zag-js/date-picker": "^1.33.1", "@zag-js/dialog": "^1.33.1", + "@zag-js/editable": "^1.33.1", "@zag-js/floating-panel": "^1.33.1", - "@zag-js/menu": "^1.33.1", + "@zag-js/listbox": "^1.33.1", + "@zag-js/menu": "^1.33.1", + "@zag-js/number-input": "^1.33.1", + "@zag-js/password-input": "^1.33.1", + "@zag-js/pin-input": "^1.33.1", "@zag-js/popper": "^1.33.1", + "@zag-js/radio-group": "^1.33.1", "@zag-js/select": "^1.33.1", "@zag-js/signature-pad": "^1.33.1", "@zag-js/switch": "^1.33.1", "@zag-js/tabs": "^1.33.1", + "@zag-js/timer": "^1.33.1", "@zag-js/toast": "^1.33.1", "@zag-js/toggle-group": "^1.33.1", "@zag-js/tree-view": "^1.33.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 341012f..2d7728f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,15 @@ importers: '@zag-js/accordion': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/angle-slider': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/avatar': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/carousel': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/checkbox': specifier: ^1.33.1 version: 1.33.1 @@ -47,15 +53,33 @@ importers: '@zag-js/dialog': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/editable': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/floating-panel': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/listbox': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/menu': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/number-input': + specifier: ^1.33.1 + version: 1.33.1 + '@zag-js/password-input': + specifier: ^1.33.1 + version: 1.33.1 + '@zag-js/pin-input': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/popper': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/radio-group': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/select': specifier: ^1.33.1 version: 1.33.1 @@ -68,6 +92,9 @@ importers: '@zag-js/tabs': specifier: ^1.33.1 version: 1.33.1 + '@zag-js/timer': + specifier: ^1.33.1 + version: 1.33.1 '@zag-js/toast': specifier: ^1.33.1 version: 1.33.1 @@ -177,6 +204,9 @@ packages: '@internationalized/date@3.11.0': resolution: {integrity: sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q==} + '@internationalized/number@3.6.5': + resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==} + '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -255,12 +285,18 @@ packages: '@zag-js/anatomy@1.33.1': resolution: {integrity: sha512-iME14VHGGEPNMakilI6qvEkv9sll4AFZHpeoMLpczesw5hmqQjjNRifDTPR+idqCb8O8PdkAPE9hyMeP+4JjtA==} + '@zag-js/angle-slider@1.33.1': + resolution: {integrity: sha512-Y44IND5koNWD/EMKEWJbuEnzNW9y1WsrQFFvKRsMp/m3n60hiLa8qtZHoZWm8eOZCKFlsjVJ0gueEuZp43nobA==} + '@zag-js/aria-hidden@1.33.1': resolution: {integrity: sha512-TpRAtssDHVfra5qxigk7w1NMf/crKu615INu6GAbNNMUBWD1rPZAfxdg/xe/BAcxLy+XM5/q62dVSNvpjXzN3g==} '@zag-js/avatar@1.33.1': resolution: {integrity: sha512-D8HBPvIVLoty14CDx6wWfdfcalr/pf2FgJ0N7VTgExvZt8t64JWJarL75ZkIB3ROaNe4RMFdzabz1uc7BlcDyg==} + '@zag-js/carousel@1.33.1': + resolution: {integrity: sha512-FB72jCHhTTn0gXsWwDT/DrGMpBHQTxlKvwjEiBGkcprWVpptN0WGJR+EtX2Si/668sdH/471rew2DKA+h5k6Tw==} + '@zag-js/checkbox@1.33.1': resolution: {integrity: sha512-3rIPXB3O7hZukyjKpRAOn+Ob7jByBmDNU7wdpS2HRv7Urv9i5jUExlwayevw/a6JHQaT7mR1dL4culTyX+fJVA==} @@ -298,6 +334,9 @@ packages: '@zag-js/dom-query@1.33.1': resolution: {integrity: sha512-Iyl0D3nLvJuMkkuRy22xhj4pkzexUCDlRpCzqIrOMDKsmFka/WV9PIclZKVpMECTi9dEQmJuGTjBVaCOReLu+Q==} + '@zag-js/editable@1.33.1': + resolution: {integrity: sha512-uLLwopl5naET76ND+/GZDVMlXaAIwepAhmfNA+Esj4Upgtd3lpD5SNzJiVuyzZ0ewVyp2cuXHHAfNiibhkoFlA==} + '@zag-js/floating-panel@1.33.1': resolution: {integrity: sha512-MKtFyC3xxCUmHEnugR+KMcVIX7FdHsoZfDxcKc74h+2M6FAmk6YB8lByoY9pkCR9ems/5DkHcMU9cVVJ9kiFqA==} @@ -310,21 +349,39 @@ packages: '@zag-js/interact-outside@1.33.1': resolution: {integrity: sha512-XnqwYsGw0GVmjBpDziwWXKE/+KeZLgRnjEpyVr6HMATMGD+c4j6TmIbI9OGEaWliLuwvHdTclkmK4WYTaAGmiw==} + '@zag-js/listbox@1.33.1': + resolution: {integrity: sha512-8XT+6T82xG3BJwC7VYu/I1W8Hxyjgpke8tB1odQSWOV23pVXXPbol7wQbtoieSVeNDsZD8K12CpB40oRVrcSHA==} + '@zag-js/live-region@1.33.1': resolution: {integrity: sha512-KbU2wUSMd01fY7dgc9WhvU2x07FxNHKSCrn+fFUnB+Qoy6iiVv0A729JDbzPUUcpBV0BFoQ3qNdBDVyBalbpaQ==} '@zag-js/menu@1.33.1': resolution: {integrity: sha512-QihwaFCgGcrPbJSoP73nt749/rlUANiIrCU//8WWfQTgv0NBJprBD7d3banDNlK9ZSGmvELcpyQ/fKU4cfn0GQ==} + '@zag-js/number-input@1.33.1': + resolution: {integrity: sha512-5YKr8uagIDGXp3hIqo4IUBGxS5WhH0xM1CQf2zimfDWvBOng+Y+MH/4Lwu9wKuyIq/J3SJqsjO+2OOF7u6ju/g==} + + '@zag-js/password-input@1.33.1': + resolution: {integrity: sha512-pJrz50JhQLTfiatehATr40udJYggYmJ7V/7/dBKqthGpMwoaVV3bmtKFSenFGc2mMb5Rlf9KKqHO/dYB7jpNiA==} + + '@zag-js/pin-input@1.33.1': + resolution: {integrity: sha512-q6/DRsIV6ZDKzkFmdzbcsVBm7+I7hMlrsLr/P/jH0/fYE5T9t+1m9ll5j7/5RHFJHQ1WajHpdt5ad5mfXMuxKA==} + '@zag-js/popper@1.33.1': resolution: {integrity: sha512-DNKRh/SRXB2wcvVYK1wvcEufS4vfVXJOv23QUee761bTv4nrPNll5pZFsYEHatiCNkAmO0MRRYA2Sc6jk9nxNA==} + '@zag-js/radio-group@1.33.1': + resolution: {integrity: sha512-W/T8Hea3Z4mWCErm2fJc/EYabxRkKHFJStSClyllqknF3Y+b42MaKGuub1IcACO3pe6csLTkomdxy1qDLWl/dg==} + '@zag-js/rect-utils@1.33.1': resolution: {integrity: sha512-vCIgZF/z8oeYfUhGUgRiNEfOS8on4rUXi4vtL4IvHSdAv5VxZw4ODoLhIzRGT3BwsiMfr8qJ8fmrcR2oFRFQgA==} '@zag-js/remove-scroll@1.33.1': resolution: {integrity: sha512-5+Mvboqlmv8EdJoixAbGrftFVWZTznsVJn40BuB/6fYQeqdsZ2vFmSmSIr7btFOPcj3BcTMo0SbWNNta3fAOrg==} + '@zag-js/scroll-snap@1.33.1': + resolution: {integrity: sha512-GLEb+YJj800ia2zyTFxVZomQ1cFSShazUQ/1uAxX0Lj7+aZK88cZhIn7AI0+yBXTPBS0zrZDhBPsGEDQX+Q9Fw==} + '@zag-js/select@1.33.1': resolution: {integrity: sha512-eG+Ftdse0zvCAkXBMNZVBlM+KNvFRKHToxlxgid6wOd5QgRGwr4HaJuWaz908nBIZRYMFVvC+lLaygUVORHmGg==} @@ -340,6 +397,9 @@ packages: '@zag-js/tabs@1.33.1': resolution: {integrity: sha512-Xquhso7jUch9UrG5N+5vNfR8S2bWUk6EDpBBArY0X5oPSnlzgwJcjWh98hH1QyHX3JmWZN4kAfVKUxNdQxRnVw==} + '@zag-js/timer@1.33.1': + resolution: {integrity: sha512-GgqntefAEQbf66aNgA6NL9Rtrrxcd0/IJVddTj1/xihCnJ8u6AOU4syG5tie0Tpc2caDAntOwlYjpEy3n2AGcA==} + '@zag-js/toast@1.33.1': resolution: {integrity: sha512-kI2/VJcBQGgHpmuWiIDqPn8ejFEODh5YhjWbnvjGRG+x3XoPuMq6hhxXV6VWJslbZJtTmzxDcP+Xamdrf1hbZA==} @@ -818,6 +878,10 @@ snapshots: dependencies: '@swc/helpers': 0.5.18 + '@internationalized/number@3.6.5': + dependencies: + '@swc/helpers': 0.5.18 + '@pkgr/core@0.2.9': {} '@swc/helpers@0.5.18': @@ -929,6 +993,15 @@ snapshots: '@zag-js/anatomy@1.33.1': {} + '@zag-js/angle-slider@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/rect-utils': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/aria-hidden@1.33.1': dependencies: '@zag-js/dom-query': 1.33.1 @@ -941,6 +1014,15 @@ snapshots: '@zag-js/types': 1.33.1 '@zag-js/utils': 1.33.1 + '@zag-js/carousel@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/scroll-snap': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/checkbox@1.33.1': dependencies: '@zag-js/anatomy': 1.33.1 @@ -1026,6 +1108,15 @@ snapshots: dependencies: '@zag-js/types': 1.33.1 + '@zag-js/editable@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/interact-outside': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/floating-panel@1.33.1': dependencies: '@zag-js/anatomy': 1.33.1 @@ -1050,6 +1141,16 @@ snapshots: '@zag-js/dom-query': 1.33.1 '@zag-js/utils': 1.33.1 + '@zag-js/listbox@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/collection': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/focus-visible': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/live-region@1.33.1': {} '@zag-js/menu@1.33.1': @@ -1063,18 +1164,56 @@ snapshots: '@zag-js/types': 1.33.1 '@zag-js/utils': 1.33.1 + '@zag-js/number-input@1.33.1': + dependencies: + '@internationalized/number': 3.6.5 + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + + '@zag-js/password-input@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + + '@zag-js/pin-input@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/popper@1.33.1': dependencies: '@floating-ui/dom': 1.7.5 '@zag-js/dom-query': 1.33.1 '@zag-js/utils': 1.33.1 + '@zag-js/radio-group@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/focus-visible': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/rect-utils@1.33.1': {} '@zag-js/remove-scroll@1.33.1': dependencies: '@zag-js/dom-query': 1.33.1 + '@zag-js/scroll-snap@1.33.1': + dependencies: + '@zag-js/dom-query': 1.33.1 + '@zag-js/select@1.33.1': dependencies: '@zag-js/anatomy': 1.33.1 @@ -1116,6 +1255,14 @@ snapshots: '@zag-js/types': 1.33.1 '@zag-js/utils': 1.33.1 + '@zag-js/timer@1.33.1': + dependencies: + '@zag-js/anatomy': 1.33.1 + '@zag-js/core': 1.33.1 + '@zag-js/dom-query': 1.33.1 + '@zag-js/types': 1.33.1 + '@zag-js/utils': 1.33.1 + '@zag-js/toast@1.33.1': dependencies: '@zag-js/anatomy': 1.33.1 diff --git a/priv/static/accordion.mjs b/priv/static/accordion.mjs index c16d972..e6bd251 100644 --- a/priv/static/accordion.mjs +++ b/priv/static/accordion.mjs @@ -22,7 +22,7 @@ import { queryAll, remove, warn -} from "./chunk-GFGFZBBD.mjs"; +} from "./chunk-IXOYOLUJ.mjs"; // ../node_modules/.pnpm/@zag-js+accordion@1.33.1/node_modules/@zag-js/accordion/dist/index.mjs var anatomy = createAnatomy("accordion").parts("root", "item", "itemTrigger", "itemContent", "itemIndicator"); diff --git a/priv/static/angle-slider.mjs b/priv/static/angle-slider.mjs new file mode 100644 index 0000000..453a203 --- /dev/null +++ b/priv/static/angle-slider.mjs @@ -0,0 +1,524 @@ +import { + createRect, + getPointAngle +} from "./chunk-BMVNROAE.mjs"; +import { + Component, + VanillaMachine, + createAnatomy, + createMachine, + createProps, + createSplitProps, + dataAttr, + getBoolean, + getEventPoint, + getEventStep, + getNativeEvent, + getNumber, + getString, + isLeftClick, + normalizeProps, + raf, + setElementValue, + snapValueToStep, + trackPointerMove +} from "./chunk-IXOYOLUJ.mjs"; + +// ../node_modules/.pnpm/@zag-js+angle-slider@1.33.1/node_modules/@zag-js/angle-slider/dist/index.mjs +var anatomy = createAnatomy("angle-slider").parts( + "root", + "label", + "thumb", + "valueText", + "control", + "track", + "markerGroup", + "marker" +); +var parts = anatomy.build(); +var getRootId = (ctx) => ctx.ids?.root ?? `angle-slider:${ctx.id}`; +var getThumbId = (ctx) => ctx.ids?.thumb ?? `angle-slider:${ctx.id}:thumb`; +var getHiddenInputId = (ctx) => ctx.ids?.hiddenInput ?? `angle-slider:${ctx.id}:input`; +var getControlId = (ctx) => ctx.ids?.control ?? `angle-slider:${ctx.id}:control`; +var getValueTextId = (ctx) => ctx.ids?.valueText ?? `angle-slider:${ctx.id}:value-text`; +var getLabelId = (ctx) => ctx.ids?.label ?? `angle-slider:${ctx.id}:label`; +var getHiddenInputEl = (ctx) => ctx.getById(getHiddenInputId(ctx)); +var getControlEl = (ctx) => ctx.getById(getControlId(ctx)); +var getThumbEl = (ctx) => ctx.getById(getThumbId(ctx)); +var MIN_VALUE = 0; +var MAX_VALUE = 359; +function getAngle(controlEl, point, angularOffset) { + const rect = createRect(controlEl.getBoundingClientRect()); + const angle = getPointAngle(rect, point); + if (angularOffset != null) { + return angle - angularOffset; + } + return angle; +} +function clampAngle(degree) { + return Math.min(Math.max(degree, MIN_VALUE), MAX_VALUE); +} +function constrainAngle(degree, step) { + const clampedDegree = clampAngle(degree); + const upperStep = Math.ceil(clampedDegree / step); + const nearestStep = Math.round(clampedDegree / step); + return upperStep >= clampedDegree / step ? upperStep * step === MAX_VALUE ? MIN_VALUE : upperStep * step : nearestStep * step; +} +function snapAngleToStep(value, step) { + return snapValueToStep(value, MIN_VALUE, MAX_VALUE, step); +} +function connect(service, normalize) { + const { state, send, context, prop, computed, scope } = service; + const dragging = state.matches("dragging"); + const value = context.get("value"); + const valueAsDegree = computed("valueAsDegree"); + const disabled = prop("disabled"); + const invalid = prop("invalid"); + const readOnly = prop("readOnly"); + const interactive = computed("interactive"); + const ariaLabel = prop("aria-label"); + const ariaLabelledBy = prop("aria-labelledby"); + return { + value, + valueAsDegree, + dragging, + setValue(value2) { + send({ type: "VALUE.SET", value: value2 }); + }, + getRootProps() { + return normalize.element({ + ...parts.root.attrs, + id: getRootId(scope), + "data-disabled": dataAttr(disabled), + "data-invalid": dataAttr(invalid), + "data-readonly": dataAttr(readOnly), + style: { + "--value": value, + "--angle": valueAsDegree + } + }); + }, + getLabelProps() { + return normalize.label({ + ...parts.label.attrs, + id: getLabelId(scope), + htmlFor: getHiddenInputId(scope), + "data-disabled": dataAttr(disabled), + "data-invalid": dataAttr(invalid), + "data-readonly": dataAttr(readOnly), + onClick(event) { + if (!interactive) return; + event.preventDefault(); + getThumbEl(scope)?.focus(); + } + }); + }, + getHiddenInputProps() { + return normalize.element({ + type: "hidden", + value, + name: prop("name"), + id: getHiddenInputId(scope) + }); + }, + getControlProps() { + return normalize.element({ + ...parts.control.attrs, + role: "presentation", + id: getControlId(scope), + "data-disabled": dataAttr(disabled), + "data-invalid": dataAttr(invalid), + "data-readonly": dataAttr(readOnly), + onPointerDown(event) { + if (!interactive) return; + if (!isLeftClick(event)) return; + const point = getEventPoint(event); + const controlEl = event.currentTarget; + const thumbEl = getThumbEl(scope); + const composedPath = getNativeEvent(event).composedPath(); + const isOverThumb = thumbEl && composedPath.includes(thumbEl); + let angularOffset = null; + if (isOverThumb) { + const clickAngle = getAngle(controlEl, point); + angularOffset = clickAngle - value; + } + send({ type: "CONTROL.POINTER_DOWN", point, angularOffset }); + event.stopPropagation(); + }, + style: { + touchAction: "none", + userSelect: "none", + WebkitUserSelect: "none" + } + }); + }, + getThumbProps() { + return normalize.element({ + ...parts.thumb.attrs, + id: getThumbId(scope), + role: "slider", + "aria-label": ariaLabel, + "aria-labelledby": ariaLabelledBy ?? getLabelId(scope), + "aria-valuemax": 360, + "aria-valuemin": 0, + "aria-valuenow": value, + tabIndex: readOnly || interactive ? 0 : void 0, + "data-disabled": dataAttr(disabled), + "data-invalid": dataAttr(invalid), + "data-readonly": dataAttr(readOnly), + onFocus() { + send({ type: "THUMB.FOCUS" }); + }, + onBlur() { + send({ type: "THUMB.BLUR" }); + }, + onKeyDown(event) { + if (!interactive) return; + const step = getEventStep(event) * prop("step"); + switch (event.key) { + case "ArrowLeft": + case "ArrowUp": + event.preventDefault(); + send({ type: "THUMB.ARROW_DEC", step }); + break; + case "ArrowRight": + case "ArrowDown": + event.preventDefault(); + send({ type: "THUMB.ARROW_INC", step }); + break; + case "Home": + event.preventDefault(); + send({ type: "THUMB.HOME" }); + break; + case "End": + event.preventDefault(); + send({ type: "THUMB.END" }); + break; + } + }, + style: { + rotate: `var(--angle)` + } + }); + }, + getValueTextProps() { + return normalize.element({ + ...parts.valueText.attrs, + id: getValueTextId(scope) + }); + }, + getMarkerGroupProps() { + return normalize.element({ + ...parts.markerGroup.attrs + }); + }, + getMarkerProps(props2) { + let markerState; + if (props2.value < value) { + markerState = "under-value"; + } else if (props2.value > value) { + markerState = "over-value"; + } else { + markerState = "at-value"; + } + return normalize.element({ + ...parts.marker.attrs, + "data-value": props2.value, + "data-state": markerState, + "data-disabled": dataAttr(disabled), + style: { + "--marker-value": props2.value, + rotate: `calc(var(--marker-value) * 1deg)` + } + }); + } + }; +} +var machine = createMachine({ + props({ props: props2 }) { + return { + step: 1, + defaultValue: 0, + ...props2 + }; + }, + context({ prop, bindable }) { + return { + value: bindable(() => ({ + defaultValue: prop("defaultValue"), + value: prop("value"), + onChange(value) { + prop("onValueChange")?.({ value, valueAsDegree: `${value}deg` }); + } + })) + }; + }, + refs() { + return { + thumbDragOffset: null + }; + }, + computed: { + interactive: ({ prop }) => !(prop("disabled") || prop("readOnly")), + valueAsDegree: ({ context }) => `${context.get("value")}deg` + }, + watch({ track, context, action }) { + track([() => context.get("value")], () => { + action(["syncInputElement"]); + }); + }, + initialState() { + return "idle"; + }, + on: { + "VALUE.SET": { + actions: ["setValue"] + } + }, + states: { + idle: { + on: { + "CONTROL.POINTER_DOWN": { + target: "dragging", + actions: ["setThumbDragOffset", "setPointerValue", "focusThumb"] + }, + "THUMB.FOCUS": { + target: "focused" + } + } + }, + focused: { + on: { + "CONTROL.POINTER_DOWN": { + target: "dragging", + actions: ["setThumbDragOffset", "setPointerValue", "focusThumb"] + }, + "THUMB.ARROW_DEC": { + actions: ["decrementValue", "invokeOnChangeEnd"] + }, + "THUMB.ARROW_INC": { + actions: ["incrementValue", "invokeOnChangeEnd"] + }, + "THUMB.HOME": { + actions: ["setValueToMin", "invokeOnChangeEnd"] + }, + "THUMB.END": { + actions: ["setValueToMax", "invokeOnChangeEnd"] + }, + "THUMB.BLUR": { + target: "idle" + } + } + }, + dragging: { + entry: ["focusThumb"], + effects: ["trackPointerMove"], + on: { + "DOC.POINTER_UP": { + target: "focused", + actions: ["invokeOnChangeEnd", "clearThumbDragOffset"] + }, + "DOC.POINTER_MOVE": { + actions: ["setPointerValue"] + } + } + } + }, + implementations: { + effects: { + trackPointerMove({ scope, send }) { + return trackPointerMove(scope.getDoc(), { + onPointerMove(info) { + send({ type: "DOC.POINTER_MOVE", point: info.point }); + }, + onPointerUp() { + send({ type: "DOC.POINTER_UP" }); + } + }); + } + }, + actions: { + syncInputElement({ scope, context }) { + const inputEl = getHiddenInputEl(scope); + setElementValue(inputEl, context.get("value").toString()); + }, + invokeOnChangeEnd({ context, prop, computed }) { + prop("onValueChangeEnd")?.({ + value: context.get("value"), + valueAsDegree: computed("valueAsDegree") + }); + }, + setPointerValue({ scope, event, context, prop, refs }) { + const controlEl = getControlEl(scope); + if (!controlEl) return; + const angularOffset = refs.get("thumbDragOffset"); + const deg = getAngle(controlEl, event.point, angularOffset); + context.set("value", constrainAngle(deg, prop("step"))); + }, + setValueToMin({ context }) { + context.set("value", MIN_VALUE); + }, + setValueToMax({ context }) { + context.set("value", MAX_VALUE); + }, + setValue({ context, event }) { + context.set("value", clampAngle(event.value)); + }, + decrementValue({ context, event, prop }) { + const value = snapAngleToStep(context.get("value") - event.step, event.step ?? prop("step")); + context.set("value", value); + }, + incrementValue({ context, event, prop }) { + const value = snapAngleToStep(context.get("value") + event.step, event.step ?? prop("step")); + context.set("value", value); + }, + focusThumb({ scope }) { + raf(() => { + getThumbEl(scope)?.focus({ preventScroll: true }); + }); + }, + setThumbDragOffset({ refs, event }) { + refs.set("thumbDragOffset", event.angularOffset ?? null); + }, + clearThumbDragOffset({ refs }) { + refs.set("thumbDragOffset", null); + } + } + } +}); +var props = createProps()([ + "aria-label", + "aria-labelledby", + "dir", + "disabled", + "getRootNode", + "id", + "ids", + "invalid", + "name", + "onValueChange", + "onValueChangeEnd", + "readOnly", + "step", + "value", + "defaultValue" +]); +var splitProps = createSplitProps(props); + +// components/angle-slider.ts +var AngleSlider = class extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props2) { + return new VanillaMachine(machine, props2); + } + initApi() { + return connect(this.machine.service, normalizeProps); + } + render() { + const rootEl = this.el.querySelector('[data-scope="angle-slider"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + const labelEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="label"]' + ); + if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps()); + const hiddenInputEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="hidden-input"]' + ); + if (hiddenInputEl) this.spreadProps(hiddenInputEl, this.api.getHiddenInputProps()); + const controlEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + const thumbEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="thumb"]' + ); + if (thumbEl) this.spreadProps(thumbEl, this.api.getThumbProps()); + const valueTextEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="value-text"]' + ); + if (valueTextEl) this.spreadProps(valueTextEl, this.api.getValueTextProps()); + const markerGroupEl = this.el.querySelector( + '[data-scope="angle-slider"][data-part="marker-group"]' + ); + if (markerGroupEl) this.spreadProps(markerGroupEl, this.api.getMarkerGroupProps()); + this.el.querySelectorAll('[data-scope="angle-slider"][data-part="marker"]').forEach((markerEl) => { + const valueStr = markerEl.dataset.value; + if (valueStr == null) return; + const value = Number(valueStr); + if (Number.isNaN(value)) return; + this.spreadProps(markerEl, this.api.getMarkerProps({ value })); + }); + } +}; + +// hooks/angle-slider.ts +var AngleSliderHook = { + mounted() { + const el = this.el; + const value = getNumber(el, "value"); + const defaultValue = getNumber(el, "defaultValue"); + const controlled = getBoolean(el, "controlled"); + const zag = new AngleSlider(el, { + id: el.id, + ...controlled && value !== void 0 ? { value } : { defaultValue: defaultValue ?? 0 }, + step: getNumber(el, "step") ?? 1, + disabled: getBoolean(el, "disabled"), + readOnly: getBoolean(el, "readOnly"), + invalid: getBoolean(el, "invalid"), + name: getString(el, "name"), + dir: getString(el, "dir", ["ltr", "rtl"]), + onValueChange: (details) => { + const hiddenInput = el.querySelector( + '[data-scope="angle-slider"][data-part="hidden-input"]' + ); + if (hiddenInput) { + hiddenInput.value = String(details.value); + hiddenInput.dispatchEvent(new Event("input", { bubbles: true })); + hiddenInput.dispatchEvent(new Event("change", { bubbles: true })); + } + const eventName = getString(el, "onValueChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + value: details.value, + valueAsDegree: details.valueAsDegree, + id: el.id + }); + } + const clientName = getString(el, "onValueChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id } + }) + ); + } + } + }); + zag.init(); + this.angleSlider = zag; + this.handlers = []; + }, + updated() { + const value = getNumber(this.el, "value"); + const controlled = getBoolean(this.el, "controlled"); + this.angleSlider?.updateProps({ + id: this.el.id, + ...controlled && value !== void 0 ? { value } : {}, + step: getNumber(this.el, "step") ?? 1, + disabled: getBoolean(this.el, "disabled"), + readOnly: getBoolean(this.el, "readOnly"), + invalid: getBoolean(this.el, "invalid"), + name: getString(this.el, "name") + }); + }, + destroyed() { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.angleSlider?.destroy(); + } +}; +export { + AngleSliderHook as AngleSlider +}; diff --git a/priv/static/avatar.mjs b/priv/static/avatar.mjs new file mode 100644 index 0000000..8db3a0e --- /dev/null +++ b/priv/static/avatar.mjs @@ -0,0 +1,229 @@ +import { + Component, + VanillaMachine, + createAnatomy, + createMachine, + createProps, + createSplitProps, + getString, + normalizeProps, + observeAttributes, + observeChildren +} from "./chunk-IXOYOLUJ.mjs"; + +// ../node_modules/.pnpm/@zag-js+avatar@1.33.1/node_modules/@zag-js/avatar/dist/index.mjs +var anatomy = createAnatomy("avatar").parts("root", "image", "fallback"); +var parts = anatomy.build(); +var getRootId = (ctx) => ctx.ids?.root ?? `avatar:${ctx.id}`; +var getImageId = (ctx) => ctx.ids?.image ?? `avatar:${ctx.id}:image`; +var getFallbackId = (ctx) => ctx.ids?.fallback ?? `avatar:${ctx.id}:fallback`; +var getRootEl = (ctx) => ctx.getById(getRootId(ctx)); +var getImageEl = (ctx) => ctx.getById(getImageId(ctx)); +function connect(service, normalize) { + const { state, send, prop, scope } = service; + const loaded = state.matches("loaded"); + return { + loaded, + setSrc(src) { + const img = getImageEl(scope); + img?.setAttribute("src", src); + }, + setLoaded() { + send({ type: "img.loaded", src: "api" }); + }, + setError() { + send({ type: "img.error", src: "api" }); + }, + getRootProps() { + return normalize.element({ + ...parts.root.attrs, + dir: prop("dir"), + id: getRootId(scope) + }); + }, + getImageProps() { + return normalize.img({ + ...parts.image.attrs, + hidden: !loaded, + dir: prop("dir"), + id: getImageId(scope), + "data-state": loaded ? "visible" : "hidden", + onLoad() { + send({ type: "img.loaded", src: "element" }); + }, + onError() { + send({ type: "img.error", src: "element" }); + } + }); + }, + getFallbackProps() { + return normalize.element({ + ...parts.fallback.attrs, + dir: prop("dir"), + id: getFallbackId(scope), + hidden: loaded, + "data-state": loaded ? "hidden" : "visible" + }); + } + }; +} +var machine = createMachine({ + initialState() { + return "loading"; + }, + effects: ["trackImageRemoval", "trackSrcChange"], + on: { + "src.change": { + target: "loading" + }, + "img.unmount": { + target: "error" + } + }, + states: { + loading: { + entry: ["checkImageStatus"], + on: { + "img.loaded": { + target: "loaded", + actions: ["invokeOnLoad"] + }, + "img.error": { + target: "error", + actions: ["invokeOnError"] + } + } + }, + error: { + on: { + "img.loaded": { + target: "loaded", + actions: ["invokeOnLoad"] + } + } + }, + loaded: { + on: { + "img.error": { + target: "error", + actions: ["invokeOnError"] + } + } + } + }, + implementations: { + actions: { + invokeOnLoad({ prop }) { + prop("onStatusChange")?.({ status: "loaded" }); + }, + invokeOnError({ prop }) { + prop("onStatusChange")?.({ status: "error" }); + }, + checkImageStatus({ send, scope }) { + const imageEl = getImageEl(scope); + if (!imageEl?.complete) return; + const type = hasLoaded(imageEl) ? "img.loaded" : "img.error"; + send({ type, src: "ssr" }); + } + }, + effects: { + trackImageRemoval({ send, scope }) { + const rootEl = getRootEl(scope); + return observeChildren(rootEl, { + callback(records) { + const removedNodes = Array.from(records[0].removedNodes); + const removed = removedNodes.find( + (node) => node.nodeType === Node.ELEMENT_NODE && node.matches("[data-scope=avatar][data-part=image]") + ); + if (removed) { + send({ type: "img.unmount" }); + } + } + }); + }, + trackSrcChange({ send, scope }) { + const imageEl = getImageEl(scope); + return observeAttributes(imageEl, { + attributes: ["src", "srcset"], + callback() { + send({ type: "src.change" }); + } + }); + } + } + } +}); +function hasLoaded(image) { + return image.complete && image.naturalWidth !== 0 && image.naturalHeight !== 0; +} +var props = createProps()(["dir", "id", "ids", "onStatusChange", "getRootNode"]); +var splitProps = createSplitProps(props); + +// components/avatar.ts +var Avatar = class extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props2) { + return new VanillaMachine(machine, props2); + } + initApi() { + return connect(this.machine.service, normalizeProps); + } + render() { + const rootEl = this.el.querySelector('[data-scope="avatar"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + const imageEl = this.el.querySelector('[data-scope="avatar"][data-part="image"]'); + if (imageEl) this.spreadProps(imageEl, this.api.getImageProps()); + const fallbackEl = this.el.querySelector( + '[data-scope="avatar"][data-part="fallback"]' + ); + if (fallbackEl) this.spreadProps(fallbackEl, this.api.getFallbackProps()); + } +}; + +// hooks/avatar.ts +var AvatarHook = { + mounted() { + const el = this.el; + const src = getString(el, "src"); + const zag = new Avatar(el, { + id: el.id, + onStatusChange: (details) => { + const eventName = getString(el, "onStatusChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { status: details.status, id: el.id }); + } + const clientName = getString(el, "onStatusChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id } + }) + ); + } + }, + ...src !== void 0 ? {} : {} + }); + zag.init(); + this.avatar = zag; + if (src !== void 0) { + zag.api.setSrc(src); + } + this.handlers = []; + }, + updated() { + const src = getString(this.el, "src"); + if (src !== void 0 && this.avatar) { + this.avatar.api.setSrc(src); + } + }, + destroyed() { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.avatar?.destroy(); + } +}; +export { + AvatarHook as Avatar +}; diff --git a/priv/static/carousel.mjs b/priv/static/carousel.mjs new file mode 100644 index 0000000..90616c9 --- /dev/null +++ b/priv/static/carousel.mjs @@ -0,0 +1,1129 @@ +import { + Component, + VanillaMachine, + add, + addDomEvent, + ariaAttr, + callAll, + clampValue, + contains, + createAnatomy, + createMachine, + createProps, + createSplitProps, + dataAttr, + ensureProps, + getBoolean, + getComputedStyle as getComputedStyle2, + getDir, + getEventKey, + getEventTarget, + getNumber, + getString, + getTabbables, + isFocusable, + isLeftClick, + isObject, + nextIndex, + normalizeProps, + prevIndex, + queryAll, + raf, + remove, + resizeObserverBorderBox, + throttle, + trackPointerMove, + uniq +} from "./chunk-IXOYOLUJ.mjs"; + +// ../node_modules/.pnpm/@zag-js+scroll-snap@1.33.1/node_modules/@zag-js/scroll-snap/dist/index.mjs +var getDirection = (element) => getComputedStyle2(element).direction; +function getScrollPadding(element) { + const style = getComputedStyle2(element); + const rect = element.getBoundingClientRect(); + let xBeforeRaw = style.getPropertyValue("scroll-padding-left").replace("auto", "0px"); + let yBeforeRaw = style.getPropertyValue("scroll-padding-top").replace("auto", "0px"); + let xAfterRaw = style.getPropertyValue("scroll-padding-right").replace("auto", "0px"); + let yAfterRaw = style.getPropertyValue("scroll-padding-bottom").replace("auto", "0px"); + function convert(raw, size) { + let n = parseFloat(raw); + if (/%/.test(raw)) { + n /= 100; + n *= size; + } + return Number.isNaN(n) ? 0 : n; + } + let xBefore = convert(xBeforeRaw, rect.width); + let yBefore = convert(yBeforeRaw, rect.height); + let xAfter = convert(xAfterRaw, rect.width); + let yAfter = convert(yAfterRaw, rect.height); + return { + x: { before: xBefore, after: xAfter }, + y: { before: yBefore, after: yAfter } + }; +} +function isRectIntersecting(a, b, axis = "both") { + return axis === "x" && a.right >= b.left && a.left <= b.right || axis === "y" && a.bottom >= b.top && a.top <= b.bottom || axis === "both" && a.right >= b.left && a.left <= b.right && a.bottom >= b.top && a.top <= b.bottom; +} +function getDescendants(parent) { + let children = []; + for (const child of parent.children) { + children = children.concat(child, getDescendants(child)); + } + return children; +} +function getSnapPositions(parent, subtree = false) { + const parentRect = parent.getBoundingClientRect(); + const dir = getDirection(parent); + const isRtl = dir === "rtl"; + const positions = { + x: { start: [], center: [], end: [] }, + y: { start: [], center: [], end: [] } + }; + const children = subtree ? getDescendants(parent) : parent.children; + for (const axis of ["x", "y"]) { + const orthogonalAxis = axis === "x" ? "y" : "x"; + const axisStart = axis === "x" ? "left" : "top"; + const axisEnd = axis === "x" ? "right" : "bottom"; + const axisSize = axis === "x" ? "width" : "height"; + const axisScroll = axis === "x" ? "scrollLeft" : "scrollTop"; + const useRtlCalc = isRtl && axis === "x"; + for (const child of children) { + const childRect = child.getBoundingClientRect(); + if (!isRectIntersecting(parentRect, childRect, orthogonalAxis)) { + continue; + } + const childStyle = getComputedStyle2(child); + let [childAlignY, childAlignX] = childStyle.getPropertyValue("scroll-snap-align").split(" "); + if (typeof childAlignX === "undefined") { + childAlignX = childAlignY; + } + const childAlign = axis === "x" ? childAlignX : childAlignY; + let childOffsetStart; + let childOffsetEnd; + let childOffsetCenter; + if (useRtlCalc) { + const scrollOffset = Math.abs(parent[axisScroll]); + const rightOffset = parentRect[axisEnd] - childRect[axisEnd] + scrollOffset; + childOffsetStart = rightOffset; + childOffsetEnd = rightOffset + childRect[axisSize]; + childOffsetCenter = rightOffset + childRect[axisSize] / 2; + } else { + childOffsetStart = childRect[axisStart] - parentRect[axisStart] + parent[axisScroll]; + childOffsetEnd = childOffsetStart + childRect[axisSize]; + childOffsetCenter = childOffsetStart + childRect[axisSize] / 2; + } + switch (childAlign) { + case "none": + break; + case "start": + positions[axis].start.push({ node: child, position: childOffsetStart }); + break; + case "center": + positions[axis].center.push({ node: child, position: childOffsetCenter }); + break; + case "end": + positions[axis].end.push({ node: child, position: childOffsetEnd }); + break; + } + } + } + return positions; +} +function getScrollSnapPositions(element) { + const dir = getDirection(element); + const rect = element.getBoundingClientRect(); + const scrollPadding = getScrollPadding(element); + const snapPositions = getSnapPositions(element); + const maxScroll = { + x: element.scrollWidth - element.offsetWidth, + y: element.scrollHeight - element.offsetHeight + }; + const isRtl = dir === "rtl"; + const usesNegativeScrollLeft = isRtl && element.scrollLeft <= 0; + let xPositions; + if (isRtl) { + xPositions = uniq2( + [ + ...snapPositions.x.start.map((v) => v.position - scrollPadding.x.after), + ...snapPositions.x.center.map((v) => v.position - rect.width / 2), + ...snapPositions.x.end.map((v) => v.position - rect.width + scrollPadding.x.before) + ].map(clamp(0, maxScroll.x)) + ); + if (usesNegativeScrollLeft) { + xPositions = xPositions.map((pos) => -pos); + } + } else { + xPositions = uniq2( + [ + ...snapPositions.x.start.map((v) => v.position - scrollPadding.x.before), + ...snapPositions.x.center.map((v) => v.position - rect.width / 2), + ...snapPositions.x.end.map((v) => v.position - rect.width + scrollPadding.x.after) + ].map(clamp(0, maxScroll.x)) + ); + } + return { + x: xPositions, + y: uniq2( + [ + ...snapPositions.y.start.map((v) => v.position - scrollPadding.y.before), + ...snapPositions.y.center.map((v) => v.position - rect.height / 2), + ...snapPositions.y.end.map((v) => v.position - rect.height + scrollPadding.y.after) + ].map(clamp(0, maxScroll.y)) + ) + }; +} +function findSnapPoint(parent, axis, predicate) { + const dir = getDirection(parent); + const scrollPadding = getScrollPadding(parent); + const snapPositions = getSnapPositions(parent); + const items = [...snapPositions[axis].start, ...snapPositions[axis].center, ...snapPositions[axis].end]; + const isRtl = dir === "rtl"; + const usesNegativeScrollLeft = isRtl && axis === "x" && parent.scrollLeft <= 0; + for (const item of items) { + if (predicate(item.node)) { + let position; + if (axis === "x" && isRtl) { + position = item.position - scrollPadding.x.after; + if (usesNegativeScrollLeft) { + position = -position; + } + } else { + position = item.position - (axis === "x" ? scrollPadding.x.before : scrollPadding.y.before); + } + return position; + } + } +} +var uniq2 = (arr) => [...new Set(arr)]; +var clamp = (min, max) => (value) => Math.max(min, Math.min(max, value)); + +// ../node_modules/.pnpm/@zag-js+carousel@1.33.1/node_modules/@zag-js/carousel/dist/index.mjs +var anatomy = createAnatomy("carousel").parts( + "root", + "itemGroup", + "item", + "control", + "nextTrigger", + "prevTrigger", + "indicatorGroup", + "indicator", + "autoplayTrigger", + "progressText" +); +var parts = anatomy.build(); +var getRootId = (ctx) => ctx.ids?.root ?? `carousel:${ctx.id}`; +var getItemId = (ctx, index) => ctx.ids?.item?.(index) ?? `carousel:${ctx.id}:item:${index}`; +var getItemGroupId = (ctx) => ctx.ids?.itemGroup ?? `carousel:${ctx.id}:item-group`; +var getNextTriggerId = (ctx) => ctx.ids?.nextTrigger ?? `carousel:${ctx.id}:next-trigger`; +var getPrevTriggerId = (ctx) => ctx.ids?.prevTrigger ?? `carousel:${ctx.id}:prev-trigger`; +var getIndicatorGroupId = (ctx) => ctx.ids?.indicatorGroup ?? `carousel:${ctx.id}:indicator-group`; +var getIndicatorId = (ctx, index) => ctx.ids?.indicator?.(index) ?? `carousel:${ctx.id}:indicator:${index}`; +var getItemGroupEl = (ctx) => ctx.getById(getItemGroupId(ctx)); +var getItemEls = (ctx) => queryAll(getItemGroupEl(ctx), `[data-part=item]`); +var getIndicatorEl = (ctx, page) => ctx.getById(getIndicatorId(ctx, page)); +var syncTabIndex = (ctx) => { + const el = getItemGroupEl(ctx); + if (!el) return; + const tabbables = getTabbables(el); + el.setAttribute("tabindex", tabbables.length > 0 ? "-1" : "0"); +}; +function connect(service, normalize) { + const { state, context, computed, send, scope, prop } = service; + const isPlaying = state.matches("autoplay"); + const isDragging = state.matches("dragging"); + const canScrollNext = computed("canScrollNext"); + const canScrollPrev = computed("canScrollPrev"); + const horizontal = computed("isHorizontal"); + const autoSize = prop("autoSize"); + const pageSnapPoints = Array.from(context.get("pageSnapPoints")); + const page = context.get("page"); + const slidesPerPage = prop("slidesPerPage"); + const padding = prop("padding"); + const translations = prop("translations"); + return { + isPlaying, + isDragging, + page, + pageSnapPoints, + canScrollNext, + canScrollPrev, + getProgress() { + return page / pageSnapPoints.length; + }, + getProgressText() { + const details = { page: page + 1, totalPages: pageSnapPoints.length }; + return translations.progressText?.(details) ?? ""; + }, + scrollToIndex(index, instant) { + send({ type: "INDEX.SET", index, instant }); + }, + scrollTo(index, instant) { + send({ type: "PAGE.SET", index, instant }); + }, + scrollNext(instant) { + send({ type: "PAGE.NEXT", instant }); + }, + scrollPrev(instant) { + send({ type: "PAGE.PREV", instant }); + }, + play() { + send({ type: "AUTOPLAY.START" }); + }, + pause() { + send({ type: "AUTOPLAY.PAUSE" }); + }, + isInView(index) { + return Array.from(context.get("slidesInView")).includes(index); + }, + refresh() { + send({ type: "SNAP.REFRESH" }); + }, + getRootProps() { + return normalize.element({ + ...parts.root.attrs, + id: getRootId(scope), + role: "region", + "aria-roledescription": "carousel", + "data-orientation": prop("orientation"), + dir: prop("dir"), + style: { + "--slides-per-page": slidesPerPage, + "--slide-spacing": prop("spacing"), + "--slide-item-size": autoSize ? "auto" : "calc(100% / var(--slides-per-page) - var(--slide-spacing) * (var(--slides-per-page) - 1) / var(--slides-per-page))" + } + }); + }, + getItemGroupProps() { + return normalize.element({ + ...parts.itemGroup.attrs, + id: getItemGroupId(scope), + "data-orientation": prop("orientation"), + "data-dragging": dataAttr(isDragging), + dir: prop("dir"), + "aria-live": isPlaying ? "off" : "polite", + onFocus(event) { + if (!contains(event.currentTarget, getEventTarget(event))) return; + send({ type: "VIEWPORT.FOCUS" }); + }, + onBlur(event) { + if (contains(event.currentTarget, event.relatedTarget)) return; + send({ type: "VIEWPORT.BLUR" }); + }, + onMouseDown(event) { + if (event.defaultPrevented) return; + if (!prop("allowMouseDrag")) return; + if (!isLeftClick(event)) return; + const target = getEventTarget(event); + if (isFocusable(target) && target !== event.currentTarget) return; + event.preventDefault(); + send({ type: "DRAGGING.START" }); + }, + onWheel: throttle((event) => { + const axis = prop("orientation") === "horizontal" ? "deltaX" : "deltaY"; + const isScrollingLeft = event[axis] < 0; + if (isScrollingLeft && !computed("canScrollPrev")) return; + const isScrollingRight = event[axis] > 0; + if (isScrollingRight && !computed("canScrollNext")) return; + send({ type: "USER.SCROLL" }); + }, 150), + onTouchStart() { + send({ type: "USER.SCROLL" }); + }, + style: { + display: autoSize ? "flex" : "grid", + gap: "var(--slide-spacing)", + scrollSnapType: [horizontal ? "x" : "y", prop("snapType")].join(" "), + gridAutoFlow: horizontal ? "column" : "row", + scrollbarWidth: "none", + overscrollBehaviorX: "contain", + [horizontal ? "gridAutoColumns" : "gridAutoRows"]: autoSize ? void 0 : "var(--slide-item-size)", + [horizontal ? "scrollPaddingInline" : "scrollPaddingBlock"]: padding, + [horizontal ? "paddingInline" : "paddingBlock"]: padding, + [horizontal ? "overflowX" : "overflowY"]: "auto" + } + }); + }, + getItemProps(props2) { + const isInView = context.get("slidesInView").includes(props2.index); + return normalize.element({ + ...parts.item.attrs, + id: getItemId(scope, props2.index), + dir: prop("dir"), + role: "group", + "data-index": props2.index, + "data-inview": dataAttr(isInView), + "aria-roledescription": "slide", + "data-orientation": prop("orientation"), + "aria-label": translations.item(props2.index, prop("slideCount")), + "aria-hidden": ariaAttr(!isInView), + style: { + flex: "0 0 auto", + [horizontal ? "maxWidth" : "maxHeight"]: "100%", + scrollSnapAlign: (() => { + const snapAlign = props2.snapAlign ?? "start"; + const slidesPerMove = prop("slidesPerMove"); + const perMove = slidesPerMove === "auto" ? Math.floor(prop("slidesPerPage")) : slidesPerMove; + const shouldSnap = (props2.index + perMove) % perMove === 0; + return shouldSnap ? snapAlign : void 0; + })() + } + }); + }, + getControlProps() { + return normalize.element({ + ...parts.control.attrs, + "data-orientation": prop("orientation") + }); + }, + getPrevTriggerProps() { + return normalize.button({ + ...parts.prevTrigger.attrs, + id: getPrevTriggerId(scope), + type: "button", + disabled: !canScrollPrev, + dir: prop("dir"), + "aria-label": translations.prevTrigger, + "data-orientation": prop("orientation"), + "aria-controls": getItemGroupId(scope), + onClick(event) { + if (event.defaultPrevented) return; + send({ type: "PAGE.PREV", src: "trigger" }); + } + }); + }, + getNextTriggerProps() { + return normalize.button({ + ...parts.nextTrigger.attrs, + dir: prop("dir"), + id: getNextTriggerId(scope), + type: "button", + "aria-label": translations.nextTrigger, + "data-orientation": prop("orientation"), + "aria-controls": getItemGroupId(scope), + disabled: !canScrollNext, + onClick(event) { + if (event.defaultPrevented) return; + send({ type: "PAGE.NEXT", src: "trigger" }); + } + }); + }, + getIndicatorGroupProps() { + return normalize.element({ + ...parts.indicatorGroup.attrs, + dir: prop("dir"), + id: getIndicatorGroupId(scope), + "data-orientation": prop("orientation"), + onKeyDown(event) { + if (event.defaultPrevented) return; + const src = "indicator"; + const keyMap = { + ArrowDown(event2) { + if (horizontal) return; + send({ type: "PAGE.NEXT", src }); + event2.preventDefault(); + }, + ArrowUp(event2) { + if (horizontal) return; + send({ type: "PAGE.PREV", src }); + event2.preventDefault(); + }, + ArrowRight(event2) { + if (!horizontal) return; + send({ type: "PAGE.NEXT", src }); + event2.preventDefault(); + }, + ArrowLeft(event2) { + if (!horizontal) return; + send({ type: "PAGE.PREV", src }); + event2.preventDefault(); + }, + Home(event2) { + send({ type: "PAGE.SET", index: 0, src }); + event2.preventDefault(); + }, + End(event2) { + send({ type: "PAGE.SET", index: pageSnapPoints.length - 1, src }); + event2.preventDefault(); + } + }; + const key = getEventKey(event, { + dir: prop("dir"), + orientation: prop("orientation") + }); + const exec = keyMap[key]; + exec?.(event); + } + }); + }, + getIndicatorProps(props2) { + return normalize.button({ + ...parts.indicator.attrs, + dir: prop("dir"), + id: getIndicatorId(scope, props2.index), + type: "button", + "data-orientation": prop("orientation"), + "data-index": props2.index, + "data-readonly": dataAttr(props2.readOnly), + "data-current": dataAttr(props2.index === page), + "aria-label": translations.indicator(props2.index), + onClick(event) { + if (event.defaultPrevented) return; + if (props2.readOnly) return; + send({ type: "PAGE.SET", index: props2.index, src: "indicator" }); + } + }); + }, + getAutoplayTriggerProps() { + return normalize.button({ + ...parts.autoplayTrigger.attrs, + type: "button", + "data-orientation": prop("orientation"), + "data-pressed": dataAttr(isPlaying), + "aria-label": isPlaying ? translations.autoplayStop : translations.autoplayStart, + onClick(event) { + if (event.defaultPrevented) return; + send({ type: isPlaying ? "AUTOPLAY.PAUSE" : "AUTOPLAY.START" }); + } + }); + }, + getProgressTextProps() { + return normalize.element({ + ...parts.progressText.attrs + }); + } + }; +} +var machine = createMachine({ + props({ props: props2 }) { + ensureProps(props2, ["slideCount"], "carousel"); + return { + dir: "ltr", + defaultPage: 0, + orientation: "horizontal", + snapType: "mandatory", + loop: !!props2.autoplay, + slidesPerPage: 1, + slidesPerMove: "auto", + spacing: "0px", + autoplay: false, + allowMouseDrag: false, + inViewThreshold: 0.6, + autoSize: false, + ...props2, + translations: { + nextTrigger: "Next slide", + prevTrigger: "Previous slide", + indicator: (index) => `Go to slide ${index + 1}`, + item: (index, count) => `${index + 1} of ${count}`, + autoplayStart: "Start slide rotation", + autoplayStop: "Stop slide rotation", + progressText: ({ page, totalPages }) => `${page} / ${totalPages}`, + ...props2.translations + } + }; + }, + refs() { + return { + timeoutRef: void 0 + }; + }, + initialState({ prop }) { + return prop("autoplay") ? "autoplay" : "idle"; + }, + context({ prop, bindable, getContext }) { + return { + page: bindable(() => ({ + defaultValue: prop("defaultPage"), + value: prop("page"), + onChange(page) { + const ctx = getContext(); + const pageSnapPoints = ctx.get("pageSnapPoints"); + prop("onPageChange")?.({ page, pageSnapPoint: pageSnapPoints[page] }); + } + })), + pageSnapPoints: bindable(() => { + return { + defaultValue: prop("autoSize") ? Array.from({ length: prop("slideCount") }, (_, i) => i) : getPageSnapPoints(prop("slideCount"), prop("slidesPerMove"), prop("slidesPerPage")) + }; + }), + slidesInView: bindable(() => ({ + defaultValue: [] + })) + }; + }, + computed: { + isRtl: ({ prop }) => prop("dir") === "rtl", + isHorizontal: ({ prop }) => prop("orientation") === "horizontal", + canScrollNext: ({ prop, context }) => prop("loop") || context.get("page") < context.get("pageSnapPoints").length - 1, + canScrollPrev: ({ prop, context }) => prop("loop") || context.get("page") > 0, + autoplayInterval: ({ prop }) => { + const autoplay = prop("autoplay"); + return isObject(autoplay) ? autoplay.delay : 4e3; + } + }, + watch({ track, action, context, prop, send }) { + track([() => prop("slidesPerPage"), () => prop("slidesPerMove")], () => { + action(["setSnapPoints"]); + }); + track([() => context.get("page")], () => { + action(["scrollToPage", "focusIndicatorEl"]); + }); + track([() => prop("orientation"), () => prop("autoSize"), () => prop("dir")], () => { + action(["setSnapPoints", "scrollToPage"]); + }); + track([() => prop("slideCount")], () => { + send({ type: "SNAP.REFRESH", src: "slide.count" }); + }); + track([() => !!prop("autoplay")], () => { + send({ type: prop("autoplay") ? "AUTOPLAY.START" : "AUTOPLAY.PAUSE", src: "autoplay.prop.change" }); + }); + }, + on: { + "PAGE.NEXT": { + target: "idle", + actions: ["clearScrollEndTimer", "setNextPage"] + }, + "PAGE.PREV": { + target: "idle", + actions: ["clearScrollEndTimer", "setPrevPage"] + }, + "PAGE.SET": { + target: "idle", + actions: ["clearScrollEndTimer", "setPage"] + }, + "INDEX.SET": { + target: "idle", + actions: ["clearScrollEndTimer", "setMatchingPage"] + }, + "SNAP.REFRESH": { + actions: ["setSnapPoints", "clampPage"] + }, + "PAGE.SCROLL": { + actions: ["scrollToPage"] + } + }, + effects: ["trackSlideMutation", "trackSlideIntersections", "trackSlideResize"], + entry: ["setSnapPoints", "setPage"], + exit: ["clearScrollEndTimer"], + states: { + idle: { + on: { + "DRAGGING.START": { + target: "dragging", + actions: ["invokeDragStart"] + }, + "AUTOPLAY.START": { + target: "autoplay", + actions: ["invokeAutoplayStart"] + }, + "USER.SCROLL": { + target: "userScroll" + }, + "VIEWPORT.FOCUS": { + target: "focus" + } + } + }, + focus: { + effects: ["trackKeyboardScroll"], + on: { + "VIEWPORT.BLUR": { + target: "idle" + }, + "PAGE.NEXT": { + actions: ["clearScrollEndTimer", "setNextPage"] + }, + "PAGE.PREV": { + actions: ["clearScrollEndTimer", "setPrevPage"] + }, + "PAGE.SET": { + actions: ["clearScrollEndTimer", "setPage"] + }, + "INDEX.SET": { + actions: ["clearScrollEndTimer", "setMatchingPage"] + }, + "USER.SCROLL": { + target: "userScroll" + } + } + }, + dragging: { + effects: ["trackPointerMove"], + entry: ["disableScrollSnap"], + on: { + DRAGGING: { + actions: ["scrollSlides", "invokeDragging"] + }, + "DRAGGING.END": { + target: "idle", + actions: ["endDragging", "invokeDraggingEnd"] + } + } + }, + userScroll: { + effects: ["trackScroll"], + on: { + "DRAGGING.START": { + target: "dragging", + actions: ["invokeDragStart"] + }, + "SCROLL.END": [ + { + guard: "isFocused", + target: "focus", + actions: ["setClosestPage"] + }, + { + target: "idle", + actions: ["setClosestPage"] + } + ] + } + }, + autoplay: { + effects: ["trackDocumentVisibility", "trackScroll", "autoUpdateSlide"], + exit: ["invokeAutoplayEnd"], + on: { + "AUTOPLAY.TICK": { + actions: ["setNextPage", "invokeAutoplay"] + }, + "DRAGGING.START": { + target: "dragging", + actions: ["invokeDragStart"] + }, + "AUTOPLAY.PAUSE": { + target: "idle" + } + } + } + }, + implementations: { + guards: { + isFocused: ({ scope }) => scope.isActiveElement(getItemGroupEl(scope)) + }, + effects: { + autoUpdateSlide({ computed, send }) { + const id = setInterval(() => { + send({ + type: computed("canScrollNext") ? "AUTOPLAY.TICK" : "AUTOPLAY.PAUSE", + src: "autoplay.interval" + }); + }, computed("autoplayInterval")); + return () => clearInterval(id); + }, + trackSlideMutation({ scope, send }) { + const el = getItemGroupEl(scope); + if (!el) return; + const win = scope.getWin(); + const observer = new win.MutationObserver(() => { + send({ type: "SNAP.REFRESH", src: "slide.mutation" }); + syncTabIndex(scope); + }); + syncTabIndex(scope); + observer.observe(el, { childList: true, subtree: true }); + return () => observer.disconnect(); + }, + trackSlideResize({ scope, send }) { + const el = getItemGroupEl(scope); + if (!el) return; + const exec = () => { + send({ type: "SNAP.REFRESH", src: "slide.resize" }); + }; + raf(() => { + exec(); + raf(() => { + send({ type: "PAGE.SCROLL", instant: true }); + }); + }); + const itemEls = getItemEls(scope); + itemEls.forEach(exec); + const cleanups = itemEls.map((el2) => resizeObserverBorderBox.observe(el2, exec)); + return callAll(...cleanups); + }, + trackSlideIntersections({ scope, prop, context }) { + const el = getItemGroupEl(scope); + const win = scope.getWin(); + const observer = new win.IntersectionObserver( + (entries) => { + const slidesInView = entries.reduce((acc, entry) => { + const target = entry.target; + const index = Number(target.dataset.index ?? "-1"); + if (index == null || Number.isNaN(index) || index === -1) return acc; + return entry.isIntersecting ? add(acc, index) : remove(acc, index); + }, context.get("slidesInView")); + context.set("slidesInView", uniq(slidesInView)); + }, + { + root: el, + threshold: prop("inViewThreshold") + } + ); + getItemEls(scope).forEach((slide) => observer.observe(slide)); + return () => observer.disconnect(); + }, + trackScroll({ send, refs, scope }) { + const el = getItemGroupEl(scope); + if (!el) return; + const onScroll = () => { + clearTimeout(refs.get("timeoutRef")); + refs.set("timeoutRef", void 0); + refs.set( + "timeoutRef", + setTimeout(() => { + send({ type: "SCROLL.END" }); + }, 150) + ); + }; + return addDomEvent(el, "scroll", onScroll, { passive: true }); + }, + trackDocumentVisibility({ scope, send }) { + const doc = scope.getDoc(); + const onVisibilityChange = () => { + if (doc.visibilityState === "visible") return; + send({ type: "AUTOPLAY.PAUSE", src: "doc.hidden" }); + }; + return addDomEvent(doc, "visibilitychange", onVisibilityChange); + }, + trackPointerMove({ scope, send }) { + const doc = scope.getDoc(); + return trackPointerMove(doc, { + onPointerMove({ event }) { + send({ type: "DRAGGING", left: -event.movementX, top: -event.movementY }); + }, + onPointerUp() { + send({ type: "DRAGGING.END" }); + } + }); + }, + trackKeyboardScroll({ scope, send, context }) { + const win = scope.getWin(); + const onKeyDown = (event) => { + switch (event.key) { + case "ArrowRight": + event.preventDefault(); + send({ type: "PAGE.NEXT" }); + break; + case "ArrowLeft": + event.preventDefault(); + send({ type: "PAGE.PREV" }); + break; + case "Home": + event.preventDefault(); + send({ type: "PAGE.SET", index: 0 }); + break; + case "End": + event.preventDefault(); + send({ type: "PAGE.SET", index: context.get("pageSnapPoints").length - 1 }); + } + }; + return addDomEvent(win, "keydown", onKeyDown, { capture: true }); + } + }, + actions: { + clearScrollEndTimer({ refs }) { + if (refs.get("timeoutRef") == null) return; + clearTimeout(refs.get("timeoutRef")); + refs.set("timeoutRef", void 0); + }, + scrollToPage({ context, event, scope, computed, flush }) { + const behavior = event.instant ? "instant" : "smooth"; + const index = clampValue(event.index ?? context.get("page"), 0, context.get("pageSnapPoints").length - 1); + const el = getItemGroupEl(scope); + if (!el) return; + const axis = computed("isHorizontal") ? "left" : "top"; + flush(() => { + el.scrollTo({ [axis]: context.get("pageSnapPoints")[index], behavior }); + }); + }, + setClosestPage({ context, scope, computed }) { + const el = getItemGroupEl(scope); + if (!el) return; + const scrollPosition = computed("isHorizontal") ? el.scrollLeft : el.scrollTop; + const page = context.get("pageSnapPoints").findIndex((point) => Math.abs(point - scrollPosition) < 1); + if (page === -1) return; + context.set("page", page); + }, + setNextPage({ context, prop, state }) { + const loop = state.matches("autoplay") || prop("loop"); + const page = nextIndex(context.get("pageSnapPoints"), context.get("page"), { loop }); + context.set("page", page); + }, + setPrevPage({ context, prop, state }) { + const loop = state.matches("autoplay") || prop("loop"); + const page = prevIndex(context.get("pageSnapPoints"), context.get("page"), { loop }); + context.set("page", page); + }, + setMatchingPage({ context, event, computed, scope }) { + const el = getItemGroupEl(scope); + if (!el) return; + const snapPoint = findSnapPoint( + el, + computed("isHorizontal") ? "x" : "y", + (node) => node.dataset.index === event.index.toString() + ); + if (snapPoint == null) return; + const page = context.get("pageSnapPoints").findIndex((point) => Math.abs(point - snapPoint) < 1); + context.set("page", page); + }, + setPage({ context, event }) { + const page = event.index ?? context.get("page"); + context.set("page", page); + }, + clampPage({ context }) { + const index = clampValue(context.get("page"), 0, context.get("pageSnapPoints").length - 1); + context.set("page", index); + }, + setSnapPoints({ context, computed, scope }) { + const el = getItemGroupEl(scope); + if (!el) return; + const scrollSnapPoints = getScrollSnapPositions(el); + context.set("pageSnapPoints", computed("isHorizontal") ? scrollSnapPoints.x : scrollSnapPoints.y); + }, + disableScrollSnap({ scope }) { + const el = getItemGroupEl(scope); + if (!el) return; + const styles = getComputedStyle(el); + el.dataset.scrollSnapType = styles.getPropertyValue("scroll-snap-type"); + el.style.setProperty("scroll-snap-type", "none"); + }, + scrollSlides({ scope, event }) { + const el = getItemGroupEl(scope); + el?.scrollBy({ left: event.left, top: event.top, behavior: "instant" }); + }, + endDragging({ scope, context, computed }) { + const el = getItemGroupEl(scope); + if (!el) return; + const isHorizontal = computed("isHorizontal"); + const scrollPos = isHorizontal ? el.scrollLeft : el.scrollTop; + const snapPoints = context.get("pageSnapPoints"); + const closest = snapPoints.reduce((closest2, curr) => { + return Math.abs(curr - scrollPos) < Math.abs(closest2 - scrollPos) ? curr : closest2; + }, snapPoints[0]); + raf(() => { + el.scrollTo({ + left: isHorizontal ? closest : el.scrollLeft, + top: isHorizontal ? el.scrollTop : closest, + behavior: "smooth" + }); + context.set("page", snapPoints.indexOf(closest)); + const scrollSnapType = el.dataset.scrollSnapType; + if (scrollSnapType) { + el.style.setProperty("scroll-snap-type", scrollSnapType); + delete el.dataset.scrollSnapType; + } + }); + }, + focusIndicatorEl({ context, event, scope }) { + if (event.src !== "indicator") return; + const el = getIndicatorEl(scope, context.get("page")); + if (!el) return; + raf(() => el.focus({ preventScroll: true })); + }, + invokeDragStart({ context, prop }) { + prop("onDragStatusChange")?.({ type: "dragging.start", isDragging: true, page: context.get("page") }); + }, + invokeDragging({ context, prop }) { + prop("onDragStatusChange")?.({ type: "dragging", isDragging: true, page: context.get("page") }); + }, + invokeDraggingEnd({ context, prop }) { + prop("onDragStatusChange")?.({ type: "dragging.end", isDragging: false, page: context.get("page") }); + }, + invokeAutoplay({ context, prop }) { + prop("onAutoplayStatusChange")?.({ type: "autoplay", isPlaying: true, page: context.get("page") }); + }, + invokeAutoplayStart({ context, prop }) { + prop("onAutoplayStatusChange")?.({ type: "autoplay.start", isPlaying: true, page: context.get("page") }); + }, + invokeAutoplayEnd({ context, prop }) { + prop("onAutoplayStatusChange")?.({ type: "autoplay.stop", isPlaying: false, page: context.get("page") }); + } + } + } +}); +function getPageSnapPoints(totalSlides, slidesPerMove, slidesPerPage) { + if (totalSlides == null || slidesPerPage <= 0) { + return []; + } + const snapPoints = []; + const perMove = slidesPerMove === "auto" ? Math.floor(slidesPerPage) : slidesPerMove; + if (perMove <= 0) { + return []; + } + for (let i = 0; i < totalSlides; i += perMove) { + if (i + slidesPerPage > totalSlides) break; + snapPoints.push(i); + } + return snapPoints; +} +var props = createProps()([ + "dir", + "getRootNode", + "id", + "ids", + "loop", + "page", + "defaultPage", + "onPageChange", + "orientation", + "slideCount", + "slidesPerPage", + "slidesPerMove", + "spacing", + "padding", + "autoplay", + "allowMouseDrag", + "inViewThreshold", + "translations", + "snapType", + "autoSize", + "onDragStatusChange", + "onAutoplayStatusChange" +]); +var splitProps = createSplitProps(props); +var indicatorProps = createProps()(["index", "readOnly"]); +var splitIndicatorProps = createSplitProps(indicatorProps); +var itemProps = createProps()(["index", "snapAlign"]); +var splitItemProps = createSplitProps(itemProps); + +// components/carousel.ts +var Carousel = class extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + initMachine(props2) { + return new VanillaMachine(machine, props2); + } + initApi() { + return connect(this.machine.service, normalizeProps); + } + render() { + const rootEl = this.el.querySelector('[data-scope="carousel"][data-part="root"]') ?? this.el; + this.spreadProps(rootEl, this.api.getRootProps()); + const controlEl = this.el.querySelector( + '[data-scope="carousel"][data-part="control"]' + ); + if (controlEl) this.spreadProps(controlEl, this.api.getControlProps()); + const itemGroupEl = this.el.querySelector( + '[data-scope="carousel"][data-part="item-group"]' + ); + if (itemGroupEl) this.spreadProps(itemGroupEl, this.api.getItemGroupProps()); + const slideCount = Number(this.el.dataset.slideCount) || 0; + for (let i = 0; i < slideCount; i++) { + const itemEl = this.el.querySelector( + `[data-scope="carousel"][data-part="item"][data-index="${i}"]` + ); + if (itemEl) this.spreadProps(itemEl, this.api.getItemProps({ index: i })); + } + const prevTriggerEl = this.el.querySelector( + '[data-scope="carousel"][data-part="prev-trigger"]' + ); + if (prevTriggerEl) this.spreadProps(prevTriggerEl, this.api.getPrevTriggerProps()); + const nextTriggerEl = this.el.querySelector( + '[data-scope="carousel"][data-part="next-trigger"]' + ); + if (nextTriggerEl) this.spreadProps(nextTriggerEl, this.api.getNextTriggerProps()); + const autoplayTriggerEl = this.el.querySelector( + '[data-scope="carousel"][data-part="autoplay-trigger"]' + ); + if (autoplayTriggerEl) this.spreadProps(autoplayTriggerEl, this.api.getAutoplayTriggerProps()); + const indicatorGroupEl = this.el.querySelector( + '[data-scope="carousel"][data-part="indicator-group"]' + ); + if (indicatorGroupEl) this.spreadProps(indicatorGroupEl, this.api.getIndicatorGroupProps()); + const indicatorCount = this.api.pageSnapPoints.length; + for (let i = 0; i < indicatorCount; i++) { + const indicatorEl = this.el.querySelector( + `[data-scope="carousel"][data-part="indicator"][data-index="${i}"]` + ); + if (indicatorEl) + this.spreadProps(indicatorEl, this.api.getIndicatorProps({ index: i })); + } + const progressTextEl = this.el.querySelector( + '[data-scope="carousel"][data-part="progress-text"]' + ); + if (progressTextEl) this.spreadProps(progressTextEl, this.api.getProgressTextProps()); + } +}; + +// hooks/carousel.ts +var CarouselHook = { + mounted() { + const el = this.el; + const page = getNumber(el, "page"); + const defaultPage = getNumber(el, "defaultPage"); + const controlled = getBoolean(el, "controlled"); + const slideCount = getNumber(el, "slideCount"); + if (slideCount == null || slideCount < 1) { + return; + } + const zag = new Carousel(el, { + id: el.id, + slideCount, + ...controlled && page !== void 0 ? { page } : { defaultPage: defaultPage ?? 0 }, + dir: getDir(el), + orientation: getString(el, "orientation", [ + "horizontal", + "vertical" + ]), + slidesPerPage: getNumber(el, "slidesPerPage") ?? 1, + slidesPerMove: getString(el, "slidesPerMove") === "auto" ? "auto" : getNumber(el, "slidesPerMove"), + loop: getBoolean(el, "loop"), + autoplay: getBoolean(el, "autoplay") ? { delay: getNumber(el, "autoplayDelay") ?? 4e3 } : false, + allowMouseDrag: getBoolean(el, "allowMouseDrag"), + spacing: getString(el, "spacing") ?? "0px", + padding: getString(el, "padding"), + inViewThreshold: getNumber(el, "inViewThreshold") ?? 0.6, + snapType: getString(el, "snapType", ["proximity", "mandatory"]), + autoSize: getBoolean(el, "autoSize"), + onPageChange: (details) => { + const eventName = getString(el, "onPageChange"); + if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { + this.pushEvent(eventName, { + page: details.page, + pageSnapPoint: details.pageSnapPoint, + id: el.id + }); + } + const clientName = getString(el, "onPageChangeClient"); + if (clientName) { + el.dispatchEvent( + new CustomEvent(clientName, { + bubbles: true, + detail: { value: details, id: el.id } + }) + ); + } + } + }); + zag.init(); + this.carousel = zag; + this.handlers = []; + }, + updated() { + const slideCount = getNumber(this.el, "slideCount"); + if (slideCount == null || slideCount < 1) return; + const page = getNumber(this.el, "page"); + const controlled = getBoolean(this.el, "controlled"); + this.carousel?.updateProps({ + id: this.el.id, + slideCount, + ...controlled && page !== void 0 ? { page } : {}, + dir: getDir(this.el), + orientation: getString(this.el, "orientation", [ + "horizontal", + "vertical" + ]), + slidesPerPage: getNumber(this.el, "slidesPerPage") ?? 1, + loop: getBoolean(this.el, "loop"), + allowMouseDrag: getBoolean(this.el, "allowMouseDrag") + }); + }, + destroyed() { + if (this.handlers) { + for (const h of this.handlers) this.removeHandleEvent(h); + } + this.carousel?.destroy(); + } +}; +export { + CarouselHook as Carousel +}; diff --git a/priv/static/checkbox.mjs b/priv/static/checkbox.mjs index 74942be..30be8a2 100644 --- a/priv/static/checkbox.mjs +++ b/priv/static/checkbox.mjs @@ -1,7 +1,7 @@ import { isFocusVisible, trackFocusVisible -} from "./chunk-EAMC7PNF.mjs"; +} from "./chunk-TEV2GE3U.mjs"; import { Component, VanillaMachine, @@ -21,7 +21,7 @@ import { trackFormControl, trackPress, visuallyHiddenStyle -} from "./chunk-GFGFZBBD.mjs"; +} from "./chunk-IXOYOLUJ.mjs"; // ../node_modules/.pnpm/@zag-js+checkbox@1.33.1/node_modules/@zag-js/checkbox/dist/index.mjs var anatomy = createAnatomy("checkbox").parts("root", "label", "control", "indicator"); diff --git a/priv/static/chunk-2PO3TGCF.mjs b/priv/static/chunk-2PO3TGCF.mjs new file mode 100644 index 0000000..fc89379 --- /dev/null +++ b/priv/static/chunk-2PO3TGCF.mjs @@ -0,0 +1,1322 @@ +import { + chunk, + hasProp, + isEqual, + isObject, + nextIndex, + prevIndex +} from "./chunk-IXOYOLUJ.mjs"; + +// ../node_modules/.pnpm/@zag-js+collection@1.33.1/node_modules/@zag-js/collection/dist/index.mjs +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +var fallback = { + itemToValue(item) { + if (typeof item === "string") return item; + if (isObject(item) && hasProp(item, "value")) return item.value; + return ""; + }, + itemToString(item) { + if (typeof item === "string") return item; + if (isObject(item) && hasProp(item, "label")) return item.label; + return fallback.itemToValue(item); + }, + isItemDisabled(item) { + if (isObject(item) && hasProp(item, "disabled")) return !!item.disabled; + return false; + } +}; +var ListCollection = class _ListCollection { + constructor(options) { + this.options = options; + __publicField(this, "items"); + __publicField(this, "indexMap", null); + __publicField(this, "copy", (items) => { + return new _ListCollection({ ...this.options, items: items ?? [...this.items] }); + }); + __publicField(this, "isEqual", (other) => { + return isEqual(this.items, other.items); + }); + __publicField(this, "setItems", (items) => { + return this.copy(items); + }); + __publicField(this, "getValues", (items = this.items) => { + const values = []; + for (const item of items) { + const value = this.getItemValue(item); + if (value != null) values.push(value); + } + return values; + }); + __publicField(this, "find", (value) => { + if (value == null) return null; + const index = this.indexOf(value); + return index !== -1 ? this.at(index) : null; + }); + __publicField(this, "findMany", (values) => { + const result = []; + for (const value of values) { + const item = this.find(value); + if (item != null) result.push(item); + } + return result; + }); + __publicField(this, "at", (index) => { + if (!this.options.groupBy && !this.options.groupSort) { + return this.items[index] ?? null; + } + let idx = 0; + const groups = this.group(); + for (const [, items] of groups) { + for (const item of items) { + if (idx === index) return item; + idx++; + } + } + return null; + }); + __publicField(this, "sortFn", (valueA, valueB) => { + const indexA = this.indexOf(valueA); + const indexB = this.indexOf(valueB); + return (indexA ?? 0) - (indexB ?? 0); + }); + __publicField(this, "sort", (values) => { + return [...values].sort(this.sortFn.bind(this)); + }); + __publicField(this, "getItemValue", (item) => { + if (item == null) return null; + return this.options.itemToValue?.(item) ?? fallback.itemToValue(item); + }); + __publicField(this, "getItemDisabled", (item) => { + if (item == null) return false; + return this.options.isItemDisabled?.(item) ?? fallback.isItemDisabled(item); + }); + __publicField(this, "stringifyItem", (item) => { + if (item == null) return null; + return this.options.itemToString?.(item) ?? fallback.itemToString(item); + }); + __publicField(this, "stringify", (value) => { + if (value == null) return null; + return this.stringifyItem(this.find(value)); + }); + __publicField(this, "stringifyItems", (items, separator = ", ") => { + const strs = []; + for (const item of items) { + const str = this.stringifyItem(item); + if (str != null) strs.push(str); + } + return strs.join(separator); + }); + __publicField(this, "stringifyMany", (value, separator) => { + return this.stringifyItems(this.findMany(value), separator); + }); + __publicField(this, "has", (value) => { + return this.indexOf(value) !== -1; + }); + __publicField(this, "hasItem", (item) => { + if (item == null) return false; + return this.has(this.getItemValue(item)); + }); + __publicField(this, "group", () => { + const { groupBy, groupSort } = this.options; + if (!groupBy) return [["", [...this.items]]]; + const groups = /* @__PURE__ */ new Map(); + this.items.forEach((item, index) => { + const groupKey = groupBy(item, index); + if (!groups.has(groupKey)) { + groups.set(groupKey, []); + } + groups.get(groupKey).push(item); + }); + let entries = Array.from(groups.entries()); + if (groupSort) { + entries.sort(([a], [b]) => { + if (typeof groupSort === "function") return groupSort(a, b); + if (Array.isArray(groupSort)) { + const indexA = groupSort.indexOf(a); + const indexB = groupSort.indexOf(b); + if (indexA === -1) return 1; + if (indexB === -1) return -1; + return indexA - indexB; + } + if (groupSort === "asc") return a.localeCompare(b); + if (groupSort === "desc") return b.localeCompare(a); + return 0; + }); + } + return entries; + }); + __publicField(this, "getNextValue", (value, step = 1, clamp = false) => { + let index = this.indexOf(value); + if (index === -1) return null; + index = clamp ? Math.min(index + step, this.size - 1) : index + step; + while (index <= this.size && this.getItemDisabled(this.at(index))) index++; + return this.getItemValue(this.at(index)); + }); + __publicField(this, "getPreviousValue", (value, step = 1, clamp = false) => { + let index = this.indexOf(value); + if (index === -1) return null; + index = clamp ? Math.max(index - step, 0) : index - step; + while (index >= 0 && this.getItemDisabled(this.at(index))) index--; + return this.getItemValue(this.at(index)); + }); + __publicField(this, "indexOf", (value) => { + if (value == null) return -1; + if (!this.options.groupBy && !this.options.groupSort) { + return this.items.findIndex((item) => this.getItemValue(item) === value); + } + if (!this.indexMap) { + this.indexMap = /* @__PURE__ */ new Map(); + let idx = 0; + const groups = this.group(); + for (const [, items] of groups) { + for (const item of items) { + const itemValue = this.getItemValue(item); + if (itemValue != null) { + this.indexMap.set(itemValue, idx); + } + idx++; + } + } + } + return this.indexMap.get(value) ?? -1; + }); + __publicField(this, "getByText", (text, current) => { + const currentIndex = current != null ? this.indexOf(current) : -1; + const isSingleKey = text.length === 1; + for (let i = 0; i < this.items.length; i++) { + const item = this.items[(currentIndex + i + 1) % this.items.length]; + if (isSingleKey && this.getItemValue(item) === current) continue; + if (this.getItemDisabled(item)) continue; + if (match(this.stringifyItem(item), text)) return item; + } + return void 0; + }); + __publicField(this, "search", (queryString, options2) => { + const { state, currentValue, timeout = 350 } = options2; + const search = state.keysSoFar + queryString; + const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]); + const query = isRepeated ? search[0] : search; + const item = this.getByText(query, currentValue); + const value = this.getItemValue(item); + function cleanup() { + clearTimeout(state.timer); + state.timer = -1; + } + function update(value2) { + state.keysSoFar = value2; + cleanup(); + if (value2 !== "") { + state.timer = +setTimeout(() => { + update(""); + cleanup(); + }, timeout); + } + } + update(search); + return value; + }); + __publicField(this, "update", (value, item) => { + let index = this.indexOf(value); + if (index === -1) return this; + return this.copy([...this.items.slice(0, index), item, ...this.items.slice(index + 1)]); + }); + __publicField(this, "upsert", (value, item, mode = "append") => { + let index = this.indexOf(value); + if (index === -1) { + const fn = mode === "append" ? this.append : this.prepend; + return fn(item); + } + return this.copy([...this.items.slice(0, index), item, ...this.items.slice(index + 1)]); + }); + __publicField(this, "insert", (index, ...items) => { + return this.copy(insert(this.items, index, ...items)); + }); + __publicField(this, "insertBefore", (value, ...items) => { + let toIndex = this.indexOf(value); + if (toIndex === -1) { + if (this.items.length === 0) toIndex = 0; + else return this; + } + return this.copy(insert(this.items, toIndex, ...items)); + }); + __publicField(this, "insertAfter", (value, ...items) => { + let toIndex = this.indexOf(value); + if (toIndex === -1) { + if (this.items.length === 0) toIndex = 0; + else return this; + } + return this.copy(insert(this.items, toIndex + 1, ...items)); + }); + __publicField(this, "prepend", (...items) => { + return this.copy(insert(this.items, 0, ...items)); + }); + __publicField(this, "append", (...items) => { + return this.copy(insert(this.items, this.items.length, ...items)); + }); + __publicField(this, "filter", (fn) => { + const filteredItems = this.items.filter((item, index) => fn(this.stringifyItem(item), index, item)); + return this.copy(filteredItems); + }); + __publicField(this, "remove", (...itemsOrValues) => { + const values = itemsOrValues.map( + (itemOrValue) => typeof itemOrValue === "string" ? itemOrValue : this.getItemValue(itemOrValue) + ); + return this.copy( + this.items.filter((item) => { + const value = this.getItemValue(item); + if (value == null) return false; + return !values.includes(value); + }) + ); + }); + __publicField(this, "move", (value, toIndex) => { + const fromIndex = this.indexOf(value); + if (fromIndex === -1) return this; + return this.copy(move(this.items, [fromIndex], toIndex)); + }); + __publicField(this, "moveBefore", (value, ...values) => { + let toIndex = this.items.findIndex((item) => this.getItemValue(item) === value); + if (toIndex === -1) return this; + let indices = values.map((value2) => this.items.findIndex((item) => this.getItemValue(item) === value2)).sort((a, b) => a - b); + return this.copy(move(this.items, indices, toIndex)); + }); + __publicField(this, "moveAfter", (value, ...values) => { + let toIndex = this.items.findIndex((item) => this.getItemValue(item) === value); + if (toIndex === -1) return this; + let indices = values.map((value2) => this.items.findIndex((item) => this.getItemValue(item) === value2)).sort((a, b) => a - b); + return this.copy(move(this.items, indices, toIndex + 1)); + }); + __publicField(this, "reorder", (fromIndex, toIndex) => { + return this.copy(move(this.items, [fromIndex], toIndex)); + }); + __publicField(this, "compareValue", (a, b) => { + const indexA = this.indexOf(a); + const indexB = this.indexOf(b); + if (indexA < indexB) return -1; + if (indexA > indexB) return 1; + return 0; + }); + __publicField(this, "range", (from, to) => { + let keys = []; + let key = from; + while (key != null) { + let item = this.find(key); + if (item) keys.push(key); + if (key === to) return keys; + key = this.getNextValue(key); + } + return []; + }); + __publicField(this, "getValueRange", (from, to) => { + if (from && to) { + if (this.compareValue(from, to) <= 0) { + return this.range(from, to); + } + return this.range(to, from); + } + return []; + }); + __publicField(this, "toString", () => { + let result = ""; + for (const item of this.items) { + const value = this.getItemValue(item); + const label = this.stringifyItem(item); + const disabled = this.getItemDisabled(item); + const itemString = [value, label, disabled].filter(Boolean).join(":"); + result += itemString + ","; + } + return result; + }); + __publicField(this, "toJSON", () => { + return { + size: this.size, + first: this.firstValue, + last: this.lastValue + }; + }); + this.items = [...options.items]; + } + /** + * Returns the number of items in the collection + */ + get size() { + return this.items.length; + } + /** + * Returns the first value in the collection + */ + get firstValue() { + let index = 0; + while (this.getItemDisabled(this.at(index))) index++; + return this.getItemValue(this.at(index)); + } + /** + * Returns the last value in the collection + */ + get lastValue() { + let index = this.size - 1; + while (this.getItemDisabled(this.at(index))) index--; + return this.getItemValue(this.at(index)); + } + *[Symbol.iterator]() { + yield* this.items; + } +}; +var match = (label, query) => { + return !!label?.toLowerCase().startsWith(query.toLowerCase()); +}; +function insert(items, index, ...values) { + return [...items.slice(0, index), ...values, ...items.slice(index)]; +} +function move(items, indices, toIndex) { + indices = [...indices].sort((a, b) => a - b); + const itemsToMove = indices.map((i) => items[i]); + for (let i = indices.length - 1; i >= 0; i--) { + items = [...items.slice(0, indices[i]), ...items.slice(indices[i] + 1)]; + } + toIndex = Math.max(0, toIndex - indices.filter((i) => i < toIndex).length); + return [...items.slice(0, toIndex), ...itemsToMove, ...items.slice(toIndex)]; +} +var GridCollection = class extends ListCollection { + constructor(options) { + const { columnCount } = options; + super(options); + __publicField(this, "columnCount"); + __publicField(this, "rows", null); + __publicField(this, "getRows", () => { + if (!this.rows) { + this.rows = chunk([...this.items], this.columnCount); + } + return this.rows; + }); + __publicField(this, "getRowCount", () => { + return Math.ceil(this.items.length / this.columnCount); + }); + __publicField(this, "getCellIndex", (row, column) => { + return row * this.columnCount + column; + }); + __publicField(this, "getCell", (row, column) => { + return this.at(this.getCellIndex(row, column)); + }); + __publicField(this, "getValueCell", (value) => { + const index = this.indexOf(value); + if (index === -1) return null; + const row = Math.floor(index / this.columnCount); + const column = index % this.columnCount; + return { row, column }; + }); + __publicField(this, "getLastEnabledColumnIndex", (row) => { + for (let col = this.columnCount - 1; col >= 0; col--) { + const cell = this.getCell(row, col); + if (cell && !this.getItemDisabled(cell)) { + return col; + } + } + return null; + }); + __publicField(this, "getFirstEnabledColumnIndex", (row) => { + for (let col = 0; col < this.columnCount; col++) { + const cell = this.getCell(row, col); + if (cell && !this.getItemDisabled(cell)) { + return col; + } + } + return null; + }); + __publicField(this, "getPreviousRowValue", (value, loop = false) => { + const currentCell = this.getValueCell(value); + if (currentCell === null) return null; + const rows = this.getRows(); + const rowCount = rows.length; + let prevRowIndex = currentCell.row; + let prevColumnIndex = currentCell.column; + for (let i = 1; i <= rowCount; i++) { + prevRowIndex = prevIndex(rows, prevRowIndex, { loop }); + const prevRow = rows[prevRowIndex]; + if (!prevRow) continue; + const prevCell = prevRow[prevColumnIndex]; + if (!prevCell) { + const lastColumnIndex = this.getLastEnabledColumnIndex(prevRowIndex); + if (lastColumnIndex != null) { + prevColumnIndex = lastColumnIndex; + } + } + const cell = this.getCell(prevRowIndex, prevColumnIndex); + if (!this.getItemDisabled(cell)) { + return this.getItemValue(cell); + } + } + return this.firstValue; + }); + __publicField(this, "getNextRowValue", (value, loop = false) => { + const currentCell = this.getValueCell(value); + if (currentCell === null) return null; + const rows = this.getRows(); + const rowCount = rows.length; + let nextRowIndex = currentCell.row; + let nextColumnIndex = currentCell.column; + for (let i = 1; i <= rowCount; i++) { + nextRowIndex = nextIndex(rows, nextRowIndex, { loop }); + const nextRow = rows[nextRowIndex]; + if (!nextRow) continue; + const nextCell = nextRow[nextColumnIndex]; + if (!nextCell) { + const lastColumnIndex = this.getLastEnabledColumnIndex(nextRowIndex); + if (lastColumnIndex != null) { + nextColumnIndex = lastColumnIndex; + } + } + const cell = this.getCell(nextRowIndex, nextColumnIndex); + if (!this.getItemDisabled(cell)) { + return this.getItemValue(cell); + } + } + return this.lastValue; + }); + this.columnCount = columnCount; + } +}; +function isGridCollection(v) { + return hasProp(v, "columnCount") && hasProp(v, "getRows"); +} +var Selection = class _Selection extends Set { + constructor(values = []) { + super(values); + __publicField(this, "selectionMode", "single"); + __publicField(this, "deselectable", true); + __publicField(this, "copy", () => { + const clone = new _Selection([...this]); + return this.sync(clone); + }); + __publicField(this, "sync", (other) => { + other.selectionMode = this.selectionMode; + other.deselectable = this.deselectable; + return other; + }); + __publicField(this, "isEmpty", () => { + return this.size === 0; + }); + __publicField(this, "isSelected", (value) => { + if (this.selectionMode === "none" || value == null) { + return false; + } + return this.has(value); + }); + __publicField(this, "canSelect", (collection, value) => { + return this.selectionMode !== "none" || !collection.getItemDisabled(collection.find(value)); + }); + __publicField(this, "firstSelectedValue", (collection) => { + let firstValue = null; + for (let value of this) { + if (!firstValue || collection.compareValue(value, firstValue) < 0) { + firstValue = value; + } + } + return firstValue; + }); + __publicField(this, "lastSelectedValue", (collection) => { + let lastValue = null; + for (let value of this) { + if (!lastValue || collection.compareValue(value, lastValue) > 0) { + lastValue = value; + } + } + return lastValue; + }); + __publicField(this, "extendSelection", (collection, anchorValue, targetValue) => { + if (this.selectionMode === "none") { + return this; + } + if (this.selectionMode === "single") { + return this.replaceSelection(collection, targetValue); + } + const selection = this.copy(); + const lastSelected = Array.from(this).pop(); + for (let key of collection.getValueRange(anchorValue, lastSelected ?? targetValue)) { + selection.delete(key); + } + for (let key of collection.getValueRange(targetValue, anchorValue)) { + if (this.canSelect(collection, key)) { + selection.add(key); + } + } + return selection; + }); + __publicField(this, "toggleSelection", (collection, value) => { + if (this.selectionMode === "none") { + return this; + } + if (this.selectionMode === "single" && !this.isSelected(value)) { + return this.replaceSelection(collection, value); + } + const selection = this.copy(); + if (selection.has(value)) { + selection.delete(value); + } else if (selection.canSelect(collection, value)) { + selection.add(value); + } + return selection; + }); + __publicField(this, "replaceSelection", (collection, value) => { + if (this.selectionMode === "none") { + return this; + } + if (value == null) { + return this; + } + if (!this.canSelect(collection, value)) { + return this; + } + const selection = new _Selection([value]); + return this.sync(selection); + }); + __publicField(this, "setSelection", (values2) => { + if (this.selectionMode === "none") { + return this; + } + let selection = new _Selection(); + for (let value of values2) { + if (value != null) { + selection.add(value); + if (this.selectionMode === "single") { + break; + } + } + } + return this.sync(selection); + }); + __publicField(this, "clearSelection", () => { + const selection = this.copy(); + if (selection.deselectable && selection.size > 0) { + selection.clear(); + } + return selection; + }); + __publicField(this, "select", (collection, value, forceToggle) => { + if (this.selectionMode === "none") { + return this; + } + if (this.selectionMode === "single") { + if (this.isSelected(value) && this.deselectable) { + return this.toggleSelection(collection, value); + } else { + return this.replaceSelection(collection, value); + } + } else if (this.selectionMode === "multiple" || forceToggle) { + return this.toggleSelection(collection, value); + } else { + return this.replaceSelection(collection, value); + } + }); + __publicField(this, "deselect", (value) => { + const selection = this.copy(); + selection.delete(value); + return selection; + }); + __publicField(this, "isEqual", (other) => { + return isEqual(Array.from(this), Array.from(other)); + }); + } +}; +function access(node, indexPath, options) { + for (let i = 0; i < indexPath.length; i++) node = options.getChildren(node, indexPath.slice(i + 1))[indexPath[i]]; + return node; +} +function ancestorIndexPaths(indexPaths) { + const sortedPaths = sortIndexPaths(indexPaths); + const result = []; + const seen = /* @__PURE__ */ new Set(); + for (const indexPath of sortedPaths) { + const key = indexPath.join(); + if (!seen.has(key)) { + seen.add(key); + result.push(indexPath); + } + } + return result; +} +function compareIndexPaths(a, b) { + for (let i = 0; i < Math.min(a.length, b.length); i++) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + return a.length - b.length; +} +function sortIndexPaths(indexPaths) { + return indexPaths.sort(compareIndexPaths); +} +function find(node, options) { + let found; + visit(node, { + ...options, + onEnter: (child, indexPath) => { + if (options.predicate(child, indexPath)) { + found = child; + return "stop"; + } + } + }); + return found; +} +function findAll(node, options) { + const found = []; + visit(node, { + onEnter: (child, indexPath) => { + if (options.predicate(child, indexPath)) found.push(child); + }, + getChildren: options.getChildren + }); + return found; +} +function findIndexPath(node, options) { + let found; + visit(node, { + onEnter: (child, indexPath) => { + if (options.predicate(child, indexPath)) { + found = [...indexPath]; + return "stop"; + } + }, + getChildren: options.getChildren + }); + return found; +} +function reduce(node, options) { + let result = options.initialResult; + visit(node, { + ...options, + onEnter: (child, indexPath) => { + result = options.nextResult(result, child, indexPath); + } + }); + return result; +} +function flatMap(node, options) { + return reduce(node, { + ...options, + initialResult: [], + nextResult: (result, child, indexPath) => { + result.push(...options.transform(child, indexPath)); + return result; + } + }); +} +function filter(node, options) { + const { predicate, create, getChildren } = options; + const filterRecursive = (node2, indexPath) => { + const children = getChildren(node2, indexPath); + const filteredChildren = []; + children.forEach((child, index) => { + const childIndexPath = [...indexPath, index]; + const filteredChild = filterRecursive(child, childIndexPath); + if (filteredChild) filteredChildren.push(filteredChild); + }); + const isRoot = indexPath.length === 0; + const nodeMatches = predicate(node2, indexPath); + const hasFilteredChildren = filteredChildren.length > 0; + if (isRoot || nodeMatches || hasFilteredChildren) { + return create(node2, filteredChildren, indexPath); + } + return null; + }; + return filterRecursive(node, []) || create(node, [], []); +} +function flatten(rootNode, options) { + const nodes = []; + let idx = 0; + const idxMap = /* @__PURE__ */ new Map(); + const parentMap = /* @__PURE__ */ new Map(); + visit(rootNode, { + getChildren: options.getChildren, + onEnter: (node, indexPath) => { + if (!idxMap.has(node)) { + idxMap.set(node, idx++); + } + const children = options.getChildren(node, indexPath); + children.forEach((child) => { + if (!parentMap.has(child)) { + parentMap.set(child, node); + } + if (!idxMap.has(child)) { + idxMap.set(child, idx++); + } + }); + const _children = children.length > 0 ? children.map((child) => idxMap.get(child)) : void 0; + const parent = parentMap.get(node); + const _parent = parent ? idxMap.get(parent) : void 0; + const _index = idxMap.get(node); + nodes.push({ ...node, _children, _parent, _index }); + } + }); + return nodes; +} +function insertOperation(index, nodes) { + return { type: "insert", index, nodes }; +} +function removeOperation(indexes) { + return { type: "remove", indexes }; +} +function replaceOperation() { + return { type: "replace" }; +} +function splitIndexPath(indexPath) { + return [indexPath.slice(0, -1), indexPath[indexPath.length - 1]]; +} +function getInsertionOperations(indexPath, nodes, operations = /* @__PURE__ */ new Map()) { + const [parentIndexPath, index] = splitIndexPath(indexPath); + for (let i = parentIndexPath.length - 1; i >= 0; i--) { + const parentKey = parentIndexPath.slice(0, i).join(); + switch (operations.get(parentKey)?.type) { + case "remove": + continue; + } + operations.set(parentKey, replaceOperation()); + } + const operation = operations.get(parentIndexPath.join()); + switch (operation?.type) { + case "remove": + operations.set(parentIndexPath.join(), { + type: "removeThenInsert", + removeIndexes: operation.indexes, + insertIndex: index, + insertNodes: nodes + }); + break; + default: + operations.set(parentIndexPath.join(), insertOperation(index, nodes)); + } + return operations; +} +function getRemovalOperations(indexPaths) { + const operations = /* @__PURE__ */ new Map(); + const indexesToRemove = /* @__PURE__ */ new Map(); + for (const indexPath of indexPaths) { + const parentKey = indexPath.slice(0, -1).join(); + const value = indexesToRemove.get(parentKey) ?? []; + value.push(indexPath[indexPath.length - 1]); + indexesToRemove.set( + parentKey, + value.sort((a, b) => a - b) + ); + } + for (const indexPath of indexPaths) { + for (let i = indexPath.length - 2; i >= 0; i--) { + const parentKey = indexPath.slice(0, i).join(); + if (!operations.has(parentKey)) { + operations.set(parentKey, replaceOperation()); + } + } + } + for (const [parentKey, indexes] of indexesToRemove) { + operations.set(parentKey, removeOperation(indexes)); + } + return operations; +} +function getReplaceOperations(indexPath, node) { + const operations = /* @__PURE__ */ new Map(); + const [parentIndexPath, index] = splitIndexPath(indexPath); + for (let i = parentIndexPath.length - 1; i >= 0; i--) { + const parentKey = parentIndexPath.slice(0, i).join(); + operations.set(parentKey, replaceOperation()); + } + operations.set(parentIndexPath.join(), { + type: "removeThenInsert", + removeIndexes: [index], + insertIndex: index, + insertNodes: [node] + }); + return operations; +} +function mutate(node, operations, options) { + return map(node, { + ...options, + getChildren: (node2, indexPath) => { + const key = indexPath.join(); + const operation = operations.get(key); + switch (operation?.type) { + case "replace": + case "remove": + case "removeThenInsert": + case "insert": + return options.getChildren(node2, indexPath); + default: + return []; + } + }, + transform: (node2, children, indexPath) => { + const key = indexPath.join(); + const operation = operations.get(key); + switch (operation?.type) { + case "remove": + return options.create( + node2, + children.filter((_, index) => !operation.indexes.includes(index)), + indexPath + ); + case "removeThenInsert": + const updatedChildren = children.filter((_, index) => !operation.removeIndexes.includes(index)); + const adjustedIndex = operation.removeIndexes.reduce( + (index, removedIndex) => removedIndex < index ? index - 1 : index, + operation.insertIndex + ); + return options.create(node2, splice(updatedChildren, adjustedIndex, 0, ...operation.insertNodes), indexPath); + case "insert": + return options.create(node2, splice(children, operation.index, 0, ...operation.nodes), indexPath); + case "replace": + return options.create(node2, children, indexPath); + default: + return node2; + } + } + }); +} +function splice(array, start, deleteCount, ...items) { + return [...array.slice(0, start), ...items, ...array.slice(start + deleteCount)]; +} +function map(node, options) { + const childrenMap = {}; + visit(node, { + ...options, + onLeave: (child, indexPath) => { + const keyIndexPath = [0, ...indexPath]; + const key = keyIndexPath.join(); + const transformed = options.transform(child, childrenMap[key] ?? [], indexPath); + const parentKey = keyIndexPath.slice(0, -1).join(); + const parentChildren = childrenMap[parentKey] ?? []; + parentChildren.push(transformed); + childrenMap[parentKey] = parentChildren; + } + }); + return childrenMap[""][0]; +} +function insert2(node, options) { + const { nodes, at } = options; + if (at.length === 0) throw new Error(`Can't insert nodes at the root`); + const state = getInsertionOperations(at, nodes); + return mutate(node, state, options); +} +function replace(node, options) { + if (options.at.length === 0) return options.node; + const operations = getReplaceOperations(options.at, options.node); + return mutate(node, operations, options); +} +function remove(node, options) { + if (options.indexPaths.length === 0) return node; + for (const indexPath of options.indexPaths) { + if (indexPath.length === 0) throw new Error(`Can't remove the root node`); + } + const operations = getRemovalOperations(options.indexPaths); + return mutate(node, operations, options); +} +function move2(node, options) { + if (options.indexPaths.length === 0) return node; + for (const indexPath of options.indexPaths) { + if (indexPath.length === 0) throw new Error(`Can't move the root node`); + } + if (options.to.length === 0) throw new Error(`Can't move nodes to the root`); + const _ancestorIndexPaths = ancestorIndexPaths(options.indexPaths); + const nodesToInsert = _ancestorIndexPaths.map((indexPath) => access(node, indexPath, options)); + const operations = getInsertionOperations(options.to, nodesToInsert, getRemovalOperations(_ancestorIndexPaths)); + return mutate(node, operations, options); +} +function visit(node, options) { + const { onEnter, onLeave, getChildren } = options; + let indexPath = []; + let stack = [{ node }]; + const getIndexPath = options.reuseIndexPath ? () => indexPath : () => indexPath.slice(); + while (stack.length > 0) { + let wrapper = stack[stack.length - 1]; + if (wrapper.state === void 0) { + const enterResult = onEnter?.(wrapper.node, getIndexPath()); + if (enterResult === "stop") return; + wrapper.state = enterResult === "skip" ? -1 : 0; + } + const children = wrapper.children || getChildren(wrapper.node, getIndexPath()); + wrapper.children || (wrapper.children = children); + if (wrapper.state !== -1) { + if (wrapper.state < children.length) { + let currentIndex = wrapper.state; + indexPath.push(currentIndex); + stack.push({ node: children[currentIndex] }); + wrapper.state = currentIndex + 1; + continue; + } + const leaveResult = onLeave?.(wrapper.node, getIndexPath()); + if (leaveResult === "stop") return; + } + indexPath.pop(); + stack.pop(); + } +} +var TreeCollection = class _TreeCollection { + constructor(options) { + this.options = options; + __publicField(this, "rootNode"); + __publicField(this, "isEqual", (other) => { + return isEqual(this.rootNode, other.rootNode); + }); + __publicField(this, "getNodeChildren", (node) => { + return this.options.nodeToChildren?.(node) ?? fallbackMethods.nodeToChildren(node) ?? []; + }); + __publicField(this, "resolveIndexPath", (valueOrIndexPath) => { + return typeof valueOrIndexPath === "string" ? this.getIndexPath(valueOrIndexPath) : valueOrIndexPath; + }); + __publicField(this, "resolveNode", (valueOrIndexPath) => { + const indexPath = this.resolveIndexPath(valueOrIndexPath); + return indexPath ? this.at(indexPath) : void 0; + }); + __publicField(this, "getNodeChildrenCount", (node) => { + return this.options.nodeToChildrenCount?.(node) ?? fallbackMethods.nodeToChildrenCount(node); + }); + __publicField(this, "getNodeValue", (node) => { + return this.options.nodeToValue?.(node) ?? fallbackMethods.nodeToValue(node); + }); + __publicField(this, "getNodeDisabled", (node) => { + return this.options.isNodeDisabled?.(node) ?? fallbackMethods.isNodeDisabled(node); + }); + __publicField(this, "stringify", (value) => { + const node = this.findNode(value); + if (!node) return null; + return this.stringifyNode(node); + }); + __publicField(this, "stringifyNode", (node) => { + return this.options.nodeToString?.(node) ?? fallbackMethods.nodeToString(node); + }); + __publicField(this, "getFirstNode", (rootNode = this.rootNode, opts = {}) => { + let firstChild; + visit(rootNode, { + getChildren: this.getNodeChildren, + onEnter: (node, indexPath) => { + if (this.isSameNode(node, rootNode)) return; + if (opts.skip?.({ value: this.getNodeValue(node), node, indexPath })) return "skip"; + if (!firstChild && indexPath.length > 0 && !this.getNodeDisabled(node)) { + firstChild = node; + return "stop"; + } + } + }); + return firstChild; + }); + __publicField(this, "getLastNode", (rootNode = this.rootNode, opts = {}) => { + let lastChild; + visit(rootNode, { + getChildren: this.getNodeChildren, + onEnter: (node, indexPath) => { + if (this.isSameNode(node, rootNode)) return; + if (opts.skip?.({ value: this.getNodeValue(node), node, indexPath })) return "skip"; + if (indexPath.length > 0 && !this.getNodeDisabled(node)) { + lastChild = node; + } + } + }); + return lastChild; + }); + __publicField(this, "at", (indexPath) => { + return access(this.rootNode, indexPath, { + getChildren: this.getNodeChildren + }); + }); + __publicField(this, "findNode", (value, rootNode = this.rootNode) => { + return find(rootNode, { + getChildren: this.getNodeChildren, + predicate: (node) => this.getNodeValue(node) === value + }); + }); + __publicField(this, "findNodes", (values, rootNode = this.rootNode) => { + const v = new Set(values.filter((v2) => v2 != null)); + return findAll(rootNode, { + getChildren: this.getNodeChildren, + predicate: (node) => v.has(this.getNodeValue(node)) + }); + }); + __publicField(this, "sort", (values) => { + return values.reduce((acc, value) => { + const indexPath = this.getIndexPath(value); + if (indexPath) acc.push({ value, indexPath }); + return acc; + }, []).sort((a, b) => compareIndexPaths(a.indexPath, b.indexPath)).map(({ value }) => value); + }); + __publicField(this, "getIndexPath", (value) => { + return findIndexPath(this.rootNode, { + getChildren: this.getNodeChildren, + predicate: (node) => this.getNodeValue(node) === value + }); + }); + __publicField(this, "getValue", (indexPath) => { + const node = this.at(indexPath); + return node ? this.getNodeValue(node) : void 0; + }); + __publicField(this, "getValuePath", (indexPath) => { + if (!indexPath) return []; + const valuePath = []; + let currentPath = [...indexPath]; + while (currentPath.length > 0) { + const node = this.at(currentPath); + if (node) valuePath.unshift(this.getNodeValue(node)); + currentPath.pop(); + } + return valuePath; + }); + __publicField(this, "getDepth", (value) => { + const indexPath = findIndexPath(this.rootNode, { + getChildren: this.getNodeChildren, + predicate: (node) => this.getNodeValue(node) === value + }); + return indexPath?.length ?? 0; + }); + __publicField(this, "isSameNode", (node, other) => { + return this.getNodeValue(node) === this.getNodeValue(other); + }); + __publicField(this, "isRootNode", (node) => { + return this.isSameNode(node, this.rootNode); + }); + __publicField(this, "contains", (parentIndexPath, valueIndexPath) => { + if (!parentIndexPath || !valueIndexPath) return false; + return valueIndexPath.slice(0, parentIndexPath.length).every((_, i) => parentIndexPath[i] === valueIndexPath[i]); + }); + __publicField(this, "getNextNode", (value, opts = {}) => { + let found = false; + let nextNode; + visit(this.rootNode, { + getChildren: this.getNodeChildren, + onEnter: (node, indexPath) => { + if (this.isRootNode(node)) return; + const nodeValue = this.getNodeValue(node); + if (opts.skip?.({ value: nodeValue, node, indexPath })) { + if (nodeValue === value) { + found = true; + } + return "skip"; + } + if (found && !this.getNodeDisabled(node)) { + nextNode = node; + return "stop"; + } + if (nodeValue === value) { + found = true; + } + } + }); + return nextNode; + }); + __publicField(this, "getPreviousNode", (value, opts = {}) => { + let previousNode; + let found = false; + visit(this.rootNode, { + getChildren: this.getNodeChildren, + onEnter: (node, indexPath) => { + if (this.isRootNode(node)) return; + const nodeValue = this.getNodeValue(node); + if (opts.skip?.({ value: nodeValue, node, indexPath })) { + return "skip"; + } + if (nodeValue === value) { + found = true; + return "stop"; + } + if (!this.getNodeDisabled(node)) { + previousNode = node; + } + } + }); + return found ? previousNode : void 0; + }); + __publicField(this, "getParentNodes", (valueOrIndexPath) => { + const indexPath = this.resolveIndexPath(valueOrIndexPath)?.slice(); + if (!indexPath) return []; + const result = []; + while (indexPath.length > 0) { + indexPath.pop(); + const parentNode = this.at(indexPath); + if (parentNode && !this.isRootNode(parentNode)) { + result.unshift(parentNode); + } + } + return result; + }); + __publicField(this, "getDescendantNodes", (valueOrIndexPath, options2) => { + const parentNode = this.resolveNode(valueOrIndexPath); + if (!parentNode) return []; + const result = []; + visit(parentNode, { + getChildren: this.getNodeChildren, + onEnter: (node, nodeIndexPath) => { + if (nodeIndexPath.length === 0) return; + if (!options2?.withBranch && this.isBranchNode(node)) return; + result.push(node); + } + }); + return result; + }); + __publicField(this, "getDescendantValues", (valueOrIndexPath, options2) => { + const children = this.getDescendantNodes(valueOrIndexPath, options2); + return children.map((child) => this.getNodeValue(child)); + }); + __publicField(this, "getParentIndexPath", (indexPath) => { + return indexPath.slice(0, -1); + }); + __publicField(this, "getParentNode", (valueOrIndexPath) => { + const indexPath = this.resolveIndexPath(valueOrIndexPath); + return indexPath ? this.at(this.getParentIndexPath(indexPath)) : void 0; + }); + __publicField(this, "visit", (opts) => { + const { skip, ...rest } = opts; + visit(this.rootNode, { + ...rest, + getChildren: this.getNodeChildren, + onEnter: (node, indexPath) => { + if (this.isRootNode(node)) return; + if (skip?.({ value: this.getNodeValue(node), node, indexPath })) return "skip"; + return rest.onEnter?.(node, indexPath); + } + }); + }); + __publicField(this, "getPreviousSibling", (indexPath) => { + const parentNode = this.getParentNode(indexPath); + if (!parentNode) return; + const siblings = this.getNodeChildren(parentNode); + let idx = indexPath[indexPath.length - 1]; + while (--idx >= 0) { + const sibling = siblings[idx]; + if (!this.getNodeDisabled(sibling)) return sibling; + } + return; + }); + __publicField(this, "getNextSibling", (indexPath) => { + const parentNode = this.getParentNode(indexPath); + if (!parentNode) return; + const siblings = this.getNodeChildren(parentNode); + let idx = indexPath[indexPath.length - 1]; + while (++idx < siblings.length) { + const sibling = siblings[idx]; + if (!this.getNodeDisabled(sibling)) return sibling; + } + return; + }); + __publicField(this, "getSiblingNodes", (indexPath) => { + const parentNode = this.getParentNode(indexPath); + return parentNode ? this.getNodeChildren(parentNode) : []; + }); + __publicField(this, "getValues", (rootNode = this.rootNode) => { + const values = flatMap(rootNode, { + getChildren: this.getNodeChildren, + transform: (node) => [this.getNodeValue(node)] + }); + return values.slice(1); + }); + __publicField(this, "isValidDepth", (indexPath, depth) => { + if (depth == null) return true; + if (typeof depth === "function") return depth(indexPath.length); + return indexPath.length === depth; + }); + __publicField(this, "isBranchNode", (node) => { + return this.getNodeChildren(node).length > 0 || this.getNodeChildrenCount(node) != null; + }); + __publicField(this, "getBranchValues", (rootNode = this.rootNode, opts = {}) => { + let values = []; + visit(rootNode, { + getChildren: this.getNodeChildren, + onEnter: (node, indexPath) => { + if (indexPath.length === 0) return; + const nodeValue = this.getNodeValue(node); + if (opts.skip?.({ value: nodeValue, node, indexPath })) return "skip"; + if (this.isBranchNode(node) && this.isValidDepth(indexPath, opts.depth)) { + values.push(this.getNodeValue(node)); + } + } + }); + return values; + }); + __publicField(this, "flatten", (rootNode = this.rootNode) => { + return flatten(rootNode, { getChildren: this.getNodeChildren }); + }); + __publicField(this, "_create", (node, children) => { + if (this.getNodeChildren(node).length > 0 || children.length > 0) { + return { ...node, children }; + } + return { ...node }; + }); + __publicField(this, "_insert", (rootNode, indexPath, nodes) => { + return this.copy( + insert2(rootNode, { at: indexPath, nodes, getChildren: this.getNodeChildren, create: this._create }) + ); + }); + __publicField(this, "copy", (rootNode) => { + return new _TreeCollection({ ...this.options, rootNode }); + }); + __publicField(this, "_replace", (rootNode, indexPath, node) => { + return this.copy( + replace(rootNode, { at: indexPath, node, getChildren: this.getNodeChildren, create: this._create }) + ); + }); + __publicField(this, "_move", (rootNode, indexPaths, to) => { + return this.copy(move2(rootNode, { indexPaths, to, getChildren: this.getNodeChildren, create: this._create })); + }); + __publicField(this, "_remove", (rootNode, indexPaths) => { + return this.copy(remove(rootNode, { indexPaths, getChildren: this.getNodeChildren, create: this._create })); + }); + __publicField(this, "replace", (indexPath, node) => { + return this._replace(this.rootNode, indexPath, node); + }); + __publicField(this, "remove", (indexPaths) => { + return this._remove(this.rootNode, indexPaths); + }); + __publicField(this, "insertBefore", (indexPath, nodes) => { + const parentNode = this.getParentNode(indexPath); + return parentNode ? this._insert(this.rootNode, indexPath, nodes) : void 0; + }); + __publicField(this, "insertAfter", (indexPath, nodes) => { + const parentNode = this.getParentNode(indexPath); + if (!parentNode) return; + const nextIndex2 = [...indexPath.slice(0, -1), indexPath[indexPath.length - 1] + 1]; + return this._insert(this.rootNode, nextIndex2, nodes); + }); + __publicField(this, "move", (fromIndexPaths, toIndexPath) => { + return this._move(this.rootNode, fromIndexPaths, toIndexPath); + }); + __publicField(this, "filter", (predicate) => { + const filteredRoot = filter(this.rootNode, { + predicate, + getChildren: this.getNodeChildren, + create: this._create + }); + return this.copy(filteredRoot); + }); + __publicField(this, "toJSON", () => { + return this.getValues(this.rootNode); + }); + this.rootNode = options.rootNode; + } +}; +var fallbackMethods = { + nodeToValue(node) { + if (typeof node === "string") return node; + if (isObject(node) && hasProp(node, "value")) return node.value; + return ""; + }, + nodeToString(node) { + if (typeof node === "string") return node; + if (isObject(node) && hasProp(node, "label")) return node.label; + return fallbackMethods.nodeToValue(node); + }, + isNodeDisabled(node) { + if (isObject(node) && hasProp(node, "disabled")) return !!node.disabled; + return false; + }, + nodeToChildren(node) { + return node.children; + }, + nodeToChildrenCount(node) { + if (isObject(node) && hasProp(node, "childrenCount")) return node.childrenCount; + } +}; + +export { + ListCollection, + GridCollection, + isGridCollection, + Selection, + TreeCollection +}; diff --git a/priv/static/chunk-BMVNROAE.mjs b/priv/static/chunk-BMVNROAE.mjs new file mode 100644 index 0000000..199581d --- /dev/null +++ b/priv/static/chunk-BMVNROAE.mjs @@ -0,0 +1,432 @@ +// ../node_modules/.pnpm/@zag-js+rect-utils@1.33.1/node_modules/@zag-js/rect-utils/dist/index.mjs +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +var AffineTransform = class _AffineTransform { + constructor([m00, m01, m02, m10, m11, m12] = [0, 0, 0, 0, 0, 0]) { + __publicField(this, "m00"); + __publicField(this, "m01"); + __publicField(this, "m02"); + __publicField(this, "m10"); + __publicField(this, "m11"); + __publicField(this, "m12"); + __publicField(this, "rotate", (...args) => { + return this.prepend(_AffineTransform.rotate(...args)); + }); + __publicField(this, "scale", (...args) => { + return this.prepend(_AffineTransform.scale(...args)); + }); + __publicField(this, "translate", (...args) => { + return this.prepend(_AffineTransform.translate(...args)); + }); + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + } + applyTo(point) { + const { x, y } = point; + const { m00, m01, m02, m10, m11, m12 } = this; + return { + x: m00 * x + m01 * y + m02, + y: m10 * x + m11 * y + m12 + }; + } + prepend(other) { + return new _AffineTransform([ + this.m00 * other.m00 + this.m01 * other.m10, + // m00 + this.m00 * other.m01 + this.m01 * other.m11, + // m01 + this.m00 * other.m02 + this.m01 * other.m12 + this.m02, + // m02 + this.m10 * other.m00 + this.m11 * other.m10, + // m10 + this.m10 * other.m01 + this.m11 * other.m11, + // m11 + this.m10 * other.m02 + this.m11 * other.m12 + this.m12 + // m12 + ]); + } + append(other) { + return new _AffineTransform([ + other.m00 * this.m00 + other.m01 * this.m10, + // m00 + other.m00 * this.m01 + other.m01 * this.m11, + // m01 + other.m00 * this.m02 + other.m01 * this.m12 + other.m02, + // m02 + other.m10 * this.m00 + other.m11 * this.m10, + // m10 + other.m10 * this.m01 + other.m11 * this.m11, + // m11 + other.m10 * this.m02 + other.m11 * this.m12 + other.m12 + // m12 + ]); + } + get determinant() { + return this.m00 * this.m11 - this.m01 * this.m10; + } + get isInvertible() { + const det = this.determinant; + return isFinite(det) && isFinite(this.m02) && isFinite(this.m12) && det !== 0; + } + invert() { + const det = this.determinant; + return new _AffineTransform([ + this.m11 / det, + // m00 + -this.m01 / det, + // m01 + (this.m01 * this.m12 - this.m11 * this.m02) / det, + // m02 + -this.m10 / det, + // m10 + this.m00 / det, + // m11 + (this.m10 * this.m02 - this.m00 * this.m12) / det + // m12 + ]); + } + get array() { + return [this.m00, this.m01, this.m02, this.m10, this.m11, this.m12, 0, 0, 1]; + } + get float32Array() { + return new Float32Array(this.array); + } + // Static + static get identity() { + return new _AffineTransform([1, 0, 0, 0, 1, 0]); + } + static rotate(theta, origin) { + const rotation = new _AffineTransform([Math.cos(theta), -Math.sin(theta), 0, Math.sin(theta), Math.cos(theta), 0]); + if (origin && (origin.x !== 0 || origin.y !== 0)) { + return _AffineTransform.multiply( + _AffineTransform.translate(origin.x, origin.y), + rotation, + _AffineTransform.translate(-origin.x, -origin.y) + ); + } + return rotation; + } + static scale(sx, sy = sx, origin = { x: 0, y: 0 }) { + const scale = new _AffineTransform([sx, 0, 0, 0, sy, 0]); + if (origin.x !== 0 || origin.y !== 0) { + return _AffineTransform.multiply( + _AffineTransform.translate(origin.x, origin.y), + scale, + _AffineTransform.translate(-origin.x, -origin.y) + ); + } + return scale; + } + static translate(tx, ty) { + return new _AffineTransform([1, 0, tx, 0, 1, ty]); + } + static multiply(...[first, ...rest]) { + if (!first) return _AffineTransform.identity; + return rest.reduce((result, item) => result.prepend(item), first); + } + get a() { + return this.m00; + } + get b() { + return this.m10; + } + get c() { + return this.m01; + } + get d() { + return this.m11; + } + get tx() { + return this.m02; + } + get ty() { + return this.m12; + } + get scaleComponents() { + return { x: this.a, y: this.d }; + } + get translationComponents() { + return { x: this.tx, y: this.ty }; + } + get skewComponents() { + return { x: this.c, y: this.b }; + } + toString() { + return `matrix(${this.a}, ${this.b}, ${this.c}, ${this.d}, ${this.tx}, ${this.ty})`; + } +}; +function getPointAngle(rect, point, reference = rect.center) { + const x = point.x - reference.x; + const y = point.y - reference.y; + const deg = Math.atan2(x, y) * (180 / Math.PI) + 180; + return 360 - deg; +} +var clamp = (value, min3, max2) => Math.min(Math.max(value, min3), max2); +var clampPoint = (position, size, boundaryRect) => { + const x = clamp(position.x, boundaryRect.x, boundaryRect.x + boundaryRect.width - size.width); + const y = clamp(position.y, boundaryRect.y, boundaryRect.y + boundaryRect.height - size.height); + return { x, y }; +}; +var defaultMinSize = { + width: 0, + height: 0 +}; +var defaultMaxSize = { + width: Infinity, + height: Infinity +}; +var clampSize = (size, minSize = defaultMinSize, maxSize = defaultMaxSize) => { + return { + width: Math.min(Math.max(size.width, minSize.width), maxSize.width), + height: Math.min(Math.max(size.height, minSize.height), maxSize.height) + }; +}; +var createPoint = (x, y) => ({ x, y }); +var subtractPoints = (a, b) => { + if (!b) return a; + return createPoint(a.x - b.x, a.y - b.y); +}; +var addPoints = (a, b) => createPoint(a.x + b.x, a.y + b.y); +function createRect(r) { + const { x, y, width, height } = r; + const midX = x + width / 2; + const midY = y + height / 2; + return { + x, + y, + width, + height, + minX: x, + minY: y, + maxX: x + width, + maxY: y + height, + midX, + midY, + center: createPoint(midX, midY) + }; +} +function getRectCorners(v) { + const top = createPoint(v.minX, v.minY); + const right = createPoint(v.maxX, v.minY); + const bottom = createPoint(v.maxX, v.maxY); + const left = createPoint(v.minX, v.maxY); + return { top, right, bottom, left }; +} +var constrainRect = (rect, boundary) => { + const left = Math.max(boundary.x, Math.min(rect.x, boundary.x + boundary.width - rect.width)); + const top = Math.max(boundary.y, Math.min(rect.y, boundary.y + boundary.height - rect.height)); + return { + x: left, + y: top, + width: Math.min(rect.width, boundary.width), + height: Math.min(rect.height, boundary.height) + }; +}; +var isSizeEqual = (a, b) => { + return a.width === b?.width && a.height === b?.height; +}; +var isPointEqual = (a, b) => { + return a.x === b?.x && a.y === b?.y; +}; +var styleCache = /* @__PURE__ */ new WeakMap(); +function getCacheComputedStyle(el) { + if (!styleCache.has(el)) { + const win = el.ownerDocument.defaultView || window; + styleCache.set(el, win.getComputedStyle(el)); + } + return styleCache.get(el); +} +function getElementRect(el, opts = {}) { + return createRect(getClientRect(el, opts)); +} +function getClientRect(el, opts = {}) { + const { excludeScrollbar = false, excludeBorders = false } = opts; + const { x, y, width, height } = el.getBoundingClientRect(); + const r = { x, y, width, height }; + const style = getCacheComputedStyle(el); + const { borderLeftWidth, borderTopWidth, borderRightWidth, borderBottomWidth } = style; + const borderXWidth = sum(borderLeftWidth, borderRightWidth); + const borderYWidth = sum(borderTopWidth, borderBottomWidth); + if (excludeBorders) { + r.width -= borderXWidth; + r.height -= borderYWidth; + r.x += px(borderLeftWidth); + r.y += px(borderTopWidth); + } + if (excludeScrollbar) { + const scrollbarWidth = el.offsetWidth - el.clientWidth - borderXWidth; + const scrollbarHeight = el.offsetHeight - el.clientHeight - borderYWidth; + r.width -= scrollbarWidth; + r.height -= scrollbarHeight; + } + return r; +} +var px = (v) => parseFloat(v.replace("px", "")); +var sum = (...vals) => vals.reduce((sum2, v) => sum2 + (v ? px(v) : 0), 0); +var { min, max } = Math; +function getWindowRect(win, opts = {}) { + return createRect(getViewportRect(win, opts)); +} +function getViewportRect(win, opts) { + const { excludeScrollbar = false } = opts; + const { innerWidth, innerHeight, document: doc, visualViewport } = win; + const width = visualViewport?.width || innerWidth; + const height = visualViewport?.height || innerHeight; + const rect = { x: 0, y: 0, width, height }; + if (excludeScrollbar) { + const scrollbarWidth = innerWidth - doc.documentElement.clientWidth; + const scrollbarHeight = innerHeight - doc.documentElement.clientHeight; + rect.width -= scrollbarWidth; + rect.height -= scrollbarHeight; + } + return rect; +} +function getElementPolygon(rectValue, placement) { + const rect = createRect(rectValue); + const { top, right, left, bottom } = getRectCorners(rect); + const [base] = placement.split("-"); + return { + top: [left, top, right, bottom], + right: [top, right, bottom, left], + bottom: [top, left, bottom, right], + left: [right, top, left, bottom] + }[base]; +} +function isPointInPolygon(polygon, point) { + const { x, y } = point; + let c = false; + for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { + const xi = polygon[i].x; + const yi = polygon[i].y; + const xj = polygon[j].x; + const yj = polygon[j].y; + if (yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) { + c = !c; + } + } + return c; +} +var compassDirectionMap = { + n: { x: 0.5, y: 0 }, + ne: { x: 1, y: 0 }, + e: { x: 1, y: 0.5 }, + se: { x: 1, y: 1 }, + s: { x: 0.5, y: 1 }, + sw: { x: 0, y: 1 }, + w: { x: 0, y: 0.5 }, + nw: { x: 0, y: 0 } +}; +var oppositeDirectionMap = { + n: "s", + ne: "sw", + e: "w", + se: "nw", + s: "n", + sw: "ne", + w: "e", + nw: "se" +}; +var { sign, abs, min: min2 } = Math; +function getRectExtentPoint(rect, direction) { + const { minX, minY, maxX, maxY, midX, midY } = rect; + const x = direction.includes("w") ? minX : direction.includes("e") ? maxX : midX; + const y = direction.includes("n") ? minY : direction.includes("s") ? maxY : midY; + return { x, y }; +} +function getOppositeDirection(direction) { + return oppositeDirectionMap[direction]; +} +function resizeRect(rect, offset, direction, opts) { + const { scalingOriginMode, lockAspectRatio } = opts; + const extent = getRectExtentPoint(rect, direction); + const oppositeDirection = getOppositeDirection(direction); + const oppositeExtent = getRectExtentPoint(rect, oppositeDirection); + if (scalingOriginMode === "center") { + offset = { x: offset.x * 2, y: offset.y * 2 }; + } + const newExtent = { + x: extent.x + offset.x, + y: extent.y + offset.y + }; + const multiplier = { + x: compassDirectionMap[direction].x * 2 - 1, + y: compassDirectionMap[direction].y * 2 - 1 + }; + const newSize = { + width: newExtent.x - oppositeExtent.x, + height: newExtent.y - oppositeExtent.y + }; + const scaleX = multiplier.x * newSize.width / rect.width; + const scaleY = multiplier.y * newSize.height / rect.height; + const largestMagnitude = abs(scaleX) > abs(scaleY) ? scaleX : scaleY; + const scale = lockAspectRatio ? { x: largestMagnitude, y: largestMagnitude } : { + x: extent.x === oppositeExtent.x ? 1 : scaleX, + y: extent.y === oppositeExtent.y ? 1 : scaleY + }; + if (extent.y === oppositeExtent.y) { + scale.y = abs(scale.y); + } else if (sign(scale.y) !== sign(scaleY)) { + scale.y *= -1; + } + if (extent.x === oppositeExtent.x) { + scale.x = abs(scale.x); + } else if (sign(scale.x) !== sign(scaleX)) { + scale.x *= -1; + } + switch (scalingOriginMode) { + case "extent": + return transformRect(rect, AffineTransform.scale(scale.x, scale.y, oppositeExtent), false); + case "center": + return transformRect( + rect, + AffineTransform.scale(scale.x, scale.y, { + x: rect.midX, + y: rect.midY + }), + false + ); + } +} +function createRectFromPoints(initialPoint, finalPoint, normalized = true) { + if (normalized) { + return { + x: min2(finalPoint.x, initialPoint.x), + y: min2(finalPoint.y, initialPoint.y), + width: abs(finalPoint.x - initialPoint.x), + height: abs(finalPoint.y - initialPoint.y) + }; + } + return { + x: initialPoint.x, + y: initialPoint.y, + width: finalPoint.x - initialPoint.x, + height: finalPoint.y - initialPoint.y + }; +} +function transformRect(rect, transform, normalized = true) { + const p1 = transform.applyTo({ x: rect.minX, y: rect.minY }); + const p2 = transform.applyTo({ x: rect.maxX, y: rect.maxY }); + return createRectFromPoints(p1, p2, normalized); +} + +export { + getPointAngle, + clampPoint, + clampSize, + subtractPoints, + addPoints, + createRect, + constrainRect, + isSizeEqual, + isPointEqual, + getElementRect, + getWindowRect, + getElementPolygon, + isPointInPolygon, + resizeRect +}; diff --git a/priv/static/chunk-EENFWNGI.mjs b/priv/static/chunk-EENFWNGI.mjs new file mode 100644 index 0000000..832e989 --- /dev/null +++ b/priv/static/chunk-EENFWNGI.mjs @@ -0,0 +1,1957 @@ +import { + compact, + getComputedStyle as getComputedStyle2, + getWindow, + isHTMLElement, + isNull, + noop, + raf +} from "./chunk-IXOYOLUJ.mjs"; + +// ../node_modules/.pnpm/@floating-ui+utils@0.2.10/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs +var sides = ["top", "right", "bottom", "left"]; +var min = Math.min; +var max = Math.max; +var round = Math.round; +var floor = Math.floor; +var createCoords = (v) => ({ + x: v, + y: v +}); +var oppositeSideMap = { + left: "right", + right: "left", + bottom: "top", + top: "bottom" +}; +var oppositeAlignmentMap = { + start: "end", + end: "start" +}; +function clamp(start, value, end) { + return max(start, min(value, end)); +} +function evaluate(value, param) { + return typeof value === "function" ? value(param) : value; +} +function getSide(placement) { + return placement.split("-")[0]; +} +function getAlignment(placement) { + return placement.split("-")[1]; +} +function getOppositeAxis(axis) { + return axis === "x" ? "y" : "x"; +} +function getAxisLength(axis) { + return axis === "y" ? "height" : "width"; +} +var yAxisSides = /* @__PURE__ */ new Set(["top", "bottom"]); +function getSideAxis(placement) { + return yAxisSides.has(getSide(placement)) ? "y" : "x"; +} +function getAlignmentAxis(placement) { + return getOppositeAxis(getSideAxis(placement)); +} +function getAlignmentSides(placement, rects, rtl) { + if (rtl === void 0) { + rtl = false; + } + const alignment = getAlignment(placement); + const alignmentAxis = getAlignmentAxis(placement); + const length = getAxisLength(alignmentAxis); + let mainAlignmentSide = alignmentAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top"; + if (rects.reference[length] > rects.floating[length]) { + mainAlignmentSide = getOppositePlacement(mainAlignmentSide); + } + return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)]; +} +function getExpandedPlacements(placement) { + const oppositePlacement = getOppositePlacement(placement); + return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)]; +} +function getOppositeAlignmentPlacement(placement) { + return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]); +} +var lrPlacement = ["left", "right"]; +var rlPlacement = ["right", "left"]; +var tbPlacement = ["top", "bottom"]; +var btPlacement = ["bottom", "top"]; +function getSideList(side, isStart, rtl) { + switch (side) { + case "top": + case "bottom": + if (rtl) return isStart ? rlPlacement : lrPlacement; + return isStart ? lrPlacement : rlPlacement; + case "left": + case "right": + return isStart ? tbPlacement : btPlacement; + default: + return []; + } +} +function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) { + const alignment = getAlignment(placement); + let list = getSideList(getSide(placement), direction === "start", rtl); + if (alignment) { + list = list.map((side) => side + "-" + alignment); + if (flipAlignment) { + list = list.concat(list.map(getOppositeAlignmentPlacement)); + } + } + return list; +} +function getOppositePlacement(placement) { + return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]); +} +function expandPaddingObject(padding) { + return { + top: 0, + right: 0, + bottom: 0, + left: 0, + ...padding + }; +} +function getPaddingObject(padding) { + return typeof padding !== "number" ? expandPaddingObject(padding) : { + top: padding, + right: padding, + bottom: padding, + left: padding + }; +} +function rectToClientRect(rect) { + const { + x, + y, + width, + height + } = rect; + return { + width, + height, + top: y, + left: x, + right: x + width, + bottom: y + height, + x, + y + }; +} + +// ../node_modules/.pnpm/@floating-ui+core@1.7.4/node_modules/@floating-ui/core/dist/floating-ui.core.mjs +function computeCoordsFromPlacement(_ref, placement, rtl) { + let { + reference, + floating + } = _ref; + const sideAxis = getSideAxis(placement); + const alignmentAxis = getAlignmentAxis(placement); + const alignLength = getAxisLength(alignmentAxis); + const side = getSide(placement); + const isVertical = sideAxis === "y"; + const commonX = reference.x + reference.width / 2 - floating.width / 2; + const commonY = reference.y + reference.height / 2 - floating.height / 2; + const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2; + let coords; + switch (side) { + case "top": + coords = { + x: commonX, + y: reference.y - floating.height + }; + break; + case "bottom": + coords = { + x: commonX, + y: reference.y + reference.height + }; + break; + case "right": + coords = { + x: reference.x + reference.width, + y: commonY + }; + break; + case "left": + coords = { + x: reference.x - floating.width, + y: commonY + }; + break; + default: + coords = { + x: reference.x, + y: reference.y + }; + } + switch (getAlignment(placement)) { + case "start": + coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1); + break; + case "end": + coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1); + break; + } + return coords; +} +async function detectOverflow(state, options) { + var _await$platform$isEle; + if (options === void 0) { + options = {}; + } + const { + x, + y, + platform: platform2, + rects, + elements, + strategy + } = state; + const { + boundary = "clippingAncestors", + rootBoundary = "viewport", + elementContext = "floating", + altBoundary = false, + padding = 0 + } = evaluate(options, state); + const paddingObject = getPaddingObject(padding); + const altContext = elementContext === "floating" ? "reference" : "floating"; + const element = elements[altBoundary ? altContext : elementContext]; + const clippingClientRect = rectToClientRect(await platform2.getClippingRect({ + element: ((_await$platform$isEle = await (platform2.isElement == null ? void 0 : platform2.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || await (platform2.getDocumentElement == null ? void 0 : platform2.getDocumentElement(elements.floating)), + boundary, + rootBoundary, + strategy + })); + const rect = elementContext === "floating" ? { + x, + y, + width: rects.floating.width, + height: rects.floating.height + } : rects.reference; + const offsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(elements.floating)); + const offsetScale = await (platform2.isElement == null ? void 0 : platform2.isElement(offsetParent)) ? await (platform2.getScale == null ? void 0 : platform2.getScale(offsetParent)) || { + x: 1, + y: 1 + } : { + x: 1, + y: 1 + }; + const elementClientRect = rectToClientRect(platform2.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform2.convertOffsetParentRelativeRectToViewportRelativeRect({ + elements, + rect, + offsetParent, + strategy + }) : rect); + return { + top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y, + bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y, + left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x, + right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x + }; +} +var computePosition = async (reference, floating, config) => { + const { + placement = "bottom", + strategy = "absolute", + middleware = [], + platform: platform2 + } = config; + const validMiddleware = middleware.filter(Boolean); + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(floating)); + let rects = await platform2.getElementRects({ + reference, + floating, + strategy + }); + let { + x, + y + } = computeCoordsFromPlacement(rects, placement, rtl); + let statefulPlacement = placement; + let middlewareData = {}; + let resetCount = 0; + for (let i = 0; i < validMiddleware.length; i++) { + var _platform$detectOverf; + const { + name, + fn + } = validMiddleware[i]; + const { + x: nextX, + y: nextY, + data, + reset + } = await fn({ + x, + y, + initialPlacement: placement, + placement: statefulPlacement, + strategy, + middlewareData, + rects, + platform: { + ...platform2, + detectOverflow: (_platform$detectOverf = platform2.detectOverflow) != null ? _platform$detectOverf : detectOverflow + }, + elements: { + reference, + floating + } + }); + x = nextX != null ? nextX : x; + y = nextY != null ? nextY : y; + middlewareData = { + ...middlewareData, + [name]: { + ...middlewareData[name], + ...data + } + }; + if (reset && resetCount <= 50) { + resetCount++; + if (typeof reset === "object") { + if (reset.placement) { + statefulPlacement = reset.placement; + } + if (reset.rects) { + rects = reset.rects === true ? await platform2.getElementRects({ + reference, + floating, + strategy + }) : reset.rects; + } + ({ + x, + y + } = computeCoordsFromPlacement(rects, statefulPlacement, rtl)); + } + i = -1; + } + } + return { + x, + y, + placement: statefulPlacement, + strategy, + middlewareData + }; +}; +var arrow = (options) => ({ + name: "arrow", + options, + async fn(state) { + const { + x, + y, + placement, + rects, + platform: platform2, + elements, + middlewareData + } = state; + const { + element, + padding = 0 + } = evaluate(options, state) || {}; + if (element == null) { + return {}; + } + const paddingObject = getPaddingObject(padding); + const coords = { + x, + y + }; + const axis = getAlignmentAxis(placement); + const length = getAxisLength(axis); + const arrowDimensions = await platform2.getDimensions(element); + const isYAxis = axis === "y"; + const minProp = isYAxis ? "top" : "left"; + const maxProp = isYAxis ? "bottom" : "right"; + const clientProp = isYAxis ? "clientHeight" : "clientWidth"; + const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length]; + const startDiff = coords[axis] - rects.reference[axis]; + const arrowOffsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(element)); + let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0; + if (!clientSize || !await (platform2.isElement == null ? void 0 : platform2.isElement(arrowOffsetParent))) { + clientSize = elements.floating[clientProp] || rects.floating[length]; + } + const centerToReference = endDiff / 2 - startDiff / 2; + const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1; + const minPadding = min(paddingObject[minProp], largestPossiblePadding); + const maxPadding = min(paddingObject[maxProp], largestPossiblePadding); + const min$1 = minPadding; + const max2 = clientSize - arrowDimensions[length] - maxPadding; + const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference; + const offset3 = clamp(min$1, center, max2); + const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset3 && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0; + const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max2 : 0; + return { + [axis]: coords[axis] + alignmentOffset, + data: { + [axis]: offset3, + centerOffset: center - offset3 - alignmentOffset, + ...shouldAddOffset && { + alignmentOffset + } + }, + reset: shouldAddOffset + }; + } +}); +var flip = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "flip", + options, + async fn(state) { + var _middlewareData$arrow, _middlewareData$flip; + const { + placement, + middlewareData, + rects, + initialPlacement, + platform: platform2, + elements + } = state; + const { + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = true, + fallbackPlacements: specifiedFallbackPlacements, + fallbackStrategy = "bestFit", + fallbackAxisSideDirection = "none", + flipAlignment = true, + ...detectOverflowOptions + } = evaluate(options, state); + if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) { + return {}; + } + const side = getSide(placement); + const initialSideAxis = getSideAxis(initialPlacement); + const isBasePlacement = getSide(initialPlacement) === initialPlacement; + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); + const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement)); + const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== "none"; + if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) { + fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl)); + } + const placements2 = [initialPlacement, ...fallbackPlacements]; + const overflow = await platform2.detectOverflow(state, detectOverflowOptions); + const overflows = []; + let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || []; + if (checkMainAxis) { + overflows.push(overflow[side]); + } + if (checkCrossAxis) { + const sides2 = getAlignmentSides(placement, rects, rtl); + overflows.push(overflow[sides2[0]], overflow[sides2[1]]); + } + overflowsData = [...overflowsData, { + placement, + overflows + }]; + if (!overflows.every((side2) => side2 <= 0)) { + var _middlewareData$flip2, _overflowsData$filter; + const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1; + const nextPlacement = placements2[nextIndex]; + if (nextPlacement) { + const ignoreCrossAxisOverflow = checkCrossAxis === "alignment" ? initialSideAxis !== getSideAxis(nextPlacement) : false; + if (!ignoreCrossAxisOverflow || // We leave the current main axis only if every placement on that axis + // overflows the main axis. + overflowsData.every((d) => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) { + return { + data: { + index: nextIndex, + overflows: overflowsData + }, + reset: { + placement: nextPlacement + } + }; + } + } + let resetPlacement = (_overflowsData$filter = overflowsData.filter((d) => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement; + if (!resetPlacement) { + switch (fallbackStrategy) { + case "bestFit": { + var _overflowsData$filter2; + const placement2 = (_overflowsData$filter2 = overflowsData.filter((d) => { + if (hasFallbackAxisSideDirection) { + const currentSideAxis = getSideAxis(d.placement); + return currentSideAxis === initialSideAxis || // Create a bias to the `y` side axis due to horizontal + // reading directions favoring greater width. + currentSideAxis === "y"; + } + return true; + }).map((d) => [d.placement, d.overflows.filter((overflow2) => overflow2 > 0).reduce((acc, overflow2) => acc + overflow2, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0]; + if (placement2) { + resetPlacement = placement2; + } + break; + } + case "initialPlacement": + resetPlacement = initialPlacement; + break; + } + } + if (placement !== resetPlacement) { + return { + reset: { + placement: resetPlacement + } + }; + } + } + return {}; + } + }; +}; +function getSideOffsets(overflow, rect) { + return { + top: overflow.top - rect.height, + right: overflow.right - rect.width, + bottom: overflow.bottom - rect.height, + left: overflow.left - rect.width + }; +} +function isAnySideFullyClipped(overflow) { + return sides.some((side) => overflow[side] >= 0); +} +var hide = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "hide", + options, + async fn(state) { + const { + rects, + platform: platform2 + } = state; + const { + strategy = "referenceHidden", + ...detectOverflowOptions + } = evaluate(options, state); + switch (strategy) { + case "referenceHidden": { + const overflow = await platform2.detectOverflow(state, { + ...detectOverflowOptions, + elementContext: "reference" + }); + const offsets = getSideOffsets(overflow, rects.reference); + return { + data: { + referenceHiddenOffsets: offsets, + referenceHidden: isAnySideFullyClipped(offsets) + } + }; + } + case "escaped": { + const overflow = await platform2.detectOverflow(state, { + ...detectOverflowOptions, + altBoundary: true + }); + const offsets = getSideOffsets(overflow, rects.floating); + return { + data: { + escapedOffsets: offsets, + escaped: isAnySideFullyClipped(offsets) + } + }; + } + default: { + return {}; + } + } + } + }; +}; +var originSides = /* @__PURE__ */ new Set(["left", "top"]); +async function convertValueToCoords(state, options) { + const { + placement, + platform: platform2, + elements + } = state; + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); + const side = getSide(placement); + const alignment = getAlignment(placement); + const isVertical = getSideAxis(placement) === "y"; + const mainAxisMulti = originSides.has(side) ? -1 : 1; + const crossAxisMulti = rtl && isVertical ? -1 : 1; + const rawValue = evaluate(options, state); + let { + mainAxis, + crossAxis, + alignmentAxis + } = typeof rawValue === "number" ? { + mainAxis: rawValue, + crossAxis: 0, + alignmentAxis: null + } : { + mainAxis: rawValue.mainAxis || 0, + crossAxis: rawValue.crossAxis || 0, + alignmentAxis: rawValue.alignmentAxis + }; + if (alignment && typeof alignmentAxis === "number") { + crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis; + } + return isVertical ? { + x: crossAxis * crossAxisMulti, + y: mainAxis * mainAxisMulti + } : { + x: mainAxis * mainAxisMulti, + y: crossAxis * crossAxisMulti + }; +} +var offset = function(options) { + if (options === void 0) { + options = 0; + } + return { + name: "offset", + options, + async fn(state) { + var _middlewareData$offse, _middlewareData$arrow; + const { + x, + y, + placement, + middlewareData + } = state; + const diffCoords = await convertValueToCoords(state, options); + if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) { + return {}; + } + return { + x: x + diffCoords.x, + y: y + diffCoords.y, + data: { + ...diffCoords, + placement + } + }; + } + }; +}; +var shift = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "shift", + options, + async fn(state) { + const { + x, + y, + placement, + platform: platform2 + } = state; + const { + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = false, + limiter = { + fn: (_ref) => { + let { + x: x2, + y: y2 + } = _ref; + return { + x: x2, + y: y2 + }; + } + }, + ...detectOverflowOptions + } = evaluate(options, state); + const coords = { + x, + y + }; + const overflow = await platform2.detectOverflow(state, detectOverflowOptions); + const crossAxis = getSideAxis(getSide(placement)); + const mainAxis = getOppositeAxis(crossAxis); + let mainAxisCoord = coords[mainAxis]; + let crossAxisCoord = coords[crossAxis]; + if (checkMainAxis) { + const minSide = mainAxis === "y" ? "top" : "left"; + const maxSide = mainAxis === "y" ? "bottom" : "right"; + const min2 = mainAxisCoord + overflow[minSide]; + const max2 = mainAxisCoord - overflow[maxSide]; + mainAxisCoord = clamp(min2, mainAxisCoord, max2); + } + if (checkCrossAxis) { + const minSide = crossAxis === "y" ? "top" : "left"; + const maxSide = crossAxis === "y" ? "bottom" : "right"; + const min2 = crossAxisCoord + overflow[minSide]; + const max2 = crossAxisCoord - overflow[maxSide]; + crossAxisCoord = clamp(min2, crossAxisCoord, max2); + } + const limitedCoords = limiter.fn({ + ...state, + [mainAxis]: mainAxisCoord, + [crossAxis]: crossAxisCoord + }); + return { + ...limitedCoords, + data: { + x: limitedCoords.x - x, + y: limitedCoords.y - y, + enabled: { + [mainAxis]: checkMainAxis, + [crossAxis]: checkCrossAxis + } + } + }; + } + }; +}; +var limitShift = function(options) { + if (options === void 0) { + options = {}; + } + return { + options, + fn(state) { + const { + x, + y, + placement, + rects, + middlewareData + } = state; + const { + offset: offset3 = 0, + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = true + } = evaluate(options, state); + const coords = { + x, + y + }; + const crossAxis = getSideAxis(placement); + const mainAxis = getOppositeAxis(crossAxis); + let mainAxisCoord = coords[mainAxis]; + let crossAxisCoord = coords[crossAxis]; + const rawOffset = evaluate(offset3, state); + const computedOffset = typeof rawOffset === "number" ? { + mainAxis: rawOffset, + crossAxis: 0 + } : { + mainAxis: 0, + crossAxis: 0, + ...rawOffset + }; + if (checkMainAxis) { + const len = mainAxis === "y" ? "height" : "width"; + const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis; + const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis; + if (mainAxisCoord < limitMin) { + mainAxisCoord = limitMin; + } else if (mainAxisCoord > limitMax) { + mainAxisCoord = limitMax; + } + } + if (checkCrossAxis) { + var _middlewareData$offse, _middlewareData$offse2; + const len = mainAxis === "y" ? "width" : "height"; + const isOriginSide = originSides.has(getSide(placement)); + const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis); + const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0); + if (crossAxisCoord < limitMin) { + crossAxisCoord = limitMin; + } else if (crossAxisCoord > limitMax) { + crossAxisCoord = limitMax; + } + } + return { + [mainAxis]: mainAxisCoord, + [crossAxis]: crossAxisCoord + }; + } + }; +}; +var size = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "size", + options, + async fn(state) { + var _state$middlewareData, _state$middlewareData2; + const { + placement, + rects, + platform: platform2, + elements + } = state; + const { + apply = () => { + }, + ...detectOverflowOptions + } = evaluate(options, state); + const overflow = await platform2.detectOverflow(state, detectOverflowOptions); + const side = getSide(placement); + const alignment = getAlignment(placement); + const isYAxis = getSideAxis(placement) === "y"; + const { + width, + height + } = rects.floating; + let heightSide; + let widthSide; + if (side === "top" || side === "bottom") { + heightSide = side; + widthSide = alignment === (await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)) ? "start" : "end") ? "left" : "right"; + } else { + widthSide = side; + heightSide = alignment === "end" ? "top" : "bottom"; + } + const maximumClippingHeight = height - overflow.top - overflow.bottom; + const maximumClippingWidth = width - overflow.left - overflow.right; + const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight); + const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth); + const noShift = !state.middlewareData.shift; + let availableHeight = overflowAvailableHeight; + let availableWidth = overflowAvailableWidth; + if ((_state$middlewareData = state.middlewareData.shift) != null && _state$middlewareData.enabled.x) { + availableWidth = maximumClippingWidth; + } + if ((_state$middlewareData2 = state.middlewareData.shift) != null && _state$middlewareData2.enabled.y) { + availableHeight = maximumClippingHeight; + } + if (noShift && !alignment) { + const xMin = max(overflow.left, 0); + const xMax = max(overflow.right, 0); + const yMin = max(overflow.top, 0); + const yMax = max(overflow.bottom, 0); + if (isYAxis) { + availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right)); + } else { + availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom)); + } + } + await apply({ + ...state, + availableWidth, + availableHeight + }); + const nextDimensions = await platform2.getDimensions(elements.floating); + if (width !== nextDimensions.width || height !== nextDimensions.height) { + return { + reset: { + rects: true + } + }; + } + return {}; + } + }; +}; + +// ../node_modules/.pnpm/@floating-ui+utils@0.2.10/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs +function hasWindow() { + return typeof window !== "undefined"; +} +function getNodeName(node) { + if (isNode(node)) { + return (node.nodeName || "").toLowerCase(); + } + return "#document"; +} +function getWindow2(node) { + var _node$ownerDocument; + return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window; +} +function getDocumentElement(node) { + var _ref; + return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement; +} +function isNode(value) { + if (!hasWindow()) { + return false; + } + return value instanceof Node || value instanceof getWindow2(value).Node; +} +function isElement(value) { + if (!hasWindow()) { + return false; + } + return value instanceof Element || value instanceof getWindow2(value).Element; +} +function isHTMLElement2(value) { + if (!hasWindow()) { + return false; + } + return value instanceof HTMLElement || value instanceof getWindow2(value).HTMLElement; +} +function isShadowRoot(value) { + if (!hasWindow() || typeof ShadowRoot === "undefined") { + return false; + } + return value instanceof ShadowRoot || value instanceof getWindow2(value).ShadowRoot; +} +var invalidOverflowDisplayValues = /* @__PURE__ */ new Set(["inline", "contents"]); +function isOverflowElement(element) { + const { + overflow, + overflowX, + overflowY, + display + } = getComputedStyle3(element); + return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !invalidOverflowDisplayValues.has(display); +} +var tableElements = /* @__PURE__ */ new Set(["table", "td", "th"]); +function isTableElement(element) { + return tableElements.has(getNodeName(element)); +} +var topLayerSelectors = [":popover-open", ":modal"]; +function isTopLayer(element) { + return topLayerSelectors.some((selector) => { + try { + return element.matches(selector); + } catch (_e) { + return false; + } + }); +} +var transformProperties = ["transform", "translate", "scale", "rotate", "perspective"]; +var willChangeValues = ["transform", "translate", "scale", "rotate", "perspective", "filter"]; +var containValues = ["paint", "layout", "strict", "content"]; +function isContainingBlock(elementOrCss) { + const webkit = isWebKit(); + const css = isElement(elementOrCss) ? getComputedStyle3(elementOrCss) : elementOrCss; + return transformProperties.some((value) => css[value] ? css[value] !== "none" : false) || (css.containerType ? css.containerType !== "normal" : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== "none" : false) || !webkit && (css.filter ? css.filter !== "none" : false) || willChangeValues.some((value) => (css.willChange || "").includes(value)) || containValues.some((value) => (css.contain || "").includes(value)); +} +function getContainingBlock(element) { + let currentNode = getParentNode(element); + while (isHTMLElement2(currentNode) && !isLastTraversableNode(currentNode)) { + if (isContainingBlock(currentNode)) { + return currentNode; + } else if (isTopLayer(currentNode)) { + return null; + } + currentNode = getParentNode(currentNode); + } + return null; +} +function isWebKit() { + if (typeof CSS === "undefined" || !CSS.supports) return false; + return CSS.supports("-webkit-backdrop-filter", "none"); +} +var lastTraversableNodeNames = /* @__PURE__ */ new Set(["html", "body", "#document"]); +function isLastTraversableNode(node) { + return lastTraversableNodeNames.has(getNodeName(node)); +} +function getComputedStyle3(element) { + return getWindow2(element).getComputedStyle(element); +} +function getNodeScroll(element) { + if (isElement(element)) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; + } + return { + scrollLeft: element.scrollX, + scrollTop: element.scrollY + }; +} +function getParentNode(node) { + if (getNodeName(node) === "html") { + return node; + } + const result = ( + // Step into the shadow DOM of the parent of a slotted node. + node.assignedSlot || // DOM Element detected. + node.parentNode || // ShadowRoot detected. + isShadowRoot(node) && node.host || // Fallback. + getDocumentElement(node) + ); + return isShadowRoot(result) ? result.host : result; +} +function getNearestOverflowAncestor(node) { + const parentNode = getParentNode(node); + if (isLastTraversableNode(parentNode)) { + return node.ownerDocument ? node.ownerDocument.body : node.body; + } + if (isHTMLElement2(parentNode) && isOverflowElement(parentNode)) { + return parentNode; + } + return getNearestOverflowAncestor(parentNode); +} +function getOverflowAncestors(node, list, traverseIframes) { + var _node$ownerDocument2; + if (list === void 0) { + list = []; + } + if (traverseIframes === void 0) { + traverseIframes = true; + } + const scrollableAncestor = getNearestOverflowAncestor(node); + const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body); + const win = getWindow2(scrollableAncestor); + if (isBody) { + const frameElement = getFrameElement(win); + return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []); + } + return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes)); +} +function getFrameElement(win) { + return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null; +} + +// ../node_modules/.pnpm/@floating-ui+dom@1.7.5/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs +function getCssDimensions(element) { + const css = getComputedStyle3(element); + let width = parseFloat(css.width) || 0; + let height = parseFloat(css.height) || 0; + const hasOffset = isHTMLElement2(element); + const offsetWidth = hasOffset ? element.offsetWidth : width; + const offsetHeight = hasOffset ? element.offsetHeight : height; + const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight; + if (shouldFallback) { + width = offsetWidth; + height = offsetHeight; + } + return { + width, + height, + $: shouldFallback + }; +} +function unwrapElement(element) { + return !isElement(element) ? element.contextElement : element; +} +function getScale(element) { + const domElement = unwrapElement(element); + if (!isHTMLElement2(domElement)) { + return createCoords(1); + } + const rect = domElement.getBoundingClientRect(); + const { + width, + height, + $ + } = getCssDimensions(domElement); + let x = ($ ? round(rect.width) : rect.width) / width; + let y = ($ ? round(rect.height) : rect.height) / height; + if (!x || !Number.isFinite(x)) { + x = 1; + } + if (!y || !Number.isFinite(y)) { + y = 1; + } + return { + x, + y + }; +} +var noOffsets = /* @__PURE__ */ createCoords(0); +function getVisualOffsets(element) { + const win = getWindow2(element); + if (!isWebKit() || !win.visualViewport) { + return noOffsets; + } + return { + x: win.visualViewport.offsetLeft, + y: win.visualViewport.offsetTop + }; +} +function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) { + if (isFixed === void 0) { + isFixed = false; + } + if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow2(element)) { + return false; + } + return isFixed; +} +function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) { + if (includeScale === void 0) { + includeScale = false; + } + if (isFixedStrategy === void 0) { + isFixedStrategy = false; + } + const clientRect = element.getBoundingClientRect(); + const domElement = unwrapElement(element); + let scale = createCoords(1); + if (includeScale) { + if (offsetParent) { + if (isElement(offsetParent)) { + scale = getScale(offsetParent); + } + } else { + scale = getScale(element); + } + } + const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0); + let x = (clientRect.left + visualOffsets.x) / scale.x; + let y = (clientRect.top + visualOffsets.y) / scale.y; + let width = clientRect.width / scale.x; + let height = clientRect.height / scale.y; + if (domElement) { + const win = getWindow2(domElement); + const offsetWin = offsetParent && isElement(offsetParent) ? getWindow2(offsetParent) : offsetParent; + let currentWin = win; + let currentIFrame = getFrameElement(currentWin); + while (currentIFrame && offsetParent && offsetWin !== currentWin) { + const iframeScale = getScale(currentIFrame); + const iframeRect = currentIFrame.getBoundingClientRect(); + const css = getComputedStyle3(currentIFrame); + const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x; + const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y; + x *= iframeScale.x; + y *= iframeScale.y; + width *= iframeScale.x; + height *= iframeScale.y; + x += left; + y += top; + currentWin = getWindow2(currentIFrame); + currentIFrame = getFrameElement(currentWin); + } + } + return rectToClientRect({ + width, + height, + x, + y + }); +} +function getWindowScrollBarX(element, rect) { + const leftScroll = getNodeScroll(element).scrollLeft; + if (!rect) { + return getBoundingClientRect(getDocumentElement(element)).left + leftScroll; + } + return rect.left + leftScroll; +} +function getHTMLOffset(documentElement, scroll) { + const htmlRect = documentElement.getBoundingClientRect(); + const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect); + const y = htmlRect.top + scroll.scrollTop; + return { + x, + y + }; +} +function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) { + let { + elements, + rect, + offsetParent, + strategy + } = _ref; + const isFixed = strategy === "fixed"; + const documentElement = getDocumentElement(offsetParent); + const topLayer = elements ? isTopLayer(elements.floating) : false; + if (offsetParent === documentElement || topLayer && isFixed) { + return rect; + } + let scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + let scale = createCoords(1); + const offsets = createCoords(0); + const isOffsetParentAnElement = isHTMLElement2(offsetParent); + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + if (isHTMLElement2(offsetParent)) { + const offsetRect = getBoundingClientRect(offsetParent); + scale = getScale(offsetParent); + offsets.x = offsetRect.x + offsetParent.clientLeft; + offsets.y = offsetRect.y + offsetParent.clientTop; + } + } + const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0); + return { + width: rect.width * scale.x, + height: rect.height * scale.y, + x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x, + y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y + }; +} +function getClientRects(element) { + return Array.from(element.getClientRects()); +} +function getDocumentRect(element) { + const html = getDocumentElement(element); + const scroll = getNodeScroll(element); + const body = element.ownerDocument.body; + const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth); + const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight); + let x = -scroll.scrollLeft + getWindowScrollBarX(element); + const y = -scroll.scrollTop; + if (getComputedStyle3(body).direction === "rtl") { + x += max(html.clientWidth, body.clientWidth) - width; + } + return { + width, + height, + x, + y + }; +} +var SCROLLBAR_MAX = 25; +function getViewportRect(element, strategy) { + const win = getWindow2(element); + const html = getDocumentElement(element); + const visualViewport = win.visualViewport; + let width = html.clientWidth; + let height = html.clientHeight; + let x = 0; + let y = 0; + if (visualViewport) { + width = visualViewport.width; + height = visualViewport.height; + const visualViewportBased = isWebKit(); + if (!visualViewportBased || visualViewportBased && strategy === "fixed") { + x = visualViewport.offsetLeft; + y = visualViewport.offsetTop; + } + } + const windowScrollbarX = getWindowScrollBarX(html); + if (windowScrollbarX <= 0) { + const doc = html.ownerDocument; + const body = doc.body; + const bodyStyles = getComputedStyle(body); + const bodyMarginInline = doc.compatMode === "CSS1Compat" ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0; + const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline); + if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) { + width -= clippingStableScrollbarWidth; + } + } else if (windowScrollbarX <= SCROLLBAR_MAX) { + width += windowScrollbarX; + } + return { + width, + height, + x, + y + }; +} +var absoluteOrFixed = /* @__PURE__ */ new Set(["absolute", "fixed"]); +function getInnerBoundingClientRect(element, strategy) { + const clientRect = getBoundingClientRect(element, true, strategy === "fixed"); + const top = clientRect.top + element.clientTop; + const left = clientRect.left + element.clientLeft; + const scale = isHTMLElement2(element) ? getScale(element) : createCoords(1); + const width = element.clientWidth * scale.x; + const height = element.clientHeight * scale.y; + const x = left * scale.x; + const y = top * scale.y; + return { + width, + height, + x, + y + }; +} +function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) { + let rect; + if (clippingAncestor === "viewport") { + rect = getViewportRect(element, strategy); + } else if (clippingAncestor === "document") { + rect = getDocumentRect(getDocumentElement(element)); + } else if (isElement(clippingAncestor)) { + rect = getInnerBoundingClientRect(clippingAncestor, strategy); + } else { + const visualOffsets = getVisualOffsets(element); + rect = { + x: clippingAncestor.x - visualOffsets.x, + y: clippingAncestor.y - visualOffsets.y, + width: clippingAncestor.width, + height: clippingAncestor.height + }; + } + return rectToClientRect(rect); +} +function hasFixedPositionAncestor(element, stopNode) { + const parentNode = getParentNode(element); + if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) { + return false; + } + return getComputedStyle3(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode); +} +function getClippingElementAncestors(element, cache) { + const cachedResult = cache.get(element); + if (cachedResult) { + return cachedResult; + } + let result = getOverflowAncestors(element, [], false).filter((el) => isElement(el) && getNodeName(el) !== "body"); + let currentContainingBlockComputedStyle = null; + const elementIsFixed = getComputedStyle3(element).position === "fixed"; + let currentNode = elementIsFixed ? getParentNode(element) : element; + while (isElement(currentNode) && !isLastTraversableNode(currentNode)) { + const computedStyle = getComputedStyle3(currentNode); + const currentNodeIsContaining = isContainingBlock(currentNode); + if (!currentNodeIsContaining && computedStyle.position === "fixed") { + currentContainingBlockComputedStyle = null; + } + const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && absoluteOrFixed.has(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode); + if (shouldDropCurrentNode) { + result = result.filter((ancestor) => ancestor !== currentNode); + } else { + currentContainingBlockComputedStyle = computedStyle; + } + currentNode = getParentNode(currentNode); + } + cache.set(element, result); + return result; +} +function getClippingRect(_ref) { + let { + element, + boundary, + rootBoundary, + strategy + } = _ref; + const elementClippingAncestors = boundary === "clippingAncestors" ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary); + const clippingAncestors = [...elementClippingAncestors, rootBoundary]; + const firstClippingAncestor = clippingAncestors[0]; + const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => { + const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy); + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + return accRect; + }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy)); + return { + width: clippingRect.right - clippingRect.left, + height: clippingRect.bottom - clippingRect.top, + x: clippingRect.left, + y: clippingRect.top + }; +} +function getDimensions(element) { + const { + width, + height + } = getCssDimensions(element); + return { + width, + height + }; +} +function getRectRelativeToOffsetParent(element, offsetParent, strategy) { + const isOffsetParentAnElement = isHTMLElement2(offsetParent); + const documentElement = getDocumentElement(offsetParent); + const isFixed = strategy === "fixed"; + const rect = getBoundingClientRect(element, true, isFixed, offsetParent); + let scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + const offsets = createCoords(0); + function setLeftRTLScrollbarOffset() { + offsets.x = getWindowScrollBarX(documentElement); + } + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + if (isOffsetParentAnElement) { + const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent); + offsets.x = offsetRect.x + offsetParent.clientLeft; + offsets.y = offsetRect.y + offsetParent.clientTop; + } else if (documentElement) { + setLeftRTLScrollbarOffset(); + } + } + if (isFixed && !isOffsetParentAnElement && documentElement) { + setLeftRTLScrollbarOffset(); + } + const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0); + const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x; + const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y; + return { + x, + y, + width: rect.width, + height: rect.height + }; +} +function isStaticPositioned(element) { + return getComputedStyle3(element).position === "static"; +} +function getTrueOffsetParent(element, polyfill) { + if (!isHTMLElement2(element) || getComputedStyle3(element).position === "fixed") { + return null; + } + if (polyfill) { + return polyfill(element); + } + let rawOffsetParent = element.offsetParent; + if (getDocumentElement(element) === rawOffsetParent) { + rawOffsetParent = rawOffsetParent.ownerDocument.body; + } + return rawOffsetParent; +} +function getOffsetParent(element, polyfill) { + const win = getWindow2(element); + if (isTopLayer(element)) { + return win; + } + if (!isHTMLElement2(element)) { + let svgOffsetParent = getParentNode(element); + while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) { + if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) { + return svgOffsetParent; + } + svgOffsetParent = getParentNode(svgOffsetParent); + } + return win; + } + let offsetParent = getTrueOffsetParent(element, polyfill); + while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) { + offsetParent = getTrueOffsetParent(offsetParent, polyfill); + } + if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) { + return win; + } + return offsetParent || getContainingBlock(element) || win; +} +var getElementRects = async function(data) { + const getOffsetParentFn = this.getOffsetParent || getOffsetParent; + const getDimensionsFn = this.getDimensions; + const floatingDimensions = await getDimensionsFn(data.floating); + return { + reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy), + floating: { + x: 0, + y: 0, + width: floatingDimensions.width, + height: floatingDimensions.height + } + }; +}; +function isRTL(element) { + return getComputedStyle3(element).direction === "rtl"; +} +var platform = { + convertOffsetParentRelativeRectToViewportRelativeRect, + getDocumentElement, + getClippingRect, + getOffsetParent, + getElementRects, + getClientRects, + getDimensions, + getScale, + isElement, + isRTL +}; +function rectsAreEqual(a, b) { + return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height; +} +function observeMove(element, onMove) { + let io = null; + let timeoutId; + const root = getDocumentElement(element); + function cleanup() { + var _io; + clearTimeout(timeoutId); + (_io = io) == null || _io.disconnect(); + io = null; + } + function refresh(skip, threshold) { + if (skip === void 0) { + skip = false; + } + if (threshold === void 0) { + threshold = 1; + } + cleanup(); + const elementRectForRootMargin = element.getBoundingClientRect(); + const { + left, + top, + width, + height + } = elementRectForRootMargin; + if (!skip) { + onMove(); + } + if (!width || !height) { + return; + } + const insetTop = floor(top); + const insetRight = floor(root.clientWidth - (left + width)); + const insetBottom = floor(root.clientHeight - (top + height)); + const insetLeft = floor(left); + const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px"; + const options = { + rootMargin, + threshold: max(0, min(1, threshold)) || 1 + }; + let isFirstUpdate = true; + function handleObserve(entries) { + const ratio = entries[0].intersectionRatio; + if (ratio !== threshold) { + if (!isFirstUpdate) { + return refresh(); + } + if (!ratio) { + timeoutId = setTimeout(() => { + refresh(false, 1e-7); + }, 1e3); + } else { + refresh(false, ratio); + } + } + if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) { + refresh(); + } + isFirstUpdate = false; + } + try { + io = new IntersectionObserver(handleObserve, { + ...options, + // Handle