namespace Htmx.ApiDemo.Templates.Components; /// /// CSS-native DropdownMenu using <details>/<summary>. /// Position: "left-0 top-full mt-1" (default) | "right-0 top-full mt-1" | etc. /// Items: pre-built HTML — use BuildItem() helper for consistent styling. /// public sealed class DropdownMenu : DropdownMenuBase { private const string ItemClasses = "relative flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm " + "outline-none transition-colors hover:bg-accent hover:text-accent-foreground " + "focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50"; private readonly byte[] _triggerClassesData; private readonly byte[] _triggerData; private readonly byte[] _positionData; private readonly byte[] _itemsData; public DropdownMenu( IHtmxComponent trigger, IEnumerable<(string Label, string Href, bool IsSeparator)> items, string position = "left-0 top-full mt-1") { // Render trigger to bytes var writer = new System.Buffers.ArrayBufferWriter(); trigger.Render(new HtmxRenderContext(writer)); _triggerData = writer.WrittenSpan.ToArray(); _triggerClassesData = []; // trigger already supplies its own classes _positionData = position.ToUtf8Bytes(); var sb = new System.Text.StringBuilder(); foreach (var (label, href, isSeparator) in items) { if (isSeparator) { sb.Append("""
"""); } else if (string.IsNullOrEmpty(href)) { sb.Append($""""""); } else { sb.Append($"""{label}"""); } } _itemsData = sb.ToString().ToUtf8Bytes(); } protected override void RenderTriggerClasses(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_triggerClassesData); protected override void RenderTrigger(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_triggerData); protected override void RenderPosition(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_positionData); protected override void RenderItems(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_itemsData); }