diff --git a/README.md b/README.md index ae97fcc..66ee9d8 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Add `corex` to your `mix.exs` dependencies: ```elixir def deps do [ - {:corex, "~> 0.1.0-alpha.25"} + {:corex, "~> 0.1.0-alpha.26"} ] end ``` diff --git a/assets/components/combobox.ts b/assets/components/combobox.ts index f641321..5fe5d92 100644 --- a/assets/components/combobox.ts +++ b/assets/components/combobox.ts @@ -22,7 +22,7 @@ export class Combobox extends Component { this.options = options; } - private getCollection() { + getCollection() { const items = this.options || this.allOptions || []; if (this.hasGroups) { @@ -45,7 +45,7 @@ export class Combobox extends Component { // eslint-disable-next-line @typescript-eslint/no-explicit-any initMachine(props: Props): VanillaMachine { - const getCollection = this.getCollection.bind(this); + const getCollection = () => this.getCollection(); return new VanillaMachine(machine, { ...props, @@ -61,14 +61,17 @@ export class Combobox extends Component { } }, onInputValueChange: (details: InputValueChangeDetails) => { - const filtered = this.allOptions.filter((item: ComboboxItem) => - item.label.toLowerCase().includes(details.inputValue.toLowerCase()) - ); - this.options = filtered.length > 0 ? filtered : this.allOptions; - if (props.onInputValueChange) { props.onInputValueChange(details); } + if (this.el.hasAttribute("data-filter")) { + const filtered = this.allOptions.filter((item: ComboboxItem) => + item.label.toLowerCase().includes(details.inputValue.toLowerCase()) + ); + this.options = filtered.length > 0 ? filtered : this.allOptions; + } else { + this.options = this.allOptions; + } }, }); } @@ -94,38 +97,88 @@ export class Combobox extends Component { .querySelectorAll('[data-scope="combobox"][data-part="item-group"]:not([data-template])') .forEach((el) => el.remove()); - if (this.hasGroups) { + contentEl + .querySelectorAll('[data-scope="combobox"][data-part="empty"]:not([data-template])') + .forEach((el) => el.remove()); + + const items = this.options?.length ? this.options : this.allOptions; + + if (items.length === 0) { + const emptyTemplate = templatesContainer.querySelector( + '[data-scope="combobox"][data-part="empty"][data-template]' + ); + if (emptyTemplate) { + const emptyEl = emptyTemplate.cloneNode(true) as HTMLElement; + emptyEl.removeAttribute("data-template"); + contentEl.appendChild(emptyEl); + } + } else if (this.hasGroups) { const groups = this.api.collection.group?.() ?? []; this.renderGroupedItems(contentEl, templatesContainer, groups); } else { - const items = this.options?.length ? this.options : this.allOptions; this.renderFlatItems(contentEl, templatesContainer, items); } } + buildOrderedBlocks( + items: ComboboxItem[] + ): ( + | { type: "default"; items: ComboboxItem[] } + | { type: "group"; groupId: string; items: ComboboxItem[] } + )[] { + const blocks: ( + | { type: "default"; items: ComboboxItem[] } + | { type: "group"; groupId: string; items: ComboboxItem[] } + )[] = []; + let current: + | { type: "default"; items: ComboboxItem[] } + | { type: "group"; groupId: string; items: ComboboxItem[] } + | null = null; + + for (const item of items) { + const groupKey = item.group ?? ""; + if (groupKey === "") { + if (current?.type !== "default") { + current = { type: "default", items: [] }; + blocks.push(current); + } + current.items.push(item); + } else { + if (current?.type !== "group" || current.groupId !== groupKey) { + current = { type: "group", groupId: groupKey, items: [] }; + blocks.push(current); + } + current.items.push(item); + } + } + return blocks; + } + renderGroupedItems( contentEl: HTMLElement, templatesContainer: HTMLElement, - groups: [string | null, ComboboxItem[]][] + _groups: [string | null, ComboboxItem[]][] ): void { - for (const [groupId, groupItems] of groups) { - if (groupId == null) continue; + const items = this.options?.length ? this.options : this.allOptions; + const blocks = this.buildOrderedBlocks(items); + for (const block of blocks) { + const templateId = block.type === "default" ? "default" : block.groupId; const groupTemplate = templatesContainer.querySelector( - `[data-scope="combobox"][data-part="item-group"][data-id="${groupId}"][data-template]` + `[data-scope="combobox"][data-part="item-group"][data-id="${templateId}"][data-template]` ); if (!groupTemplate) continue; const groupEl = groupTemplate.cloneNode(true) as HTMLElement; groupEl.removeAttribute("data-template"); - this.spreadProps(groupEl, this.api.getItemGroupProps({ id: groupId })); + this.spreadProps(groupEl, this.api.getItemGroupProps({ id: templateId })); const labelEl = groupEl.querySelector( '[data-scope="combobox"][data-part="item-group-label"]' ); if (labelEl) { - this.spreadProps(labelEl, this.api.getItemGroupLabelProps({ htmlFor: groupId })); + this.spreadProps(labelEl, this.api.getItemGroupLabelProps({ htmlFor: templateId })); } const groupContentEl = groupEl.querySelector( @@ -135,7 +188,7 @@ export class Combobox extends Component { groupContentEl.innerHTML = ""; - for (const item of groupItems) { + for (const item of block.items) { const itemEl = this.cloneItem(templatesContainer, item); if (itemEl) groupContentEl.appendChild(itemEl); } @@ -171,7 +224,6 @@ export class Combobox extends Component { const textEl = el.querySelector('[data-scope="combobox"][data-part="item-text"]'); if (textEl) { this.spreadProps(textEl, this.api.getItemTextProps({ item })); - // Only set textContent if there are no child elements (custom HTML slots preserve their structure) if (textEl.children.length === 0) { textEl.textContent = item.label || ""; } diff --git a/assets/hooks/combobox.ts b/assets/hooks/combobox.ts index aef5013..dd4a092 100644 --- a/assets/hooks/combobox.ts +++ b/assets/hooks/combobox.ts @@ -55,7 +55,7 @@ const ComboboxHook: Hook = { invalid: getBoolean(el, "invalid"), allowCustomValue: false, selectionBehavior: "replace", - name: "", + name: getString(el, "name"), form: getString(el, "form"), readOnly: getBoolean(el, "readOnly"), required: getBoolean(el, "required"), @@ -122,65 +122,12 @@ const ComboboxHook: Hook = { } }, onValueChange: (details: ValueChangeDetails) => { - const valueInput = el.querySelector( - '[data-scope="combobox"][data-part="value-input"]' + const hiddenInput = el.querySelector( + '[data-scope="combobox"][data-part="input"]' ); - if (valueInput) { - const toId = (val: string) => { - const item = allItems.find( - (i: { id?: string; label?: string }) => String(i.id ?? "") === val || i.label === val - ); - return item ? String(item.id ?? "") : val; - }; - const idValue = - details.value.length === 0 - ? "" - : details.value.length === 1 - ? toId(String(details.value[0])) - : details.value.map((v) => toId(String(v))).join(","); - valueInput.value = idValue; - - const formId = valueInput.getAttribute("form"); - let form: HTMLFormElement | null = null; - - if (formId) { - form = document.getElementById(formId) as HTMLFormElement; - } else { - form = valueInput.closest("form"); - } - - const changeEvent = new Event("change", { - bubbles: true, - cancelable: true, - }); - valueInput.dispatchEvent(changeEvent); - - const inputEvent = new Event("input", { - bubbles: true, - cancelable: true, - }); - valueInput.dispatchEvent(inputEvent); - - if (form && form.hasAttribute("phx-change")) { - requestAnimationFrame(() => { - const formElement = form.querySelector("input, select, textarea") as HTMLElement; - if (formElement) { - const formChangeEvent = new Event("change", { - bubbles: true, - cancelable: true, - }); - formElement.dispatchEvent(formChangeEvent); - } else { - const formChangeEvent = new Event("change", { - bubbles: true, - cancelable: true, - }); - form.dispatchEvent(formChangeEvent); - } - }); - } + if (hiddenInput) { + hiddenInput.dispatchEvent(new Event("change", { bubbles: true })); } - const eventName = getString(el, "onValueChange"); if (eventName && !this.liveSocket.main.isDead && this.liveSocket.main.isConnected()) { pushEvent(eventName, { @@ -211,39 +158,6 @@ const ComboboxHook: Hook = { combobox.setAllOptions(allItems); combobox.init(); - const visibleInput = el.querySelector( - '[data-scope="combobox"][data-part="input"]' - ); - if (visibleInput) { - visibleInput.removeAttribute("name"); - visibleInput.removeAttribute("form"); - } - - const initialValue = getBoolean(el, "controlled") - ? getStringList(el, "value") - : getStringList(el, "defaultValue"); - - if (initialValue && initialValue.length > 0) { - const selectedItems = allItems.filter((item: { id?: string }) => - initialValue.includes(item.id ?? "") - ); - if (selectedItems.length > 0) { - const inputValue = selectedItems - .map((item: { label?: string }) => item.label ?? "") - .join(", "); - if (combobox.api && typeof combobox.api.setInputValue === "function") { - combobox.api.setInputValue(inputValue); - } else { - const inputEl = el.querySelector( - '[data-scope="combobox"][data-part="input"]' - ); - if (inputEl) { - inputEl.value = inputValue; - } - } - } - } - this.combobox = combobox; this.handlers = []; }, @@ -255,10 +169,12 @@ const ComboboxHook: Hook = { if (this.combobox) { this.combobox.hasGroups = hasGroups; this.combobox.setAllOptions(newCollection); + this.combobox.render(); this.combobox.updateProps({ ...(getBoolean(this.el, "controlled") ? { value: getStringList(this.el, "value") } : { defaultValue: getStringList(this.el, "defaultValue") }), + collection: this.combobox.getCollection(), name: getString(this.el, "name"), form: getString(this.el, "form"), disabled: getBoolean(this.el, "disabled"), @@ -268,15 +184,6 @@ const ComboboxHook: Hook = { required: getBoolean(this.el, "required"), readOnly: getBoolean(this.el, "readOnly"), }); - - const inputEl = this.el.querySelector( - '[data-scope="combobox"][data-part="input"]' - ); - if (inputEl) { - inputEl.removeAttribute("name"); - inputEl.removeAttribute("form"); - inputEl.name = ""; - } } }, @@ -286,7 +193,6 @@ const ComboboxHook: Hook = { this.removeHandleEvent(handler); } } - this.combobox?.destroy(); }, }; diff --git a/design/components/combobox.css b/design/components/combobox.css index 7731191..f455e0a 100644 --- a/design/components/combobox.css +++ b/design/components/combobox.css @@ -14,6 +14,14 @@ @apply ui-label; } + .combobox [data-scope="combobox"][data-part="empty"] { + @apply ui-label; + + list-style: none; + padding-inline: var(--spacing-ui-padding); + padding-block: var(--spacing-mini-padding-sm); + } + .combobox [data-scope="combobox"][data-part="control"] { display: flex; width: 100%; @@ -66,6 +74,13 @@ .combobox [data-scope="combobox"][data-part="item-group"] { display: flex; flex-direction: column; + min-height: fit-content; + } + + .combobox [data-scope="combobox"][data-part="item-group-content"] { + min-height: fit-content; + display: flex; + flex-direction: column; } .combobox [data-scope="combobox"][data-part="item-group-label"] { @@ -81,6 +96,13 @@ .combobox [data-scope="combobox"][data-part="item"] { @apply ui-item; + + align-items: flex-start; + min-height: fit-content; + } + + .combobox [data-scope="combobox"][data-part="item"] [data-part="item-text"] { + min-height: fit-content; } .combobox [data-scope="combobox"][data-part="error"] { diff --git a/e2e/assets/corex/components/combobox.css b/e2e/assets/corex/components/combobox.css index 7731191..f455e0a 100644 --- a/e2e/assets/corex/components/combobox.css +++ b/e2e/assets/corex/components/combobox.css @@ -14,6 +14,14 @@ @apply ui-label; } + .combobox [data-scope="combobox"][data-part="empty"] { + @apply ui-label; + + list-style: none; + padding-inline: var(--spacing-ui-padding); + padding-block: var(--spacing-mini-padding-sm); + } + .combobox [data-scope="combobox"][data-part="control"] { display: flex; width: 100%; @@ -66,6 +74,13 @@ .combobox [data-scope="combobox"][data-part="item-group"] { display: flex; flex-direction: column; + min-height: fit-content; + } + + .combobox [data-scope="combobox"][data-part="item-group-content"] { + min-height: fit-content; + display: flex; + flex-direction: column; } .combobox [data-scope="combobox"][data-part="item-group-label"] { @@ -81,6 +96,13 @@ .combobox [data-scope="combobox"][data-part="item"] { @apply ui-item; + + align-items: flex-start; + min-height: fit-content; + } + + .combobox [data-scope="combobox"][data-part="item"] [data-part="item-text"] { + min-height: fit-content; } .combobox [data-scope="combobox"][data-part="error"] { diff --git a/e2e/lib/e2e/cldr.ex b/e2e/lib/e2e/cldr.ex new file mode 100644 index 0000000..2029b48 --- /dev/null +++ b/e2e/lib/e2e/cldr.ex @@ -0,0 +1,5 @@ +defmodule E2e.Cldr do + use Cldr, + locales: [:en], + providers: [Cldr.Territory] +end diff --git a/e2e/lib/e2e/form/combobox.ex b/e2e/lib/e2e/form/combobox.ex new file mode 100644 index 0000000..eeb3197 --- /dev/null +++ b/e2e/lib/e2e/form/combobox.ex @@ -0,0 +1,22 @@ +defmodule E2e.Form.Combobox do + use Ecto.Schema + import Ecto.Changeset + alias E2e.Form.Combobox + + embedded_schema do + field :name, :string + field :airport, :string + end + + @doc false + def change_combobox(%Combobox{} = combobox, attrs \\ %{}) do + changeset(combobox, attrs) + end + + @doc false + def changeset(%Combobox{} = combobox, attrs) do + combobox + |> cast(attrs, [:name, :airport]) + |> validate_required([:name, :airport]) + end +end diff --git a/e2e/lib/e2e/place.ex b/e2e/lib/e2e/place.ex new file mode 100644 index 0000000..b255a9b --- /dev/null +++ b/e2e/lib/e2e/place.ex @@ -0,0 +1,282 @@ +defmodule E2e.Place do + @moduledoc """ + The Place context. + """ + + import Ecto.Query, warn: false + alias E2e.Repo + + alias E2e.Place.City + alias E2e.Place.Airport + + @doc """ + Returns the list of cities. + + ## Examples + + iex> list_cities() + [%City{}, ...] + + """ + def list_cities do + Repo.all(City) + end + + @doc """ + Gets a single city. + + Raises `Ecto.NoResultsError` if the City does not exist. + + ## Examples + + iex> get_city!(123) + %City{} + + iex> get_city!(456) + ** (Ecto.NoResultsError) + + """ + def get_city!(id), do: Repo.get!(City, id) + + @doc """ + Creates a city. + + ## Examples + + iex> create_city(%{field: value}) + {:ok, %City{}} + + iex> create_city(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_city(attrs \\ %{}) do + %City{} + |> City.changeset(attrs) + |> Repo.insert(on_conflict: :nothing, conflict_target: :id) + end + + @doc """ + Updates a city. + + ## Examples + + iex> update_city(city, %{field: new_value}) + {:ok, %City{}} + + iex> update_city(city, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_city(%City{} = city, attrs) do + city + |> City.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a city. + + ## Examples + + iex> delete_city(city) + {:ok, %City{}} + + iex> delete_city(city) + {:error, %Ecto.Changeset{}} + + """ + def delete_city(%City{} = city) do + Repo.delete(city) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking city changes. + + ## Examples + + iex> change_city(city) + %Ecto.Changeset{data: %City{}} + + """ + def change_city(%City{} = city, attrs \\ %{}) do + City.changeset(city, attrs) + end + + def list_cities(query) when is_binary(query) do + City + |> where([c], ilike(c.name, ^"#{query}%") or ilike(c.iata_code, ^"#{query}%")) + |> limit(50) + |> Repo.all() + end + + def list_airports(query) when is_binary(query) do + Airport + |> where([a], ilike(a.name, ^"#{query}%") or ilike(a.iata_code, ^"#{query}%")) + |> limit(50) + |> Repo.all() + end + + @doc """ + Returns the list of airports. + + ## Examples + + iex> list_airports() + [%Airport{}, ...] + + """ + def list_airports do + Repo.all(Airport) + end + + def list_airports_first(count) when is_integer(count) do + from(Airport, order_by: [asc: :name], limit: ^count) |> Repo.all() + end + + def list_airports_first(count, offset) when is_integer(count) and is_integer(offset) do + from(Airport, order_by: [asc: :name], limit: ^count, offset: ^offset) |> Repo.all() + end + + @popular_iata_by_continent [ + {"Popular in Europe", ["CDG", "LHR", "FRA", "AMS", "MAD", "BCN", "FCO", "ZRH", "VIE"]}, + {"Popular in Asia", ["NRT", "HND", "ICN", "PVG", "HKG", "SIN", "BKK", "KUL"]}, + {"Popular in North America", ["JFK", "LAX", "ORD", "YYZ", "MIA", "SFO"]}, + {"Popular in Middle East", ["DXB", "AUH", "DOH"]}, + {"Popular in Oceania", ["SYD", "AKL", "MEL"]} + ] + + def list_popular_airports_by_continent do + all_codes = @popular_iata_by_continent |> Enum.flat_map(fn {_, codes} -> codes end) + + airports = + from(a in Airport, where: a.iata_code in ^all_codes) + |> Repo.all() + + airport_map = Map.new(airports, &{&1.iata_code, &1}) + + for {continent, codes} <- @popular_iata_by_continent, + code <- codes, + airport = airport_map[code], + !is_nil(airport) do + {airport, continent} + end + end + + def search_airports(term, limit, offset) + when is_binary(term) and is_integer(limit) and is_integer(offset) do + search = "%#{term}%" + + Airport + |> where([a], ilike(a.name, ^search) or ilike(a.iata_code, ^search)) + |> order_by([a], asc: a.name) + |> limit(^limit) + |> offset(^offset) + |> Repo.all() + end + + @doc """ + Gets a single airport. + + Raises `Ecto.NoResultsError` if the Airport does not exist. + + ## Examples + + iex> get_airport!(123) + %Airport{} + + iex> get_airport!(456) + ** (Ecto.NoResultsError) + + """ + def get_airport!(id), do: Repo.get!(Airport, id) + + @doc """ + Creates a airport. + + ## Examples + + iex> create_airport(%{field: value}) + {:ok, %Airport{}} + + iex> create_airport(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_airport(attrs \\ %{}) do + %Airport{} + |> Airport.changeset(attrs) + |> Repo.insert(on_conflict: :nothing, conflict_target: :id) + end + + @doc """ + Updates a airport. + + ## Examples + + iex> update_airport(airport, %{field: new_value}) + {:ok, %Airport{}} + + iex> update_airport(airport, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_airport(%Airport{} = airport, attrs) do + airport + |> Airport.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a airport. + + ## Examples + + iex> delete_airport(airport) + {:ok, %Airport{}} + + iex> delete_airport(airport) + {:error, %Ecto.Changeset{}} + + """ + def delete_airport(%Airport{} = airport) do + Repo.delete(airport) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking airport changes. + + ## Examples + + iex> change_airport(airport) + %Ecto.Changeset{data: %Airport{}} + + """ + def change_airport(%Airport{} = airport, attrs \\ %{}) do + Airport.changeset(airport, attrs) + end + + def list_random_city do + Repo.one( + from City, + order_by: fragment("RANDOM()"), + limit: 1 + ) + end + + def list_random_airport do + Repo.one( + from Airport, + order_by: fragment("RANDOM()"), + limit: 1 + ) + end + + def list_random_airports(count) when is_integer(count) do + from(Airport, order_by: fragment("RANDOM()"), limit: ^count) |> Repo.all() + end + + def search_airports(term) do + search_airports(term, 20, 0) + end +end diff --git a/e2e/lib/e2e/place/airport.ex b/e2e/lib/e2e/place/airport.ex new file mode 100644 index 0000000..fd2305e --- /dev/null +++ b/e2e/lib/e2e/place/airport.ex @@ -0,0 +1,47 @@ +defmodule E2e.Place.Airport do + use Ecto.Schema + import Ecto.Changeset + alias E2e.Place.City + + @primary_key {:id, :string, autogenerate: false} + + schema "airports" do + field :name, :string + field :time_zone, :string + field :iata_code, :string + field :city_name, :string + field :iata_city_code, :string + field :iata_country_code, :string + field :icao_code, :string + field :latitude, :float + field :longitude, :float + + embeds_one :city, City, on_replace: :delete + end + + @doc false + def changeset(airport, attrs) do + airport + |> cast(attrs, [ + :id, + :iata_code, + :name, + :city_name, + :iata_city_code, + :iata_country_code, + :icao_code, + :latitude, + :longitude, + :time_zone + ]) + |> validate_required([ + :iata_code, + :name, + :iata_city_code, + :iata_country_code, + :latitude, + :longitude, + :time_zone + ]) + end +end diff --git a/e2e/lib/e2e/place/city.ex b/e2e/lib/e2e/place/city.ex new file mode 100644 index 0000000..8156018 --- /dev/null +++ b/e2e/lib/e2e/place/city.ex @@ -0,0 +1,23 @@ +defmodule E2e.Place.City do + use Ecto.Schema + import Ecto.Changeset + alias E2e.Place.Airport + + @primary_key {:id, :string, autogenerate: false} + + schema "cities" do + field :name, :string + field :iata_code, :string + field :iata_country_code, :string + + embeds_many :airports, Airport, on_replace: :delete + end + + @doc false + def changeset(city, attrs) do + city + |> cast(attrs, [:id, :iata_code, :iata_country_code, :name]) + |> validate_required([:iata_code, :iata_country_code, :name]) + |> cast_embed(:airports) + end +end diff --git a/e2e/lib/e2e/place/helper.ex b/e2e/lib/e2e/place/helper.ex new file mode 100644 index 0000000..e72d616 --- /dev/null +++ b/e2e/lib/e2e/place/helper.ex @@ -0,0 +1,48 @@ +defmodule E2e.Place.Helper do + @moduledoc false + alias E2e.Place + + require Logger + + def country_name_from_code(country_code) do + {:ok, country_name} = Cldr.Territory.display_name(country_code) + country_name + end + + # Save from seeds + def fetch_and_insert_cities() do + priv_dir = :code.priv_dir(:e2e) + file_path = Path.join([priv_dir, "repo", "seeds", "cities.tar.gz"]) + + {:ok, file} = File.open(file_path, [:read, :utf8, :compressed]) + + all_cities = IO.read(file, :eof) + + File.close(file) + + for city <- Jason.decode!(all_cities) do + case Place.create_city(city) do + {:error, changeset} -> Logger.debug(changeset) + {:ok, _city} -> :ok + end + end + end + + def fetch_and_insert_airports() do + priv_dir = :code.priv_dir(:e2e) + file_path = Path.join([priv_dir, "repo", "seeds", "airports.tar.gz"]) + + {:ok, file} = File.open(file_path, [:read, :utf8, :compressed]) + + all_airports = IO.read(file, :eof) + + File.close(file) + + for airport <- Jason.decode!(all_airports) do + case Place.create_airport(airport) do + {:error, changeset} -> Logger.debug(changeset) + {:ok, _airport} -> :ok + end + end + end +end diff --git a/e2e/lib/e2e_web/components/layouts.ex b/e2e/lib/e2e_web/components/layouts.ex index 67369cd..3350070 100644 --- a/e2e/lib/e2e_web/components/layouts.ex +++ b/e2e/lib/e2e_web/components/layouts.ex @@ -389,7 +389,9 @@ defmodule E2eWeb.Layouts do id: "combobox", children: [ [label: "Controller", id: "/#{locale}/combobox"], - [label: "Live", id: "/#{locale}/live/combobox"] + [label: "Live", id: "/#{locale}/live/combobox"], + [label: "Fetch", id: "/#{locale}/live/combobox-fetch"], + [label: "Form", id: "/#{locale}/live/combobox-form"] ] ], [ diff --git a/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex index 99ea30b..ff967f5 100644 --- a/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex @@ -23,6 +23,7 @@ <.accordion + id="my-accordion" class="accordion" items={ Corex.Content.new([ diff --git a/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex index 951b1dd..cfc59e7 100644 --- a/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex @@ -19,6 +19,7 @@ %{label: "Austria", id: "aut"} ]} > + <:empty>No results <:trigger> <.icon name="hero-chevron-down" /> @@ -44,6 +45,7 @@ %{label: "Mexico", id: "mex", group: "North America"} ]} > + <:empty>No results <:trigger> <.icon name="hero-chevron-down" /> @@ -62,6 +64,7 @@ %{label: "Austria", id: "aut"} ]} > + <:empty>No results <:label> Country of residence @@ -93,6 +96,7 @@ %{label: "South Korea", id: "kor", group: "Asia"} ]} > + <:empty>No results <:item :let={item}> {item.label} diff --git a/e2e/lib/e2e_web/live/combobox_fetch.ex b/e2e/lib/e2e_web/live/combobox_fetch.ex new file mode 100644 index 0000000..b909401 --- /dev/null +++ b/e2e/lib/e2e_web/live/combobox_fetch.ex @@ -0,0 +1,120 @@ +defmodule E2eWeb.ComboboxFetch do + use E2eWeb, :live_view + + @items [ + %{id: "fra", label: "France"}, + %{id: "deu", label: "Germany"}, + %{id: "gbr", label: "United Kingdom"}, + %{id: "esp", label: "Spain"}, + %{id: "ita", label: "Italy"}, + %{id: "bel", label: "Belgium"}, + %{id: "nld", label: "Netherlands"}, + %{id: "che", label: "Switzerland"}, + %{id: "aut", label: "Austria"}, + %{id: "jpn", label: "Japan"}, + %{id: "chn", label: "China"}, + %{id: "kor", label: "South Korea"}, + %{id: "tha", label: "Thailand"}, + %{id: "usa", label: "USA"}, + %{id: "can", label: "Canada"}, + %{id: "mex", label: "Mexico"}, + %{id: "aus", label: "Australia"}, + %{id: "nzl", label: "New Zealand"}, + %{id: "are", label: "UAE"}, + %{id: "tur", label: "Turkey"} + ] + + @popular [ + %{id: "fra", label: "France", group: "Popular in Europe"}, + %{id: "deu", label: "Germany", group: "Popular in Europe"}, + %{id: "jpn", label: "Japan", group: "Popular in Asia"}, + %{id: "chn", label: "China", group: "Popular in Asia"}, + %{id: "usa", label: "USA", group: "Popular in North America"}, + %{id: "can", label: "Canada", group: "Popular in North America"}, + %{id: "aus", label: "Australia", group: "Popular in Oceania"}, + %{id: "nzl", label: "New Zealand", group: "Popular in Oceania"}, + %{id: "are", label: "UAE", group: "Popular in Middle East"}, + %{id: "tur", label: "Turkey", group: "Popular in Middle East"} + ] + + def mount(_params, _session, socket) do + {:ok, + socket + |> assign(:items, @popular) + |> assign(:search_term, nil)} + end + + def handle_event("search", %{"reason" => "clear-trigger"}, socket) do + {:noreply, + socket + |> assign(:items, @popular) + |> assign(:search_term, nil)} + end + + def handle_event("search", %{"reason" => "item-select"}, socket) do + {:noreply, socket} + end + + def handle_event("search", %{"value" => value, "reason" => "input-change"}, socket) + when byte_size(value) < 1 do + {:noreply, + socket + |> assign(:items, @popular) + |> assign(:search_term, nil)} + end + + def handle_event("search", %{"value" => value, "reason" => "input-change"}, socket) do + term = String.downcase(value) + + filtered = + @items + |> Enum.filter(fn item -> + String.contains?(String.downcase(item.label), term) + end) + + {:noreply, + socket + |> assign(:items, filtered) + |> assign(:search_term, value)} + end + + def handle_event("search", _params, socket) do + {:noreply, socket} + end + + def render(assigns) do + ~H""" + +
+

Combobox

+

Server-side Filtering

+
+ + <.header> + Server-side filtering + <:subtitle> + Use on_input_value_change to filter on the server. Useful for large item lists. +

Can be used with a database query to filter the items or a local list.

+ + + + <.combobox + id="country-combobox" + class="combobox" + placeholder="Search countries..." + collection={@items} + filter={false} + on_input_value_change="search" + > + <:empty>No results + <:trigger> + <.icon name="hero-chevron-down" /> + + <:clear_trigger> + <.icon name="hero-backspace" /> + + +
+ """ + end +end diff --git a/e2e/lib/e2e_web/live/combobox_form.ex b/e2e/lib/e2e_web/live/combobox_form.ex new file mode 100644 index 0000000..080b624 --- /dev/null +++ b/e2e/lib/e2e_web/live/combobox_form.ex @@ -0,0 +1,202 @@ +defmodule E2eWeb.ComboboxForm do + use E2eWeb, :live_view + + alias Corex.Toast + alias E2e.Form.Combobox + alias E2e.Place + + @max_items 60 + + @impl true + def mount(_params, _session, socket) do + combobox = %Combobox{} + raw = Place.list_popular_airports_by_continent() + + {:ok, + socket + |> assign(:combobox, combobox) + |> assign(:form, to_form(Combobox.change_combobox(combobox), as: :combobox)) + |> assign(:airports, format_popular_airports(raw)) + |> assign(:raw_airports, Enum.map(raw, fn {a, _} -> a end)) + |> assign(:search_term, nil) + |> assign(:value, [])} + end + + @impl true + def handle_event("validate", %{"combobox" => params}, socket) do + airport = + case socket.assigns[:value] do + v when is_list(v) and v != [] -> List.first(v) + _ -> params["airport"] || "" + end + + params = Map.put(params, "airport", airport) + + changeset = + socket.assigns.combobox + |> Combobox.change_combobox(params) + + {:noreply, + socket + |> assign(:form, to_form(changeset, as: :combobox)) + |> assign(:combobox, Ecto.Changeset.apply_changes(changeset))} + end + + @impl true + def handle_event("save", %{"combobox" => params}, socket) do + case Combobox.change_combobox(socket.assigns.combobox, params) do + %Ecto.Changeset{valid?: true} = changeset -> + combobox = Ecto.Changeset.apply_changes(changeset) + params = %{"name" => combobox.name, "airport" => combobox.airport} + saved_form = to_form(Combobox.change_combobox(combobox, params), as: :combobox) + + {:noreply, + socket + |> Toast.push_toast( + "Saved", + "Name: #{combobox.name}, Airport: #{combobox.airport}", + :success + ) + |> assign(:combobox, combobox) + |> assign(:form, saved_form) + |> assign(:value, if(combobox.airport != "", do: [combobox.airport], else: []))} + + %Ecto.Changeset{} = changeset -> + changeset = Map.put(changeset, :action, :validate) + {:noreply, assign(socket, form: to_form(changeset, as: :combobox))} + end + end + + def handle_event("search_airports", %{"reason" => "item-select"}, socket) do + {:noreply, socket} + end + + def handle_event("search_airports", %{"reason" => "clear-trigger"}, socket) do + raw = Place.list_popular_airports_by_continent() + + {:noreply, + socket + |> assign(:airports, format_popular_airports(raw)) + |> assign(:raw_airports, Enum.map(raw, fn {a, _} -> a end)) + |> assign(:search_term, nil) + |> assign(:value, [])} + end + + def handle_event("search_airports", %{"value" => value, "reason" => "input-change"}, socket) + when byte_size(value) < 1 do + raw = Place.list_popular_airports_by_continent() + + {:noreply, + socket + |> assign(:airports, format_popular_airports(raw)) + |> assign(:raw_airports, Enum.map(raw, fn {a, _} -> a end)) + |> assign(:search_term, nil)} + end + + def handle_event("search_airports", %{"value" => value, "reason" => "input-change"}, socket) do + raw = Place.search_airports(value, @max_items, 0) + + {:noreply, + socket + |> assign(:airports, format_airports(raw)) + |> assign(:raw_airports, raw) + |> assign(:search_term, value)} + end + + def handle_event("search_airports", _params, socket) do + {:noreply, socket} + end + + def handle_event("airport_selected", %{"value" => value}, socket) do + {:noreply, + socket + |> assign(:value, value)} + end + + defp format_popular_airports(airports_with_continent) do + Enum.map(airports_with_continent, fn {airport, continent} -> + %{ + id: airport.iata_code, + label: "#{airport.name} (#{airport.iata_code})", + group: continent + } + end) + end + + defp format_airports(airports) do + sorted = + Enum.sort_by(airports, fn a -> + {a.city_name || "Other", a.name} + end) + + city_counts = + sorted + |> Enum.group_by(&(&1.city_name || "Other")) + |> Map.new(fn {city, list} -> {city, length(list)} end) + + Enum.map(sorted, fn airport -> + city = airport.city_name || "Other" + group = if city_counts[city] > 1, do: city, else: nil + + %{ + id: airport.iata_code, + label: "#{airport.name} (#{airport.iata_code})", + group: group + } + end) + end + + @impl true + def render(assigns) do + ~H""" + +
+

Combobox

+

Form with validation

+
+ + <.header> + Combobox form + <:subtitle>Phoenix form with Ecto validation, database fetching and server side fitlering combobox. + + + <.form + for={@form} + id={get_form_id(@form)} + phx-change="validate" + phx-submit="save" + class="flex flex-col gap-4 max-w-md" + > + <.input field={@form[:name]} type="text" label="Name" /> + + <.combobox + field={@form[:airport]} + id="airport-combobox" + class="combobox" + placeholder="Search airports..." + collection={@airports} + filter={false} + on_input_value_change="search_airports" + > + <:label>Airport + <:empty>No results + <:trigger> + <.icon name="hero-chevron-down" /> + + <:clear_trigger> + <.icon name="hero-backspace" /> + + <:error :let={msg}> + <.icon name="hero-exclamation-circle" class="icon" /> + {msg} + + + + <.button type="submit" class="button button--accent"> + Save + + +
+ """ + end +end diff --git a/e2e/lib/e2e_web/live/combobox_live.ex b/e2e/lib/e2e_web/live/combobox_live.ex index b9232d6..0c6adc9 100644 --- a/e2e/lib/e2e_web/live/combobox_live.ex +++ b/e2e/lib/e2e_web/live/combobox_live.ex @@ -34,6 +34,7 @@ defmodule E2eWeb.ComboboxLive do %{label: "Austria", id: "aut"} ]} > + <:empty>No results <:trigger> <.icon name="hero-chevron-down" /> @@ -59,6 +60,7 @@ defmodule E2eWeb.ComboboxLive do %{label: "Mexico", id: "mex", group: "North America"} ]} > + <:empty>No results <:trigger> <.icon name="hero-chevron-down" /> @@ -77,6 +79,7 @@ defmodule E2eWeb.ComboboxLive do %{label: "Austria", id: "aut"} ]} > + <:empty>No results <:item :let={item}> {item.label} @@ -105,6 +108,7 @@ defmodule E2eWeb.ComboboxLive do %{label: "South Korea", id: "kor", group: "Asia"} ]} > + <:empty>No results <:item :let={item}> {item.label} diff --git a/e2e/lib/e2e_web/router.ex b/e2e/lib/e2e_web/router.ex index 7fe73a2..f313202 100644 --- a/e2e/lib/e2e_web/router.ex +++ b/e2e/lib/e2e_web/router.ex @@ -40,6 +40,8 @@ defmodule E2eWeb.Router do live "/live/clipboard", ClipboardLive live "/live/collapsible", CollapsibleLive live "/live/combobox", ComboboxLive + live "/live/combobox-fetch", ComboboxFetch + live "/live/combobox-form", ComboboxForm live "/live/date-picker", DatePickerLive live "/live/dialog", DialogLive live "/live/menu", MenuLive diff --git a/e2e/mix.exs b/e2e/mix.exs index bc611a6..ee79b79 100644 --- a/e2e/mix.exs +++ b/e2e/mix.exs @@ -70,7 +70,9 @@ defmodule E2e.MixProject do {:corex, path: "../../corex"}, {:wallaby, "~> 0.30", only: :test}, {:a11y_audit, "~> 0.3.1", only: :test}, - {:flagpack, "~> 0.6.0"} + {:flagpack, "~> 0.6.0"}, + {:ex_cldr, "~> 2.47"}, + {:ex_cldr_territories, "~> 2.10.0"} ] end diff --git a/e2e/mix.lock b/e2e/mix.lock index db3fbe7..2b8311d 100644 --- a/e2e/mix.lock +++ b/e2e/mix.lock @@ -3,6 +3,7 @@ "bandit": {:hex, :bandit, "1.10.2", "d15ea32eb853b5b42b965b24221eb045462b2ba9aff9a0bda71157c06338cbff", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "27b2a61b647914b1726c2ced3601473be5f7aa6bb468564a688646a689b3ee45"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, + "cldr_utils": {:hex, :cldr_utils, "2.29.4", "11437b0bf9a0d57db4eccdf751c49f675a04fa4261c5dae1e23552a0347e25c9", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "e72a43e69a3f546979085cbdbeae7e9049998cd21cedfdd796cff9155998114e"}, "db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, @@ -10,6 +11,8 @@ "ecto_sql": {:hex, :ecto_sql, "3.13.4", "b6e9d07557ddba62508a9ce4a484989a5bb5e9a048ae0e695f6d93f095c25d60", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2b38cf0749ca4d1c5a8bcbff79bbe15446861ca12a61f9fba604486cb6b62a14"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"}, + "ex_cldr": {:hex, :ex_cldr, "2.47.0", "350cab41e7deac2ab65cedf71e21e055a52927543dc84570abd8c686ac00cb4d", [:mix], [{:cldr_utils, "~> 2.28", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19 or ~> 1.0", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "3e454cbe4354f042795ae0562686e5137d4cfb953f3bc54c87077ac24c17be09"}, + "ex_cldr_territories": {:hex, :ex_cldr_territories, "2.10.0", "2ae852c43b7a6689bcf18f0325f362a71c7ab5496d1c20b5b94867eda7fd95fa", [:mix], [{:ex_cldr, "~> 2.42", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "13f084f9283f8ab1ba5bf3aead936f008341297a8291be6236efaffd1a200e95"}, "expo": {:hex, :expo, "1.1.1", "4202e1d2ca6e2b3b63e02f69cfe0a404f77702b041d02b58597c00992b601db5", [:mix], [], "hexpm", "5fb308b9cb359ae200b7e23d37c76978673aa1b06e2b3075d814ce12c5811640"}, "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, "finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"}, @@ -44,7 +47,7 @@ "postgrex": {:hex, :postgrex, "0.22.0", "fb027b58b6eab1f6de5396a2abcdaaeb168f9ed4eccbb594e6ac393b02078cbd", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a68c4261e299597909e03e6f8ff5a13876f5caadaddd0d23af0d0a61afcc5d84"}, "req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "swoosh": {:hex, :swoosh, "1.21.0", "9f4fa629447774cfc9ad684d8a87a85384e8fce828b6390dd535dfbd43c9ee2a", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9127157bfb33b7e154d0f1ba4e888e14b08ede84e81dedcb318a2f33dbc6db51"}, + "swoosh": {:hex, :swoosh, "1.22.0", "0d65a95f89aedb5011af13295742294e309b4b4aaca556858d81e3b372b58abc", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c01ced23d8786d1ee1a03e4c16574290b2ccd6267beb8c81d081c4a34574ef6e"}, "tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"}, diff --git a/e2e/priv/repo/migrations/20260219074711_create_airports.exs b/e2e/priv/repo/migrations/20260219074711_create_airports.exs new file mode 100644 index 0000000..8756756 --- /dev/null +++ b/e2e/priv/repo/migrations/20260219074711_create_airports.exs @@ -0,0 +1,19 @@ +defmodule E2e.Repo.Migrations.CreateAirports do + use Ecto.Migration + + def change do + create table(:airports, primary_key: false) do + add :id, :string, primary_key: true + add :iata_code, :string + add :name, :string + add :city_name, :string + add :iata_city_code, :string + add :iata_country_code, :string + add :icao_code, :string + add :latitude, :float + add :longitude, :float + add :time_zone, :string + add :city, :map + end + end +end diff --git a/e2e/priv/repo/migrations/20260219074754_create_cities.exs b/e2e/priv/repo/migrations/20260219074754_create_cities.exs new file mode 100644 index 0000000..699ef92 --- /dev/null +++ b/e2e/priv/repo/migrations/20260219074754_create_cities.exs @@ -0,0 +1,13 @@ +defmodule E2e.Repo.Migrations.CreateCities do + use Ecto.Migration + + def change do + create table(:cities, primary_key: false) do + add :id, :string, primary_key: true + add :iata_code, :string + add :iata_country_code, :string + add :name, :string + add :airports, :jsonb, default: "[]" + end + end +end diff --git a/e2e/priv/repo/seeds.exs b/e2e/priv/repo/seeds.exs index 9360310..68f8c29 100644 --- a/e2e/priv/repo/seeds.exs +++ b/e2e/priv/repo/seeds.exs @@ -9,3 +9,12 @@ # # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. +alias E2e.Place.Helper +alias E2e.Place + +Helper.fetch_and_insert_airports() + +Helper.fetch_and_insert_cities() + +IO.inspect(Enum.count(Place.list_cities()), label: "number of Cities") +IO.inspect(Enum.count(Place.list_airports()), label: "number of Airports") diff --git a/e2e/priv/repo/seeds/airports.tar.gz b/e2e/priv/repo/seeds/airports.tar.gz new file mode 100644 index 0000000..820df6a Binary files /dev/null and b/e2e/priv/repo/seeds/airports.tar.gz differ diff --git a/e2e/priv/repo/seeds/cities.tar.gz b/e2e/priv/repo/seeds/cities.tar.gz new file mode 100644 index 0000000..8731edb Binary files /dev/null and b/e2e/priv/repo/seeds/cities.tar.gz differ diff --git a/guides/installation.md b/guides/installation.md index abc48a0..747f725 100644 --- a/guides/installation.md +++ b/guides/installation.md @@ -44,7 +44,7 @@ Add `corex` to your `mix.exs` dependencies: ```elixir def deps do [ - {:corex, "~> 0.1.0-alpha.25"} + {:corex, "~> 0.1.0-alpha.26"} ] end ``` diff --git a/lib/components/accordion.ex b/lib/components/accordion.ex index 15f0d9e..967e85b 100644 --- a/lib/components/accordion.ex +++ b/lib/components/accordion.ex @@ -270,7 +270,7 @@ defmodule Corex.Accordion do ``` If you wish to use the default Corex styling, you can use the class `accordion` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/angle_slider.ex b/lib/components/angle_slider.ex index 1ae9acd..3b25414 100644 --- a/lib/components/angle_slider.ex +++ b/lib/components/angle_slider.ex @@ -55,6 +55,7 @@ defmodule Corex.AngleSlider do end end ``` + ## API Control @@ -76,20 +77,35 @@ defmodule Corex.AngleSlider do end ``` - ## Machine API + ## Styling - The slider API exposes the following methods: + Use data attributes to target elements: - | Method | Type | Description | - |--------|------|-------------| - | value | number | The current value of the angle slider | - | valueAsDegree | string | The current value as a degree string | - | setValue | (value: number) => void | Sets the value of the angle slider | - | dragging | boolean | Whether the slider is being dragged | + ```css + [data-scope="angle-slider"][data-part="root"] {} + [data-scope="angle-slider"][data-part="control"] {} + [data-scope="angle-slider"][data-part="thumb"] {} + [data-scope="angle-slider"][data-part="value-text"] {} + [data-scope="angle-slider"][data-part="marker-group"] {} + [data-scope="angle-slider"][data-part="marker"] {} + ``` - ## Styling + If you wish to use the default Corex styling, you can use the class `angle-slider` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/angle-slider.css"; + ``` + + You can then use modifiers + + ```heex + <.angle_slider class="angle-slider angle-slider--accent angle-slider--lg" value={0} /> + ``` - Use data attributes: `[data-scope="angle-slider"][data-part="root"]`, `control`, `thumb`, `value-text` (with `value` and `text` spans), `marker-group`, `marker`. + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/angle-slider#modifiers) ''' @doc type: :component diff --git a/lib/components/avatar.ex b/lib/components/avatar.ex index 228a707..fbce1b0 100644 --- a/lib/components/avatar.ex +++ b/lib/components/avatar.ex @@ -14,7 +14,33 @@ defmodule Corex.Avatar do ## Styling - Use data attributes: `[data-scope="avatar"][data-part="root"]`, `image`, `fallback`, `skeleton`. + Use data attributes to target elements: + + ```css + [data-scope="avatar"][data-part="root"] {} + [data-scope="avatar"][data-part="image"] {} + [data-scope="avatar"][data-part="fallback"] {} + [data-scope="avatar"][data-part="skeleton"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `avatar` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/avatar.css"; + ``` + + You can then use modifiers + + ```heex + <.avatar class="avatar avatar--accent avatar--lg"> + <:fallback>JD + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/avatar#modifiers) ''' @doc type: :component diff --git a/lib/components/carousel.ex b/lib/components/carousel.ex index 8e4da76..e469350 100644 --- a/lib/components/carousel.ex +++ b/lib/components/carousel.ex @@ -21,7 +21,36 @@ defmodule Corex.Carousel do ## Styling - Use data attributes: `[data-scope="carousel"][data-part="root"]`, `control`, `item-group`, `item`, `prev-trigger`, `next-trigger`, `indicator-group`, `indicator`. + Use data attributes to target elements: + + ```css + [data-scope="carousel"][data-part="root"] {} + [data-scope="carousel"][data-part="control"] {} + [data-scope="carousel"][data-part="item-group"] {} + [data-scope="carousel"][data-part="item"] {} + [data-scope="carousel"][data-part="prev-trigger"] {} + [data-scope="carousel"][data-part="next-trigger"] {} + [data-scope="carousel"][data-part="indicator-group"] {} + [data-scope="carousel"][data-part="indicator"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `carousel` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/carousel.css"; + ``` + + You can then use modifiers + + ```heex + <.carousel class="carousel carousel--accent carousel--lg" items={[]}> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/carousel#modifiers) ''' @doc type: :component diff --git a/lib/components/checkbox.ex b/lib/components/checkbox.ex index c0b0a7f..24ea593 100644 --- a/lib/components/checkbox.ex +++ b/lib/components/checkbox.ex @@ -195,21 +195,17 @@ defmodule Corex.Checkbox do ## Styling Use data attributes to target elements: - - `[data-scope="checkbox"][data-part="root"]` - Label wrapper - - `[data-scope="checkbox"][data-part="control"]` - Checkbox control - - `[data-scope="checkbox"][data-part="label"]` - Label text - - `[data-scope="checkbox"][data-part="input"]` - Hidden input - - `[data-scope="checkbox"][data-part="error"]` - Error message - - State-specific styling: - - `[data-state="checked"]` - When checkbox is checked - - `[data-state="unchecked"]` - When checkbox is unchecked - - `[data-disabled]` - When checkbox is disabled - - `[data-readonly]` - When checkbox is read-only - - `[data-invalid]` - When checkbox has validation errors + + ```css + [data-scope="checkbox"][data-part="root"] {} + [data-scope="checkbox"][data-part="control"] {} + [data-scope="checkbox"][data-part="label"] {} + [data-scope="checkbox"][data-part="input"] {} + [data-scope="checkbox"][data-part="error"] {} + ``` If you wish to use the default Corex styling, you can use the class `checkbox` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/clipboard.ex b/lib/components/clipboard.ex index 02bdd96..414f467 100644 --- a/lib/components/clipboard.ex +++ b/lib/components/clipboard.ex @@ -77,7 +77,7 @@ defmodule Corex.Clipboard do ``` If you wish to use the default Corex styling, you can use the class `clipboard` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/collapsible.ex b/lib/components/collapsible.ex index 6ef1c96..f8b2959 100644 --- a/lib/components/collapsible.ex +++ b/lib/components/collapsible.ex @@ -71,7 +71,7 @@ defmodule Corex.Collapsible do ``` If you wish to use the default Corex styling, you can use the class `collapsible` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/combobox.ex b/lib/components/combobox.ex index c736324..c09dce3 100644 --- a/lib/components/combobox.ex +++ b/lib/components/combobox.ex @@ -21,6 +21,7 @@ defmodule Corex.Combobox do %{label: "Austria", id: "aut"} ]} > + <:empty>No results <:trigger> <.icon name="hero-chevron-down" /> @@ -49,6 +50,7 @@ defmodule Corex.Combobox do %{label: "Mexico", id: "mex", group: "North America"} ]} > + <:empty>No results <:trigger> <.icon name="hero-chevron-down" /> @@ -74,6 +76,7 @@ defmodule Corex.Combobox do %{label: "Austria", id: "aut"} ]} > + <:empty>No results <:item :let={item}> {item.label} @@ -109,6 +112,7 @@ defmodule Corex.Combobox do %{label: "South Korea", id: "kor", group: "Asia"} ]} > + <:empty>No results <:item :let={item}> {item.label} @@ -124,25 +128,79 @@ defmodule Corex.Combobox do ``` + + ### Server-side Filtering + + Use `on_input_value_change` to filter on the server. This example uses a local list; replace with a database query for real apps. + + ```heex + defmodule MyAppWeb.CountryCombobox do + use MyAppWeb, :live_view + + @items [ + %{id: "fra", label: "France"}, + %{id: "bel", label: "Belgium"}, + %{id: "deu", label: "Germany"}, + %{id: "usa", label: "USA"}, + %{id: "jpn", label: "Japan"} + ] + + def mount(_params, _session, socket) do + {:ok, assign(socket, items: [])} + end + + def handle_event("search", %{"value" => value, "reason" => "input-change"}, socket) do + filtered = + if byte_size(value) < 1 do + [] + else + term = String.downcase(value) + Enum.filter(@items, fn item -> + String.contains?(String.downcase(item.label), term) + end) + end + + {:noreply, assign(socket, items: filtered)} + end + + def render(assigns) do + ~H""" + <.combobox + id="country-combobox" + collection={@items} + on_input_value_change="search" + > + <:empty>No results + <:trigger><.icon name="hero-chevron-down" /> + + """ + end + end + ``` + ## Styling Use data attributes to target elements: - - `[data-scope="combobox"][data-part="root"]` - Container - - `[data-scope="combobox"][data-part="control"]` - Control wrapper - - `[data-scope="combobox"][data-part="input"]` - Input field - - `[data-scope="combobox"][data-part="trigger"]` - Trigger button - - `[data-scope="combobox"][data-part="clear-trigger"]` - Clear button - - `[data-scope="combobox"][data-part="content"]` - Dropdown content - - `[data-scope="combobox"][data-part="item-group"]` - Group container - - `[data-scope="combobox"][data-part="item-group-label"]` - Group label - - `[data-scope="combobox"][data-part="item"]` - Item wrapper - - `[data-scope="combobox"][data-part="item-text"]` - Item text - - `[data-scope="combobox"][data-part="item-indicator"]` - Optional indicator + + ```css + [data-scope="combobox"][data-part="root"] {} + [data-scope="combobox"][data-part="control"] {} + [data-scope="combobox"][data-part="input"] {} + [data-scope="combobox"][data-part="trigger"] {} + [data-scope="combobox"][data-part="clear-trigger"] {} + [data-scope="combobox"][data-part="content"] {} + [data-scope="combobox"][data-part="empty"] {} + [data-scope="combobox"][data-part="item-group"] {} + [data-scope="combobox"][data-part="item-group-label"] {} + [data-scope="combobox"][data-part="item"] {} + [data-scope="combobox"][data-part="item-text"] {} + [data-scope="combobox"][data-part="item-indicator"] {} + ``` If you wish to use the default Corex styling, you can use the class `combobox` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; @@ -153,7 +211,12 @@ defmodule Corex.Combobox do You can then use modifiers ```heex - <.combobox class="combobox combobox--accent combobox--lg"> + <.combobox class="combobox combobox--accent combobox--lg" collection={[]}> + <:empty>No results + <:trigger> + <.icon name="hero-chevron-down" /> + + ``` Learn more about modifiers and [Corex Design](https://corex-ui.com/components/combobox#modifiers) @@ -225,6 +288,12 @@ defmodule Corex.Combobox do attr(:read_only, :boolean, default: false, doc: "Whether the combobox is read only") attr(:required, :boolean, default: false, doc: "Whether the combobox is required") + attr(:filter, :boolean, + default: true, + doc: + "When true, filter options client-side by input value. Set to false when using on_input_value_change for server-side filtering" + ) + attr(:on_input_value_change, :string, default: nil, doc: "The server event name to trigger on input value change" @@ -240,6 +309,7 @@ defmodule Corex.Combobox do attr(:rest, :global) slot(:label, required: false, doc: "The label content") + slot(:empty, required: true, doc: "Content to display when there are no results") slot(:trigger, required: true, doc: "The trigger button content") slot(:clear_trigger, required: false, doc: "The clear button content") slot(:item_indicator, required: false, doc: "Optional indicator for selected items") @@ -316,15 +386,14 @@ defmodule Corex.Combobox do name: @name, read_only: @read_only, required: @required, on_open_change: @on_open_change, on_open_change_client: @on_open_change_client, on_input_value_change: @on_input_value_change, on_value_change: @on_value_change, open: @open, positioning: @positioning, - bubble: @bubble, disabled: @disabled + bubble: @bubble, disabled: @disabled, filter: @filter })}> -
- +
-
+
{render_slot(@label)}
-
+
-
- {render_slot(@error, msg)} -
-
-
    -
-
-
-
- {group} -
-
-
  • - - {render_slot(@item, item)} - - - {item.label} - - - {render_slot(@item_indicator)} - -
  • -
    -
    - -
  • +
    +
      +
    +
    +
  • +
    + {render_slot(@error, msg)} +
    +
    +
  • + {render_slot(@empty)} +
  • +
    +
    + {group} +
    +
    +
  • {render_slot(@item, item)} @@ -373,6 +433,18 @@ defmodule Corex.Combobox do
  • + +
  • + + {render_slot(@item, item)} + + + {item.label} + + + {render_slot(@item_indicator)} + +
  • """ diff --git a/lib/components/combobox/anatomy.ex b/lib/components/combobox/anatomy.ex index 3b1e036..fbaf353 100644 --- a/lib/components/combobox/anatomy.ex +++ b/lib/components/combobox/anatomy.ex @@ -31,7 +31,8 @@ defmodule Corex.Combobox.Anatomy do on_open_change_client: nil, on_input_value_change: nil, on_value_change: nil, - bubble: false + bubble: false, + filter: true ] @type t :: %__MODULE__{ diff --git a/lib/components/combobox/connect.ex b/lib/components/combobox/connect.ex index 0cc7956..883e457 100644 --- a/lib/components/combobox/connect.ex +++ b/lib/components/combobox/connect.ex @@ -45,7 +45,8 @@ defmodule Corex.Combobox.Connect do "data-on-input-value-change" => assigns.on_input_value_change, "data-on-value-change" => assigns.on_value_change, "data-positioning" => Corex.Json.encode!(assigns.positioning), - "data-bubble" => get_boolean(assigns.bubble) + "data-bubble" => get_boolean(assigns.bubble), + "data-filter" => get_boolean(assigns.filter) } end diff --git a/lib/components/date-picker.ex b/lib/components/date-picker.ex index 2f66aba..04855c1 100644 --- a/lib/components/date-picker.ex +++ b/lib/components/date-picker.ex @@ -229,6 +229,24 @@ defmodule Corex.DatePicker do [data-scope="date-picker"][data-part="positioner"] {} [data-scope="date-picker"][data-part="content"] {} ``` + + If you wish to use the default Corex styling, you can use the class `date-picker` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/date-picker.css"; + ``` + + You can then use modifiers + + ```heex + <.date_picker class="date-picker date-picker--accent date-picker--lg" id="my-date-picker"> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/date-picker#modifiers) ''' use Phoenix.Component diff --git a/lib/components/dialog.ex b/lib/components/dialog.ex index 0d85539..e78c651 100644 --- a/lib/components/dialog.ex +++ b/lib/components/dialog.ex @@ -106,7 +106,7 @@ defmodule Corex.Dialog do ``` If you wish to use the default Corex styling, you can use the class `dialog` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/editable.ex b/lib/components/editable.ex index 7d8162d..8c019b9 100644 --- a/lib/components/editable.ex +++ b/lib/components/editable.ex @@ -19,7 +19,41 @@ defmodule Corex.Editable do ## Styling - Use data attributes: `[data-scope="editable"][data-part="root"]`, `area`, `label`, `input`, `preview`, `edit-trigger`, `control`, `submit-trigger`, `cancel-trigger`. + Use data attributes to target elements: + + ```css + [data-scope="editable"][data-part="root"] {} + [data-scope="editable"][data-part="area"] {} + [data-scope="editable"][data-part="label"] {} + [data-scope="editable"][data-part="input"] {} + [data-scope="editable"][data-part="preview"] {} + [data-scope="editable"][data-part="edit-trigger"] {} + [data-scope="editable"][data-part="control"] {} + [data-scope="editable"][data-part="submit-trigger"] {} + [data-scope="editable"][data-part="cancel-trigger"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `editable` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/editable.css"; + ``` + + You can then use modifiers + + ```heex + <.editable class="editable editable--accent editable--lg" value=""> + <:label>Label + <:edit_trigger>Edit + <:submit_trigger>Save + <:cancel_trigger>Cancel + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/editable#modifiers) ''' @doc type: :component diff --git a/lib/components/floating_panel.ex b/lib/components/floating_panel.ex index e8e8366..588f8dc 100644 --- a/lib/components/floating_panel.ex +++ b/lib/components/floating_panel.ex @@ -35,7 +35,40 @@ defmodule Corex.FloatingPanel do ## Styling - Use data attributes: `[data-scope="floating-panel"][data-part="root"]`, `trigger`, `positioner`, `content`, `title`, `header`, `body`, `drag-trigger`, `resize-trigger`, `close-trigger`, `control`, `stage-trigger`. + Use data attributes to target elements: + + ```css + [data-scope="floating-panel"][data-part="root"] {} + [data-scope="floating-panel"][data-part="trigger"] {} + [data-scope="floating-panel"][data-part="positioner"] {} + [data-scope="floating-panel"][data-part="content"] {} + [data-scope="floating-panel"][data-part="title"] {} + [data-scope="floating-panel"][data-part="header"] {} + [data-scope="floating-panel"][data-part="body"] {} + [data-scope="floating-panel"][data-part="drag-trigger"] {} + [data-scope="floating-panel"][data-part="resize-trigger"] {} + [data-scope="floating-panel"][data-part="close-trigger"] {} + [data-scope="floating-panel"][data-part="control"] {} + [data-scope="floating-panel"][data-part="stage-trigger"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `floating-panel` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/floating-panel.css"; + ``` + + You can then use modifiers + + ```heex + <.floating_panel class="floating-panel floating-panel--accent floating-panel--lg"> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/floating-panel#modifiers) ''' @doc type: :component diff --git a/lib/components/listbox.ex b/lib/components/listbox.ex index f678849..4b6d68f 100644 --- a/lib/components/listbox.ex +++ b/lib/components/listbox.ex @@ -114,7 +114,35 @@ defmodule Corex.Listbox do ## Styling - Use data attributes: `[data-scope="listbox"][data-part="root"]`, `content`, `item`, `item-text`, `item-indicator`, `item-group`, `item-group-label`. + Use data attributes to target elements: + + ```css + [data-scope="listbox"][data-part="root"] {} + [data-scope="listbox"][data-part="content"] {} + [data-scope="listbox"][data-part="item"] {} + [data-scope="listbox"][data-part="item-text"] {} + [data-scope="listbox"][data-part="item-indicator"] {} + [data-scope="listbox"][data-part="item-group"] {} + [data-scope="listbox"][data-part="item-group-label"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `listbox` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/listbox.css"; + ``` + + You can then use modifiers + + ```heex + <.listbox class="listbox listbox--accent listbox--lg" collection={[]}> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/listbox#modifiers) ''' @doc type: :component diff --git a/lib/components/menu.ex b/lib/components/menu.ex index 77cfb47..cd13971 100644 --- a/lib/components/menu.ex +++ b/lib/components/menu.ex @@ -226,7 +226,7 @@ defmodule Corex.Menu do ``` If you wish to use the default Corex styling, you can use the class `menu` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; @@ -234,6 +234,13 @@ defmodule Corex.Menu do @import "../corex/components/menu.css"; ``` + You can then use modifiers + + ```heex + <.menu class="menu menu--accent menu--lg"> + + ``` + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/menu#modifiers) ''' diff --git a/lib/components/number_input.ex b/lib/components/number_input.ex index 8d4e15b..0624fd1 100644 --- a/lib/components/number_input.ex +++ b/lib/components/number_input.ex @@ -39,7 +39,34 @@ defmodule Corex.NumberInput do ## Styling - Use data attributes: `[data-scope="number-input"][data-part="root"]`, `control`, `input`, `trigger-group`, `decrement-trigger`, `increment-trigger`, `scrubber`. + Use data attributes to target elements: + + ```css + [data-scope="number-input"][data-part="root"] {} + [data-scope="number-input"][data-part="control"] {} + [data-scope="number-input"][data-part="input"] {} + [data-scope="number-input"][data-part="trigger-group"] {} + [data-scope="number-input"][data-part="decrement-trigger"] {} + [data-scope="number-input"][data-part="increment-trigger"] {} + [data-scope="number-input"][data-part="scrubber"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `number-input` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/number-input.css"; + ``` + + You can then use modifiers + + ```heex + <.number_input class="number-input number-input--accent number-input--lg" /> + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/number-input#modifiers) ''' @doc type: :component diff --git a/lib/components/password_input.ex b/lib/components/password_input.ex index f045d91..d0d84c0 100644 --- a/lib/components/password_input.ex +++ b/lib/components/password_input.ex @@ -16,7 +16,36 @@ defmodule Corex.PasswordInput do ## Styling - Use data attributes: `[data-scope="password-input"][data-part="root"]`, `label`, `control`, `input`, `visibility-trigger`, `indicator`. + Use data attributes to target elements: + + ```css + [data-scope="password-input"][data-part="root"] {} + [data-scope="password-input"][data-part="label"] {} + [data-scope="password-input"][data-part="control"] {} + [data-scope="password-input"][data-part="input"] {} + [data-scope="password-input"][data-part="visibility-trigger"] {} + [data-scope="password-input"][data-part="indicator"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `password-input` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/password-input.css"; + ``` + + You can then use modifiers + + ```heex + <.password_input class="password-input password-input--accent password-input--lg"> + <:visible_indicator><.icon name="hero-eye" /> + <:hidden_indicator><.icon name="hero-eye-slash" /> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/password-input#modifiers) ''' @doc type: :component diff --git a/lib/components/pin_input.ex b/lib/components/pin_input.ex index ba89450..5f313ba 100644 --- a/lib/components/pin_input.ex +++ b/lib/components/pin_input.ex @@ -14,7 +14,33 @@ defmodule Corex.PinInput do ## Styling - Use data attributes: `[data-scope="pin-input"][data-part="root"]`, `label`, `control`, `input` (per index). + Use data attributes to target elements: + + ```css + [data-scope="pin-input"][data-part="root"] {} + [data-scope="pin-input"][data-part="label"] {} + [data-scope="pin-input"][data-part="control"] {} + [data-scope="pin-input"][data-part="input"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `pin-input` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/pin-input.css"; + ``` + + You can then use modifiers + + ```heex + <.pin_input class="pin-input pin-input--accent pin-input--lg" count={4}> + <:label>Code + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/pin-input#modifiers) ''' @doc type: :component diff --git a/lib/components/radio_group.ex b/lib/components/radio_group.ex index 50c3bb5..5f33e8f 100644 --- a/lib/components/radio_group.ex +++ b/lib/components/radio_group.ex @@ -25,7 +25,35 @@ defmodule Corex.RadioGroup do ## Styling - Use data attributes: `[data-scope="radio-group"][data-part="root"]`, `label`, `indicator`, `item`, `item-text`, `item-control`, `item-hidden-input`. + Use data attributes to target elements: + + ```css + [data-scope="radio-group"][data-part="root"] {} + [data-scope="radio-group"][data-part="label"] {} + [data-scope="radio-group"][data-part="indicator"] {} + [data-scope="radio-group"][data-part="item"] {} + [data-scope="radio-group"][data-part="item-text"] {} + [data-scope="radio-group"][data-part="item-control"] {} + [data-scope="radio-group"][data-part="item-hidden-input"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `radio-group` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/radio-group.css"; + ``` + + You can then use modifiers + + ```heex + <.radio_group class="radio-group radio-group--accent radio-group--lg" items={[]}> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/radio-group#modifiers) ''' @doc type: :component diff --git a/lib/components/select.ex b/lib/components/select.ex index 2a2af92..91e68cb 100644 --- a/lib/components/select.ex +++ b/lib/components/select.ex @@ -331,21 +331,23 @@ defmodule Corex.Select do ## Styling Use data attributes to target elements: - - `[data-scope="select"][data-part="root"]` - Label wrapper - - `[data-scope="select"][data-part="control"]` - Select control - - `[data-scope="select"][data-part="label"]` - Label text - - `[data-scope="select"][data-part="input"]` - Hidden input - - `[data-scope="select"][data-part="error"]` - Error message - - State-specific styling: - - `[data-state="open"]` - When select is open - - `[data-state="closed"]` - When select is closed - - `[data-disabled]` - When select is disabled - - `[data-readonly]` - When select is read-only - - `[data-invalid]` - When select has validation errors + + ```css + [data-scope="select"][data-part="root"] {} + [data-scope="select"][data-part="control"] {} + [data-scope="select"][data-part="label"] {} + [data-scope="select"][data-part="input"] {} + [data-scope="select"][data-part="error"] {} + [data-scope="select"][data-part="trigger"] {} + [data-scope="select"][data-part="item-group"] {} + [data-scope="select"][data-part="item-group-label"] {} + [data-scope="select"][data-part="item"] {} + [data-scope="select"][data-part="item-text"] {} + [data-scope="select"][data-part="item-indicator"] {} + ``` If you wish to use the default Corex styling, you can use the class `select` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/signature_pad.ex b/lib/components/signature_pad.ex index 3d4dbcc..c98db5c 100644 --- a/lib/components/signature_pad.ex +++ b/lib/components/signature_pad.ex @@ -221,7 +221,7 @@ defmodule Corex.SignaturePad do ``` If you wish to use the default Corex styling, you can use the class `signature-pad` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/switch.ex b/lib/components/switch.ex index ef00c2d..6431c87 100644 --- a/lib/components/switch.ex +++ b/lib/components/switch.ex @@ -173,22 +173,18 @@ defmodule Corex.Switch do ## Styling Use data attributes to target elements: - - `[data-scope="switch"][data-part="root"]` - Label wrapper - - `[data-scope="switch"][data-part="control"]` - Switch track - - `[data-scope="switch"][data-part="thumb"]` - Switch thumb/handle - - `[data-scope="switch"][data-part="label"]` - Label text - - `[data-scope="switch"][data-part="input"]` - Hidden input - - `[data-scope="switch"][data-part="error"]` - Error message - - State-specific styling: - - `[data-state="checked"]` - When switch is on - - `[data-state="unchecked"]` - When switch is off - - `[data-disabled]` - When switch is disabled - - `[data-readonly]` - When switch is read-only - - `[data-invalid]` - When switch has validation errors + + ```css + [data-scope="switch"][data-part="root"] {} + [data-scope="switch"][data-part="control"] {} + [data-scope="switch"][data-part="thumb"] {} + [data-scope="switch"][data-part="label"] {} + [data-scope="switch"][data-part="input"] {} + [data-scope="switch"][data-part="error"] {} + ``` If you wish to use the default Corex styling, you can use the class `switch` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/tabs.ex b/lib/components/tabs.ex index eba2f4f..f7683b4 100644 --- a/lib/components/tabs.ex +++ b/lib/components/tabs.ex @@ -215,7 +215,7 @@ defmodule Corex.Tabs do ``` If you wish to use the default Corex styling, you can use the class `tabs` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/timer.ex b/lib/components/timer.ex index f77938c..5404a9f 100644 --- a/lib/components/timer.ex +++ b/lib/components/timer.ex @@ -30,7 +30,34 @@ defmodule Corex.Timer do ## Styling - Use data attributes: `[data-scope="timer"][data-part="root"]`, `area`, `item`, `separator`, `control`, `action-trigger`. + Use data attributes to target elements: + + ```css + [data-scope="timer"][data-part="root"] {} + [data-scope="timer"][data-part="area"] {} + [data-scope="timer"][data-part="item"] {} + [data-scope="timer"][data-part="separator"] {} + [data-scope="timer"][data-part="control"] {} + [data-scope="timer"][data-part="action-trigger"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `timer` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/timer.css"; + ``` + + You can then use modifiers + + ```heex + <.timer class="timer timer--accent timer--lg"> + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/timer#modifiers) ''' @doc type: :component diff --git a/lib/components/toast.ex b/lib/components/toast.ex index 11a93c9..7e6c321 100644 --- a/lib/components/toast.ex +++ b/lib/components/toast.ex @@ -45,14 +45,17 @@ defmodule Corex.Toast do ## Styling Use data attributes to target elements: - - `[data-scope="toast"][data-part="group"]` - Toast container - - `[data-scope="toast"][data-part="root"]` - Individual toast root - - `[data-scope="toast"][data-part="title"]` - Toast title - - `[data-scope="toast"][data-part="description"]` - Toast description - - `[data-scope="toast"][data-part="close-trigger"]` - Close button + + ```css + [data-scope="toast"][data-part="group"] {} + [data-scope="toast"][data-part="root"] {} + [data-scope="toast"][data-part="title"] {} + [data-scope="toast"][data-part="description"] {} + [data-scope="toast"][data-part="close-trigger"] {} + ``` If you wish to use the default Corex styling, you can use the class `toast` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/toggle_group.ex b/lib/components/toggle_group.ex index f2d0fe8..61281ec 100644 --- a/lib/components/toggle_group.ex +++ b/lib/components/toggle_group.ex @@ -104,11 +104,14 @@ defmodule Corex.ToggleGroup do ## Styling Use data attributes to target elements: - - `[data-scope="toggle-group"][data-part="root"]` - Container - - `[data-scope="toggle-group"][data-part="item"]` - Item wrapper + + ```css + [data-scope="toggle-group"][data-part="root"] {} + [data-scope="toggle-group"][data-part="item"] {} + ``` If you wish to use the default Corex styling, you can use the class `toggle-group` on the component. - This requires to install mix corex.design first and import the component css file. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; diff --git a/lib/components/tree_view.ex b/lib/components/tree_view.ex index a58e429..33eebd2 100644 --- a/lib/components/tree_view.ex +++ b/lib/components/tree_view.ex @@ -41,7 +41,36 @@ defmodule Corex.TreeView do ## Styling - Target with `[data-scope="tree-view"][data-part="root"]`, `[data-part="tree"]`, `[data-part="item"]`, `[data-part="branch"]`, `[data-part="branch-control"]`, `[data-part="branch-content"]`, `[data-part="branch-indicator"]`. + Use data attributes to target elements: + + ```css + [data-scope="tree-view"][data-part="root"] {} + [data-scope="tree-view"][data-part="tree"] {} + [data-scope="tree-view"][data-part="item"] {} + [data-scope="tree-view"][data-part="branch"] {} + [data-scope="tree-view"][data-part="branch-control"] {} + [data-scope="tree-view"][data-part="branch-content"] {} + [data-scope="tree-view"][data-part="branch-indicator"] {} + ``` + + If you wish to use the default Corex styling, you can use the class `tree-view` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/tree-view.css"; + ``` + + You can then use modifiers + + ```heex + <.tree_view class="tree-view tree-view--accent tree-view--lg" items={[]}> + <:label>Label + + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/tree-view#modifiers) ''' @doc type: :component diff --git a/lib/list.ex b/lib/list.ex index a8652a6..207c7db 100644 --- a/lib/list.ex +++ b/lib/list.ex @@ -1,7 +1,14 @@ defmodule Corex.List do - @moduledoc """ - List item structure for flat selectable items. - """ + @moduledoc ~S''' + List items for flat selectable items to be used with: + + - [Combobox](Corex.Combobox.html) + - [Listbox](Corex.Listbox.html) + - [Select](Corex.Select.html) + + Use `Corex.List.new/1` to build a list of items from keyword lists or maps. + + ''' defmodule Item do @moduledoc """ @@ -9,6 +16,7 @@ defmodule Corex.List do Use it to create flat lists of selectable items for: - [Combobox](Corex.Combobox.html) + - [Listbox](Corex.Listbox.html) - [Select](Corex.Select.html) ## Fields diff --git a/lib/tree.ex b/lib/tree.ex index 0f39b71..d2d52a8 100644 --- a/lib/tree.ex +++ b/lib/tree.ex @@ -1,7 +1,13 @@ defmodule Corex.Tree do - @moduledoc """ - Tree item structure for hierarchical/nested items. - """ + @moduledoc ~S''' + Tree items for hierarchical/nested structures to be used with: + + - [Menu](Corex.Menu.html) + - [TreeView](Corex.TreeView.html) + + Use `Corex.Tree.new/1` to build a list of items from keyword lists or maps. + + ''' defmodule Item do @moduledoc """ @@ -9,6 +15,7 @@ defmodule Corex.Tree do Use it to create hierarchical/nested structures for: - [Menu](Corex.Menu.html) + - [TreeView](Corex.TreeView.html) ## Fields diff --git a/mix.exs b/mix.exs index 1ab1ad6..728d368 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Corex.MixProject do use Mix.Project - @version "0.1.0-alpha.25" + @version "0.1.0-alpha.26" @elixir_requirement "~> 1.15" def project do diff --git a/package.json b/package.json index db011d2..b5553d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "corex", - "version": "0.1.0-alpha.25", + "version": "0.1.0-alpha.26", "description": "The official JavaScript client for the Corex Phoenix Hooks.", "license": "MIT", "module": "./priv/static/corex.mjs", @@ -62,36 +62,36 @@ "@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/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", - "@zag-js/collapsible": "^1.33.1", - "@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/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", - "@zag-js/types": "^1.33.1", - "@zag-js/vanilla": "^1.33.1", +"@zag-js/accordion": "^1.34.0", + "@zag-js/angle-slider": "^1.34.0", + "@zag-js/avatar": "^1.34.0", + "@zag-js/carousel": "^1.34.0", + "@zag-js/collection": "^1.34.0", + "@zag-js/checkbox": "^1.34.0", + "@zag-js/clipboard": "^1.34.0", + "@zag-js/collapsible": "^1.34.0", + "@zag-js/combobox": "^1.34.0", + "@zag-js/date-picker": "^1.34.0", + "@zag-js/dialog": "^1.34.0", + "@zag-js/editable": "^1.34.0", + "@zag-js/floating-panel": "^1.34.0", + "@zag-js/listbox": "^1.34.0", + "@zag-js/menu": "^1.34.0", + "@zag-js/number-input": "^1.34.0", + "@zag-js/password-input": "^1.34.0", + "@zag-js/pin-input": "^1.34.0", + "@zag-js/popper": "^1.34.0", + "@zag-js/radio-group": "^1.34.0", + "@zag-js/select": "^1.34.0", + "@zag-js/signature-pad": "^1.34.0", + "@zag-js/switch": "^1.34.0", + "@zag-js/tabs": "^1.34.0", + "@zag-js/timer": "^1.34.0", + "@zag-js/toast": "^1.34.0", + "@zag-js/toggle-group": "^1.34.0", + "@zag-js/tree-view": "^1.34.0", + "@zag-js/types": "^1.34.0", + "@zag-js/vanilla": "^1.34.0", "eslint": "^9.39.2", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d7728f..9ad876f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,95 +21,95 @@ importers: specifier: ^8.0.0 version: 8.54.0(eslint@9.39.2)(typescript@5.9.3) '@zag-js/accordion': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/angle-slider': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/avatar': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/carousel': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/checkbox': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/clipboard': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/collapsible': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/collection': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/combobox': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/date-picker': - specifier: ^1.33.1 - version: 1.33.1(@internationalized/date@3.11.0) + specifier: ^1.34.0 + version: 1.34.0(@internationalized/date@3.11.0) '@zag-js/dialog': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/editable': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/floating-panel': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/listbox': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/menu': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/number-input': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/password-input': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/pin-input': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/popper': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/radio-group': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/select': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/signature-pad': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/switch': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/tabs': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/timer': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/toast': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/toggle-group': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/tree-view': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/types': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 '@zag-js/vanilla': - specifier: ^1.33.1 - version: 1.33.1 + specifier: ^1.34.0 + version: 1.34.0 eslint: specifier: ^9.39.2 version: 9.39.2 @@ -279,144 +279,144 @@ packages: resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@zag-js/accordion@1.33.1': - resolution: {integrity: sha512-D80BZxceCIrxaXCi4CWDIzrCNJtojTGysD23C8FOxEGm9pQVuF7NvIdes7lbfUvwlZypMUUvhVlh8kKXN9uyeQ==} + '@zag-js/accordion@1.34.0': + resolution: {integrity: sha512-4zK897tjM72D65dBZ+DKwxzJXV1Hy/QjDANDiLukpVE8oD+bO4jwh5i8Ee7U3n8YuJFluM7Ajn+xZ/HQdg9yJA==} - '@zag-js/anatomy@1.33.1': - resolution: {integrity: sha512-iME14VHGGEPNMakilI6qvEkv9sll4AFZHpeoMLpczesw5hmqQjjNRifDTPR+idqCb8O8PdkAPE9hyMeP+4JjtA==} + '@zag-js/anatomy@1.34.0': + resolution: {integrity: sha512-c13dPCG7Sa8Xqhz25AoZFWvMkhw5RlNgTOPD0Z6ZXpURNauLn1r5C9a/H0SAdO66yOsPHJoK8LZxoL4BiGE8KQ==} - '@zag-js/angle-slider@1.33.1': - resolution: {integrity: sha512-Y44IND5koNWD/EMKEWJbuEnzNW9y1WsrQFFvKRsMp/m3n60hiLa8qtZHoZWm8eOZCKFlsjVJ0gueEuZp43nobA==} + '@zag-js/angle-slider@1.34.0': + resolution: {integrity: sha512-WEQTxxiqqfh025Bq7mhfYQc0AMua8dWWjmCOrGxXQD2kVI58RMNNCBImcNVYyvyCd97Om5rcaVEzaJ248xBNxQ==} - '@zag-js/aria-hidden@1.33.1': - resolution: {integrity: sha512-TpRAtssDHVfra5qxigk7w1NMf/crKu615INu6GAbNNMUBWD1rPZAfxdg/xe/BAcxLy+XM5/q62dVSNvpjXzN3g==} + '@zag-js/aria-hidden@1.34.0': + resolution: {integrity: sha512-aloo68LsjiHpZdR+qzNNBuFP6F72gPfwpBmPmcVUQbHp0o5nhT+oxmcqg1A3xeuJkvJibQVqgaSep2YMcVZSQQ==} - '@zag-js/avatar@1.33.1': - resolution: {integrity: sha512-D8HBPvIVLoty14CDx6wWfdfcalr/pf2FgJ0N7VTgExvZt8t64JWJarL75ZkIB3ROaNe4RMFdzabz1uc7BlcDyg==} + '@zag-js/avatar@1.34.0': + resolution: {integrity: sha512-rlJ8xpNLqSFxpyFtO2j+JRI0LTPgqzviEWSO/ejb+1w/VvB2SHPfslHfJsLqll9yUwnEvEn6+kEalcTPn/V5RQ==} - '@zag-js/carousel@1.33.1': - resolution: {integrity: sha512-FB72jCHhTTn0gXsWwDT/DrGMpBHQTxlKvwjEiBGkcprWVpptN0WGJR+EtX2Si/668sdH/471rew2DKA+h5k6Tw==} + '@zag-js/carousel@1.34.0': + resolution: {integrity: sha512-g9pmUfWQlII0glsltLJb12L6EXsw+ms69xELAF58pZxwxK4hss0M2ZZRRY/JOgU9nX6UtZTs8CdfaKI+bIyP5g==} - '@zag-js/checkbox@1.33.1': - resolution: {integrity: sha512-3rIPXB3O7hZukyjKpRAOn+Ob7jByBmDNU7wdpS2HRv7Urv9i5jUExlwayevw/a6JHQaT7mR1dL4culTyX+fJVA==} + '@zag-js/checkbox@1.34.0': + resolution: {integrity: sha512-47rEtbHXUh5t8bgk3j18lDvhoMTkxTKLBRUKx/TlNYl0r6/RwUWp4yRUpOM0NQkV3wvFyMc6LqTrOKRK6KKngg==} - '@zag-js/clipboard@1.33.1': - resolution: {integrity: sha512-BcuHY3h7fOgR8yX0JHHN/SIAfZOGwrMF1AXKpqeY9Xq2R0lbDMEyXBwT7rQtQUBWCkoSau1e3Nk8ey1yOsWmYw==} + '@zag-js/clipboard@1.34.0': + resolution: {integrity: sha512-Wkm4sWwXcIvpeZEdaUQB7Oy91UMUm1Rfyb/UjWUv12gMlXTAmdUucaT2yR7cPanYzUzwOR3ZNepwOxC2CjYE6w==} - '@zag-js/collapsible@1.33.1': - resolution: {integrity: sha512-FnEaoIufmYM4kFUET6gusFD7J5cAu/PY78BQ4BqhT3I6sS9FWiu/eHCCsFf/6BqhtqtiCQoki/O5g0arZqOZfw==} + '@zag-js/collapsible@1.34.0': + resolution: {integrity: sha512-P2d4DLVpVbLP0Qfq/rWjyCSgj380LKcnU04xLQV4ujAESx6Ga+w6MbW09ywCZGi1JPWlRu6A06bhVSRvyHTGHg==} - '@zag-js/collection@1.33.1': - resolution: {integrity: sha512-4Js8oWS0C1zETlQzqJRny63uV/e54R6OerHfJfH9qAzkZuQnhMqZOAA4q6N+5GG6vb8WGB3927jS1A+Zn/pZuQ==} + '@zag-js/collection@1.34.0': + resolution: {integrity: sha512-2fU7l/iRpCSsngRYTfbnVOFLGhlylk4+IuJgWZPpBHafDv5hg14Ju5OZK0U+SCFSkHDvN/vnr/4QllIftBWsfQ==} - '@zag-js/combobox@1.33.1': - resolution: {integrity: sha512-9K2i5P+zf6T9Cqa9idzYXvEC/If5gDDbQWYgqflO18ptB0dTvfKkihBsA4/PEig3Ayvj/UGFTlFlbC17M5aACQ==} + '@zag-js/combobox@1.34.0': + resolution: {integrity: sha512-fsTfGCDBbyYaWAmsF7VsOh5LIpqhbd2IxMLl41I8GPeQIRkiqomuOjEmq4vNiNIBEGVPMREFWb67ADlivZcPOQ==} - '@zag-js/core@1.33.1': - resolution: {integrity: sha512-8hnw0/CFTytcYiIRij4Orpni2a79NSiH6Em+58A9AqMJGX8UE1zh6GsLWgrKQPiEiC8Cf3WgNXgCddJKpm8/Yw==} + '@zag-js/core@1.34.0': + resolution: {integrity: sha512-+w6zQ5PiBNbUd2xu91FUQruIAHcTYsueUNlXz3biAEAcU7pzre3Es4ApTepkbijYjcsiXzI/QwTtujot53eVyA==} - '@zag-js/date-picker@1.33.1': - resolution: {integrity: sha512-PfVvttb83DosW9p9BXRAkNsk/duueicd7sEVdOGfgfIs3QJeVn+jvuli8Z2A0oQCok3VCfBwXd+MiwKjyLRpIg==} + '@zag-js/date-picker@1.34.0': + resolution: {integrity: sha512-0jF2clkuwyK6fIIkHd8RsqWO+/zl6xwK5HHEJoLHWEbWaJ4DvOfjadQD/5bHGIbCulZb37yHKgidI3pcQ1R3uw==} peerDependencies: '@internationalized/date': '>=3.0.0' - '@zag-js/date-utils@1.33.1': - resolution: {integrity: sha512-hnM/IJ4jBHHCcVNfZyjvAI/0suW6c2XFYwcjM6xoGyG4P1x7YU9H9vuhp8mv7XDj4qqQFS/x8+UEcytZG9wtAg==} + '@zag-js/date-utils@1.34.0': + resolution: {integrity: sha512-9LjsBA4oCq1+jq33CTw469lhNhCajAvN1QyNGLTfzRdUDfaJE1/egv3akM2Gn+FaKU1pOWB11uJiK+VR6Eui9w==} peerDependencies: '@internationalized/date': '>=3.0.0' - '@zag-js/dialog@1.33.1': - resolution: {integrity: sha512-OUjcIby0VSFBULpakDQJL+gtpVR13hvMZDydUm44LF5ygfoe5E7mfp24Q09VGgvbofOZTuwAK5xKTV/AaSX/MQ==} + '@zag-js/dialog@1.34.0': + resolution: {integrity: sha512-w52x1jCgpmaOx5zmJ2878sX6yIjz7QN/ICT5eH22uoyZQ1YKl1XlFUYf3SOCzY3svs4gSRHLd+/KFA/u0YEGGQ==} - '@zag-js/dismissable@1.33.1': - resolution: {integrity: sha512-ZER2LFMTdhQxkIMuT3EMg6vZCjVjttDJJP8g6d7kSARcxN75myUG+H8qZqj9JbH5WSF6Xaf++O+LMUgwzIeixw==} + '@zag-js/dismissable@1.34.0': + resolution: {integrity: sha512-R+H2E1gasWhChnBBXgzWSnok5NIL4IG9XfVPFTOWNz5OldqOTWH6p0xZgVbO85/ifBDmF3C+663ZeaZ1EW49EA==} - '@zag-js/dom-query@1.33.1': - resolution: {integrity: sha512-Iyl0D3nLvJuMkkuRy22xhj4pkzexUCDlRpCzqIrOMDKsmFka/WV9PIclZKVpMECTi9dEQmJuGTjBVaCOReLu+Q==} + '@zag-js/dom-query@1.34.0': + resolution: {integrity: sha512-bK3rEAsY130SPrRsWn+M4Dim2Ne92Sq+RsRQ4I9UTOa98OeSPVc8F77zPdoc4aKS+XAWWpzNASTfDjYhAy1gKQ==} - '@zag-js/editable@1.33.1': - resolution: {integrity: sha512-uLLwopl5naET76ND+/GZDVMlXaAIwepAhmfNA+Esj4Upgtd3lpD5SNzJiVuyzZ0ewVyp2cuXHHAfNiibhkoFlA==} + '@zag-js/editable@1.34.0': + resolution: {integrity: sha512-ABJl+HusXMFidoGClcuH8rOhkw/9StKKak9WSQbHRcgd4f3GQQt7h8ENMaxmaK2FwZKywcoNnVB4+OdssVAxWQ==} - '@zag-js/floating-panel@1.33.1': - resolution: {integrity: sha512-MKtFyC3xxCUmHEnugR+KMcVIX7FdHsoZfDxcKc74h+2M6FAmk6YB8lByoY9pkCR9ems/5DkHcMU9cVVJ9kiFqA==} + '@zag-js/floating-panel@1.34.0': + resolution: {integrity: sha512-5bd/5sOGfHunfKFXxQm96QbS6RgezB72nHW6Ir/wD1/CxClcRKILYwznx6E3BxpqPaOq8s6JdsMnLdzSoASoAw==} - '@zag-js/focus-trap@1.33.1': - resolution: {integrity: sha512-aX1YpER7dsegKroNGMnBDfcS14Z9LTdwESSXFDc9C9jFo45qOzfhxmXR+a5rsveMRkvhMFxGffrbpwfvZbRs0A==} + '@zag-js/focus-trap@1.34.0': + resolution: {integrity: sha512-vejkVmkqQ5Uv3/bhFaFovC2ownIwN+3Bh2CGLhqTI+bijsGtbFpmcpmwVXxnWQdO9VKaesygChFLmgQaFJ9UWw==} - '@zag-js/focus-visible@1.33.1': - resolution: {integrity: sha512-xnk2BwO6jYuudj4jMzNYD4AxgaD2sqnLHkwmHImOnVa5frbYziGzevo9iJWC+2THyqQjUXLQ6Zfo6J/Hi3KyNQ==} + '@zag-js/focus-visible@1.34.0': + resolution: {integrity: sha512-tHtvqxiBsNfzDaZaD8qpSBl7PRndeDrVLgCX80M0bcYu6f1RHJY53qGriBFqocRuKlNtSASE39JAicKCojQzpw==} - '@zag-js/interact-outside@1.33.1': - resolution: {integrity: sha512-XnqwYsGw0GVmjBpDziwWXKE/+KeZLgRnjEpyVr6HMATMGD+c4j6TmIbI9OGEaWliLuwvHdTclkmK4WYTaAGmiw==} + '@zag-js/interact-outside@1.34.0': + resolution: {integrity: sha512-U7E3k2pkF7xh9cxU3za1BaiAo33kCdW/iulicPutTVEgSa09bXARa2/v/ISrpAKj/Crau69UAfxjjl4D56fxnw==} - '@zag-js/listbox@1.33.1': - resolution: {integrity: sha512-8XT+6T82xG3BJwC7VYu/I1W8Hxyjgpke8tB1odQSWOV23pVXXPbol7wQbtoieSVeNDsZD8K12CpB40oRVrcSHA==} + '@zag-js/listbox@1.34.0': + resolution: {integrity: sha512-T1KHdYpaN/iIL2w14ySG4wLLti/az62CIe1Tnx9szuo7nC38GeeSsWtQJxc+DRKYTPNC9PDvbImKS6CsdcaaSg==} - '@zag-js/live-region@1.33.1': - resolution: {integrity: sha512-KbU2wUSMd01fY7dgc9WhvU2x07FxNHKSCrn+fFUnB+Qoy6iiVv0A729JDbzPUUcpBV0BFoQ3qNdBDVyBalbpaQ==} + '@zag-js/live-region@1.34.0': + resolution: {integrity: sha512-GTlVqRLJaZpqSg6j6XufxrD2z0EIaTp1tIddU4USceNb38SDb4JbFf/DB6IATRS506W27kfxQW1J4uau5bJLEg==} - '@zag-js/menu@1.33.1': - resolution: {integrity: sha512-QihwaFCgGcrPbJSoP73nt749/rlUANiIrCU//8WWfQTgv0NBJprBD7d3banDNlK9ZSGmvELcpyQ/fKU4cfn0GQ==} + '@zag-js/menu@1.34.0': + resolution: {integrity: sha512-qruSZvJuLVBi0Af8YkB/7+fRAmLuAu6MSMLuIW2WT4FlcsqLk9AEnYXD5X/shUKJmNiIvZisyaZlpzMxkNHChQ==} - '@zag-js/number-input@1.33.1': - resolution: {integrity: sha512-5YKr8uagIDGXp3hIqo4IUBGxS5WhH0xM1CQf2zimfDWvBOng+Y+MH/4Lwu9wKuyIq/J3SJqsjO+2OOF7u6ju/g==} + '@zag-js/number-input@1.34.0': + resolution: {integrity: sha512-xuq7skBhR4zH7bf9quuADOTQwl2FVdv/Wpsetj4epN+ZLJk31UwRYYYusfEhXZ3B9N9m7qdgADEF1zJLjwtm+g==} - '@zag-js/password-input@1.33.1': - resolution: {integrity: sha512-pJrz50JhQLTfiatehATr40udJYggYmJ7V/7/dBKqthGpMwoaVV3bmtKFSenFGc2mMb5Rlf9KKqHO/dYB7jpNiA==} + '@zag-js/password-input@1.34.0': + resolution: {integrity: sha512-OAkJNz443Pm90GVBomfDEhI20ADZEGv/23MprP3x1+sV/Ppn9HOTWUWSyghVQzIfocDK0X5zPygIE/XaJVaxBQ==} - '@zag-js/pin-input@1.33.1': - resolution: {integrity: sha512-q6/DRsIV6ZDKzkFmdzbcsVBm7+I7hMlrsLr/P/jH0/fYE5T9t+1m9ll5j7/5RHFJHQ1WajHpdt5ad5mfXMuxKA==} + '@zag-js/pin-input@1.34.0': + resolution: {integrity: sha512-PpH0MGW67tnNupApXv5a0LzzkYgzVxgomcirCrWjqPGMa+ZHBy6T0ZdHBVR/zBfOwtB/s/wiS883n18TIJ0Ekg==} - '@zag-js/popper@1.33.1': - resolution: {integrity: sha512-DNKRh/SRXB2wcvVYK1wvcEufS4vfVXJOv23QUee761bTv4nrPNll5pZFsYEHatiCNkAmO0MRRYA2Sc6jk9nxNA==} + '@zag-js/popper@1.34.0': + resolution: {integrity: sha512-JCcQUfe8aSkE1Pgfes30xx5Fea2O3eIOhMKr7pMAFByYw9otLEimFaA3T8OU11O9gTgy5qlStAljbxpJYFB4pg==} - '@zag-js/radio-group@1.33.1': - resolution: {integrity: sha512-W/T8Hea3Z4mWCErm2fJc/EYabxRkKHFJStSClyllqknF3Y+b42MaKGuub1IcACO3pe6csLTkomdxy1qDLWl/dg==} + '@zag-js/radio-group@1.34.0': + resolution: {integrity: sha512-xqUVmMrFgN/9/oBa/rvuTnKVc1KAH1jpHNsIYhMIAAbyR96Kl8LxRC0tq+gnwLFGijHnxjnTfqqHSwul+k3Suw==} - '@zag-js/rect-utils@1.33.1': - resolution: {integrity: sha512-vCIgZF/z8oeYfUhGUgRiNEfOS8on4rUXi4vtL4IvHSdAv5VxZw4ODoLhIzRGT3BwsiMfr8qJ8fmrcR2oFRFQgA==} + '@zag-js/rect-utils@1.34.0': + resolution: {integrity: sha512-KUJrU0xb1L53PFDDP0y/VuOzKDIFAFvfWru6AWIR1HaDDcX62K9s51mg9E27LgHSsXn1/yUTR2Nktb+wXy1dOg==} - '@zag-js/remove-scroll@1.33.1': - resolution: {integrity: sha512-5+Mvboqlmv8EdJoixAbGrftFVWZTznsVJn40BuB/6fYQeqdsZ2vFmSmSIr7btFOPcj3BcTMo0SbWNNta3fAOrg==} + '@zag-js/remove-scroll@1.34.0': + resolution: {integrity: sha512-StGyXyFdKXU5P86M7Jqiwh5dy9UwguXva2WuO+eM4vCfI/GvS76JoznjJqjF08uKBRelJdm+JDO0+KJj2OGqUA==} - '@zag-js/scroll-snap@1.33.1': - resolution: {integrity: sha512-GLEb+YJj800ia2zyTFxVZomQ1cFSShazUQ/1uAxX0Lj7+aZK88cZhIn7AI0+yBXTPBS0zrZDhBPsGEDQX+Q9Fw==} + '@zag-js/scroll-snap@1.34.0': + resolution: {integrity: sha512-WJ6u75xIlQvtglLon4HADDBjbj9frQsmIAoeTkhDw7RaHNwT4Z0jqilPtkP87wBkSTNRc5/xuifgPSpm+81o3Q==} - '@zag-js/select@1.33.1': - resolution: {integrity: sha512-eG+Ftdse0zvCAkXBMNZVBlM+KNvFRKHToxlxgid6wOd5QgRGwr4HaJuWaz908nBIZRYMFVvC+lLaygUVORHmGg==} + '@zag-js/select@1.34.0': + resolution: {integrity: sha512-Cg3aMo4cMF2UWoP5oy4N/iwKR3vMbmrP12azlvQ0ZIjwGoDVBzUKbVvTo6W4Br5+PJWhzlYKphkf3RoCwpYB/w==} - '@zag-js/signature-pad@1.33.1': - resolution: {integrity: sha512-bnTuG28F1A5Kdt+tsveBgNFhRG71vBBIoW8xVW+udph+9XhWfxsLC2j/O6QlnPgYEjOPUlG6/4wNT4LHzLQYUQ==} + '@zag-js/signature-pad@1.34.0': + resolution: {integrity: sha512-nyBOhncvaGa+FpAPM54q5oPnu0UNSK3EedIZdEAB1GZi0sI7wYmcKnKGxxN1QIwT4kWXbDWoA0y1ivHZFZFgdg==} - '@zag-js/store@1.33.1': - resolution: {integrity: sha512-FYkrR9IskD5wyKjYUAHWwdGf/C3FmnactfHR9/6dm9YzNO/+jtWxYsFnHQB8dUm9/6VxAZHofw3FbuyPRJ/x3g==} + '@zag-js/store@1.34.0': + resolution: {integrity: sha512-ITHPt9mZT7OQUicVGVI3ajRa3/VZvXGiIPXo3YsQtajM62N3RvlbjyDRqfy0ApmW1/uQzO+ftj+v6BbDWBe2IQ==} - '@zag-js/switch@1.33.1': - resolution: {integrity: sha512-2jl/R4CKLYvk+4cmSYFo3D2gQ+1ts9H7Y4yH98o9rXgPMvdEM9KMKX1FTqJRIY7v6ZkcNbvV/vKP3bDvMdTpug==} + '@zag-js/switch@1.34.0': + resolution: {integrity: sha512-g9ZJDHDSD57VPYmNpARHhEmZPn5M3l+SCcHIT73IeiCR0EEmvKtNzd7YruAUGZBeFDch4g3lLAPqD4FPMgIEyA==} - '@zag-js/tabs@1.33.1': - resolution: {integrity: sha512-Xquhso7jUch9UrG5N+5vNfR8S2bWUk6EDpBBArY0X5oPSnlzgwJcjWh98hH1QyHX3JmWZN4kAfVKUxNdQxRnVw==} + '@zag-js/tabs@1.34.0': + resolution: {integrity: sha512-UW4uTdutIl5tgkUOsmRH68K7zhar2MNg5usYGeykQu0Gk/GeEir+R1VvU6NFBu9Tc5nSq4VvEGytwjlNkNDU5A==} - '@zag-js/timer@1.33.1': - resolution: {integrity: sha512-GgqntefAEQbf66aNgA6NL9Rtrrxcd0/IJVddTj1/xihCnJ8u6AOU4syG5tie0Tpc2caDAntOwlYjpEy3n2AGcA==} + '@zag-js/timer@1.34.0': + resolution: {integrity: sha512-ppH1XRvp83y68ww1qLiKkzwUOgWpb5g47nHU3Q20zq3l/gfWIhjfkTcqp+ak/0C/Vk4ZvmP5Jhfn+oFFpAbTFg==} - '@zag-js/toast@1.33.1': - resolution: {integrity: sha512-kI2/VJcBQGgHpmuWiIDqPn8ejFEODh5YhjWbnvjGRG+x3XoPuMq6hhxXV6VWJslbZJtTmzxDcP+Xamdrf1hbZA==} + '@zag-js/toast@1.34.0': + resolution: {integrity: sha512-v9mWi5dBpUGXJLMvSVMOR/mmujENKhE2v2M6VJ/+0bilBYwcX3VW05OywSYaTlxi5vt9n2cOpvFDstMAnuRJHQ==} - '@zag-js/toggle-group@1.33.1': - resolution: {integrity: sha512-KZaMFN5u26d8elAcdu6LDC7byltpzeoemXHMMa7H/1upS3/98ESKUzx1VlA5SSTAinU4t9+rXoR3VTtP2RJbTw==} + '@zag-js/toggle-group@1.34.0': + resolution: {integrity: sha512-zXw9A5jTygdWgjsQi+gasvXaxekIbDTCbB0vrbLVZE/10aSO+v70INZmhlbffl6MjykqMD9HeV2eKvdpg0Uudw==} - '@zag-js/tree-view@1.33.1': - resolution: {integrity: sha512-5SiwSGdcqiGoCQl46pvEAgGkM5gTsPpLLPXB2Eqfojm2fm2oev73+1gWsZt1/sX/qsIQ1hH3a2h44rXW1W2IWg==} + '@zag-js/tree-view@1.34.0': + resolution: {integrity: sha512-10WbUg/w9OlohHabJWqwFx3PSyqUdxyF7dHWJQKLxZo6J1YUrp1KXIC6+/NOSKtJ0F0olZbRJnrTAvgkejo97Q==} - '@zag-js/types@1.33.1': - resolution: {integrity: sha512-huJdwaeyptKDuZqhhFQRWNiMAJEdei4fTAQ3xIBw07GW27zKwust4Bn0y+8PYlnVVQn2auH4lpIXXwPccFRclQ==} + '@zag-js/types@1.34.0': + resolution: {integrity: sha512-0DRMFFfq77ofAQIXQrqieGAOvFezKx6VDrdgMsV7zhQRbTPBXxA7VjDdd0mSSk8sHFiA1AL7Jxzc5zFrQcOpow==} - '@zag-js/utils@1.33.1': - resolution: {integrity: sha512-N73enDcveuto5BdYd15m7bu08vd+Re//eufgzGyKPWuzFowEFV77si1v9zZjmK9eXVMTFyde/TPal3aHv4VEJg==} + '@zag-js/utils@1.34.0': + resolution: {integrity: sha512-rdk7T3M8Rj+Wew7jtQuaWzxT/htbk2PCoLuF2yj3ldef+4txVE2uVQWodeM5mp7vArvMJxXvipnJJtkTXEPNkw==} - '@zag-js/vanilla@1.33.1': - resolution: {integrity: sha512-XTv+eugUCNotlEpw70kd5zb2Ao+q2+Daphas3DFkEqCTToiiVYTF8csh/oUSNh2mDV9hgjc9lCxJTF3kzO6CSA==} + '@zag-js/vanilla@1.34.0': + resolution: {integrity: sha512-svVl2KrUNgB4O4l2LQu2f6crb+Qw5kfRL54WJUaVGeJZrrD3kjoXtcCMUP4nLNqX/YVnY4d2kPn/qskU+JUc1A==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -983,324 +983,327 @@ snapshots: '@typescript-eslint/types': 8.54.0 eslint-visitor-keys: 4.2.1 - '@zag-js/accordion@1.33.1': + '@zag-js/accordion@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/anatomy@1.33.1': {} + '@zag-js/anatomy@1.34.0': {} - '@zag-js/angle-slider@1.33.1': + '@zag-js/angle-slider@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/rect-utils': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/aria-hidden@1.33.1': + '@zag-js/aria-hidden@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 + '@zag-js/dom-query': 1.34.0 - '@zag-js/avatar@1.33.1': + '@zag-js/avatar@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/carousel@1.33.1': + '@zag-js/carousel@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/scroll-snap': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/checkbox@1.33.1': + '@zag-js/checkbox@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/clipboard@1.33.1': + '@zag-js/clipboard@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/collapsible@1.33.1': + '@zag-js/collapsible@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/collection@1.33.1': + '@zag-js/collection@1.34.0': dependencies: - '@zag-js/utils': 1.33.1 + '@zag-js/utils': 1.34.0 - '@zag-js/combobox@1.33.1': + '@zag-js/combobox@1.34.0': dependencies: - '@zag-js/anatomy': 1.33.1 - '@zag-js/aria-hidden': 1.33.1 - '@zag-js/collection': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/dismissable': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/popper': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/aria-hidden': 1.34.0 + '@zag-js/collection': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dismissable': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/popper': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/core@1.33.1': + '@zag-js/core@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/dom-query': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/date-picker@1.33.1(@internationalized/date@3.11.0)': + '@zag-js/date-picker@1.34.0(@internationalized/date@3.11.0)': dependencies: '@internationalized/date': 3.11.0 - '@zag-js/anatomy': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/date-utils': 1.33.1(@internationalized/date@3.11.0) - '@zag-js/dismissable': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/live-region': 1.33.1 - '@zag-js/popper': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 - - '@zag-js/date-utils@1.33.1(@internationalized/date@3.11.0)': + '@zag-js/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/date-utils': 1.34.0(@internationalized/date@3.11.0) + '@zag-js/dismissable': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/live-region': 1.34.0 + '@zag-js/popper': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 + + '@zag-js/date-utils@1.34.0(@internationalized/date@3.11.0)': dependencies: '@internationalized/date': 3.11.0 - '@zag-js/dialog@1.33.1': + '@zag-js/dialog@1.34.0': dependencies: - '@zag-js/anatomy': 1.33.1 - '@zag-js/aria-hidden': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/dismissable': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/focus-trap': 1.33.1 - '@zag-js/remove-scroll': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/aria-hidden': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dismissable': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-trap': 1.34.0 + '@zag-js/remove-scroll': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/dismissable@1.33.1': + '@zag-js/dismissable@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 - '@zag-js/interact-outside': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/dom-query': 1.34.0 + '@zag-js/interact-outside': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/dom-query@1.33.1': + '@zag-js/dom-query@1.34.0': dependencies: - '@zag-js/types': 1.33.1 + '@zag-js/types': 1.34.0 - '@zag-js/editable@1.33.1': + '@zag-js/editable@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/interact-outside': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/floating-panel@1.33.1': + '@zag-js/floating-panel@1.34.0': dependencies: - '@zag-js/anatomy': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/popper': 1.33.1 - '@zag-js/rect-utils': 1.33.1 - '@zag-js/store': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/popper': 1.34.0 + '@zag-js/rect-utils': 1.34.0 + '@zag-js/store': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/focus-trap@1.33.1': + '@zag-js/focus-trap@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 + '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible@1.33.1': + '@zag-js/focus-visible@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 + '@zag-js/dom-query': 1.34.0 - '@zag-js/interact-outside@1.33.1': + '@zag-js/interact-outside@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/dom-query': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/listbox@1.33.1': + '@zag-js/listbox@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/collection': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/live-region@1.33.1': {} + '@zag-js/live-region@1.34.0': {} - '@zag-js/menu@1.33.1': + '@zag-js/menu@1.34.0': dependencies: - '@zag-js/anatomy': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/dismissable': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/popper': 1.33.1 - '@zag-js/rect-utils': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dismissable': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/popper': 1.34.0 + '@zag-js/rect-utils': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/number-input@1.33.1': + '@zag-js/number-input@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/password-input@1.33.1': + '@zag-js/password-input@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/pin-input@1.33.1': + '@zag-js/pin-input@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/popper@1.33.1': + '@zag-js/popper@1.34.0': dependencies: '@floating-ui/dom': 1.7.5 - '@zag-js/dom-query': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/dom-query': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/radio-group@1.33.1': + '@zag-js/radio-group@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/rect-utils@1.33.1': {} + '@zag-js/rect-utils@1.34.0': {} - '@zag-js/remove-scroll@1.33.1': + '@zag-js/remove-scroll@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 + '@zag-js/dom-query': 1.34.0 - '@zag-js/scroll-snap@1.33.1': + '@zag-js/scroll-snap@1.34.0': dependencies: - '@zag-js/dom-query': 1.33.1 + '@zag-js/dom-query': 1.34.0 - '@zag-js/select@1.33.1': + '@zag-js/select@1.34.0': dependencies: - '@zag-js/anatomy': 1.33.1 - '@zag-js/collection': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/dismissable': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/popper': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/collection': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dismissable': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/popper': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/signature-pad@1.33.1': + '@zag-js/signature-pad@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 perfect-freehand: 1.2.3 - '@zag-js/store@1.33.1': + '@zag-js/store@1.34.0': dependencies: proxy-compare: 3.0.1 - '@zag-js/switch@1.33.1': + '@zag-js/switch@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/focus-visible': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/tabs@1.33.1': + '@zag-js/tabs@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/timer@1.33.1': + '@zag-js/timer@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/toast@1.33.1': + '@zag-js/toast@1.34.0': dependencies: - '@zag-js/anatomy': 1.33.1 - '@zag-js/core': 1.33.1 - '@zag-js/dismissable': 1.33.1 - '@zag-js/dom-query': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dismissable': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/toggle-group@1.33.1': + '@zag-js/toggle-group@1.34.0': 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/anatomy': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/tree-view@1.33.1': + '@zag-js/tree-view@1.34.0': 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/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/anatomy': 1.34.0 + '@zag-js/collection': 1.34.0 + '@zag-js/core': 1.34.0 + '@zag-js/dom-query': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 - '@zag-js/types@1.33.1': + '@zag-js/types@1.34.0': dependencies: csstype: 3.2.3 - '@zag-js/utils@1.33.1': {} + '@zag-js/utils@1.34.0': {} - '@zag-js/vanilla@1.33.1': + '@zag-js/vanilla@1.34.0': dependencies: - '@zag-js/core': 1.33.1 - '@zag-js/store': 1.33.1 - '@zag-js/types': 1.33.1 - '@zag-js/utils': 1.33.1 + '@zag-js/core': 1.34.0 + '@zag-js/store': 1.34.0 + '@zag-js/types': 1.34.0 + '@zag-js/utils': 1.34.0 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: diff --git a/priv/design/components/combobox.css b/priv/design/components/combobox.css index 7731191..f455e0a 100644 --- a/priv/design/components/combobox.css +++ b/priv/design/components/combobox.css @@ -14,6 +14,14 @@ @apply ui-label; } + .combobox [data-scope="combobox"][data-part="empty"] { + @apply ui-label; + + list-style: none; + padding-inline: var(--spacing-ui-padding); + padding-block: var(--spacing-mini-padding-sm); + } + .combobox [data-scope="combobox"][data-part="control"] { display: flex; width: 100%; @@ -66,6 +74,13 @@ .combobox [data-scope="combobox"][data-part="item-group"] { display: flex; flex-direction: column; + min-height: fit-content; + } + + .combobox [data-scope="combobox"][data-part="item-group-content"] { + min-height: fit-content; + display: flex; + flex-direction: column; } .combobox [data-scope="combobox"][data-part="item-group-label"] { @@ -81,6 +96,13 @@ .combobox [data-scope="combobox"][data-part="item"] { @apply ui-item; + + align-items: flex-start; + min-height: fit-content; + } + + .combobox [data-scope="combobox"][data-part="item"] [data-part="item-text"] { + min-height: fit-content; } .combobox [data-scope="combobox"][data-part="error"] { diff --git a/priv/static/accordion.mjs b/priv/static/accordion.mjs index e6bd251..2c6373d 100644 --- a/priv/static/accordion.mjs +++ b/priv/static/accordion.mjs @@ -22,9 +22,9 @@ import { queryAll, remove, warn -} from "./chunk-IXOYOLUJ.mjs"; +} from "./chunk-TZXIWZZ7.mjs"; -// ../node_modules/.pnpm/@zag-js+accordion@1.33.1/node_modules/@zag-js/accordion/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+accordion@1.34.0/node_modules/@zag-js/accordion/dist/index.mjs var anatomy = createAnatomy("accordion").parts("root", "item", "itemTrigger", "itemContent", "itemIndicator"); var parts = anatomy.build(); var getRootId = (ctx) => ctx.ids?.root ?? `accordion:${ctx.id}`; diff --git a/priv/static/angle-slider.mjs b/priv/static/angle-slider.mjs index 24298f6..5a38f0b 100644 --- a/priv/static/angle-slider.mjs +++ b/priv/static/angle-slider.mjs @@ -1,7 +1,7 @@ import { createRect, getPointAngle -} from "./chunk-BMVNROAE.mjs"; +} from "./chunk-FEZIYMNT.mjs"; import { Component, VanillaMachine, @@ -22,9 +22,9 @@ import { setElementValue, snapValueToStep, trackPointerMove -} from "./chunk-IXOYOLUJ.mjs"; +} from "./chunk-TZXIWZZ7.mjs"; -// ../node_modules/.pnpm/@zag-js+angle-slider@1.33.1/node_modules/@zag-js/angle-slider/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+angle-slider@1.34.0/node_modules/@zag-js/angle-slider/dist/index.mjs var anatomy = createAnatomy("angle-slider").parts( "root", "label", diff --git a/priv/static/avatar.mjs b/priv/static/avatar.mjs index 16aaf2b..f90feee 100644 --- a/priv/static/avatar.mjs +++ b/priv/static/avatar.mjs @@ -9,9 +9,9 @@ import { normalizeProps, observeAttributes, observeChildren -} from "./chunk-IXOYOLUJ.mjs"; +} from "./chunk-TZXIWZZ7.mjs"; -// ../node_modules/.pnpm/@zag-js+avatar@1.33.1/node_modules/@zag-js/avatar/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+avatar@1.34.0/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}`; diff --git a/priv/static/carousel.mjs b/priv/static/carousel.mjs index 90616c9..3a8e2b9 100644 --- a/priv/static/carousel.mjs +++ b/priv/static/carousel.mjs @@ -34,9 +34,9 @@ import { throttle, trackPointerMove, uniq -} from "./chunk-IXOYOLUJ.mjs"; +} from "./chunk-TZXIWZZ7.mjs"; -// ../node_modules/.pnpm/@zag-js+scroll-snap@1.33.1/node_modules/@zag-js/scroll-snap/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+scroll-snap@1.34.0/node_modules/@zag-js/scroll-snap/dist/index.mjs var getDirection = (element) => getComputedStyle2(element).direction; function getScrollPadding(element) { const style = getComputedStyle2(element); @@ -198,7 +198,7 @@ function findSnapPoint(parent, axis, predicate) { 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 +// ../node_modules/.pnpm/@zag-js+carousel@1.34.0/node_modules/@zag-js/carousel/dist/index.mjs var anatomy = createAnatomy("carousel").parts( "root", "itemGroup", diff --git a/priv/static/checkbox.mjs b/priv/static/checkbox.mjs index 30be8a2..90a7341 100644 --- a/priv/static/checkbox.mjs +++ b/priv/static/checkbox.mjs @@ -1,7 +1,7 @@ import { isFocusVisible, trackFocusVisible -} from "./chunk-TEV2GE3U.mjs"; +} from "./chunk-GXGJDSCU.mjs"; import { Component, VanillaMachine, @@ -21,9 +21,9 @@ import { trackFormControl, trackPress, visuallyHiddenStyle -} from "./chunk-IXOYOLUJ.mjs"; +} from "./chunk-TZXIWZZ7.mjs"; -// ../node_modules/.pnpm/@zag-js+checkbox@1.33.1/node_modules/@zag-js/checkbox/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+checkbox@1.34.0/node_modules/@zag-js/checkbox/dist/index.mjs var anatomy = createAnatomy("checkbox").parts("root", "label", "control", "indicator"); var parts = anatomy.build(); var getRootId = (ctx) => ctx.ids?.root ?? `checkbox:${ctx.id}`; diff --git a/priv/static/chunk-4RWUEBEQ.mjs b/priv/static/chunk-4RWUEBEQ.mjs new file mode 100644 index 0000000..84411a5 --- /dev/null +++ b/priv/static/chunk-4RWUEBEQ.mjs @@ -0,0 +1,1339 @@ +import { + chunk, + hasProp, + isEqual, + isObject, + nextIndex, + prevIndex +} from "./chunk-TZXIWZZ7.mjs"; + +// ../node_modules/.pnpm/@zag-js+collection@1.34.0/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, "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; + } + getIndexPath(valueOrValuePath) { + if (Array.isArray(valueOrValuePath)) { + if (valueOrValuePath.length === 0) return []; + const indexPath = []; + let currentChildren = this.getNodeChildren(this.rootNode); + for (let i = 0; i < valueOrValuePath.length; i++) { + const currentValue = valueOrValuePath[i]; + const matchingChildIndex = currentChildren.findIndex((child) => this.getNodeValue(child) === currentValue); + if (matchingChildIndex === -1) break; + indexPath.push(matchingChildIndex); + if (i < valueOrValuePath.length - 1) { + const currentNode = currentChildren[matchingChildIndex]; + currentChildren = this.getNodeChildren(currentNode); + } + } + return indexPath; + } else { + return findIndexPath(this.rootNode, { + getChildren: this.getNodeChildren, + predicate: (node) => this.getNodeValue(node) === valueOrValuePath + }); + } + } +}; +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-FEZIYMNT.mjs b/priv/static/chunk-FEZIYMNT.mjs new file mode 100644 index 0000000..600e5fb --- /dev/null +++ b/priv/static/chunk-FEZIYMNT.mjs @@ -0,0 +1,432 @@ +// ../node_modules/.pnpm/@zag-js+rect-utils@1.34.0/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-GXGJDSCU.mjs b/priv/static/chunk-GXGJDSCU.mjs new file mode 100644 index 0000000..bac5750 --- /dev/null +++ b/priv/static/chunk-GXGJDSCU.mjs @@ -0,0 +1,174 @@ +import { + getActiveElement, + getDocument, + getEventTarget, + getWindow, + isMac, + isVirtualClick +} from "./chunk-TZXIWZZ7.mjs"; + +// ../node_modules/.pnpm/@zag-js+focus-visible@1.34.0/node_modules/@zag-js/focus-visible/dist/index.mjs +function isValidKey(e) { + return !(e.metaKey || !isMac() && e.altKey || e.ctrlKey || e.key === "Control" || e.key === "Shift" || e.key === "Meta"); +} +var nonTextInputTypes = /* @__PURE__ */ new Set(["checkbox", "radio", "range", "color", "file", "image", "button", "submit", "reset"]); +function isKeyboardFocusEvent(isTextInput, modality, e) { + const eventTarget = e ? getEventTarget(e) : null; + const doc = getDocument(eventTarget); + const win = getWindow(eventTarget); + const activeElement = getActiveElement(doc); + isTextInput = isTextInput || activeElement instanceof win.HTMLInputElement && !nonTextInputTypes.has(activeElement?.type) || activeElement instanceof win.HTMLTextAreaElement || activeElement instanceof win.HTMLElement && activeElement.isContentEditable; + return !(isTextInput && modality === "keyboard" && e instanceof win.KeyboardEvent && !Reflect.has(FOCUS_VISIBLE_INPUT_KEYS, e.key)); +} +var currentModality = null; +var changeHandlers = /* @__PURE__ */ new Set(); +var listenerMap = /* @__PURE__ */ new Map(); +var hasEventBeforeFocus = false; +var hasBlurredWindowRecently = false; +var ignoreFocusEvent = false; +var FOCUS_VISIBLE_INPUT_KEYS = { + Tab: true, + Escape: true +}; +function triggerChangeHandlers(modality, e) { + for (let handler of changeHandlers) { + handler(modality, e); + } +} +function handleKeyboardEvent(e) { + hasEventBeforeFocus = true; + if (isValidKey(e)) { + currentModality = "keyboard"; + triggerChangeHandlers("keyboard", e); + } +} +function handlePointerEvent(e) { + currentModality = "pointer"; + if (e.type === "mousedown" || e.type === "pointerdown") { + hasEventBeforeFocus = true; + triggerChangeHandlers("pointer", e); + } +} +function handleClickEvent(e) { + if (isVirtualClick(e)) { + hasEventBeforeFocus = true; + currentModality = "virtual"; + } +} +function handleFocusEvent(e) { + const target = getEventTarget(e); + if (target === getWindow(target) || target === getDocument(target) || ignoreFocusEvent || !e.isTrusted) { + return; + } + if (!hasEventBeforeFocus && !hasBlurredWindowRecently) { + currentModality = "virtual"; + triggerChangeHandlers("virtual", e); + } + hasEventBeforeFocus = false; + hasBlurredWindowRecently = false; +} +function handleWindowBlur() { + hasEventBeforeFocus = false; + hasBlurredWindowRecently = true; +} +function setupGlobalFocusEvents(root) { + if (typeof window === "undefined" || listenerMap.get(getWindow(root))) { + return; + } + const win = getWindow(root); + const doc = getDocument(root); + let focus = win.HTMLElement.prototype.focus; + function patchedFocus() { + hasEventBeforeFocus = true; + focus.apply(this, arguments); + } + try { + Object.defineProperty(win.HTMLElement.prototype, "focus", { + configurable: true, + value: patchedFocus + }); + } catch { + } + doc.addEventListener("keydown", handleKeyboardEvent, true); + doc.addEventListener("keyup", handleKeyboardEvent, true); + doc.addEventListener("click", handleClickEvent, true); + win.addEventListener("focus", handleFocusEvent, true); + win.addEventListener("blur", handleWindowBlur, false); + if (typeof win.PointerEvent !== "undefined") { + doc.addEventListener("pointerdown", handlePointerEvent, true); + doc.addEventListener("pointermove", handlePointerEvent, true); + doc.addEventListener("pointerup", handlePointerEvent, true); + } else { + doc.addEventListener("mousedown", handlePointerEvent, true); + doc.addEventListener("mousemove", handlePointerEvent, true); + doc.addEventListener("mouseup", handlePointerEvent, true); + } + win.addEventListener( + "beforeunload", + () => { + tearDownWindowFocusTracking(root); + }, + { once: true } + ); + listenerMap.set(win, { focus }); +} +var tearDownWindowFocusTracking = (root, loadListener) => { + const win = getWindow(root); + const doc = getDocument(root); + const listenerData = listenerMap.get(win); + if (!listenerData) { + return; + } + try { + Object.defineProperty(win.HTMLElement.prototype, "focus", { + configurable: true, + value: listenerData.focus + }); + } catch { + } + doc.removeEventListener("keydown", handleKeyboardEvent, true); + doc.removeEventListener("keyup", handleKeyboardEvent, true); + doc.removeEventListener("click", handleClickEvent, true); + win.removeEventListener("focus", handleFocusEvent, true); + win.removeEventListener("blur", handleWindowBlur, false); + if (typeof win.PointerEvent !== "undefined") { + doc.removeEventListener("pointerdown", handlePointerEvent, true); + doc.removeEventListener("pointermove", handlePointerEvent, true); + doc.removeEventListener("pointerup", handlePointerEvent, true); + } else { + doc.removeEventListener("mousedown", handlePointerEvent, true); + doc.removeEventListener("mousemove", handlePointerEvent, true); + doc.removeEventListener("mouseup", handlePointerEvent, true); + } + listenerMap.delete(win); +}; +function getInteractionModality() { + return currentModality; +} +function setInteractionModality(modality) { + currentModality = modality; + triggerChangeHandlers(modality, null); +} +function isFocusVisible() { + return currentModality === "keyboard" || currentModality === "virtual"; +} +function trackFocusVisible(props = {}) { + const { isTextInput, autoFocus, onChange, root } = props; + setupGlobalFocusEvents(root); + onChange?.({ isFocusVisible: autoFocus || isFocusVisible(), modality: currentModality }); + const handler = (modality, e) => { + if (!isKeyboardFocusEvent(!!isTextInput, modality, e)) return; + onChange?.({ isFocusVisible: isFocusVisible(), modality }); + }; + changeHandlers.add(handler); + return () => { + changeHandlers.delete(handler); + }; +} + +export { + getInteractionModality, + setInteractionModality, + isFocusVisible, + trackFocusVisible +}; diff --git a/priv/static/chunk-LOW5KGCT.mjs b/priv/static/chunk-LOW5KGCT.mjs new file mode 100644 index 0000000..ca92c36 --- /dev/null +++ b/priv/static/chunk-LOW5KGCT.mjs @@ -0,0 +1,1988 @@ +import { + compact, + getComputedStyle as getComputedStyle2, + getWindow, + isHTMLElement, + isNull, + noop, + raf +} from "./chunk-TZXIWZZ7.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