@clicktap/ui 2.4.1 → 2.4.3

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.
@@ -1 +1 @@
1
- "use client";import{jsxs as l,Fragment as h,jsx as e}from"react/jsx-runtime";import{useMemo as b,Suspense as x,useState as v}from"react";import{Accordion as g,AccordionItem as w}from"../../Accordion/Accordion.js";import{Button as f}from"../../Button/Button.js";import"../../../utils/cn.js";import{ModalOverlay as N}from"../../ModalOverlay/ModalOverlay.js";import{Modal as y}from"../../Modal/Modal.js";import{Drawer as k}from"../../Drawer/Drawer.js";import{Cross as C}from"../../Icon/Cross.js";import{CrossSmall as A}from"../../Icon/CrossSmall.js";import{Search as D}from"../../Icon/Search.js";import{FilterRenderer as I}from"../FilterRenderer/FilterRenderer.js";import{mergeFilters as S}from"../../../utils/collection/mergeFilters.js";import{FilterAccordionTitle as j}from"../FilterAccordionTitle/FilterAccordionTitle.js";import{EmptyResults as L}from"../EmptyResults/EmptyResults.js";import"react-aria-components";import"../../Icon/DownArrow.js";import"framer-motion";import"../../../utils/env.js";import"../../../utils/swatch.js";import"../FilterRenderer/Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"../FilterRenderer/Renderer/useOptionFilterToggle.js";import"../FilterRenderer/Renderer/Swatch.js";import"../FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"../FilterRenderer/Renderer/Swatch/SwatchImage.js";import"../FilterRenderer/Renderer/Swatch/SwatchText.js";import"../FilterRenderer/Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"../FilterRenderer/Renderer/Range.js";function M(){return e("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:e("path",{d:"M4 6L8 10L12 6",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function V({value:a,onChange:r}){const[o,c]=v(a),m=()=>{r(o.trim())};return l("div",{className:"relative",children:[e("input",{type:"search",value:o,onChange:i=>c(i.target.value),onKeyDown:i=>{i.key==="Enter"&&(i.preventDefault(),m())},onBlur:m,placeholder:"Search products...","aria-label":"Search products",autoComplete:"off",className:"w-full h-9 pl-8 pr-8 rounded-md border border-border bg-surface text-sm text-fg placeholder:text-fg-subtle focus:outline-none focus:border-border-emphasis [&::-webkit-search-cancel-button]:hidden"}),o&&e("button",{type:"button",onClick:()=>{c(""),r("")},"aria-label":"Clear search",className:"absolute right-2 top-1/2 -translate-y-1/2 p-0.5 rounded-full hover:bg-surface-subtle transition-colors cursor-pointer",children:e(A,{})}),e("span",{className:"absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 [&>svg]:w-full [&>svg]:h-full text-fg-subtle",children:e(D,{})})]})}function ue({availableFilters:a,appliedFilters:r,onFilterChange:o,showMobileFilters:c,setShowMobileFilters:m,searchQuery:s,onSearchChange:n,totalItems:i}){const u=b(()=>S(r,a).filter(t=>t.includeInFilters!==!1),[r,a]),d=n&&e(V,{value:s??"",onChange:n},s),p=()=>u.length>0&&e(g,{selectionMode:"multiple",defaultSelectedKeys:"all",className:"pt-2 mt-3",showDivider:!1,itemClasses:{base:"py-1",title:"text-sm font-medium leading-7",trigger:"py-2 border-b border-border-subtle",indicator:'data-[open="true"]:rotate-180',content:"pt-2 pb-3"},children:u.map(t=>e(w,{textValue:t.label,title:e(j,{filter:t,appliedFilters:r,onFilterChange:o}),disableIndicatorAnimation:!0,indicator:M,children:e("div",{className:"pt-2",children:e(I,{filter:t,onFilterChange:o,appliedFilters:r,variant:"sidebar"})})},t.code))});return l(h,{children:[l("div",{className:"hidden lg:flex lg:flex-col lg:basis-[16.5rem] lg:shrink-0",children:[d,p(),r.length>0&&e("button",{type:"button",className:"text-sm text-fg-muted hover:text-fg transition-colors mt-4 mb-8 cursor-pointer",onClick:()=>o([]),children:"Clear all filters"})]}),e("div",{className:"lg:hidden",children:e(x,{children:e(N,{isOpen:c,onOpenChange:t=>m(t),isDismissable:!0,children:e(y,{children:e(k,{size:"30rem",direction:"left","aria-label":"Filters",className:"p-5",children:({close:t})=>l("div",{className:"flex flex-col w-full h-full overflow-hidden",children:[l("div",{className:"flex items-center w-full justify-between",children:[e("span",{className:"text-xl font-bold text-fg",children:"Filters"}),e(f,{variant:"ghost","aria-label":"Close",onPress:()=>t(),className:"justify-start size-auto p-0",children:e(C,{})})]}),l("div",{className:"flex flex-col flex-1 overflow-x-hidden overflow-y-auto [scrollbar-width:none] [-webkit-overflow-scrolling:touch] [&::-webkit-scrollbar]:hidden",children:[d&&e("div",{className:"mt-3",children:d}),p(),i===0&&e(L,{searchQuery:s,hasFilters:r.length>0,onClearFilters:()=>o([]),onClearSearch:s&&n?()=>n(""):void 0})]}),l("div",{className:"shrink-0 pt-3 flex flex-col gap-3 border-t border-border-subtle",children:[r.length>0&&e("button",{type:"button",className:"text-sm py-2 text-fg-muted hover:text-fg transition-colors cursor-pointer",onClick:()=>o([]),children:"Clear all"}),e(f,{className:"w-full py-6 text-base rounded-xl",variant:"primary",onPress:()=>t(),children:"View Results"})]})]})})})})})})]})}export{ue as CollectionFilters};
1
+ "use client";import{jsxs as l,Fragment as h,jsx as e}from"react/jsx-runtime";import{useMemo as b,Suspense as x,useState as v}from"react";import{Accordion as g,AccordionItem as w}from"../../Accordion/Accordion.js";import{Button as f}from"../../Button/Button.js";import"../../../utils/cn.js";import{ModalOverlay as N}from"../../ModalOverlay/ModalOverlay.js";import{Modal as y}from"../../Modal/Modal.js";import{Drawer as k}from"../../Drawer/Drawer.js";import{Cross as C}from"../../Icon/Cross.js";import{CrossSmall as A}from"../../Icon/CrossSmall.js";import{Search as D}from"../../Icon/Search.js";import{FilterRenderer as I}from"../FilterRenderer/FilterRenderer.js";import{mergeFilters as S}from"../../../utils/collection/mergeFilters.js";import{FilterAccordionTitle as j}from"../FilterAccordionTitle/FilterAccordionTitle.js";import{EmptyResults as L}from"../EmptyResults/EmptyResults.js";import"react-aria-components";import"../../Icon/DownArrow.js";import"framer-motion";import"../../../utils/frameDrivenOnUpdate.js";import"../../../utils/env.js";import"../../../utils/swatch.js";import"../FilterRenderer/Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"../FilterRenderer/Renderer/useOptionFilterToggle.js";import"../FilterRenderer/Renderer/Swatch.js";import"../FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"../FilterRenderer/Renderer/Swatch/SwatchImage.js";import"../FilterRenderer/Renderer/Swatch/SwatchText.js";import"../FilterRenderer/Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"../FilterRenderer/Renderer/Range.js";function M(){return e("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:e("path",{d:"M4 6L8 10L12 6",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function V({value:a,onChange:r}){const[o,c]=v(a),m=()=>{r(o.trim())};return l("div",{className:"relative",children:[e("input",{type:"search",value:o,onChange:i=>c(i.target.value),onKeyDown:i=>{i.key==="Enter"&&(i.preventDefault(),m())},onBlur:m,placeholder:"Search products...","aria-label":"Search products",autoComplete:"off",className:"w-full h-9 pl-8 pr-8 rounded-md border border-border bg-surface text-sm text-fg placeholder:text-fg-subtle focus:outline-none focus:border-border-emphasis [&::-webkit-search-cancel-button]:hidden"}),o&&e("button",{type:"button",onClick:()=>{c(""),r("")},"aria-label":"Clear search",className:"absolute right-2 top-1/2 -translate-y-1/2 p-0.5 rounded-full hover:bg-surface-subtle transition-colors cursor-pointer",children:e(A,{})}),e("span",{className:"absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 [&>svg]:w-full [&>svg]:h-full text-fg-subtle",children:e(D,{})})]})}function pe({availableFilters:a,appliedFilters:r,onFilterChange:o,showMobileFilters:c,setShowMobileFilters:m,searchQuery:s,onSearchChange:n,totalItems:i}){const u=b(()=>S(r,a).filter(t=>t.includeInFilters!==!1),[r,a]),d=n&&e(V,{value:s??"",onChange:n},s),p=()=>u.length>0&&e(g,{selectionMode:"multiple",defaultSelectedKeys:"all",className:"pt-2 mt-3",showDivider:!1,itemClasses:{base:"py-1",title:"text-sm font-medium leading-7",trigger:"py-2 border-b border-border-subtle",indicator:'data-[open="true"]:rotate-180',content:"pt-2 pb-3"},children:u.map(t=>e(w,{textValue:t.label,title:e(j,{filter:t,appliedFilters:r,onFilterChange:o}),disableIndicatorAnimation:!0,indicator:M,children:e("div",{className:"pt-2",children:e(I,{filter:t,onFilterChange:o,appliedFilters:r,variant:"sidebar"})})},t.code))});return l(h,{children:[l("div",{className:"hidden lg:flex lg:flex-col lg:basis-[16.5rem] lg:shrink-0",children:[d,p(),r.length>0&&e("button",{type:"button",className:"text-sm text-fg-muted hover:text-fg transition-colors mt-4 mb-8 cursor-pointer",onClick:()=>o([]),children:"Clear all filters"})]}),e("div",{className:"lg:hidden",children:e(x,{children:e(N,{isOpen:c,onOpenChange:t=>m(t),isDismissable:!0,children:e(y,{children:e(k,{size:"30rem",direction:"left","aria-label":"Filters",className:"p-5",children:({close:t})=>l("div",{className:"flex flex-col w-full h-full overflow-hidden",children:[l("div",{className:"flex items-center w-full justify-between",children:[e("span",{className:"text-xl font-bold text-fg",children:"Filters"}),e(f,{variant:"ghost","aria-label":"Close",onPress:()=>t(),className:"justify-start size-auto p-0",children:e(C,{})})]}),l("div",{className:"flex flex-col flex-1 overflow-x-hidden overflow-y-auto [scrollbar-width:none] [-webkit-overflow-scrolling:touch] [&::-webkit-scrollbar]:hidden",children:[d&&e("div",{className:"mt-3",children:d}),p(),i===0&&e(L,{searchQuery:s,hasFilters:r.length>0,onClearFilters:()=>o([]),onClearSearch:s&&n?()=>n(""):void 0})]}),l("div",{className:"shrink-0 pt-3 flex flex-col gap-3 border-t border-border-subtle",children:[r.length>0&&e("button",{type:"button",className:"text-sm py-2 text-fg-muted hover:text-fg transition-colors cursor-pointer",onClick:()=>o([]),children:"Clear all"}),e(f,{className:"w-full py-6 text-base rounded-xl",variant:"primary",onPress:()=>t(),children:"View Results"})]})]})})})})})})]})}export{pe as CollectionFilters};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{CollectionFilters as J}from"./CollectionFilters.js";import"react/jsx-runtime";import"react";import"../../Accordion/Accordion.js";import"react-aria-components";import"../../../utils/cn.js";import"../../Icon/DownArrow.js";import"../../Button/Button.js";import"../../ModalOverlay/ModalOverlay.js";import"framer-motion";import"../../Modal/Modal.js";import"../../Drawer/Drawer.js";import"../../Icon/Cross.js";import"../../Icon/CrossSmall.js";import"../../Icon/Search.js";import"../FilterRenderer/FilterRenderer.js";import"../../../utils/env.js";import"../../../utils/swatch.js";import"../FilterRenderer/Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"../FilterRenderer/Renderer/useOptionFilterToggle.js";import"../FilterRenderer/Renderer/Swatch.js";import"../FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"../FilterRenderer/Renderer/Swatch/SwatchImage.js";import"../FilterRenderer/Renderer/Swatch/SwatchText.js";import"../FilterRenderer/Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"../FilterRenderer/Renderer/Range.js";import"../../../utils/collection/mergeFilters.js";import"../FilterAccordionTitle/FilterAccordionTitle.js";import"../EmptyResults/EmptyResults.js";export{J as CollectionFilters};
2
+ import{CollectionFilters as K}from"./CollectionFilters.js";import"react/jsx-runtime";import"react";import"../../Accordion/Accordion.js";import"react-aria-components";import"../../../utils/cn.js";import"../../Icon/DownArrow.js";import"../../Button/Button.js";import"../../ModalOverlay/ModalOverlay.js";import"framer-motion";import"../../../utils/frameDrivenOnUpdate.js";import"../../Modal/Modal.js";import"../../Drawer/Drawer.js";import"../../Icon/Cross.js";import"../../Icon/CrossSmall.js";import"../../Icon/Search.js";import"../FilterRenderer/FilterRenderer.js";import"../../../utils/env.js";import"../../../utils/swatch.js";import"../FilterRenderer/Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"../FilterRenderer/Renderer/useOptionFilterToggle.js";import"../FilterRenderer/Renderer/Swatch.js";import"../FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"../FilterRenderer/Renderer/Swatch/SwatchImage.js";import"../FilterRenderer/Renderer/Swatch/SwatchText.js";import"../FilterRenderer/Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"../FilterRenderer/Renderer/Range.js";import"../../../utils/collection/mergeFilters.js";import"../FilterAccordionTitle/FilterAccordionTitle.js";import"../EmptyResults/EmptyResults.js";export{K as CollectionFilters};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{AttributeDataProvider as Q,useAttributeData as U}from"./AttributeDataProvider/AttributeDataProvider.js";import{EmptyResults as W}from"./EmptyResults/EmptyResults.js";import{ProductPrice as Y}from"./ProductPrice/ProductPrice.js";import{FilterRenderer as _}from"./FilterRenderer/FilterRenderer.js";import{CollectionFilterChips as rr}from"./CollectionFilterChips/CollectionFilterChips.js";import{FilterAccordionTitle as tr}from"./FilterAccordionTitle/FilterAccordionTitle.js";import{ProductCardSwatches as pr}from"./ProductCardSwatches/ProductCardSwatches.js";import{CollectionFilters as er}from"./CollectionFilters/CollectionFilters.js";import{CollectionToolbar as cr}from"./CollectionToolbar/CollectionToolbar.js";import"react/jsx-runtime";import"react";import"../../utils/env.js";import"../../utils/swatch.js";import"./FilterRenderer/Renderer/Option.js";import"../Checkbox/Checkbox.js";import"react-aria-components";import"../../utils/cn.js";import"./FilterRenderer/Renderer/useOptionFilterToggle.js";import"./FilterRenderer/Renderer/Swatch.js";import"./FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../Icon/CheckIcon.js";import"../../utils/color.js";import"./FilterRenderer/Renderer/Swatch/SwatchImage.js";import"./FilterRenderer/Renderer/Swatch/SwatchText.js";import"./FilterRenderer/Renderer/Boolean.js";import"../RadioGroup/RadioGroup.js";import"../Radio/Radio.js";import"./FilterRenderer/Renderer/Range.js";import"../Button/Button.js";import"../Icon/CrossSmall.js";import"../../utils/collection/getAppliedFilterChips.js";import"../../utils/collection/removeFilterChip.js";import"../Accordion/Accordion.js";import"../Icon/DownArrow.js";import"../ModalOverlay/ModalOverlay.js";import"framer-motion";import"../Modal/Modal.js";import"../Drawer/Drawer.js";import"../Icon/Cross.js";import"../Icon/Search.js";import"../../utils/collection/mergeFilters.js";import"../Select/Option.js";import"react-stately";import"../Select/Select.js";import"../Select/ButtonSelect.js";import"react-aria";import"@react-aria/interactions";import"../Select/parts.js";import"../Select/ComboBoxSelect.js";import"../Loader/Pulse.js";import"../Select/MultiButtonSelect.js";import"../Icon/FilterIcon.js";export{Q as AttributeDataProvider,rr as CollectionFilterChips,er as CollectionFilters,cr as CollectionToolbar,W as EmptyResults,tr as FilterAccordionTitle,_ as FilterRenderer,pr as ProductCardSwatches,Y as ProductPrice,U as useAttributeData};
2
+ import{AttributeDataProvider as U,useAttributeData as V}from"./AttributeDataProvider/AttributeDataProvider.js";import{EmptyResults as X}from"./EmptyResults/EmptyResults.js";import{ProductPrice as Z}from"./ProductPrice/ProductPrice.js";import{FilterRenderer as $}from"./FilterRenderer/FilterRenderer.js";import{CollectionFilterChips as or}from"./CollectionFilterChips/CollectionFilterChips.js";import{FilterAccordionTitle as ir}from"./FilterAccordionTitle/FilterAccordionTitle.js";import{ProductCardSwatches as mr}from"./ProductCardSwatches/ProductCardSwatches.js";import{CollectionFilters as lr}from"./CollectionFilters/CollectionFilters.js";import{CollectionToolbar as fr}from"./CollectionToolbar/CollectionToolbar.js";import"react/jsx-runtime";import"react";import"../../utils/env.js";import"../../utils/swatch.js";import"./FilterRenderer/Renderer/Option.js";import"../Checkbox/Checkbox.js";import"react-aria-components";import"../../utils/cn.js";import"./FilterRenderer/Renderer/useOptionFilterToggle.js";import"./FilterRenderer/Renderer/Swatch.js";import"./FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../Icon/CheckIcon.js";import"../../utils/color.js";import"./FilterRenderer/Renderer/Swatch/SwatchImage.js";import"./FilterRenderer/Renderer/Swatch/SwatchText.js";import"./FilterRenderer/Renderer/Boolean.js";import"../RadioGroup/RadioGroup.js";import"../Radio/Radio.js";import"./FilterRenderer/Renderer/Range.js";import"../Button/Button.js";import"../Icon/CrossSmall.js";import"../../utils/collection/getAppliedFilterChips.js";import"../../utils/collection/removeFilterChip.js";import"../Accordion/Accordion.js";import"../Icon/DownArrow.js";import"../ModalOverlay/ModalOverlay.js";import"framer-motion";import"../../utils/frameDrivenOnUpdate.js";import"../Modal/Modal.js";import"../Drawer/Drawer.js";import"../Icon/Cross.js";import"../Icon/Search.js";import"../../utils/collection/mergeFilters.js";import"../Select/Option.js";import"react-stately";import"../Select/Select.js";import"../Select/ButtonSelect.js";import"react-aria";import"@react-aria/interactions";import"../Select/parts.js";import"../Select/ComboBoxSelect.js";import"../Loader/Pulse.js";import"../Select/MultiButtonSelect.js";import"../Icon/FilterIcon.js";export{U as AttributeDataProvider,or as CollectionFilterChips,lr as CollectionFilters,fr as CollectionToolbar,X as EmptyResults,ir as FilterAccordionTitle,$ as FilterRenderer,mr as ProductCardSwatches,Z as ProductPrice,V as useAttributeData};
@@ -1,3 +1,3 @@
1
1
  import { DialogProps } from './Dialog.types';
2
- export declare function Dialog({ className, children, animationVariants, ...props }: DialogProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function Dialog({ className, children, animationVariants, onUpdate, ...props }: DialogProps): import("react/jsx-runtime").JSX.Element;
3
3
  export default Dialog;
@@ -1 +1 @@
1
- "use client";import{jsx as e}from"react/jsx-runtime";import{forwardRef as f}from"react";import{Dialog as m}from"react-aria-components";import{motion as u}from"framer-motion";import{cn as c}from"../../utils/cn.js";const s=f(({style:o,animationVariants:a,...t},i)=>{const r=typeof o=="function"?o(t):o;return e(m,{...t,ref:i,style:r})});let n=null;function d(){return typeof window>"u"?null:(n||(n=u.create(s)),n)}const l=d();function y({className:o,children:a,animationVariants:t,...i}){const r={className:c("p-8 outline-0 max-w-max w-screen absolute top-2/4 left-2/4","-translate-x-1/2 -translate-y-1/2","max-h-[calc(100dvh-2rem)] overflow-y-auto","bg-surface text-fg","shadow-[0_8px_24px_rgba(0,0,0,0.1)]","rounded-lg border border-solid border-border-emphasis",o),...i};return l?e(l,{...r,variants:t||{hidden:{opacity:0},visible:{opacity:1}},children:a}):e(s,{...r,children:a})}export{y as Dialog,y as default};
1
+ "use client";import{jsx as i}from"react/jsx-runtime";import{forwardRef as s}from"react";import{Dialog as d}from"react-aria-components";import{motion as u}from"framer-motion";import{cn as c}from"../../utils/cn.js";import{frameDrivenOnUpdate as p}from"../../utils/frameDrivenOnUpdate.js";const l=s(({style:o,animationVariants:r,...t},a)=>{const e=typeof o=="function"?o(t):o;return i(d,{...t,ref:a,style:e})});let n=null;function g(){return typeof window>"u"?null:(n||(n=u.create(l)),n)}const f=g();function h({className:o,children:r,animationVariants:t,onUpdate:a,...e}){const m={className:c("p-8 outline-0 max-w-max w-screen absolute top-2/4 left-2/4","-translate-x-1/2 -translate-y-1/2","max-h-[calc(100dvh-2rem)] overflow-y-auto","bg-surface text-fg","shadow-[0_8px_24px_rgba(0,0,0,0.1)]","rounded-lg border border-solid border-border-emphasis",o),...e};return f?i(f,{...m,variants:t||{hidden:{opacity:0},visible:{opacity:1}},onUpdate:p(a),children:r}):i(l,{...m,children:r})}export{h as Dialog,h as default};
@@ -1,8 +1,10 @@
1
1
  import { Key } from 'react';
2
2
  import { DialogProps as UIDialogProps } from 'react-aria-components';
3
- import { MotionStyle, Variants } from 'framer-motion';
3
+ import { MotionProps, MotionStyle, Variants } from 'framer-motion';
4
4
  export type DialogProps = Omit<UIDialogProps, 'onAnimationStart'> & {
5
5
  key?: Key;
6
6
  style?: MotionStyle;
7
7
  animationVariants?: Variants;
8
+ /** Called with the animating values every frame of the fade. */
9
+ onUpdate?: MotionProps['onUpdate'];
8
10
  };
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{Dialog as e}from"./Dialog.js";import"react/jsx-runtime";import"react";import"react-aria-components";import"framer-motion";import"../../utils/cn.js";export{e as Dialog};
2
+ import{Dialog as f}from"./Dialog.js";import"react/jsx-runtime";import"react";import"react-aria-components";import"framer-motion";import"../../utils/cn.js";import"../../utils/frameDrivenOnUpdate.js";export{f as Dialog};
@@ -1 +1 @@
1
- "use client";import{jsx as a}from"react/jsx-runtime";import{forwardRef as N,useContext as M,useState as k,useEffect as A,useId as R,useCallback as O}from"react";import{ModalOverlay as E,OverlayTriggerStateContext as w}from"react-aria-components";import{motion as T,AnimatePresence as F}from"framer-motion";import{cn as j}from"../../utils/cn.js";const q="body > [inert]:not(#__next):not([data-overlay-container])";function z(e){return Object.keys(e).some(t=>t.startsWith("__react"))}function p(){document.querySelectorAll(q).forEach(e=>e.removeAttribute("inert"))}function L(e){return!!(e.closest("[data-overlay-container]")||e.closest("#__next"))}const W=N(({style:e,children:t,...i},n)=>{const r=typeof e=="function"?e(i):e;return a(E,{...i,ref:n,style:r,children:t})});let m=null;function $(){return typeof window>"u"?null:(m||(m=T.create(W)),m)}const h=$();function g({animate:e,animation:t,setAnimation:i,className:n,animationVariants:r,isDismissable:u=!0,children:v,...x}){const C=R();A(()=>{p();const o=new MutationObserver(l=>{for(const d of l)if(d.type==="attributes"&&d.attributeName==="inert"){const f=d.target;f.parentElement===document.body?p():z(f)||f.removeAttribute("inert")}});return o.observe(document.body,{attributes:!0,attributeFilter:["inert"],subtree:!0}),()=>o.disconnect()},[]);const{key:S,shouldCloseOnInteractOutside:c,onOpenChange:s,..._}=x,P=O(o=>c?c(o):L(o),[c]),y=M(w),I=O(o=>{!u||o.target!==o.currentTarget||(s?s(!1):y?.close())},[u,s,y]),b={isDismissable:u,shouldCloseOnInteractOutside:P,onOpenChange:s,onPointerDown:I,className:j("bg-black/30 backdrop-blur-sm","fixed top-0 left-0","z-[1000]","w-screen h-[var(--visual-viewport-height)]",n),..._};return h?a(h,{isExiting:t==="hidden",onAnimationComplete:o=>{i(l=>o==="hidden"&&l==="hidden"?"unmounted":l)},variants:r||{hidden:{opacity:0,transition:{delay:.08}},visible:{opacity:1}},initial:"hidden",animate:e,exit:"hidden",...b,children:v},S||C):a(E,{...b,children:v})}function B(e){const t=e?.isOpen??!1,[i,n]=k(t?"visible":"unmounted");return A(()=>{n(t?"visible":r=>r==="visible"?"hidden":r)},[t]),[i,n]}function Q(e){const{isOpen:t}=e,i=M(w),[n,r]=B(i);return t!==void 0?a(F,{children:t&&a(g,{...e,animate:"visible",animation:n,setAnimation:r})}):n==="unmounted"?null:a(g,{...e,animate:n,animation:n,setAnimation:r})}export{Q as ModalOverlay,Q as default,B as useOverlayAnimationState};
1
+ "use client";import{jsx as a}from"react/jsx-runtime";import{forwardRef as k,useContext as M,useState as R,useEffect as A,useId as T,useCallback as b}from"react";import{ModalOverlay as E,OverlayTriggerStateContext as w}from"react-aria-components";import{motion as F,AnimatePresence as U}from"framer-motion";import{cn as j}from"../../utils/cn.js";import{frameDrivenOnUpdate as q}from"../../utils/frameDrivenOnUpdate.js";const z="body > [inert]:not(#__next):not([data-overlay-container])";function D(e){return Object.keys(e).some(t=>t.startsWith("__react"))}function O(){document.querySelectorAll(z).forEach(e=>e.removeAttribute("inert"))}function L(e){return!!(e.closest("[data-overlay-container]")||e.closest("#__next"))}const W=k(({style:e,children:t,...i},n)=>{const r=typeof e=="function"?e(i):e;return a(E,{...i,ref:n,style:r,children:t})});let m=null;function $(){return typeof window>"u"?null:(m||(m=F.create(W)),m)}const h=$();function g({animate:e,animation:t,setAnimation:i,className:n,animationVariants:r,isDismissable:u=!0,children:v,...x}){const C=T();A(()=>{O();const o=new MutationObserver(l=>{for(const d of l)if(d.type==="attributes"&&d.attributeName==="inert"){const f=d.target;f.parentElement===document.body?O():D(f)||f.removeAttribute("inert")}});return o.observe(document.body,{attributes:!0,attributeFilter:["inert"],subtree:!0}),()=>o.disconnect()},[]);const{key:S,shouldCloseOnInteractOutside:c,onOpenChange:s,onUpdate:_,...P}=x,I=b(o=>c?c(o):L(o),[c]),y=M(w),N=b(o=>{!u||o.target!==o.currentTarget||(s?s(!1):y?.close())},[u,s,y]),p={isDismissable:u,shouldCloseOnInteractOutside:I,onOpenChange:s,onPointerDown:N,className:j("bg-black/30 backdrop-blur-sm","fixed top-0 left-0","z-[1000]","w-screen h-[var(--visual-viewport-height)]",n),...P};return h?a(h,{isExiting:t==="hidden",onAnimationComplete:o=>{i(l=>o==="hidden"&&l==="hidden"?"unmounted":l)},variants:r||{hidden:{opacity:0,transition:{delay:.08}},visible:{opacity:1}},initial:"hidden",animate:e,exit:"hidden",...p,onUpdate:q(_),children:v},S||C):a(E,{...p,children:v})}function B(e){const t=e?.isOpen??!1,[i,n]=R(t?"visible":"unmounted");return A(()=>{n(t?"visible":r=>r==="visible"?"hidden":r)},[t]),[i,n]}function X(e){const{isOpen:t}=e,i=M(w),[n,r]=B(i);return t!==void 0?a(U,{children:t&&a(g,{...e,animate:"visible",animation:n,setAnimation:r})}):n==="unmounted"?null:a(g,{...e,animate:n,animation:n,setAnimation:r})}export{X as ModalOverlay,X as default,B as useOverlayAnimationState};
@@ -1,6 +1,6 @@
1
1
  import { Key, ReactNode } from 'react';
2
2
  import { ModalOverlayProps as UiModalOverlayProps, ModalRenderProps } from 'react-aria-components';
3
- import { MotionStyle, Variant } from 'framer-motion';
3
+ import { MotionProps, MotionStyle, Variant } from 'framer-motion';
4
4
  export interface ModalOverlayProps extends Omit<UiModalOverlayProps, 'children' | 'style' | 'onAnimationStart'> {
5
5
  key?: Key;
6
6
  style?: MotionStyle;
@@ -8,6 +8,8 @@ export interface ModalOverlayProps extends Omit<UiModalOverlayProps, 'children'
8
8
  visible: Variant;
9
9
  hidden: Variant;
10
10
  };
11
+ /** Called with the animating values every frame of the fade. */
12
+ onUpdate?: MotionProps['onUpdate'];
11
13
  children: ReactNode | ((values: ModalRenderProps & {
12
14
  defaultChildren: ReactNode;
13
15
  }) => ReactNode);
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{ModalOverlay as e}from"./ModalOverlay.js";import"react/jsx-runtime";import"react";import"react-aria-components";import"framer-motion";import"../../utils/cn.js";export{e as ModalOverlay};
2
+ import{ModalOverlay as l}from"./ModalOverlay.js";import"react/jsx-runtime";import"react";import"react-aria-components";import"framer-motion";import"../../utils/cn.js";import"../../utils/frameDrivenOnUpdate.js";export{l as ModalOverlay};
@@ -0,0 +1,30 @@
1
+ # BUGFIX: `ModalOverlay` and `Dialog` no longer blink at the end of each fade
2
+
3
+ **Date:** 2026-09-30
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** `ModalOverlay`, `Dialog`, and the new `utils/frameDrivenOnUpdate`
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No. Both components gain an optional `onUpdate` prop.
8
+ **Ticket:** CTAP-2075
9
+
10
+ ## What changed
11
+
12
+ Every modal blinked for one frame when its fade finished. On open the backdrop and dialog dropped
13
+ back to opacity 0 for a frame; on close the dialog flashed back to 1 and the backdrop to full dark
14
+ before unmounting.
15
+
16
+ framer-motion 11 runs `opacity` as a native browser (WAAPI) animation. When it finishes, framer sets
17
+ the final value, which it writes to the style on its next frame, and cancels the animation at once,
18
+ which removes the animation's fill. For that frame the element shows its stale inline opacity.
19
+
20
+ Both motion elements now always pass an `onUpdate`. framer never uses WAAPI for an element with
21
+ one, so the fade runs frame by frame in JavaScript and the gap is gone. Custom `animationVariants`
22
+ still apply, and the exit still completes and unmounts.
23
+
24
+ A consumer's own `onUpdate` still runs: both components accept it as a prop and call it every
25
+ frame.
26
+
27
+ ## Migrate
28
+
29
+ Nothing beyond taking the release. A fork with its own copy of these components adds
30
+ `onUpdate={frameDrivenOnUpdate(onUpdate)}` to each motion element.
@@ -1,4 +1,14 @@
1
1
  [
2
+ {
3
+ "path": "2026/09/ui-2026-09-30-modal-fade-no-final-frame-blink.md",
4
+ "module": "libs/frontend-modules/ui",
5
+ "package": "@clicktap/ui",
6
+ "date": "2026-09-30",
7
+ "title": "BUGFIX: `ModalOverlay` and `Dialog` no longer blink at the end of each fade",
8
+ "classification": "BUGFIX",
9
+ "breaking": "No. Both components gain an optional `onUpdate` prop.",
10
+ "affects": "`ModalOverlay`, `Dialog`, and the new `utils/frameDrivenOnUpdate`"
11
+ },
2
12
  {
3
13
  "path": "2026/09/ui-2026-09-24-semantic-color-token-lint-rule.md",
4
14
  "module": "libs/frontend-modules/ui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clicktap/ui",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "private": false,
5
5
  "author": "Clicktap",
6
6
  "description": "A library of React UI components and low-level hooks.",
@@ -0,0 +1,18 @@
1
+ import { MotionProps } from 'framer-motion';
2
+ type OnUpdate = NonNullable<MotionProps['onUpdate']>;
3
+ /**
4
+ * An `onUpdate` for a motion element that must not animate through WAAPI (CTAP-2075).
5
+ *
6
+ * framer-motion 11 runs `opacity` as a native (WAAPI) animation. Its finish handler sets the final
7
+ * value, which framer writes to the style on its NEXT frame, and then cancels the animation at once,
8
+ * removing the WAAPI fill. For that one frame the element shows its stale inline opacity: a modal
9
+ * blinks back to 0 at the end of its fade in, and to 1 at the end of its fade out.
10
+ *
11
+ * framer never uses WAAPI for an element that has an `onUpdate` prop, so the fade runs frame by frame
12
+ * in JS instead. This always returns one, calling the consumer's `onUpdate` when there is one.
13
+ *
14
+ * It looks removable. It is not, until framer-motion writes the final value before cancelling (check
15
+ * `AcceleratedAnimation`'s `onfinish` when upgrading to 12.x).
16
+ */
17
+ export declare function frameDrivenOnUpdate(onUpdate?: OnUpdate): OnUpdate;
18
+ export {};
@@ -0,0 +1 @@
1
+ function r(e){return n=>e?.(n)}export{r as frameDrivenOnUpdate};