phonograph/svelte/src/basic-dropdown.webc.svelte

97 lines
2.4 KiB
Svelte
Raw Normal View History

<svelte:options
customElement={{
2025-11-12 23:00:30 +00:00
// `shadowRoot` field must remain as the default, else named slots break.
props: {
button_aria_label: { attribute: "button-aria-label" },
button_class: { attribute: "button-class" },
2025-10-25 05:32:22 +00:00
alignment: { attribute: "alignment" },
},
tag: "basic-dropdown",
}}
/>
2025-11-12 23:00:30 +00:00
<!--
@component
A button with an associated popover, which can be styled for a variety of
purposes, such as menus, confirmation popups, and so on.
When used as a web component, this component is rendered with a shadow root in
order to correctly support named slots. As a result, it bundles its own
stylesheet, which is merely a copy of the "main" application stylesheet.
## Props
- `alignment`: When set to "right", the popover aligns with the right edge of
the trigger. Otherwise, it aligns with the left.
-->
<script lang="ts">
type Props = {
2025-10-25 05:32:22 +00:00
alignment?: string;
button_aria_label?: string;
button_class?: string;
2025-11-12 23:00:30 +00:00
on_toggle?(ev: ToggleEvent): unknown;
};
2025-10-25 05:32:22 +00:00
let {
alignment,
button_aria_label,
button_class = "button--secondary",
on_toggle,
}: Props = $props();
2025-11-12 23:00:30 +00:00
let popover_element: HTMLElement | undefined = $state();
2025-10-25 05:32:22 +00:00
// Hacky workaround because as of September 2025 implicit anchor association
// is still pretty broken, at least in Firefox.
let anchor_name = $state(`--anchor-${Math.floor(Math.random() * 1000000)}`);
let popover_left = $derived(
alignment?.toLocaleLowerCase("en-US") === "right"
? "unset"
: "anchor(left)",
);
let popover_right = $derived(
alignment?.toLocaleLowerCase("en-US") === "right"
? "anchor(right)"
: "unset",
);
$effect(() => {
if (on_toggle) {
popover_element?.addEventListener("toggle", on_toggle);
return () => {
popover_element?.removeEventListener("toggle", on_toggle);
};
}
});
</script>
<button
aria-label={button_aria_label}
class={["basic-dropdown__button", button_class]}
id="dropdown-button"
onclick={() => {
2025-10-25 05:32:22 +00:00
popover_element?.showPopover();
}}
2025-10-25 05:32:22 +00:00
style:anchor-name={anchor_name}
type="button"
>
<slot name="button-contents"></slot>
</button>
<div
aria-labelledby="dropdown-button"
bind:this={popover_element}
class="basic-dropdown__popover"
2025-10-25 05:32:22 +00:00
style:left={popover_left}
style:position-anchor={anchor_name}
style:right={popover_right}
popover="auto"
>
<slot name="popover"></slot>
</div>
<style lang="scss">
@use "../../sass/main";
</style>