@clicktap/ui 2.4.2 → 2.4.4
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/components/Collection/CollectionFilters/CollectionFilters.js +1 -1
- package/components/Collection/CollectionFilters/index.js +1 -1
- package/components/Collection/index.js +1 -1
- package/components/Dialog/Dialog.d.ts +1 -1
- package/components/Dialog/Dialog.js +1 -1
- package/components/Dialog/Dialog.types.d.ts +3 -1
- package/components/Dialog/index.js +1 -1
- package/components/Icon/UserIcon.js +1 -1
- package/components/ModalOverlay/ModalOverlay.d.ts +1 -1
- package/components/ModalOverlay/ModalOverlay.js +1 -1
- package/components/ModalOverlay/ModalOverlay.types.d.ts +3 -1
- package/components/ModalOverlay/index.js +1 -1
- package/docs/migrations/2026/06/ui-2026-06-17-select-standardization.md +1 -1
- package/docs/migrations/2026/07/ui-2026-07-31-ui-exports-bundler-resolution.md +2 -2
- package/docs/migrations/2026/08/ui-2026-08-07-ui-peer-contract-form-graphql-optional-peers.md +1 -1
- package/docs/migrations/2026/08/ui-2026-08-11-password-reveal-toggle.md +2 -2
- package/docs/migrations/2026/08/ui-2026-08-11-peer-ranges-capped.md +1 -1
- package/docs/migrations/2026/08/ui-2026-08-24-accordion-author-key-space.md +6 -6
- package/docs/migrations/2026/09/ui-2026-09-16-modal-overlay-detached-motion-element.md +3 -3
- package/docs/migrations/2026/09/ui-2026-09-18-pin-input-autofill-and-controlled-value.md +2 -2
- package/docs/migrations/2026/09/ui-2026-09-30-modal-fade-no-final-frame-blink.md +30 -0
- package/docs/migrations/2026/10/ui-2026-10-02-user-icon-drops-project-color-class.md +25 -0
- package/docs/migrations/index.json +23 -3
- package/package.json +1 -1
- package/utils/frameDrivenOnUpdate.d.ts +18 -0
- package/utils/frameDrivenOnUpdate.js +1 -0
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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{jsxs as
|
|
1
|
+
"use client";import{jsxs as t,jsx as e}from"react/jsx-runtime";import{cn as n}from"../../utils/cn.js";function c({className:o,size:r=16}){return t("svg",{width:r,height:r,viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",className:n("flex-shrink-0",o),stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e("path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"}),e("circle",{cx:"12",cy:"7",r:"4"})]})}export{c as UserIcon,c as default};
|
|
@@ -13,7 +13,7 @@ import { ModalOverlayProps } from './ModalOverlay.types';
|
|
|
13
13
|
* @clicktap/ui copies installed (ordinary pnpm version skew) the trigger wrote `visible` into one
|
|
14
14
|
* copy while the overlay read the other's `unmounted` default — `animate="unmounted"` names no
|
|
15
15
|
* variant, framer-motion holds `initial: "hidden"`, and every trigger-driven modal sat mounted at
|
|
16
|
-
* `opacity: 0` with zero errors (CTAP-1210,
|
|
16
|
+
* `opacity: 0` with zero errors (CTAP-1210, found by a consumer). react-aria's context is the safe dependency:
|
|
17
17
|
* if react-aria-components itself is duplicated, everything breaks loudly.
|
|
18
18
|
*
|
|
19
19
|
* Exported for unit tests only.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as a}from"react/jsx-runtime";import{forwardRef as
|
|
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
|
|
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};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Date:** 2026-06-17
|
|
4
4
|
**Package:** `@clicktap/ui`
|
|
5
|
-
**Affects:** Any app consuming `@clicktap/ui`'s `Select` / `DropdownSelect` (
|
|
5
|
+
**Affects:** Any app consuming `@clicktap/ui`'s `Select` / `DropdownSelect` (downstream projects and any fork that renders pickers, sort controls, country/region selects, or configurable-product options).
|
|
6
6
|
**Classification:** FEATURE + BREAKING
|
|
7
7
|
**Breaking:** Yes — (1) `DropdownSelect` removed, (2) the bare `<Select>` is now a **button picker**, not a combobox, (3) `DropdownSelect`'s `classNames.trigger` is now `classNames.input`, (4) the root is `w-full` by default, (5) `<Option>` is react-stately's `Item` — keyed by `key` (no `id`) and **cannot be wrapped**.
|
|
8
8
|
**Formerly named:** `2026-06-17-select-standardization.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# `@clicktap/ui` resolves its deep subpaths under `moduleResolution: bundler` (CTAP-1060)
|
|
2
2
|
|
|
3
3
|
**Date:** 2026-07-31
|
|
4
|
-
**Revised:** 2026-08-08 — the test-runner half (CTAP-1195): every runtime subpath entry gains a `default` condition, so CJS resolvers (Jest) stop falling through to the `.d.ts`. See "Test runners (Jest)" below; reported by
|
|
4
|
+
**Revised:** 2026-08-08 — the test-runner half (CTAP-1195): every runtime subpath entry gains a `default` condition, so CJS resolvers (Jest) stop falling through to the `.d.ts`. See "Test runners (Jest)" below; reported by a consumer.
|
|
5
5
|
**Package:** `@clicktap/ui`
|
|
6
6
|
**Breaking:** No — additive to the `exports` map. Existing `moduleResolution: node` / `node10`
|
|
7
7
|
consumers are unaffected (that resolver ignores `exports`); this only *adds* working resolution under
|
|
@@ -50,7 +50,7 @@ emits only `.d.ts` for it).
|
|
|
50
50
|
The original map declared only `types` + `import` conditions. That covers bundlers — but **Node CJS
|
|
51
51
|
resolution** (what Jest uses without ESM flags) matches neither, so it fell through to the `.d.ts`
|
|
52
52
|
and died on `export interface`. Any consumer whose Jest suite touches `useCooldown` (i.e. anyone
|
|
53
|
-
adopting the CTAP-1114 `useResendCode` pattern *with* its tests) hit this;
|
|
53
|
+
adopting the CTAP-1114 `useResendCode` pattern *with* its tests) hit this; one consumer worked around it with
|
|
54
54
|
a `moduleNameMapper` straight to the `.js` files.
|
|
55
55
|
|
|
56
56
|
Since this revision every runtime subpath entry carries a **`default` condition (last)** pointing at
|
package/docs/migrations/2026/08/ui-2026-08-07-ui-peer-contract-form-graphql-optional-peers.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
CTAP-1158 validated every publishable lib manifest against its real imports. For `@clicktap/ui`
|
|
13
13
|
that audit changed the peer contract and corrected a wrong dependency pin, but no guide shipped
|
|
14
14
|
with it — consumers would meet the changes as unexplained manifest churn on the next release
|
|
15
|
-
(reported by
|
|
15
|
+
(reported by a consumer, 2026-08-07). This is that guide.
|
|
16
16
|
|
|
17
17
|
## What changed
|
|
18
18
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Date:** 2026-08-11
|
|
4
4
|
**Revised:** 2026-08-11 — consumer action now says to rename a component named for the press-and-hold
|
|
5
|
-
gesture (
|
|
5
|
+
gesture (for example a `HoldToRevealButton`), not only to swap its handlers.
|
|
6
6
|
**Package:** `@clicktap/ui`
|
|
7
7
|
**Affects:** `@clicktap/ui` — `Input` with `type="password"`.
|
|
8
8
|
**Classification:** IMPROVEMENT (UX / accessibility)
|
|
@@ -69,7 +69,7 @@ Shipped in `@clicktap/ui` **0.43.6**.
|
|
|
69
69
|
and add `aria-pressed` + the state-dependent `aria-label` while you are in there. The reference
|
|
70
70
|
frontend also updated its one bespoke reveal (`UpdateEmailForm`) to match.
|
|
71
71
|
- **Rename the component if its name describes the gesture.** If you named it for press-and-hold
|
|
72
|
-
when you applied CTAP-1128 —
|
|
72
|
+
when you applied CTAP-1128 — one consumer had `HoldToRevealButton` (2026-08-11) — the name now
|
|
73
73
|
documents behaviour the code no longer implements, in the one place nobody re-reads. Rename it
|
|
74
74
|
(`PasswordRevealButton`, `ShowPasswordToggle`) in the same change as the handler swap, and grep
|
|
75
75
|
for the old name in tests and stories.
|
|
@@ -57,7 +57,7 @@ rather than as an error.
|
|
|
57
57
|
### When the duplicate is transitive — nothing of yours to pin
|
|
58
58
|
|
|
59
59
|
The paragraph above assumes the second copy comes from a declaration you own. Often it does not:
|
|
60
|
-
|
|
60
|
+
one consumer resolves `react-stately` 3.49.0 cleanly across every workspace member and still carries a
|
|
61
61
|
second copy at 3.46.0, arriving via `@adobe/react-spectrum` → `react-aria-components`. No manifest of
|
|
62
62
|
theirs names it, so there is no pin to correct.
|
|
63
63
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
**Package:** `@clicktap/ui`
|
|
5
5
|
**Affects:** every controlled or default-expanded `Accordion` — checkout payment method selection, checkout's `defaultExpandedKeys={['cart']}`, totals/discount panel, PDP mobile overview, filter groups.
|
|
6
6
|
**Classification:** BUGFIX
|
|
7
|
-
**Breaking:** No — unless you shipped a workaround that pre-prefixes keys (see
|
|
7
|
+
**Breaking:** No — unless you shipped a workaround that pre-prefixes keys (see the shim note below).
|
|
8
8
|
**Ticket:** CTAP-1404
|
|
9
9
|
|
|
10
10
|
## The defect (2.0.x regression from the CTAP-1383 react-aria rewrite)
|
|
@@ -17,11 +17,11 @@ were only `String()`ed, so:
|
|
|
17
17
|
1. `selectedKeys` / `defaultSelectedKeys` / `defaultExpandedKeys` never matched
|
|
18
18
|
any item — nothing opened, nothing highlighted. The checkout payment step's
|
|
19
19
|
method selection "not working" was this.
|
|
20
|
-
2. `onSelectionChange` handed the PREFIXED id back out —
|
|
20
|
+
2. `onSelectionChange` handed the PREFIXED id back out — a consumer's checkout
|
|
21
21
|
dispatched `".$CYBERSOURCE"` as a payment method code to the backend.
|
|
22
22
|
|
|
23
23
|
The heroui accordion this replaced handled keys correctly. Diagnosed by the
|
|
24
|
-
|
|
24
|
+
consumer's session against the installed 2.0.6 bundle.
|
|
25
25
|
|
|
26
26
|
## The fix
|
|
27
27
|
|
|
@@ -36,13 +36,13 @@ round-trips with no output translation. `"all"` and unkeyed children (positional
|
|
|
36
36
|
|
|
37
37
|
1. Take the release; no call-site changes needed — controlled accordions start
|
|
38
38
|
working as written.
|
|
39
|
-
2. **Delete compensating shims.**
|
|
40
|
-
wrapper
|
|
39
|
+
2. **Delete compensating shims.** A consumer-side `.../Accordion.tsx`
|
|
40
|
+
wrapper that pre-prefixes keys must be REMOVED when taking this
|
|
41
41
|
release — it translates keys into the prefixed space, which no longer
|
|
42
42
|
exists, so keeping it reintroduces the bug in the other direction. Check the
|
|
43
43
|
shim's own escape handling is not needed elsewhere (this fix also covers
|
|
44
44
|
escaped keys, which a bare `.$`-strip shim does not).
|
|
45
|
-
3. If any code path stored or transmitted a prefixed id (`.$…`) —
|
|
45
|
+
3. If any code path stored or transmitted a prefixed id (`.$…`) — one consumer saw
|
|
46
46
|
them reach the backend as payment codes — check persisted data for the
|
|
47
47
|
`.$` prefix.
|
|
48
48
|
|
|
@@ -28,7 +28,7 @@ observed:
|
|
|
28
28
|
keyframe resolvers synchronously on the detached element.
|
|
29
29
|
|
|
30
30
|
🚨 Thrown inside framer-motion's frame loop, so nothing reaches server logs and no React error
|
|
31
|
-
boundary catches it.
|
|
31
|
+
boundary catches it. A consumer hit it in its warranty admin: a controlled drawer (route 1) containing a
|
|
32
32
|
never-opened trigger-driven Page Builder overlay threw **13 errors on every close**, and in
|
|
33
33
|
production that broke the frame loop — the close animation and the success toast were both lost.
|
|
34
34
|
Every other trigger-driven overlay in that app (delete confirms, edit modals) carried the same
|
|
@@ -49,8 +49,8 @@ already gets from `AnimatePresence` is now the same on both paths:
|
|
|
49
49
|
|
|
50
50
|
## Consumer impact
|
|
51
51
|
|
|
52
|
-
- **Nothing to change.** Take the release. If you gated the overlay yourself in a wrapper (
|
|
53
|
-
tried it in
|
|
52
|
+
- **Nothing to change.** Take the release. If you gated the overlay yourself in a wrapper (one
|
|
53
|
+
consumer tried it in its own UI library), **remove that gate**: mounting only once already open, with the hook
|
|
54
54
|
initialized to `visible`, gives framer-motion no transition to play and leaves the backdrop stuck
|
|
55
55
|
at `opacity: 0`. The fix has to live where the animation state is derived.
|
|
56
56
|
- ⚠️ Do not read the mount as a signal. Nothing should depend on a closed trigger-driven overlay
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
**Affects:** `src/components/PinInput/PinInput.tsx`, `PinInput.types.ts`. Every consumer of `<PinInput>` (signup, forgot-password and email/password-change verification on the storefront).
|
|
6
6
|
**Classification:** BUGFIX (three) + IMPROVEMENT (`value`/`name` honored)
|
|
7
7
|
**Breaking:** No API change. **Behavioral:** passing `value` now controls the boxes (it was silently ignored); `maxLength` is no longer rendered; `autoComplete="one-time-code"` is on the first box only.
|
|
8
|
-
**Ticket:** CTAP-1749
|
|
8
|
+
**Ticket:** CTAP-1749
|
|
9
9
|
|
|
10
10
|
## What was wrong
|
|
11
11
|
|
|
12
|
-
Four things, all in this component (a consumer wrapper
|
|
12
|
+
Four things, all in this component (a consumer wrapper in a consumer's UI library only restyles it):
|
|
13
13
|
|
|
14
14
|
1. **Android autofill and Gboard's clipboard chip lost five of six digits.** The paste handler split
|
|
15
15
|
digits across the boxes correctly, but autofill and the keyboard chip insert text as an *input*
|
|
@@ -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.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# `UserIcon` no longer carries a project's color class
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-10-02
|
|
4
|
+
**Package:** `@clicktap/ui`
|
|
5
|
+
**Affects:** `components/Icon/UserIcon`
|
|
6
|
+
**Classification:** IMPROVEMENT
|
|
7
|
+
**Breaking:** No for any app that does not define the removed color token. One that does loses that color on the icon until it passes the class itself.
|
|
8
|
+
**Ticket:** CTAP-2094
|
|
9
|
+
|
|
10
|
+
## What changed
|
|
11
|
+
|
|
12
|
+
`UserIcon` hard-coded a `text-*-gray-500` class for a color token one consuming project defines in its
|
|
13
|
+
own theme. The platform defines no such token, so the class did nothing here, and it named a project in
|
|
14
|
+
the shared library. The icon now carries only `flex-shrink-0` and inherits its color like the other
|
|
15
|
+
icons, so a consumer sets the color it wants through `className`.
|
|
16
|
+
|
|
17
|
+
## Migrate
|
|
18
|
+
|
|
19
|
+
If your theme defined that token (or any color you relied on for this icon), pass it:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<UserIcon className="text-brand-gray-500" />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Nothing else.
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"path": "2026/10/ui-2026-10-02-user-icon-drops-project-color-class.md",
|
|
4
|
+
"module": "libs/frontend-modules/ui",
|
|
5
|
+
"package": "@clicktap/ui",
|
|
6
|
+
"date": "2026-10-02",
|
|
7
|
+
"title": "`UserIcon` no longer carries a project's color class",
|
|
8
|
+
"classification": "IMPROVEMENT",
|
|
9
|
+
"breaking": "No for any app that does not define the removed color token. One that does loses that color on the icon until it passes the class itself.",
|
|
10
|
+
"affects": "`components/Icon/UserIcon`"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"path": "2026/09/ui-2026-09-30-modal-fade-no-final-frame-blink.md",
|
|
14
|
+
"module": "libs/frontend-modules/ui",
|
|
15
|
+
"package": "@clicktap/ui",
|
|
16
|
+
"date": "2026-09-30",
|
|
17
|
+
"title": "BUGFIX: `ModalOverlay` and `Dialog` no longer blink at the end of each fade",
|
|
18
|
+
"classification": "BUGFIX",
|
|
19
|
+
"breaking": "No. Both components gain an optional `onUpdate` prop.",
|
|
20
|
+
"affects": "`ModalOverlay`, `Dialog`, and the new `utils/frameDrivenOnUpdate`"
|
|
21
|
+
},
|
|
2
22
|
{
|
|
3
23
|
"path": "2026/09/ui-2026-09-24-semantic-color-token-lint-rule.md",
|
|
4
24
|
"module": "libs/frontend-modules/ui",
|
|
@@ -146,7 +166,7 @@
|
|
|
146
166
|
"date": "2026-08-24",
|
|
147
167
|
"title": "Accordion controlled selection matches authored keys again",
|
|
148
168
|
"classification": "BUGFIX",
|
|
149
|
-
"breaking": "No — unless you shipped a workaround that pre-prefixes keys (see
|
|
169
|
+
"breaking": "No — unless you shipped a workaround that pre-prefixes keys (see the shim note below).",
|
|
150
170
|
"affects": "every controlled or default-expanded `Accordion` — checkout payment method selection, checkout's `defaultExpandedKeys={['cart']}`, totals/discount panel, PDP mobile overview, filter groups."
|
|
151
171
|
},
|
|
152
172
|
{
|
|
@@ -383,7 +403,7 @@
|
|
|
383
403
|
"breaking": "No — additive to the `exports` map. Existing `moduleResolution: node` / `node10`",
|
|
384
404
|
"affects": "",
|
|
385
405
|
"revised": "2026-08-08",
|
|
386
|
-
"revised_note": "2026-08-08 — the test-runner half (CTAP-1195): every runtime subpath entry gains a `default` condition, so CJS resolvers (Jest) stop falling through to the `.d.ts`. See \"Test runners (Jest)\" below; reported by
|
|
406
|
+
"revised_note": "2026-08-08 — the test-runner half (CTAP-1195): every runtime subpath entry gains a `default` condition, so CJS resolvers (Jest) stop falling through to the `.d.ts`. See \"Test runners (Jest)\" below; reported by a consumer."
|
|
387
407
|
},
|
|
388
408
|
{
|
|
389
409
|
"path": "2026/06/ui-2026-06-18-select-hiddenselect-setvalue-crash.md",
|
|
@@ -403,7 +423,7 @@
|
|
|
403
423
|
"title": "Select standardization — one `<Select searchable>`, `DropdownSelect` removed",
|
|
404
424
|
"classification": "FEATURE + BREAKING",
|
|
405
425
|
"breaking": "Yes — (1) `DropdownSelect` removed, (2) the bare `<Select>` is now a **button picker**, not a combobox, (3) `DropdownSelect`'s `classNames.trigger` is now `classNames.input`, (4) the root is `w-full` by default, (5) `<Option>` is react-stately's `Item` — keyed by `key` (no `id`) and **cannot be wrapped**.",
|
|
406
|
-
"affects": "Any app consuming `@clicktap/ui`'s `Select` / `DropdownSelect` (
|
|
426
|
+
"affects": "Any app consuming `@clicktap/ui`'s `Select` / `DropdownSelect` (downstream projects and any fork that renders pickers, sort controls, country/region selects, or configurable-product options)."
|
|
407
427
|
},
|
|
408
428
|
{
|
|
409
429
|
"path": "2026/05/ui-2026-05-29-password-check-requirements-variant.md",
|
package/package.json
CHANGED
|
@@ -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};
|