@mendylanda/ui 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,3 +47,13 @@ Customize with named `classNames`, typed option/editor/summary renderers, `Mendy
47
47
  - [Source and releases](https://github.com/MendyLanda/mendy-ui)
48
48
 
49
49
  Update the dependency and redeploy a project to receive shared fixes. MIT licensed. Shadcn attribution is included in the package.
50
+
51
+ ### Chip and menu presentation
52
+
53
+ Use `chipLabel: false` when the selected values explain themselves. A string such as `chipLabel: "SIM"` shortens the visible label while retaining the field's full accessible name. `renderSummary` supplies custom chip content independently of `renderOption`.
54
+
55
+ `FilterMenuGroup.separatorBefore` separates related categories. Within a group, `menuLayout: "inline"` puts a field's editor beside its label. Custom editors can set `editorPadding: "none"` when they own their spacing. Honor the `autoFocus` value supplied to `renderEditor`: it is false during menu previews, and the menu moves focus on click or keyboard activation.
56
+
57
+ `<MendyUIProvider menuLayout="anchored">` positions desktop editors beside the selected row at their own height. The default remains `connected`. Both use the single-panel mobile layout. A standalone `<FilterMenu anchor={searchRef}>` can align to a custom search field. The menu grows to the available viewport height, capped by `--mendy-filter-menu-max-height`, which defaults to `44rem`; large lists remain virtualized.
58
+
59
+ Chips enter with a short, staggered Motion spring. Reduced-motion preferences disable their movement. Theme radius still controls their corners.
@@ -1 +1 @@
1
- export const originalClassNames = {"mui-33228ff7d583":"pointer-events-none","mui-b75af7ef6c4d":"invisible","mui-d42ef1497900":"visible","mui-32fb090591d9":"sr-only","mui-747355bdc2a2":"absolute","mui-d2d9e1f13413":"relative","mui-ce13093ad8c9":"inset-0","mui-3ba24c062d55":"inset-x-0","mui-cced28c6dc3f":"start","mui-207bd0b3f259":"start-2","mui-cea70f23972e":"start-3","mui-96a350bd3168":"start-4","mui-361e48d0308f":"end","mui-13422ddc8d83":"end-1","mui-585f4729f611":"end-9","mui-98599e4ee250":"top-0","mui-4e45f4841abb":"top-1/2","mui-866c339ea145":"left-4","mui-3eef1ce97dba":"z-50","mui-a42d519714d6":"container","mui-a2526bc2efa3":"m-0","mui-abba77319850":"-mx-1","mui-1752f11474a6":"mx-auto","mui-d8747231f142":"my-1","mui-958e2cfcf5b6":"-ms-2","mui-8cc46fa87f41":"-me-1.5","mui-f28fea81a5cd":"mt-2","mui-cc51bda60e7e":"ml-auto","mui-496aca80e4d8":"block","mui-222f930b8752":"flex","mui-0f2a693e93e2":"grid","mui-e564b4081d7a":"hidden","mui-995cf20a9c45":"inline","mui-123306850fe1":"inline-flex","mui-3da89b3ed14b":"field-sizing-content","mui-129d6f9b1e07":"aspect-square","mui-f7d12b41bceb":"size-(--cell-size)","mui-dc797fcc7165":"size-1.5","mui-9e7600197f38":"size-2","mui-61c000c8a98a":"size-3.5","mui-100c22d5776b":"size-4","mui-0712570dcc9f":"size-6","mui-79ede756e761":"size-7","mui-ebd37e04d422":"size-8","mui-3ee10ef4c057":"size-9","mui-d0a383a9c24f":"size-10","mui-4dad94bd15e3":"size-auto","mui-a5960ac1d283":"h-(--cell-size)","mui-ce1945804664":"h-6","mui-1fb07b38c63b":"h-8","mui-539ae144f076":"h-9","mui-fdd2f85cad6b":"h-10","mui-a66e9985b093":"h-auto","mui-57c930dc1c31":"h-full","mui-ae2c4b060bb9":"h-px","mui-571ea69568d3":"max-h-(--filter-menu-height)","mui-1f061e70178e":"max-h-(--radix-dropdown-menu-content-available-height)","mui-80baa5d03af7":"max-h-[calc(var(--filter-menu-height)-2px)]","mui-1326520533f0":"max-h-[min(6rem,calc(var(--filter-menu-height)*0.25))]","mui-fb4a3e97b5bd":"max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height,20rem))]","mui-dcd942c18ddc":"max-h-[var(--radix-dropdown-menu-content-available-height)]","mui-410da8dfa8ac":"min-h-0","mui-9974acedf3f7":"min-h-9","mui-8423dc94ee06":"min-h-10","mui-50312c3ce4a3":"min-h-[60px]","mui-d48fe95d4da8":"w-(--cell-size)","mui-8867d85ed290":"w-8","mui-72b37c1a040d":"w-64","mui-37535002f0f7":"w-72","mui-919182384559":"w-[18.75rem]","mui-8dd3eece40db":"w-[var(--mendy-filter-menu-width,32rem)]","mui-8cfb5a42ef9f":"w-fit","mui-58d0413dd93c":"w-full","mui-91f82aa1df0c":"max-w-[60%]","mui-34055e6f8c1b":"max-w-[calc(100vw-2rem)]","mui-81b198a2db84":"max-w-full","mui-1458e495e3ae":"min-w-(--cell-size)","mui-184ddc11e5f9":"min-w-0","mui-e9ab1547c94d":"min-w-[8rem]","mui-7bd5bab6d7f4":"flex-1","mui-66b490dd6005":"shrink","mui-27ead27a81df":"shrink-0","mui-215061302c50":"border-collapse","mui-3a3774477287":"origin-(--radix-dropdown-menu-content-transform-origin)","mui-5dde0fd996f0":"-translate-y-1/2","mui-aa214ea38326":"transform","mui-393df0d154e0":"animate-none!","mui-7b3cd77eb999":"animate-pulse","mui-d343f41ba73e":"cursor-default","mui-acd532d8913e":"resize","mui-ae112c903ef8":"resize-y","mui-da607d0a2538":"grid-cols-[min(var(--mendy-filter-list-width,13rem),50%)_minmax(0,1fr)]","mui-b5985369ee35":"grid-rows-[minmax(0,1fr)]","mui-302c0d124a94":"flex-col","mui-faa8a23c68f6":"flex-wrap","mui-8fd2e2355cbc":"place-content-center","mui-71556df3b421":"items-center","mui-b9677bd1cb66":"items-start","mui-32c92bb1fde4":"justify-between","mui-a503dd374cca":"justify-center","mui-c62ec162662e":"justify-start","mui-19e97f3f40e6":"gap-1","mui-70007b876865":"gap-1.5","mui-074569488cca":"gap-2","mui-90fc3ec35a37":"gap-4","mui-27e6c432192f":"space-y-1","mui-267171770524":"space-y-2","mui-5a3a4ee420c2":"truncate","mui-d5111d0e9f48":"overflow-hidden","mui-3b82279f859f":"overflow-x-hidden","mui-2368e909f3a5":"overflow-y-auto","mui-998780cdef87":"overscroll-contain","mui-b005d61d0953":"rounded-full","mui-3fa8c572949b":"rounded-md","mui-52417259a4f5":"rounded-none","mui-02e603944040":"rounded-sm","mui-10c0bae8cba9":"rounded-l-md","mui-34bbd346e74c":"rounded-r-md","mui-4f1a55de40bc":"border","mui-dad556abfe15":"border-0","mui-55860251be20":"border-s","mui-3b6d5fc7b061":"border-e","mui-894c3815a57f":"border-t","mui-bbe39cfb5cc6":"border-b","mui-8450eb0f1857":"border-dashed","mui-163a623b3716":"border-border/50","mui-1267095cdeb7":"border-input","mui-9b76f456b8c9":"border-muted-foreground/50","mui-292affc1f780":"bg-accent","mui-5b272f3c5076":"bg-background","mui-a943bea85d66":"bg-border","mui-0cf6c2e8fe7e":"bg-current","mui-965cf6587342":"bg-destructive","mui-e593c38256e0":"bg-popover","mui-0c2864fba576":"bg-primary","mui-d191cb569aac":"bg-secondary","mui-8fca9236d191":"bg-transparent","mui-717b342c2d01":"fill-current","mui-04760bcd507f":"p-0","mui-1dee6e3ec67d":"p-1","mui-b97db4a9f432":"p-2","mui-094f5333853b":"p-3","mui-56b8f5e0fb12":"px-(--cell-size)","mui-1da2e8f173c5":"px-1.5","mui-e7e01cc7f4df":"px-2","mui-0f9e6672913a":"px-3","mui-9513b3968002":"px-4","mui-0194c6e46323":"px-8","mui-1fcf28756bb1":"py-1","mui-a8c1dfb1d4ef":"py-1.5","mui-b5edc3ea7c91":"py-2","mui-078d945937da":"ps-8","mui-b2100041c585":"ps-9","mui-05d4103bd67f":"pe-2","mui-0e14c365f9e8":"pe-9","mui-559069d2c2e5":"pe-16","mui-0a702d97ed7d":"pt-3","mui-1a52da5bc88c":"pr-1","mui-f5d8e661d857":"pl-2","mui-75b27e813dcc":"pl-8","mui-05ab12448317":"text-center","mui-e9347b78185f":"text-start","mui-757071a1d1c4":"text-base","mui-c74ab393b96d":"text-sm","mui-3992de70b033":"text-xs","mui-5f120ad4b50b":"text-[0.8rem]","mui-761cdb81bddb":"leading-none","mui-daaac3fbf55e":"font-medium","mui-52101fc7d8bb":"font-normal","mui-e1683b3c030a":"tracking-widest","mui-393883c1db68":"[overflow-wrap:anywhere]","mui-37cf9655f30a":"break-words","mui-726fca123972":"whitespace-normal","mui-963f6168b7d6":"whitespace-nowrap","mui-cc209238d847":"text-accent-foreground","mui-5eafab9bbefc":"text-current","mui-887b9502d5c7":"text-destructive","mui-8d4bf2704c8d":"text-destructive-foreground","mui-957a109f887f":"text-foreground","mui-35f35c41d134":"text-muted-foreground","mui-5bd2afe89f0b":"text-popover-foreground","mui-249f14ee5b6b":"text-primary","mui-abd1aaa1f5b2":"text-primary-foreground","mui-80df79ec9bb5":"text-secondary-foreground","mui-944969efb294":"underline","mui-d52f902a57e8":"underline-offset-4","mui-94f91922a10e":"opacity-0","mui-0b5a4d1257ab":"opacity-50","mui-65694092f002":"opacity-100","mui-0d4ca75de41c":"shadow-lg","mui-94ea94fde25f":"shadow-md","mui-7de07d413c13":"shadow-xs","mui-59edd3d33673":"outline-hidden","mui-318678825324":"outline","mui-dfc3376b8266":"filter","mui-eae178841c68":"transition-colors","mui-2cdd01663c76":"transition-opacity","mui-2b48746f4314":"transition-shadow","mui-b4b2fd95f324":"transition-none","mui-b30fc56058b6":"outline-none","mui-78905c9e865c":"select-none","mui-d80b6593dae5":"[--cell-size:--spacing(8)]","mui-82c2977beb8e":"[--cell-size:min(2rem,calc((100vw-3.5rem)/7))]","mui-5662a94d9dba":"[--filter-menu-height:min(30rem,var(--radix-dropdown-menu-content-available-height,30rem))]","mui-cacb19aa9640":"[scrollbar-width:thin]","mui-14bd99410890":"[unicode-bidi:isolate]","mui-9af9a4182597":"group-data-[disabled=true]:pointer-events-none","mui-ad936fcbed63":"group","mui-272ef88d6a0c":"group-data-[disabled=true]:opacity-50","mui-60052b901494":"group-data-[focused=true]/day:relative","mui-d9c5891363f9":"group/day","mui-3f96bf138f7b":"group-data-[focused=true]/day:z-10","mui-d39eb62b8363":"group-data-[focused=true]/day:border-ring","mui-5892a880410a":"group-data-[focused=true]/day:ring-1","mui-31b75c623c06":"group-data-[focused=true]/day:ring-ring","mui-f0ee83167878":"peer-disabled:cursor-not-allowed","mui-2ffc1d06387e":"peer","mui-25d26df52a1d":"peer-disabled:opacity-50","mui-32713fefe32f":"file:border-0","mui-25a74d576c42":"file:bg-transparent","mui-844abccf1fd0":"file:text-sm","mui-dc43de54f42a":"file:font-medium","mui-20315da78444":"placeholder:text-muted-foreground","mui-4fa4c190281c":"first:border-t-0","mui-37ad15a95595":"hover:bg-accent","mui-bb6a4b3e902d":"hover:bg-destructive/90","mui-af9ea085901b":"hover:bg-primary/90","mui-099700a5a311":"hover:bg-secondary/80","mui-71863717995b":"hover:bg-transparent","mui-9f1f77b92dcb":"hover:text-accent-foreground","mui-4d6720d47806":"hover:text-foreground","mui-b56720602554":"hover:underline","mui-8da02faac748":"hover:opacity-100","mui-a8c1861c5d03":"focus:bg-accent","mui-a85c4c5d7465":"focus:text-accent-foreground","mui-b906a32b31ee":"focus-visible:border-ring","mui-39be5a15b766":"focus-visible:ring-1","mui-355efe22aedb":"focus-visible:ring-2","mui-365a4654f7b1":"focus-visible:ring-[3px]","mui-db549d38ff19":"focus-visible:ring-ring","mui-392a26f4c1b0":"focus-visible:ring-ring/50","mui-1df9125eebaf":"focus-visible:outline-1","mui-223daa83ed2d":"focus-visible:outline-ring","mui-2e357972fc10":"focus-visible:outline-none","mui-50aafa8694bb":"focus-visible:ring-inset","mui-8d3c1014b967":"disabled:pointer-events-none","mui-db567af109b7":"disabled:cursor-not-allowed","mui-4600b77303d6":"disabled:opacity-50","mui-7eb7501a2ef0":"has-focus:border-ring","mui-d0449753ba7b":"has-focus:ring-[3px]","mui-fc19c8471f34":"has-focus:ring-ring/50","mui-e288cffc2ef4":"has-[>svg]:px-1.5","mui-3b01ee05b16d":"has-[>svg]:px-2.5","mui-9e08353b774f":"has-[>svg]:px-3","mui-2c0115d1c32a":"aria-disabled:opacity-50","mui-1fac1d7d1df3":"aria-invalid:border-destructive","mui-2d91b4cbc3cb":"aria-invalid:ring-destructive","mui-5491fd0b4dc5":"aria-invalid:ring-destructive/20","mui-7d900a71e5ba":"aria-selected:text-muted-foreground","mui-397f5ba426a0":"data-[disabled]:pointer-events-none","mui-9beb654fcb0d":"data-[disabled]:opacity-50","mui-6fa6a8ade7c4":"data-[inset]:pl-8","mui-bbaaa5969856":"data-[more-below=true]:[mask-image:linear-gradient(#000_calc(100%_-_1rem),transparent)]","mui-dfc89afd7885":"data-[navigation=pointer]:focus-visible:ring-0","mui-96308b55e0d6":"data-[range-end=true]:rounded-md","mui-a55dbe6f3eec":"data-[range-end=true]:rounded-r-md","mui-714051e55e8e":"data-[range-end=true]:bg-primary","mui-c764cebcd540":"data-[range-end=true]:text-primary-foreground","mui-05ec0700cd48":"data-[range-middle=true]:rounded-none","mui-5fb598fb06f4":"data-[range-middle=true]:bg-accent","mui-0813905aa141":"data-[range-middle=true]:text-accent-foreground","mui-8020e31bff67":"data-[range-start=true]:rounded-md","mui-ab65911baf1d":"data-[range-start=true]:rounded-l-md","mui-f5460d79341c":"data-[range-start=true]:bg-primary","mui-421a7d3e6631":"data-[range-start=true]:text-primary-foreground","mui-a39af81d518c":"data-[selected-single=true]:bg-primary","mui-b6991ab170de":"data-[selected-single=true]:text-primary-foreground","mui-3cc0d07ef843":"data-[selected=true]:rounded-none","mui-89f6aab7a3f8":"data-[side=bottom]:slide-in-from-top-2","mui-3550f48d66b9":"data-[side=left]:slide-in-from-right-2","mui-7720fe004f5c":"data-[side=right]:slide-in-from-left-2","mui-66b560f839de":"data-[side=top]:slide-in-from-bottom-2","mui-20553d55d224":"data-[state=checked]:border-primary","mui-57d6d6401a2f":"data-[state=checked]:bg-primary","mui-7ca12016214f":"data-[state=checked]:text-primary-foreground","mui-37d8a3f448ff":"data-[state=closed]:animate-none!","mui-0cb4489c3645":"data-[state=closed]:animate-out","mui-bb5869ef903b":"data-[state=closed]:fade-out-0","mui-f4cf4be63707":"data-[state=closed]:zoom-out-95","mui-bae1e782f277":"data-[state=open]:animate-in","mui-4027d9c589b6":"data-[state=open]:bg-accent","mui-1952cd5b00aa":"data-[state=open]:text-accent-foreground","mui-6f73a280432e":"data-[state=open]:text-foreground","mui-790ed041a953":"data-[state=open]:opacity-100","mui-45464a92c1f6":"data-[state=open]:fade-in-0","mui-9ca352480f25":"data-[state=open]:zoom-in-95","mui-f3f7a9c5e1b3":"data-[variant=destructive]:text-destructive","mui-e2bb4ac97190":"data-[variant=destructive]:focus:bg-destructive/10","mui-353a727facfd":"data-[variant=destructive]:focus:text-destructive","mui-5e49f8678d00":"motion-safe:animate-in","mui-b225f0361d28":"motion-safe:duration-200","mui-3bc474178684":"motion-safe:fade-in-0","mui-bc94a66158a3":"motion-safe:fill-mode-backwards","mui-237747164b60":"motion-reduce:animate-none","mui-0cc20f4e20c8":"sm:w-[21.875rem]","mui-56760b222da0":"md:flex-row","mui-c05c9b99939d":"md:text-sm","mui-36b12e8339c7":"rtl:rotate-180","mui-9daa2369a41b":"dark:bg-input/30","mui-b2786f463a62":"dark:hover:text-accent-foreground","mui-c88f65aa011e":"dark:aria-invalid:ring-destructive/40","mui-bd73d819941e":"dark:data-[state=checked]:bg-primary","mui-98a28c2293f7":"dark:data-[variant=destructive]:focus:bg-destructive/20","mui-d78a8526410d":"pointer-coarse:[--cell-size:min(2.5rem,calc((100vw-3.5rem)/7))]","mui-b51243872d85":"sm:pointer-fine:h-8","mui-aa0e3c036805":"sm:pointer-fine:min-h-8","mui-0288c4ea71ea":"sm:pointer-fine:py-0.5","mui-41d1475cf71a":"sm:pointer-fine:py-1.5","mui-548a450e8e53":"[&_*]:transition-none!","mui-a1c8c43d9be2":"[&_[role^=menuitem]]:min-h-9","mui-b43343fe71c0":"sm:pointer-fine:[&_[role^=menuitem]]:min-h-8","mui-cc310a330231":"[&_button]:font-semibold","mui-b7ff49878425":"[&_button]:underline","mui-d9bdd2c94504":"[&_button]:underline-offset-4","mui-bcae9d37090c":"[&_svg]:pointer-events-none","mui-c77732bd3ab4":"[&_svg]:size-3.5","mui-040e748a3fa0":"[&_svg]:shrink-0","mui-be87a4a64d81":"[&_svg:not([class*='size-'])]:size-3","mui-eb5ef2dbc2d4":"[&_svg:not([class*='size-'])]:size-4","mui-8637dfd54fbb":"[&_svg:not([class*='text-'])]:text-muted-foreground","mui-4f05a238500e":"[&::-webkit-search-cancel-button]:appearance-none","mui-74ab31abd804":"[&:first-child[data-selected=true]_button]:rounded-l-md","mui-13858191ca50":"rtl:**:[.rdp-button\\_next>svg]:rotate-180","mui-a324a7f99932":"rtl:**:[.rdp-button\\_previous>svg]:rotate-180","mui-82b114bbff3d":"data-[variant=destructive]:*:[svg]:text-destructive!","mui-6ee0c74de40c":"[&:last-child[data-selected=true]_button]:rounded-r-md","mui-6162ab70efb5":"[&:nth-child(2)[data-selected=true]_button]:rounded-l-md","mui-f157ee6bae99":"[&>div]:w-full","mui-ba417cf466b0":"[&>span]:text-xs","mui-829c8ef4f9c5":"[&>span]:opacity-70","mui-bf95f682e553":"[&>span:first-child]:mt-[calc(0.5lh-0.4375rem)]","mui-eb740dd32318":"[&>svg]:size-3.5","mui-019fd73c95db":"[&>svg]:text-muted-foreground","mui-b8293c91796c":"[[data-slot=card-content]_&]:bg-transparent","mui-8dd7e6b42262":"[[data-slot=popover-content]_&]:bg-transparent"};
1
+ export const originalClassNames = {"mui-33228ff7d583":"pointer-events-none","mui-b75af7ef6c4d":"invisible","mui-d42ef1497900":"visible","mui-32fb090591d9":"sr-only","mui-747355bdc2a2":"absolute","mui-d2d9e1f13413":"relative","mui-ce13093ad8c9":"inset-0","mui-3ba24c062d55":"inset-x-0","mui-cced28c6dc3f":"start","mui-207bd0b3f259":"start-2","mui-cea70f23972e":"start-3","mui-96a350bd3168":"start-4","mui-361e48d0308f":"end","mui-13422ddc8d83":"end-1","mui-585f4729f611":"end-9","mui-98599e4ee250":"top-0","mui-4e45f4841abb":"top-1/2","mui-866c339ea145":"left-4","mui-3eef1ce97dba":"z-50","mui-a42d519714d6":"container","mui-a2526bc2efa3":"m-0","mui-abba77319850":"-mx-1","mui-1752f11474a6":"mx-auto","mui-d8747231f142":"my-1","mui-958e2cfcf5b6":"-ms-2","mui-8cc46fa87f41":"-me-1.5","mui-f28fea81a5cd":"mt-2","mui-cc51bda60e7e":"ml-auto","mui-496aca80e4d8":"block","mui-222f930b8752":"flex","mui-0f2a693e93e2":"grid","mui-e564b4081d7a":"hidden","mui-995cf20a9c45":"inline","mui-123306850fe1":"inline-flex","mui-3da89b3ed14b":"field-sizing-content","mui-129d6f9b1e07":"aspect-square","mui-f7d12b41bceb":"size-(--cell-size)","mui-dc797fcc7165":"size-1.5","mui-9e7600197f38":"size-2","mui-61c000c8a98a":"size-3.5","mui-100c22d5776b":"size-4","mui-0712570dcc9f":"size-6","mui-79ede756e761":"size-7","mui-ebd37e04d422":"size-8","mui-3ee10ef4c057":"size-9","mui-d0a383a9c24f":"size-10","mui-4dad94bd15e3":"size-auto","mui-a5960ac1d283":"h-(--cell-size)","mui-ce1945804664":"h-6","mui-1fb07b38c63b":"h-8","mui-539ae144f076":"h-9","mui-fdd2f85cad6b":"h-10","mui-a66e9985b093":"h-auto","mui-57c930dc1c31":"h-full","mui-ae2c4b060bb9":"h-px","mui-571ea69568d3":"max-h-(--filter-menu-height)","mui-1f061e70178e":"max-h-(--radix-dropdown-menu-content-available-height)","mui-80baa5d03af7":"max-h-[calc(var(--filter-menu-height)-2px)]","mui-1326520533f0":"max-h-[min(6rem,calc(var(--filter-menu-height)*0.25))]","mui-fb4a3e97b5bd":"max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height,20rem))]","mui-dcd942c18ddc":"max-h-[var(--radix-dropdown-menu-content-available-height)]","mui-410da8dfa8ac":"min-h-0","mui-9974acedf3f7":"min-h-9","mui-8423dc94ee06":"min-h-10","mui-50312c3ce4a3":"min-h-[60px]","mui-d48fe95d4da8":"w-(--cell-size)","mui-8867d85ed290":"w-8","mui-72b37c1a040d":"w-64","mui-37535002f0f7":"w-72","mui-919182384559":"w-[18.75rem]","mui-8dd3eece40db":"w-[var(--mendy-filter-menu-width,32rem)]","mui-8cfb5a42ef9f":"w-fit","mui-58d0413dd93c":"w-full","mui-91f82aa1df0c":"max-w-[60%]","mui-34055e6f8c1b":"max-w-[calc(100vw-2rem)]","mui-81b198a2db84":"max-w-full","mui-1458e495e3ae":"min-w-(--cell-size)","mui-184ddc11e5f9":"min-w-0","mui-e9ab1547c94d":"min-w-[8rem]","mui-7bd5bab6d7f4":"flex-1","mui-66b490dd6005":"shrink","mui-27ead27a81df":"shrink-0","mui-215061302c50":"border-collapse","mui-3a3774477287":"origin-(--radix-dropdown-menu-content-transform-origin)","mui-5dde0fd996f0":"-translate-y-1/2","mui-aa214ea38326":"transform","mui-393df0d154e0":"animate-none!","mui-7b3cd77eb999":"animate-pulse","mui-d343f41ba73e":"cursor-default","mui-acd532d8913e":"resize","mui-ae112c903ef8":"resize-y","mui-da607d0a2538":"grid-cols-[min(var(--mendy-filter-list-width,13rem),50%)_minmax(0,1fr)]","mui-b5985369ee35":"grid-rows-[minmax(0,1fr)]","mui-302c0d124a94":"flex-col","mui-faa8a23c68f6":"flex-wrap","mui-8fd2e2355cbc":"place-content-center","mui-71556df3b421":"items-center","mui-b9677bd1cb66":"items-start","mui-32c92bb1fde4":"justify-between","mui-a503dd374cca":"justify-center","mui-c62ec162662e":"justify-start","mui-19e97f3f40e6":"gap-1","mui-70007b876865":"gap-1.5","mui-074569488cca":"gap-2","mui-d7db3451d154":"gap-3","mui-90fc3ec35a37":"gap-4","mui-27e6c432192f":"space-y-1","mui-267171770524":"space-y-2","mui-5a3a4ee420c2":"truncate","mui-d5111d0e9f48":"overflow-hidden","mui-0fc84a692dad":"overflow-visible","mui-3b82279f859f":"overflow-x-hidden","mui-2368e909f3a5":"overflow-y-auto","mui-998780cdef87":"overscroll-contain","mui-b005d61d0953":"rounded-full","mui-3fa8c572949b":"rounded-md","mui-52417259a4f5":"rounded-none","mui-02e603944040":"rounded-sm","mui-a22ea0ba562a":"rounded-t-none","mui-10c0bae8cba9":"rounded-l-md","mui-34bbd346e74c":"rounded-r-md","mui-4f1a55de40bc":"border","mui-dad556abfe15":"border-0","mui-55860251be20":"border-s","mui-3b6d5fc7b061":"border-e","mui-894c3815a57f":"border-t","mui-bbe39cfb5cc6":"border-b","mui-8450eb0f1857":"border-dashed","mui-163a623b3716":"border-border/50","mui-1267095cdeb7":"border-input","mui-9b76f456b8c9":"border-muted-foreground/50","mui-292affc1f780":"bg-accent","mui-5b272f3c5076":"bg-background","mui-a943bea85d66":"bg-border","mui-0cf6c2e8fe7e":"bg-current","mui-965cf6587342":"bg-destructive","mui-e593c38256e0":"bg-popover","mui-0c2864fba576":"bg-primary","mui-d191cb569aac":"bg-secondary","mui-8fca9236d191":"bg-transparent","mui-717b342c2d01":"fill-current","mui-04760bcd507f":"p-0","mui-1dee6e3ec67d":"p-1","mui-b97db4a9f432":"p-2","mui-094f5333853b":"p-3","mui-56b8f5e0fb12":"px-(--cell-size)","mui-1da2e8f173c5":"px-1.5","mui-e7e01cc7f4df":"px-2","mui-0f9e6672913a":"px-3","mui-9513b3968002":"px-4","mui-0194c6e46323":"px-8","mui-1fcf28756bb1":"py-1","mui-a8c1dfb1d4ef":"py-1.5","mui-b5edc3ea7c91":"py-2","mui-078d945937da":"ps-8","mui-b2100041c585":"ps-9","mui-05d4103bd67f":"pe-2","mui-0e14c365f9e8":"pe-9","mui-559069d2c2e5":"pe-16","mui-0a702d97ed7d":"pt-3","mui-be02e79e6a27":"pt-3!","mui-1a52da5bc88c":"pr-1","mui-f5d8e661d857":"pl-2","mui-75b27e813dcc":"pl-8","mui-05ab12448317":"text-center","mui-e9347b78185f":"text-start","mui-757071a1d1c4":"text-base","mui-c74ab393b96d":"text-sm","mui-3992de70b033":"text-xs","mui-5f120ad4b50b":"text-[0.8rem]","mui-761cdb81bddb":"leading-none","mui-daaac3fbf55e":"font-medium","mui-52101fc7d8bb":"font-normal","mui-e1683b3c030a":"tracking-widest","mui-393883c1db68":"[overflow-wrap:anywhere]","mui-37cf9655f30a":"break-words","mui-726fca123972":"whitespace-normal","mui-963f6168b7d6":"whitespace-nowrap","mui-cc209238d847":"text-accent-foreground","mui-5eafab9bbefc":"text-current","mui-887b9502d5c7":"text-destructive","mui-8d4bf2704c8d":"text-destructive-foreground","mui-957a109f887f":"text-foreground","mui-35f35c41d134":"text-muted-foreground","mui-5bd2afe89f0b":"text-popover-foreground","mui-249f14ee5b6b":"text-primary","mui-abd1aaa1f5b2":"text-primary-foreground","mui-80df79ec9bb5":"text-secondary-foreground","mui-944969efb294":"underline","mui-d52f902a57e8":"underline-offset-4","mui-94f91922a10e":"opacity-0","mui-0b5a4d1257ab":"opacity-50","mui-65694092f002":"opacity-100","mui-0d4ca75de41c":"shadow-lg","mui-94ea94fde25f":"shadow-md","mui-89c3006b2722":"shadow-none","mui-7de07d413c13":"shadow-xs","mui-59edd3d33673":"outline-hidden","mui-318678825324":"outline","mui-dfc3376b8266":"filter","mui-eae178841c68":"transition-colors","mui-2cdd01663c76":"transition-opacity","mui-2b48746f4314":"transition-shadow","mui-b4b2fd95f324":"transition-none","mui-b30fc56058b6":"outline-none","mui-78905c9e865c":"select-none","mui-d80b6593dae5":"[--cell-size:--spacing(8)]","mui-82c2977beb8e":"[--cell-size:min(2rem,calc((100vw-3.5rem)/7))]","mui-971625533e56":"[--filter-menu-height:min(var(--mendy-filter-menu-max-height,44rem),var(--radix-dropdown-menu-content-available-height,44rem))]","mui-cacb19aa9640":"[scrollbar-width:thin]","mui-14bd99410890":"[unicode-bidi:isolate]","mui-9af9a4182597":"group-data-[disabled=true]:pointer-events-none","mui-ad936fcbed63":"group","mui-272ef88d6a0c":"group-data-[disabled=true]:opacity-50","mui-60052b901494":"group-data-[focused=true]/day:relative","mui-d9c5891363f9":"group/day","mui-3f96bf138f7b":"group-data-[focused=true]/day:z-10","mui-d39eb62b8363":"group-data-[focused=true]/day:border-ring","mui-5892a880410a":"group-data-[focused=true]/day:ring-1","mui-31b75c623c06":"group-data-[focused=true]/day:ring-ring","mui-f0ee83167878":"peer-disabled:cursor-not-allowed","mui-2ffc1d06387e":"peer","mui-25d26df52a1d":"peer-disabled:opacity-50","mui-32713fefe32f":"file:border-0","mui-25a74d576c42":"file:bg-transparent","mui-844abccf1fd0":"file:text-sm","mui-dc43de54f42a":"file:font-medium","mui-20315da78444":"placeholder:text-muted-foreground","mui-a267680f9f49":"before:absolute","mui-e22234c7b474":"before:inset-x-0","mui-4d24300d447b":"before:top-0","mui-37a30b916086":"before:border-t","mui-37ad15a95595":"hover:bg-accent","mui-bb6a4b3e902d":"hover:bg-destructive/90","mui-af9ea085901b":"hover:bg-primary/90","mui-099700a5a311":"hover:bg-secondary/80","mui-71863717995b":"hover:bg-transparent","mui-9f1f77b92dcb":"hover:text-accent-foreground","mui-4d6720d47806":"hover:text-foreground","mui-b56720602554":"hover:underline","mui-8da02faac748":"hover:opacity-100","mui-a8c1861c5d03":"focus:bg-accent","mui-a85c4c5d7465":"focus:text-accent-foreground","mui-b906a32b31ee":"focus-visible:border-ring","mui-39be5a15b766":"focus-visible:ring-1","mui-355efe22aedb":"focus-visible:ring-2","mui-365a4654f7b1":"focus-visible:ring-[3px]","mui-db549d38ff19":"focus-visible:ring-ring","mui-392a26f4c1b0":"focus-visible:ring-ring/50","mui-1df9125eebaf":"focus-visible:outline-1","mui-223daa83ed2d":"focus-visible:outline-ring","mui-2e357972fc10":"focus-visible:outline-none","mui-50aafa8694bb":"focus-visible:ring-inset","mui-8d3c1014b967":"disabled:pointer-events-none","mui-db567af109b7":"disabled:cursor-not-allowed","mui-4600b77303d6":"disabled:opacity-50","mui-7eb7501a2ef0":"has-focus:border-ring","mui-d0449753ba7b":"has-focus:ring-[3px]","mui-fc19c8471f34":"has-focus:ring-ring/50","mui-e288cffc2ef4":"has-[>svg]:px-1.5","mui-3b01ee05b16d":"has-[>svg]:px-2.5","mui-9e08353b774f":"has-[>svg]:px-3","mui-2c0115d1c32a":"aria-disabled:opacity-50","mui-1fac1d7d1df3":"aria-invalid:border-destructive","mui-2d91b4cbc3cb":"aria-invalid:ring-destructive","mui-5491fd0b4dc5":"aria-invalid:ring-destructive/20","mui-7d900a71e5ba":"aria-selected:text-muted-foreground","mui-397f5ba426a0":"data-[disabled]:pointer-events-none","mui-9beb654fcb0d":"data-[disabled]:opacity-50","mui-6fa6a8ade7c4":"data-[inset]:pl-8","mui-bbaaa5969856":"data-[more-below=true]:[mask-image:linear-gradient(#000_calc(100%_-_1rem),transparent)]","mui-dfc89afd7885":"data-[navigation=pointer]:focus-visible:ring-0","mui-96308b55e0d6":"data-[range-end=true]:rounded-md","mui-a55dbe6f3eec":"data-[range-end=true]:rounded-r-md","mui-714051e55e8e":"data-[range-end=true]:bg-primary","mui-c764cebcd540":"data-[range-end=true]:text-primary-foreground","mui-05ec0700cd48":"data-[range-middle=true]:rounded-none","mui-5fb598fb06f4":"data-[range-middle=true]:bg-accent","mui-0813905aa141":"data-[range-middle=true]:text-accent-foreground","mui-8020e31bff67":"data-[range-start=true]:rounded-md","mui-ab65911baf1d":"data-[range-start=true]:rounded-l-md","mui-f5460d79341c":"data-[range-start=true]:bg-primary","mui-421a7d3e6631":"data-[range-start=true]:text-primary-foreground","mui-a39af81d518c":"data-[selected-single=true]:bg-primary","mui-b6991ab170de":"data-[selected-single=true]:text-primary-foreground","mui-3cc0d07ef843":"data-[selected=true]:rounded-none","mui-89f6aab7a3f8":"data-[side=bottom]:slide-in-from-top-2","mui-3550f48d66b9":"data-[side=left]:slide-in-from-right-2","mui-7720fe004f5c":"data-[side=right]:slide-in-from-left-2","mui-66b560f839de":"data-[side=top]:slide-in-from-bottom-2","mui-20553d55d224":"data-[state=checked]:border-primary","mui-57d6d6401a2f":"data-[state=checked]:bg-primary","mui-7ca12016214f":"data-[state=checked]:text-primary-foreground","mui-37d8a3f448ff":"data-[state=closed]:animate-none!","mui-0cb4489c3645":"data-[state=closed]:animate-out","mui-bb5869ef903b":"data-[state=closed]:fade-out-0","mui-f4cf4be63707":"data-[state=closed]:zoom-out-95","mui-bae1e782f277":"data-[state=open]:animate-in","mui-4027d9c589b6":"data-[state=open]:bg-accent","mui-1952cd5b00aa":"data-[state=open]:text-accent-foreground","mui-6f73a280432e":"data-[state=open]:text-foreground","mui-790ed041a953":"data-[state=open]:opacity-100","mui-45464a92c1f6":"data-[state=open]:fade-in-0","mui-9ca352480f25":"data-[state=open]:zoom-in-95","mui-f3f7a9c5e1b3":"data-[variant=destructive]:text-destructive","mui-e2bb4ac97190":"data-[variant=destructive]:focus:bg-destructive/10","mui-353a727facfd":"data-[variant=destructive]:focus:text-destructive","mui-237747164b60":"motion-reduce:animate-none","mui-0cc20f4e20c8":"sm:w-[21.875rem]","mui-56760b222da0":"md:flex-row","mui-c05c9b99939d":"md:text-sm","mui-36b12e8339c7":"rtl:rotate-180","mui-9daa2369a41b":"dark:bg-input/30","mui-b2786f463a62":"dark:hover:text-accent-foreground","mui-c88f65aa011e":"dark:aria-invalid:ring-destructive/40","mui-bd73d819941e":"dark:data-[state=checked]:bg-primary","mui-98a28c2293f7":"dark:data-[variant=destructive]:focus:bg-destructive/20","mui-d78a8526410d":"pointer-coarse:[--cell-size:min(2.5rem,calc((100vw-3.5rem)/7))]","mui-b51243872d85":"sm:pointer-fine:h-8","mui-aa0e3c036805":"sm:pointer-fine:min-h-8","mui-0288c4ea71ea":"sm:pointer-fine:py-0.5","mui-41d1475cf71a":"sm:pointer-fine:py-1.5","mui-548a450e8e53":"[&_*]:transition-none!","mui-a1c8c43d9be2":"[&_[role^=menuitem]]:min-h-9","mui-b43343fe71c0":"sm:pointer-fine:[&_[role^=menuitem]]:min-h-8","mui-cc310a330231":"[&_button]:font-semibold","mui-b7ff49878425":"[&_button]:underline","mui-d9bdd2c94504":"[&_button]:underline-offset-4","mui-bcae9d37090c":"[&_svg]:pointer-events-none","mui-c77732bd3ab4":"[&_svg]:size-3.5","mui-040e748a3fa0":"[&_svg]:shrink-0","mui-be87a4a64d81":"[&_svg:not([class*='size-'])]:size-3","mui-eb5ef2dbc2d4":"[&_svg:not([class*='size-'])]:size-4","mui-8637dfd54fbb":"[&_svg:not([class*='text-'])]:text-muted-foreground","mui-4f05a238500e":"[&::-webkit-search-cancel-button]:appearance-none","mui-74ab31abd804":"[&:first-child[data-selected=true]_button]:rounded-l-md","mui-13858191ca50":"rtl:**:[.rdp-button\\_next>svg]:rotate-180","mui-a324a7f99932":"rtl:**:[.rdp-button\\_previous>svg]:rotate-180","mui-82b114bbff3d":"data-[variant=destructive]:*:[svg]:text-destructive!","mui-6ee0c74de40c":"[&:last-child[data-selected=true]_button]:rounded-r-md","mui-6162ab70efb5":"[&:nth-child(2)[data-selected=true]_button]:rounded-l-md","mui-f157ee6bae99":"[&>div]:w-full","mui-ba417cf466b0":"[&>span]:text-xs","mui-829c8ef4f9c5":"[&>span]:opacity-70","mui-bf95f682e553":"[&>span:first-child]:mt-[calc(0.5lh-0.4375rem)]","mui-eb740dd32318":"[&>svg]:size-3.5","mui-019fd73c95db":"[&>svg]:text-muted-foreground","mui-b8293c91796c":"[[data-slot=card-content]_&]:bg-transparent","mui-8dd7e6b42262":"[[data-slot=popover-content]_&]:bg-transparent"};
@@ -14,13 +14,15 @@ export interface MendyUIComponents {
14
14
  export type FilterPart = "root" | "search" | "searchInput" | "menuTrigger" | "menu" | "menuList" | "menuRow" | "menuHeader" | "editor" | "chip" | "chipTrigger" | "chipRemove" | "option" | "clear";
15
15
  export type FilterClassNames = Partial<Record<FilterPart, string>>;
16
16
  export interface MendyUIOptions {
17
+ /** Desktop editor placement. Phones always use one panel with Back. */
18
+ menuLayout?: "connected" | "anchored";
17
19
  components?: Partial<MendyUIComponents>;
18
20
  classNames?: FilterClassNames;
19
21
  /** Mount dropdowns inside a local theme or dialog instead of document.body. */
20
22
  portalContainer?: HTMLElement | null;
21
23
  }
22
24
  /** Nested providers inherit defaults; local settings override matching keys. */
23
- export declare function MendyUIProvider({ children, components, classNames, portalContainer, }: MendyUIOptions & {
25
+ export declare function MendyUIProvider({ children, components, classNames, portalContainer, menuLayout, }: MendyUIOptions & {
24
26
  children: ReactNode;
25
27
  }): import("react/jsx-runtime").JSX.Element;
26
28
  export declare function useMendyUI(): MendyUIOptions;
@@ -8,13 +8,14 @@ import { Label as DefaultLabel } from "./primitives/label.js";
8
8
  import { Calendar as DefaultCalendar } from "./primitives/calendar.js";
9
9
  const Context = createContext({});
10
10
  /** Nested providers inherit defaults; local settings override matching keys. */
11
- export function MendyUIProvider({ children, components, classNames, portalContainer, }) {
11
+ export function MendyUIProvider({ children, components, classNames, portalContainer, menuLayout, }) {
12
12
  const parent = useContext(Context);
13
13
  const value = useMemo(() => ({
14
+ menuLayout: menuLayout ?? parent.menuLayout,
14
15
  components: { ...parent.components, ...components },
15
16
  classNames: { ...parent.classNames, ...classNames },
16
17
  portalContainer: portalContainer === undefined ? parent.portalContainer : portalContainer,
17
- }), [parent, components, classNames, portalContainer]);
18
+ }), [parent, components, classNames, portalContainer, menuLayout]);
18
19
  return _jsx(Context.Provider, { value: value, children: children });
19
20
  }
20
21
  export function useMendyUI() {
@@ -6,6 +6,7 @@ export interface FilterMenuGroup {
6
6
  id: string;
7
7
  label: string;
8
8
  fields: string[];
9
+ separatorBefore?: boolean;
9
10
  icon?: ReactNode;
10
11
  }
11
12
  export interface FilterRootProps {
@@ -30,9 +31,11 @@ export declare function FilterSearch({ label, placeholder, }: {
30
31
  placeholder?: string;
31
32
  }): import("react/jsx-runtime").JSX.Element;
32
33
  /** A standalone menu button for layouts without a search field. */
33
- export declare function FilterMenu({ children, asChild, }: {
34
+ export declare function FilterMenu({ children, asChild, anchor, }: {
34
35
  children?: ReactNode;
35
36
  asChild?: boolean;
37
+ /** Align a standalone trigger to an application-owned search field. */
38
+ anchor?: React.RefObject<HTMLDivElement | null>;
36
39
  }): import("react/jsx-runtime").JSX.Element;
37
40
  export declare function FilterList(): import("react/jsx-runtime").JSX.Element;
38
41
  export declare function FilterField({ id }: {
@@ -111,10 +111,10 @@ export function FilterSearch({ label = "Search", placeholder = "Search or filter
111
111
  }, children: _jsx(X, { className: "mui-61c000c8a98a", "aria-hidden": "true" }) })), _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", ref: trigger, type: "button", disabled: disabled, "aria-label": "Open filters", "aria-haspopup": "dialog", className: cn("mui-747355bdc2a2 mui-13422ddc8d83 mui-4e45f4841abb mui-79ede756e761 mui-5dde0fd996f0 mui-2cdd01663c76 mui-8da02faac748 mui-2e357972fc10 mui-39be5a15b766 mui-db549d38ff19 mui-790ed041a953", filters.active.length ? "mui-65694092f002" : "mui-0b5a4d1257ab", classNames?.menuTrigger), children: _jsx(ListFilter, { className: "mui-100c22d5776b", "aria-hidden": "true" }) }) })] }), filters.menuOpen && _jsx(FilterMenuContent, { anchor: anchor })] }));
112
112
  }
113
113
  /** A standalone menu button for layouts without a search field. */
114
- export function FilterMenu({ children = "Add filter", asChild = false, }) {
114
+ export function FilterMenu({ children = "Add filter", asChild = false, anchor, }) {
115
115
  const { filters, trigger, disabled, direction } = useRoot();
116
116
  const { classNames } = useMendyUI();
117
- return (_jsxs(DropdownMenu, { dir: direction, modal: false, open: filters.menuOpen, onOpenChange: filters.setMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { ref: trigger, asChild: asChild, className: classNames?.menuTrigger, variant: "outline", disabled: disabled, "aria-haspopup": "dialog", children: children }) }), filters.menuOpen && _jsx(FilterMenuContent, {})] }));
117
+ return (_jsxs(DropdownMenu, { dir: direction, modal: false, open: filters.menuOpen, onOpenChange: filters.setMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { ref: trigger, asChild: asChild, className: classNames?.menuTrigger, variant: "outline", disabled: disabled, "aria-haspopup": "dialog", children: children }) }), filters.menuOpen && _jsx(FilterMenuContent, { anchor: anchor })] }));
118
118
  }
119
119
  const fieldIcons = {
120
120
  single: Circle,
@@ -140,6 +140,7 @@ function FilterMenuContent({ anchor }) {
140
140
  label: entry.field.label,
141
141
  icon: entry.field.icon,
142
142
  entries: [entry],
143
+ separatorBefore: false,
143
144
  },
144
145
  ]),
145
146
  ...groups.flatMap((group) => {
@@ -155,6 +156,7 @@ function FilterMenuContent({ anchor }) {
155
156
  entry.field.removable !== false);
156
157
  return {
157
158
  id: section.id,
159
+ separatorBefore: section.separatorBefore,
158
160
  label: section.label,
159
161
  icon: section.icon ?? _jsx(Icon, {}),
160
162
  editorLabel: single?.editorLabel ??
@@ -174,17 +176,21 @@ function FilterMenuContent({ anchor }) {
174
176
  }
175
177
  }
176
178
  : undefined,
177
- content: section.entries.map((entry) => (_jsxs("div", { className: "mui-f157ee6bae99", children: [!single && (_jsx("p", { className: "mui-894c3815a57f mui-0f9e6672913a mui-0a702d97ed7d mui-3992de70b033 mui-daaac3fbf55e mui-4fa4c190281c", children: entry.field.label })), _jsx(FieldEditor, { entry: entry, active: true, autoFocus: false, showDateLabel: false, disabled: disabled || entry.field.disabled, close: () => filters.setMenuOpen(false), location: "menu" }, `${entry.id}:${clearEpoch}`)] }, entry.id))),
179
+ content: section.entries.map((entry, index) => (_jsxs("div", { className: cn("mui-184ddc11e5f9", entry.field.menuLayout === "inline"
180
+ ? "mui-222f930b8752 mui-71556df3b421 mui-32c92bb1fde4 mui-d7db3451d154 mui-0f9e6672913a mui-b5edc3ea7c91" : "mui-f157ee6bae99", index > 0 && "mui-894c3815a57f"), children: [!single && (index > 0 || entry.field.label !== section.label) && (_jsx("p", { className: cn("mui-c74ab393b96d", entry.field.menuLayout !== "inline" && "mui-0f9e6672913a mui-0a702d97ed7d mui-3992de70b033 mui-daaac3fbf55e"), children: entry.field.label })), _jsx(FieldEditor, { entry: entry, active: true, autoFocus: false, showDateLabel: false, disabled: disabled || entry.field.disabled, close: () => filters.setMenuOpen(false), location: "menu" }, `${entry.id}:${clearEpoch}`)] }, entry.id))),
178
181
  };
179
182
  });
180
183
  return (_jsx(FilterDraftCache.Provider, { value: drafts, children: _jsx(FilterMenuPanel, { sections: sections, selectedId: filters.openField, onSelect: filters.setOpenField, onClose: () => filters.setMenuOpen(false), anchor: anchor, trigger: trigger }) }));
181
184
  }
182
185
  export function FilterList() {
183
186
  const { filters, suggestions } = useRoot();
187
+ let entranceIndex = 0;
184
188
  return (_jsx(_Fragment, { children: filters.entries.flatMap((entry) => !entry.field.hidden &&
185
189
  (entry.field.isActive(entry.value) ||
186
190
  showSuggestion(entry, suggestions, filters.active.length))
187
- ? [_jsx(FieldChip, { entry: entry }, entry.id)]
191
+ ? [
192
+ _jsx(FieldChip, { entry: entry, entranceDelay: Math.min(entranceIndex++ * 0.06, 0.3) }, entry.id),
193
+ ]
188
194
  : []) }));
189
195
  }
190
196
  function summarize(field, value, choices) {
@@ -209,7 +215,7 @@ export function FilterFieldEditor({ id, autoFocus = true }) {
209
215
  const entry = filters.entries.find((entry) => entry.id === id);
210
216
  return entry ? (_jsx(FieldEditor, { entry: entry, active: true, autoFocus: autoFocus, disabled: disabled || entry.field.disabled, close: () => filters.edit(null), location: "inline" }, id)) : null;
211
217
  }
212
- function FieldChip({ entry }) {
218
+ function FieldChip({ entry, entranceDelay = 0 }) {
213
219
  const { classNames } = useMendyUI();
214
220
  const { filters, summary, suggestions, disabled, trigger } = useRoot();
215
221
  const { id, field, value } = entry;
@@ -223,7 +229,7 @@ function FieldChip({ entry }) {
223
229
  const options = useFilterOptions(id, field, active ? value : field.suggestion?.value, false);
224
230
  if (field.hidden || (!active && !suggestion))
225
231
  return null;
226
- return (_jsxs(AppliedFilter, { label: field.label, "data-mendy-ui": "", "data-slot": active ? "filter-chip" : "filter-suggestion", "data-state": active ? "applied" : "suggested", className: cn(!active && "mui-4f1a55de40bc mui-8450eb0f1857 mui-9b76f456b8c9 mui-8fca9236d191", (field.summary ?? summary).mode === "all" && "mui-a66e9985b093 mui-9974acedf3f7", classNames?.chip), disabled: chipDisabled(field, active, disabled), removeProps: { className: classNames?.chipRemove }, contentProps: {
232
+ return (_jsxs(AppliedFilter, { entranceDelay: entranceDelay, label: field.label, "data-mendy-ui": "", "data-slot": active ? "filter-chip" : "filter-suggestion", "data-state": active ? "applied" : "suggested", className: cn(!active && "mui-4f1a55de40bc mui-8450eb0f1857 mui-9b76f456b8c9 mui-8fca9236d191", (field.summary ?? summary).mode === "all" && "mui-a66e9985b093 mui-9974acedf3f7", classNames?.chip), disabled: chipDisabled(field, active, disabled), removeProps: { className: classNames?.chipRemove }, contentProps: {
227
233
  className: classNames?.editor,
228
234
  onCloseAutoFocus: (event) => {
229
235
  if (!currentlyActive.current) {
@@ -266,12 +272,19 @@ function summaryText(field, shownValue, choices, policy) {
266
272
  }
267
273
  return text;
268
274
  }
275
+ function ChipLabel({ field, active, hasValue, }) {
276
+ return (_jsxs(_Fragment, { children: [" ", (field.chipLabel !== false || !active) && (_jsxs("span", { className: cn("mui-184ddc11e5f9 mui-66b490dd6005 mui-5a3a4ee420c2 mui-14bd99410890", hasValue && "mui-91f82aa1df0c"), title: field.label, dir: "auto", children: [!active && field.suggestion?.label
277
+ ? field.suggestion.label
278
+ : typeof field.chipLabel === "string"
279
+ ? field.chipLabel
280
+ : field.label, hasValue ? ":" : ""] }))] }));
281
+ }
269
282
  function ChipSummary({ field, value, active, options, summary, }) {
270
283
  const policy = field.summary ?? summary;
271
284
  const shownValue = active ? value : field.suggestion?.value;
272
285
  const full = summarize(field, shownValue, options.selected);
273
286
  const text = summaryText(field, shownValue, options.selected, policy);
274
- return (_jsxs(_Fragment, { children: [_jsxs("span", { className: cn("mui-184ddc11e5f9 mui-66b490dd6005 mui-5a3a4ee420c2 mui-14bd99410890", full && "mui-91f82aa1df0c"), title: field.label, dir: "auto", children: [!active && field.suggestion?.label ? field.suggestion.label : field.label, full ? ":" : ""] }), field.renderSummary ? (field.renderSummary(shownValue, options.selected)) : (_jsx("span", { title: full, dir: "auto", className: cn("mui-184ddc11e5f9 mui-957a109f887f mui-393883c1db68 mui-14bd99410890", policy.mode !== "all" && "mui-5a3a4ee420c2", policy.mode === "all" && "mui-726fca123972 mui-37cf9655f30a mui-1fcf28756bb1"), style: policy.mode === "ellipsis" ? { maxWidth: policy.maxWidth ?? 180 } : undefined, children: options.resolving ? _jsx("span", { className: "mui-7b3cd77eb999", children: text || "Loading…" }) : text }))] }));
287
+ return (_jsxs(_Fragment, { children: [_jsx(ChipLabel, { field: field, active: active, hasValue: Boolean(full) }), field.renderSummary ? (field.renderSummary(shownValue, options.selected)) : (_jsx("span", { title: full, dir: "auto", className: cn("mui-184ddc11e5f9 mui-957a109f887f mui-393883c1db68 mui-14bd99410890", policy.mode !== "all" && "mui-5a3a4ee420c2", policy.mode === "all" && "mui-726fca123972 mui-37cf9655f30a mui-1fcf28756bb1"), style: policy.mode === "ellipsis" ? { maxWidth: policy.maxWidth ?? 180 } : undefined, children: options.resolving ? _jsx("span", { className: "mui-7b3cd77eb999", children: text || "Loading…" }) : text }))] }));
275
288
  }
276
289
  export function FilterClear({ children = "Clear all" }) {
277
290
  const { classNames } = useMendyUI();
@@ -342,7 +355,7 @@ function FieldEditor({ entry, active, disabled, close, location, autoFocus = tru
342
355
  requestAnimationFrame(() => trigger.current?.focus());
343
356
  }
344
357
  if (field.renderEditor)
345
- return (_jsxs("div", { "data-mendy-ui": "", ref: customRoot, className: "mui-34055e6f8c1b mui-094f5333853b", onKeyDown: (event) => {
358
+ return (_jsxs("div", { "data-mendy-ui": "", ref: customRoot, className: cn("mui-184ddc11e5f9 mui-81b198a2db84", field.editorPadding !== "none" && "mui-094f5333853b"), onKeyDown: (event) => {
346
359
  if (event.key === "Escape")
347
360
  return;
348
361
  if (event.key === "Tab" ||
@@ -350,6 +363,8 @@ function FieldEditor({ entry, active, disabled, close, location, autoFocus = tru
350
363
  event.target.matches("input, textarea, select, [contenteditable=true]")))
351
364
  event.stopPropagation();
352
365
  }, children: [field.renderEditor({
366
+ autoFocus,
367
+ location,
353
368
  value,
354
369
  draft,
355
370
  setDraft,
@@ -56,6 +56,9 @@ export interface ExternalOptions<T> {
56
56
  }
57
57
  export declare function externalOptions<T>(source: Omit<ExternalOptions<T>, "kind">): ExternalOptions<T>;
58
58
  export interface EditorContext<V> {
59
+ /** False while the menu previews an editor. The menu focuses it on click or Enter. */
60
+ autoFocus: boolean;
61
+ location: "menu" | "chip" | "inline";
59
62
  value: V;
60
63
  setValue(value: V): void;
61
64
  close(): void;
@@ -79,6 +82,11 @@ export interface FieldConfig<V> {
79
82
  loading?: boolean;
80
83
  disabled?: boolean;
81
84
  };
85
+ /** Hide a redundant chip label, or use a shorter visible label. Accessible names stay intact. */
86
+ chipLabel?: boolean | string;
87
+ /** Place this editor beside its label inside a grouped menu. */
88
+ menuLayout?: "stack" | "inline";
89
+ editorPadding?: "default" | "none";
82
90
  summary?: SummaryPolicy;
83
91
  renderSummary?: (value: V, choices: readonly Choice[]) => ReactNode;
84
92
  renderEditor?: (context: EditorContext<V>) => ReactNode;
@@ -133,6 +141,11 @@ export interface RuntimeField {
133
141
  loading?: boolean;
134
142
  disabled?: boolean;
135
143
  };
144
+ /** Hide a redundant chip label, or use a shorter visible label. Accessible names stay intact. */
145
+ chipLabel?: boolean | string;
146
+ /** Place this editor beside its label inside a grouped menu. */
147
+ menuLayout?: "stack" | "inline";
148
+ editorPadding?: "default" | "none";
136
149
  summary?: SummaryPolicy;
137
150
  renderSummary?: (value: unknown, choices: readonly Choice[]) => ReactNode;
138
151
  renderEditor?: (context: EditorContext<unknown>) => ReactNode;
@@ -6,3 +6,6 @@ export declare function handleMenuTab(event: KeyboardEvent<HTMLDivElement>, { tr
6
6
  onClose(): void;
7
7
  hasSelection: boolean;
8
8
  }): void;
9
+ /** Return from editor controls without consuming text, grid, or tree navigation keys. */
10
+ export declare function handleMenuReturn(event: KeyboardEvent<HTMLDivElement>, back: () => void): void;
11
+ export declare function preserveOutsideFocus(event: Event): void;
@@ -67,3 +67,25 @@ export function handleMenuTab(event, { trigger, editor, onClose, hasSelection, }
67
67
  onClose();
68
68
  }
69
69
  }
70
+ /** Return from editor controls without consuming text, grid, or tree navigation keys. */
71
+ export function handleMenuReturn(event, back) {
72
+ const target = event.target;
73
+ const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
74
+ if (event.key !== (rtl ? "ArrowRight" : "ArrowLeft"))
75
+ return;
76
+ if (!(target instanceof HTMLElement) || target.isContentEditable)
77
+ return;
78
+ if (target.closest('input, textarea, select, [role="grid"], [role="slider"], [role="spinbutton"], [role="combobox"], [role="tablist"], [role="tree"], [role="listbox"]'))
79
+ return;
80
+ event.preventDefault();
81
+ event.stopPropagation();
82
+ back();
83
+ }
84
+ export function preserveOutsideFocus(event) {
85
+ const focused = document.activeElement;
86
+ if (event.target instanceof HTMLElement &&
87
+ focused &&
88
+ focused !== document.body &&
89
+ !event.target.contains(focused))
90
+ event.preventDefault();
91
+ }
@@ -6,6 +6,7 @@ export interface FilterMenuSection {
6
6
  icon?: ReactNode;
7
7
  disabled: boolean;
8
8
  active: boolean;
9
+ separatorBefore?: boolean;
9
10
  clear?: () => void;
10
11
  content: ReactNode;
11
12
  }
@@ -7,7 +7,8 @@ import { FilterCollection } from "./filter-collection.js";
7
7
  import { useValueDraft } from "./use-value-draft.js";
8
8
  import { DropdownMenuContent } from "../primitives/dropdown-menu.js";
9
9
  import { useMendyUI } from "../customization.js";
10
- import { focusMenuEditor, handleMenuTab } from "./filter-menu-focus.js";
10
+ import { focusMenuEditor, handleMenuTab, handleMenuReturn, preserveOutsideFocus, } from "./filter-menu-focus.js";
11
+ import { useMenuPlacement, useEditorOffset } from "./use-menu-placement.js";
11
12
  import { useMenuPointer } from "./use-menu-pointer.js";
12
13
  import { cn } from "../utils.js";
13
14
  const desktopQuery = "(min-width: 640px)";
@@ -25,19 +26,30 @@ function subscribeViewport(listener) {
25
26
  }
26
27
  const isDesktop = () => window.innerWidth >= 40 * parseFloat(getComputedStyle(document.documentElement).fontSize);
27
28
  const serverDesktop = () => false;
29
+ function selectedSection(sections, selectedId, desktop) {
30
+ return (sections.find((section) => section.id === selectedId && !section.disabled) ??
31
+ (desktop ? sections.find((section) => !section.disabled) : undefined));
32
+ }
28
33
  /** One dialog contains the filter list and its editor, with a single-panel layout on phones. */
29
34
  export function FilterMenuPanel({ sections, selectedId, onSelect, onClose, anchor, trigger, }) {
30
- const { classNames } = useMendyUI();
35
+ const { classNames, menuLayout } = useMendyUI();
31
36
  const desktop = useSyncExternalStore(subscribeViewport, isDesktop, serverDesktop);
32
- const selected = sections.find((section) => section.id === selectedId && !section.disabled) ??
33
- (desktop ? sections.find((section) => !section.disabled) : undefined);
37
+ const selected = selectedSection(sections, selectedId, desktop);
34
38
  const content = useRef(null);
35
39
  const editor = useRef(null);
36
40
  const rows = useRef(new Map());
37
41
  const pendingEditorFocus = useRef(false);
38
- const [alignOffset, setAlignOffset] = useState(0);
39
- const [side, setSide] = useState("bottom");
42
+ const { alignOffset, side } = useMenuPlacement(anchor, trigger, desktop);
40
43
  const panelId = useId();
44
+ const detached = desktop && menuLayout === "anchored";
45
+ const editorPanel = useRef(null);
46
+ const editorOffset = useEditorOffset({
47
+ detached,
48
+ editorPanel,
49
+ content,
50
+ rows,
51
+ selectedId: selected?.id,
52
+ });
41
53
  const initialSelection = useRef(selected?.id);
42
54
  const lastSelection = useRef(selected?.id);
43
55
  const [keyboardNavigation, setKeyboardNavigation] = useState(() => trigger.current?.matches(":focus-visible") ?? false);
@@ -49,45 +61,6 @@ export function FilterMenuPanel({ sections, selectedId, onSelect, onClose, ancho
49
61
  rows.current.get(id)?.focus({ preventScroll: true });
50
62
  },
51
63
  });
52
- useLayoutEffect(() => {
53
- const button = trigger.current;
54
- if (!button)
55
- return;
56
- const update = () => {
57
- const bounds = button.getBoundingClientRect();
58
- const target = anchor?.current?.getBoundingClientRect();
59
- const rtl = getComputedStyle(button).direction === "rtl";
60
- const viewport = window.visualViewport;
61
- const viewportTop = viewport?.offsetTop ?? 0;
62
- const viewportBottom = viewportTop + (viewport?.height ?? window.innerHeight);
63
- const below = viewportBottom - bounds.bottom - 23;
64
- const above = bounds.top - viewportTop - 23;
65
- const preferredHeight = 30 * parseFloat(getComputedStyle(document.documentElement).fontSize);
66
- // A short list can fit below the trigger while its editor cannot. Choose room
67
- // for the editor before Radix constrains its height to the current side.
68
- setSide(below < preferredHeight && above > below ? "top" : "bottom");
69
- // Desktop starts at the search field's edge; mobile stays close to the icon.
70
- setAlignOffset(desktop
71
- ? rtl
72
- ? bounds.right - (target?.right ?? bounds.right)
73
- : (target?.left ?? bounds.left) - bounds.left
74
- : 0);
75
- };
76
- update();
77
- const observer = new ResizeObserver(update);
78
- observer.observe(anchor?.current ?? button);
79
- window.addEventListener("resize", update);
80
- window.addEventListener("scroll", update, true);
81
- window.visualViewport?.addEventListener("resize", update);
82
- window.visualViewport?.addEventListener("scroll", update);
83
- return () => {
84
- observer.disconnect();
85
- window.removeEventListener("resize", update);
86
- window.removeEventListener("scroll", update, true);
87
- window.visualViewport?.removeEventListener("resize", update);
88
- window.visualViewport?.removeEventListener("scroll", update);
89
- };
90
- }, [anchor, desktop, trigger]);
91
64
  useEffect(() => {
92
65
  const frame = requestAnimationFrame(() => {
93
66
  const first = rows.current.get(initialSelection.current ?? "") ??
@@ -124,14 +97,7 @@ export function FilterMenuPanel({ sections, selectedId, onSelect, onClose, ancho
124
97
  event.preventDefault();
125
98
  back();
126
99
  }
127
- }, onCloseAutoFocus: (event) => {
128
- const focused = document.activeElement;
129
- if (event.target instanceof HTMLElement &&
130
- focused &&
131
- focused !== document.body &&
132
- !event.target.contains(focused))
133
- event.preventDefault();
134
- }, onPointerDownCapture: () => {
100
+ }, onCloseAutoFocus: preserveOutsideFocus, onPointerDownCapture: () => {
135
101
  setKeyboardNavigation(false);
136
102
  pointer.cancel();
137
103
  }, onPointerLeave: pointer.cancel, onKeyDownCapture: (event) => {
@@ -146,22 +112,13 @@ export function FilterMenuPanel({ sections, selectedId, onSelect, onClose, ancho
146
112
  onClose,
147
113
  hasSelection: Boolean(selected),
148
114
  });
149
- }, onKeyDown: (event) => {
150
- const target = event.target;
151
- const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
152
- if (event.key === (rtl ? "ArrowRight" : "ArrowLeft") &&
153
- target instanceof HTMLElement &&
154
- !target.isContentEditable &&
155
- !target.closest('input, textarea, select, [role="grid"], [role="slider"], [role="spinbutton"], [role="combobox"], [role="tablist"], [role="tree"], [role="listbox"]')) {
156
- event.preventDefault();
157
- event.stopPropagation();
158
- if (desktop)
159
- rows.current.get(selected?.id ?? "")?.focus();
160
- else
161
- back();
162
- }
163
- }, className: cn("mui-5662a94d9dba mui-571ea69568d3 mui-34055e6f8c1b mui-d5111d0e9f48 mui-04760bcd507f mui-94ea94fde25f mui-393df0d154e0 mui-548a450e8e53", desktop && selected ? "mui-8dd3eece40db" : "mui-919182384559", classNames?.menu), children: _jsxs("div", { className: cn("mui-80baa5d03af7", desktop && selected
164
- ? "mui-0f2a693e93e2 mui-b5985369ee35 mui-da607d0a2538" : "mui-222f930b8752 mui-302c0d124a94"), children: [showList && (_jsx(FilterMenuList, { sections: sections, selectedId: selected?.id, initialKey: selected?.id ?? lastSelection.current, panelId: panelId, desktop: desktop, rows: rows, choose: choose, keyboardNavigation: keyboardNavigation, onPointerMove: (id, event) => {
115
+ }, onKeyDown: (event) => handleMenuReturn(event, () => {
116
+ if (desktop)
117
+ rows.current.get(selected?.id ?? "")?.focus();
118
+ else
119
+ back();
120
+ }), className: cn("mui-971625533e56 mui-571ea69568d3 mui-34055e6f8c1b mui-d5111d0e9f48 mui-04760bcd507f mui-94ea94fde25f mui-393df0d154e0 mui-548a450e8e53", desktop && selected ? "mui-8dd3eece40db" : "mui-919182384559", detached && "mui-0fc84a692dad mui-dad556abfe15 mui-8fca9236d191 mui-89c3006b2722", classNames?.menu), children: _jsxs("div", { className: cn("mui-80baa5d03af7", desktop && selected
121
+ ? "mui-0f2a693e93e2 mui-b5985369ee35 mui-da607d0a2538" : "mui-222f930b8752 mui-302c0d124a94", detached && "mui-b9677bd1cb66 mui-70007b876865"), children: [showList && (_jsx(FilterMenuList, { sections: sections, selectedId: selected?.id, initialKey: selected?.id ?? lastSelection.current, panelId: panelId, desktop: desktop, detached: detached, rows: rows, choose: choose, keyboardNavigation: keyboardNavigation, onPointerMove: (id, event) => {
165
122
  if (desktop && event.pointerType === "mouse") {
166
123
  setKeyboardNavigation(false);
167
124
  const focused = document.activeElement;
@@ -173,10 +130,14 @@ export function FilterMenuPanel({ sections, selectedId, onSelect, onClose, ancho
173
130
  }
174
131
  pointer.move(id, event);
175
132
  }
176
- } })), selected && (_jsxs("div", { onPointerEnter: pointer.cancel, className: "mui-222f930b8752 mui-410da8dfa8ac mui-184ddc11e5f9 mui-302c0d124a94", children: [_jsxs("div", { className: cn("mui-222f930b8752 mui-8423dc94ee06 mui-27ead27a81df mui-71556df3b421 mui-074569488cca mui-bbe39cfb5cc6 mui-0f9e6672913a mui-b5edc3ea7c91 mui-3992de70b033 mui-daaac3fbf55e mui-aa0e3c036805 mui-0288c4ea71ea", classNames?.menuHeader), children: [!desktop && (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: back, className: "mui-958e2cfcf5b6 mui-70007b876865 mui-e7e01cc7f4df mui-3992de70b033 mui-52101fc7d8bb", children: [_jsx(ArrowLeft, { "aria-hidden": "true", className: "mui-61c000c8a98a mui-36b12e8339c7" }), "Filters"] }), _jsx("span", { "aria-hidden": "true", className: "mui-35f35c41d134", children: "/" })] })), _jsx(MenuHeading, { label: selected.label }), selected.clear && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
177
- selected.clear?.();
178
- requestAnimationFrame(focusEditor);
179
- }, "aria-label": `Clear ${selected.label} filter`, className: "mui-8cc46fa87f41 mui-ce1945804664 mui-1da2e8f173c5 mui-3992de70b033 mui-52101fc7d8bb mui-35f35c41d134", children: "Clear" }))] }), _jsx("div", { ref: editor, id: `${panelId}-${selected.id}`, role: "group", "aria-label": selected.editorLabel, tabIndex: -1, className: cn("mui-410da8dfa8ac mui-184ddc11e5f9 mui-2368e909f3a5 mui-998780cdef87 mui-b30fc56058b6 mui-f157ee6bae99 mui-a1c8c43d9be2 mui-b43343fe71c0", classNames?.editor), children: selected.content }, selected.id)] }))] }) }));
133
+ } })), selected && (_jsxs("div", { ref: editorPanel, "data-slot": "filter-menu-editor", onPointerEnter: pointer.cancel, style: detached ? { marginTop: editorOffset } : undefined, className: cn("mui-222f930b8752 mui-410da8dfa8ac mui-184ddc11e5f9 mui-80baa5d03af7 mui-302c0d124a94", detached && "mui-d5111d0e9f48 mui-3fa8c572949b mui-4f1a55de40bc mui-e593c38256e0 mui-5bd2afe89f0b mui-94ea94fde25f"), children: [_jsx(EditorHeading, { section: selected, desktop: desktop, back: back, onCleared: focusEditor }), _jsx("div", { ref: editor, id: `${panelId}-${selected.id}`, role: "group", "aria-label": selected.editorLabel, tabIndex: -1, className: cn("mui-410da8dfa8ac mui-184ddc11e5f9 mui-3b82279f859f mui-2368e909f3a5 mui-998780cdef87 mui-b30fc56058b6 mui-f157ee6bae99 mui-a1c8c43d9be2 mui-b43343fe71c0", classNames?.editor), children: selected.content }, selected.id)] }))] }) }));
134
+ }
135
+ function EditorHeading({ section, desktop, back, onCleared, }) {
136
+ const { classNames } = useMendyUI();
137
+ return (_jsxs("div", { className: cn("mui-222f930b8752 mui-8423dc94ee06 mui-27ead27a81df mui-71556df3b421 mui-074569488cca mui-bbe39cfb5cc6 mui-0f9e6672913a mui-b5edc3ea7c91 mui-3992de70b033 mui-daaac3fbf55e mui-aa0e3c036805 mui-0288c4ea71ea", classNames?.menuHeader), children: [!desktop && (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: back, className: "mui-958e2cfcf5b6 mui-70007b876865 mui-e7e01cc7f4df mui-3992de70b033 mui-52101fc7d8bb", children: [_jsx(ArrowLeft, { "aria-hidden": "true", className: "mui-61c000c8a98a mui-36b12e8339c7" }), "Filters"] }), _jsx("span", { "aria-hidden": "true", className: "mui-35f35c41d134", children: "/" })] })), _jsx(MenuHeading, { label: section.label }), section.clear && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
138
+ section.clear?.();
139
+ requestAnimationFrame(onCleared);
140
+ }, "aria-label": `Clear ${section.label} filter`, className: "mui-8cc46fa87f41 mui-ce1945804664 mui-1da2e8f173c5 mui-3992de70b033 mui-52101fc7d8bb mui-35f35c41d134", children: "Clear" }))] }));
180
141
  }
181
142
  function MenuHeading({ label }) {
182
143
  const ref = useRef(null);
@@ -201,7 +162,7 @@ function MenuHeading({ label }) {
201
162
  }, [label]);
202
163
  return (_jsx("span", { ref: ref, onScroll: measure, title: label, tabIndex: overflow ? 0 : undefined, "data-more-below": below, className: "mui-1326520533f0 mui-184ddc11e5f9 mui-7bd5bab6d7f4 mui-2368e909f3a5 mui-726fca123972 mui-393883c1db68 mui-cacb19aa9640 mui-bbaaa5969856 mui-1df9125eebaf mui-223daa83ed2d", children: label }));
203
164
  }
204
- function FilterMenuList({ sections, selectedId, initialKey, panelId, desktop, rows, choose, keyboardNavigation, onPointerMove, }) {
165
+ function FilterMenuList({ sections, selectedId, initialKey, panelId, desktop, detached, rows, choose, keyboardNavigation, onPointerMove, }) {
205
166
  const { classNames } = useMendyUI();
206
167
  const [query, setQuery] = useValueDraft("types", () => "", "__menu:query");
207
168
  const collection = useRef(null);
@@ -211,7 +172,7 @@ function FilterMenuList({ sections, selectedId, initialKey, panelId, desktop, ro
211
172
  if (section.label.toLocaleLowerCase().includes(term))
212
173
  matches.push({ ...section, key: section.id });
213
174
  }
214
- return (_jsxs("div", { className: cn("mui-222f930b8752 mui-410da8dfa8ac mui-302c0d124a94", desktop && selectedId && "mui-3b6d5fc7b061"), children: [sections.length > 20 && (_jsx("div", { className: "mui-27ead27a81df mui-bbe39cfb5cc6 mui-b97db4a9f432", children: _jsx(Input, { type: "search", "aria-label": "Find a filter", placeholder: "Find a filter\u2026", value: query, className: "mui-b51243872d85", onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
175
+ return (_jsxs("div", { className: cn("mui-222f930b8752 mui-410da8dfa8ac mui-302c0d124a94", desktop && selectedId && !detached && "mui-3b6d5fc7b061", detached && "mui-d5111d0e9f48 mui-3fa8c572949b mui-4f1a55de40bc mui-e593c38256e0 mui-5bd2afe89f0b mui-94ea94fde25f"), children: [sections.length > 20 && (_jsx("div", { className: "mui-27ead27a81df mui-bbe39cfb5cc6 mui-b97db4a9f432", children: _jsx(Input, { type: "search", "aria-label": "Find a filter", placeholder: "Find a filter\u2026", value: query, className: "mui-b51243872d85", onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
215
176
  if (event.key === "ArrowDown") {
216
177
  event.preventDefault();
217
178
  collection.current?.focusFirst();
@@ -228,7 +189,8 @@ function FilterMenuList({ sections, selectedId, initialKey, panelId, desktop, ro
228
189
  ? `${section.active ? "Filter applied. " : ""}${index + 1} of ${matches.length}`
229
190
  : section.active
230
191
  ? "Filter applied"
231
- : undefined, "aria-expanded": selectedId === section.id, "aria-controls": selectedId === section.id ? `${panelId}-${section.id}` : undefined, "data-navigation": keyboardNavigation ? "keyboard" : "pointer", disabled: section.disabled, className: cn("mui-a66e9985b093 mui-8423dc94ee06 mui-58d0413dd93c mui-c62ec162662e mui-074569488cca mui-02e603944040 mui-e7e01cc7f4df mui-b5edc3ea7c91 mui-c74ab393b96d mui-52101fc7d8bb mui-aa0e3c036805 mui-41d1475cf71a mui-dfc89afd7885", selectedId === section.id && "mui-292affc1f780 mui-cc209238d847", classNames?.menuRow), onPointerMove: (event) => {
192
+ : undefined, "aria-expanded": selectedId === section.id, "aria-controls": selectedId === section.id ? `${panelId}-${section.id}` : undefined, "data-navigation": keyboardNavigation ? "keyboard" : "pointer", "data-separator": section.separatorBefore && !query ? "true" : undefined, disabled: section.disabled, className: cn("mui-a66e9985b093 mui-8423dc94ee06 mui-58d0413dd93c mui-c62ec162662e mui-074569488cca mui-02e603944040 mui-e7e01cc7f4df mui-b5edc3ea7c91 mui-c74ab393b96d mui-52101fc7d8bb mui-aa0e3c036805 mui-41d1475cf71a mui-dfc89afd7885", section.separatorBefore &&
193
+ !query && "mui-d2d9e1f13413 mui-a22ea0ba562a mui-be02e79e6a27 mui-a267680f9f49 mui-e22234c7b474 mui-4d24300d447b mui-37a30b916086", selectedId === section.id && "mui-292affc1f780 mui-cc209238d847", classNames?.menuRow), onPointerMove: (event) => {
232
194
  if (!section.disabled)
233
195
  onPointerMove(section.id, event);
234
196
  }, onFocus: () => {
@@ -2,8 +2,10 @@ import type { ComponentProps, ReactNode } from "react";
2
2
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuTrigger } from "../primitives/dropdown-menu.js";
3
3
  export type FilterEditorProps = ComponentProps<typeof DropdownMenu>;
4
4
  export declare function FilterEditor(props: FilterEditorProps): import("react/jsx-runtime").JSX.Element;
5
- export type FilterChipProps = ComponentProps<"div">;
6
- export declare function FilterChip({ className, ...props }: FilterChipProps): import("react/jsx-runtime").JSX.Element;
5
+ export type FilterChipProps = ComponentProps<"div"> & {
6
+ entranceDelay?: number;
7
+ };
8
+ export declare function FilterChip({ className, ref, entranceDelay, ...props }: FilterChipProps): import("react/jsx-runtime").JSX.Element;
7
9
  export type FilterEditorTriggerProps = ComponentProps<typeof DropdownMenuTrigger>;
8
10
  export declare function FilterEditorTrigger({ className, asChild, ...props }: FilterEditorTriggerProps): import("react/jsx-runtime").JSX.Element;
9
11
  export type FilterEditorContentProps = ComponentProps<typeof DropdownMenuContent>;
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useEffect, useLayoutEffect, useImperativeHandle, useRef, useState } from "react";
4
+ import { useAnimate, useReducedMotion } from "motion/react";
4
5
  import { XIcon } from "lucide-react";
5
6
  import { Button } from "../customization.js";
6
7
  import { cn } from "../utils.js";
@@ -8,8 +9,28 @@ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMe
8
9
  export function FilterEditor(props) {
9
10
  return _jsx(DropdownMenu, { modal: false, ...props });
10
11
  }
11
- export function FilterChip({ className, ...props }) {
12
- return (_jsx("div", { "data-mendy-ui": "", "data-slot": "filter-chip", className: cn("mui-123306850fe1 mui-539ae144f076 mui-81b198a2db84 mui-d5111d0e9f48 mui-3fa8c572949b mui-71556df3b421 mui-d191cb569aac mui-c74ab393b96d mui-35f35c41d134 mui-5e49f8678d00 mui-3bc474178684 motion-safe:slide-in-from-bottom-2.5 mui-b225f0361d28 mui-bc94a66158a3", className), ...props }));
12
+ export function FilterChip({ className, ref, entranceDelay = 0, ...props }) {
13
+ const [scope, animate] = useAnimate();
14
+ const reducedMotion = useReducedMotion();
15
+ const initialDelay = useRef(entranceDelay);
16
+ useImperativeHandle(ref, () => scope.current);
17
+ useLayoutEffect(() => {
18
+ if (reducedMotion) {
19
+ scope.current.style.transform = "none";
20
+ scope.current.style.opacity = "1";
21
+ return;
22
+ }
23
+ const animation = animate(scope.current, { y: [10, 0], opacity: [0, 1] }, {
24
+ type: "spring",
25
+ stiffness: 100,
26
+ damping: 10,
27
+ mass: 1,
28
+ delay: initialDelay.current,
29
+ opacity: { duration: 0.2 },
30
+ });
31
+ return () => animation.stop();
32
+ }, [animate, scope, reducedMotion]);
33
+ return (_jsx("div", { "data-mendy-ui": "", ref: scope, "data-slot": "filter-chip", className: cn("mui-123306850fe1 mui-539ae144f076 mui-81b198a2db84 mui-d5111d0e9f48 mui-3fa8c572949b mui-71556df3b421 mui-d191cb569aac mui-c74ab393b96d mui-35f35c41d134", className), ...props }));
13
34
  }
14
35
  export function FilterEditorTrigger({ className, asChild, ...props }) {
15
36
  return (_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { asChild: asChild, variant: "ghost", "data-mendy-ui": "", "data-slot": "filter-editor-trigger", "aria-haspopup": "dialog", className: cn("mui-222f930b8752 mui-57c930dc1c31 mui-66b490dd6005 mui-52417259a4f5 mui-52101fc7d8bb mui-184ddc11e5f9 mui-71556df3b421 mui-70007b876865 mui-e7e01cc7f4df mui-e9347b78185f mui-37ad15a95595 mui-4d6720d47806 mui-2e357972fc10 mui-355efe22aedb mui-50aafa8694bb mui-db549d38ff19 mui-8d3c1014b967 mui-4600b77303d6 mui-4027d9c589b6 mui-6f73a280432e", className), ...props }) }));
@@ -0,0 +1,14 @@
1
+ import type { RefObject } from "react";
2
+ /** Position the menu against the search field and the available viewport. */
3
+ export declare function useMenuPlacement(anchor: RefObject<HTMLDivElement | null> | undefined, trigger: RefObject<HTMLButtonElement | null>, desktop: boolean): {
4
+ alignOffset: number;
5
+ side: "top" | "bottom";
6
+ };
7
+ /** Follow the selected row while keeping the editor inside the viewport. */
8
+ export declare function useEditorOffset({ detached, editorPanel, content, rows, selectedId, }: {
9
+ detached: boolean;
10
+ editorPanel: RefObject<HTMLDivElement | null>;
11
+ content: RefObject<HTMLDivElement | null>;
12
+ rows: RefObject<Map<string, HTMLButtonElement>>;
13
+ selectedId?: string;
14
+ }): number;
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ import { useLayoutEffect, useState } from "react";
3
+ /** Position the menu against the search field and the available viewport. */
4
+ export function useMenuPlacement(anchor, trigger, desktop) {
5
+ const [alignOffset, setAlignOffset] = useState(0);
6
+ const [side, setSide] = useState("bottom");
7
+ useLayoutEffect(() => {
8
+ const button = trigger.current;
9
+ if (!button)
10
+ return;
11
+ const update = () => {
12
+ const bounds = button.getBoundingClientRect();
13
+ const target = anchor?.current?.getBoundingClientRect();
14
+ const rtl = getComputedStyle(button).direction === "rtl";
15
+ const viewport = window.visualViewport;
16
+ const viewportTop = viewport?.offsetTop ?? 0;
17
+ const viewportBottom = viewportTop + (viewport?.height ?? window.innerHeight);
18
+ const below = viewportBottom - bounds.bottom - 23;
19
+ const above = bounds.top - viewportTop - 23;
20
+ const preferredHeight = 30 * parseFloat(getComputedStyle(document.documentElement).fontSize);
21
+ // A short list can fit below the trigger while its editor cannot. Choose room
22
+ // for the editor before Radix constrains its height to the current side.
23
+ setSide(below < preferredHeight && above > below ? "top" : "bottom");
24
+ // Desktop starts at the search field's edge; mobile stays close to the icon.
25
+ setAlignOffset(desktop
26
+ ? rtl
27
+ ? bounds.right - (target?.right ?? bounds.right)
28
+ : (target?.left ?? bounds.left) - bounds.left
29
+ : 0);
30
+ };
31
+ update();
32
+ const observer = new ResizeObserver(update);
33
+ observer.observe(anchor?.current ?? button);
34
+ window.addEventListener("resize", update);
35
+ window.addEventListener("scroll", update, true);
36
+ window.visualViewport?.addEventListener("resize", update);
37
+ window.visualViewport?.addEventListener("scroll", update);
38
+ return () => {
39
+ observer.disconnect();
40
+ window.removeEventListener("resize", update);
41
+ window.removeEventListener("scroll", update, true);
42
+ window.visualViewport?.removeEventListener("resize", update);
43
+ window.visualViewport?.removeEventListener("scroll", update);
44
+ };
45
+ }, [anchor, desktop, trigger]);
46
+ return { alignOffset, side };
47
+ }
48
+ /** Follow the selected row while keeping the editor inside the viewport. */
49
+ export function useEditorOffset({ detached, editorPanel, content, rows, selectedId, }) {
50
+ const [editorOffset, setEditorOffset] = useState(0);
51
+ useLayoutEffect(() => {
52
+ if (!detached)
53
+ return;
54
+ const panel = editorPanel.current;
55
+ const root = content.current;
56
+ if (!panel || !root)
57
+ return;
58
+ const update = () => {
59
+ const row = rows.current.get(selectedId ?? "");
60
+ if (!row)
61
+ return;
62
+ const available = parseFloat(getComputedStyle(root).getPropertyValue("--radix-dropdown-menu-content-available-height")) || window.innerHeight - 32;
63
+ const offset = row.getBoundingClientRect().top - root.getBoundingClientRect().top;
64
+ setEditorOffset(Math.max(0, Math.min(offset - 5, available - panel.getBoundingClientRect().height - 2)));
65
+ };
66
+ update();
67
+ const observer = new ResizeObserver(update);
68
+ observer.observe(panel);
69
+ observer.observe(root);
70
+ root.addEventListener("scroll", update, true);
71
+ return () => {
72
+ observer.disconnect();
73
+ root.removeEventListener("scroll", update, true);
74
+ };
75
+ }, [detached, selectedId, editorPanel, content, rows]);
76
+ return editorOffset;
77
+ }
package/dist/styles.css CHANGED
@@ -366,6 +366,9 @@
366
366
  .mui-074569488cca:where([data-mendy-ui], [data-mendy-ui] *) {
367
367
  gap: calc(var(--spacing) * 2);
368
368
  }
369
+ .mui-d7db3451d154:where([data-mendy-ui], [data-mendy-ui] *) {
370
+ gap: calc(var(--spacing) * 3);
371
+ }
369
372
  .mui-90fc3ec35a37:where([data-mendy-ui], [data-mendy-ui] *) {
370
373
  gap: calc(var(--spacing) * 4);
371
374
  }
@@ -391,6 +394,9 @@
391
394
  .mui-d5111d0e9f48:where([data-mendy-ui], [data-mendy-ui] *) {
392
395
  overflow: hidden;
393
396
  }
397
+ .mui-0fc84a692dad:where([data-mendy-ui], [data-mendy-ui] *) {
398
+ overflow: visible;
399
+ }
394
400
  .mui-3b82279f859f:where([data-mendy-ui], [data-mendy-ui] *) {
395
401
  overflow-x: hidden;
396
402
  }
@@ -412,6 +418,10 @@
412
418
  .mui-02e603944040:where([data-mendy-ui], [data-mendy-ui] *) {
413
419
  border-radius: max(0px, calc(var(--radius, 0.5rem) - 4px));
414
420
  }
421
+ .mui-a22ea0ba562a:where([data-mendy-ui], [data-mendy-ui] *) {
422
+ border-top-left-radius: 0;
423
+ border-top-right-radius: 0;
424
+ }
415
425
  .mui-10c0bae8cba9:where([data-mendy-ui], [data-mendy-ui] *) {
416
426
  border-top-left-radius: max(0px, calc(var(--radius, 0.5rem) - 2px));
417
427
  border-bottom-left-radius: max(0px, calc(var(--radius, 0.5rem) - 2px));
@@ -550,6 +560,9 @@
550
560
  .mui-0a702d97ed7d:where([data-mendy-ui], [data-mendy-ui] *) {
551
561
  padding-top: calc(var(--spacing) * 3);
552
562
  }
563
+ .mui-be02e79e6a27:where([data-mendy-ui], [data-mendy-ui] *) {
564
+ padding-top: calc(var(--spacing) * 3) !important;
565
+ }
553
566
  .mui-1a52da5bc88c:where([data-mendy-ui], [data-mendy-ui] *) {
554
567
  padding-right: calc(var(--spacing) * 1);
555
568
  }
@@ -661,6 +674,10 @@
661
674
  --mendy-tw-shadow: 0 4px 6px -1px var(--mendy-tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--mendy-tw-shadow-color, rgb(0 0 0 / 0.1));
662
675
  box-shadow: var(--mendy-tw-inset-shadow), var(--mendy-tw-inset-ring-shadow), var(--mendy-tw-ring-offset-shadow), var(--mendy-tw-ring-shadow), var(--mendy-tw-shadow);
663
676
  }
677
+ .mui-89c3006b2722:where([data-mendy-ui], [data-mendy-ui] *) {
678
+ --mendy-tw-shadow: 0 0 #0000;
679
+ box-shadow: var(--mendy-tw-inset-shadow), var(--mendy-tw-inset-ring-shadow), var(--mendy-tw-ring-offset-shadow), var(--mendy-tw-ring-shadow), var(--mendy-tw-shadow);
680
+ }
664
681
  .mui-7de07d413c13:where([data-mendy-ui], [data-mendy-ui] *) {
665
682
  --mendy-tw-shadow: 0 1px 2px 0 var(--mendy-tw-shadow-color, rgb(0 0 0 / 0.05));
666
683
  box-shadow: var(--mendy-tw-inset-shadow), var(--mendy-tw-inset-ring-shadow), var(--mendy-tw-ring-offset-shadow), var(--mendy-tw-ring-shadow), var(--mendy-tw-shadow);
@@ -712,8 +729,8 @@
712
729
  .mui-82c2977beb8e:where([data-mendy-ui], [data-mendy-ui] *) {
713
730
  --cell-size: min(2rem, calc((100vw - 3.5rem) / 7));
714
731
  }
715
- .mui-5662a94d9dba:where([data-mendy-ui], [data-mendy-ui] *) {
716
- --filter-menu-height: min(30rem, var(--radix-dropdown-menu-content-available-height,30rem));
732
+ .mui-971625533e56:where([data-mendy-ui], [data-mendy-ui] *) {
733
+ --filter-menu-height: min(var(--mendy-filter-menu-max-height,44rem), var(--radix-dropdown-menu-content-available-height,44rem));
717
734
  }
718
735
  .mui-cacb19aa9640:where([data-mendy-ui], [data-mendy-ui] *) {
719
736
  scrollbar-width: thin;
@@ -795,10 +812,29 @@
795
812
  color: var(--muted-foreground, #737373);
796
813
  }
797
814
  }
798
- .mui-4fa4c190281c:where([data-mendy-ui], [data-mendy-ui] *) {
799
- &:first-child {
815
+ .mui-a267680f9f49:where([data-mendy-ui], [data-mendy-ui] *) {
816
+ &::before {
817
+ content: var(--mendy-tw-content);
818
+ position: absolute;
819
+ }
820
+ }
821
+ .mui-e22234c7b474:where([data-mendy-ui], [data-mendy-ui] *) {
822
+ &::before {
823
+ content: var(--mendy-tw-content);
824
+ inset-inline: calc(var(--spacing) * 0);
825
+ }
826
+ }
827
+ .mui-4d24300d447b:where([data-mendy-ui], [data-mendy-ui] *) {
828
+ &::before {
829
+ content: var(--mendy-tw-content);
830
+ top: calc(var(--spacing) * 0);
831
+ }
832
+ }
833
+ .mui-37a30b916086:where([data-mendy-ui], [data-mendy-ui] *) {
834
+ &::before {
835
+ content: var(--mendy-tw-content);
800
836
  border-top-style: var(--mendy-tw-border-style);
801
- border-top-width: 0px;
837
+ border-top-width: 1px;
802
838
  }
803
839
  }
804
840
  .mui-37ad15a95595:where([data-mendy-ui], [data-mendy-ui] *) {
@@ -1234,29 +1270,6 @@
1234
1270
  }
1235
1271
  }
1236
1272
  }
1237
- .mui-5e49f8678d00:where([data-mendy-ui], [data-mendy-ui] *) {
1238
- @media (prefers-reduced-motion: no-preference) {
1239
- animation: mendy-enter var(--mendy-tw-animation-duration,var(--mendy-tw-duration,.15s))var(--mendy-tw-ease,ease)var(--mendy-tw-animation-delay,0s)var(--mendy-tw-animation-iteration-count,1)var(--mendy-tw-animation-direction,normal)var(--mendy-tw-animation-fill-mode,none);
1240
- }
1241
- }
1242
- .mui-b225f0361d28:where([data-mendy-ui], [data-mendy-ui] *) {
1243
- @media (prefers-reduced-motion: no-preference) {
1244
- --mendy-tw-duration: 200ms;
1245
- transition-duration: 200ms;
1246
- }
1247
- }
1248
- .mui-3bc474178684:where([data-mendy-ui], [data-mendy-ui] *) {
1249
- @media (prefers-reduced-motion: no-preference) {
1250
- --mendy-tw-enter-opacity: calc(0/100);
1251
- --mendy-tw-enter-opacity: 0;
1252
- }
1253
- }
1254
- .mui-bc94a66158a3:where([data-mendy-ui], [data-mendy-ui] *) {
1255
- @media (prefers-reduced-motion: no-preference) {
1256
- animation-fill-mode: backwards;
1257
- --mendy-tw-animation-fill-mode: backwards;
1258
- }
1259
- }
1260
1273
  .mui-237747164b60:where([data-mendy-ui], [data-mendy-ui] *) {
1261
1274
  @media (prefers-reduced-motion: reduce) {
1262
1275
  animation: none;
@@ -1839,8 +1852,9 @@
1839
1852
  syntax: "*";
1840
1853
  inherits: false;
1841
1854
  }
1842
- @property --mendy-tw-duration {
1855
+ @property --mendy-tw-content {
1843
1856
  syntax: "*";
1857
+ initial-value: "";
1844
1858
  inherits: false;
1845
1859
  }
1846
1860
  @keyframes mendy-pulse {
@@ -1906,7 +1920,7 @@
1906
1920
  --mendy-tw-drop-shadow-color: initial;
1907
1921
  --mendy-tw-drop-shadow-alpha: 100%;
1908
1922
  --mendy-tw-drop-shadow-size: initial;
1909
- --mendy-tw-duration: initial;
1923
+ --mendy-tw-content: "";
1910
1924
  --mendy-tw-animation-delay: 0s;
1911
1925
  --mendy-tw-animation-direction: normal;
1912
1926
  --mendy-tw-animation-duration: initial;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mendylanda/ui",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Mendy Landa’s reusable React components. Typed filters with dynamic options, URL persistence, and customizable UI.",
5
5
  "homepage": "https://ui.mendylanda.com",
6
6
  "license": "MIT",
@@ -68,6 +68,7 @@
68
68
  "clsx": "^2.1.1",
69
69
  "date-fns": "^4.4.0",
70
70
  "lucide-react": "^1.11.0",
71
+ "motion": "12.34.2",
71
72
  "radix-ui": "^1.4.3",
72
73
  "react-day-picker": "^10.0.1",
73
74
  "tailwind-merge": "^3.5.0"