@clicktap/ui 2.3.0 → 2.3.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.
@@ -1 +1 @@
1
- "use client";import{jsx as f}from"react/jsx-runtime";import{forwardRef as p}from"react";import{Button as c}from"react-aria-components";import{cn as g}from"../../utils/cn.js";function h(t){switch(t){case"primary":return"solid";case"secondary":return"outline";case"tertiary":return"ghost";default:return t}}const w=p(function({className:s,isDisabled:n=!1,isLoading:e=!1,size:o="md",variant:a="solid",...d},b){const r=h(a),u=["flex items-center justify-center","rounded-md border-solid border","font-semibold text-sm","py-2 px-8","transition-all duration-200 ease-in-out","focus:outline-2 focus:outline focus:outline-border-subtle pressed:scale-95",o==="sm"&&"h-8",o==="md"&&"h-10",o==="lg"&&"h-12",r==="ghost"&&["bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent","border-transparent hover:border-transparent focus:border-transparent disabled:border-transparent","text-fg disabled:text-fg-subtle",e&&"disabled:text-fg"],r==="link"&&["bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent","border-transparent hover:border-transparent focus:border-transparent disabled:border-transparent","inline-flex h-auto p-0 rounded-none","underline underline-offset-2","text-fg disabled:text-fg-muted hover:opacity-70",e&&"disabled:text-fg"],r==="outline"&&["bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent","border-border hover:border-border-emphasis focus:border-border-emphasis disabled:border-border-subtle","text-fg disabled:text-fg-muted",e&&["disabled:border-border","disabled:text-fg"]],r==="solid"&&["bg-surface-inverse-hover hover:bg-surface-inverse focus:bg-surface-inverse disabled:bg-surface-inverse","border-surface-inverse-hover hover:border-fg focus:border-fg disabled:border-border-subtle","text-white disabled:text-fg-subtle",e&&["disabled:border-fg","disabled:text-white"]]];return f(c,{ref:b,type:"button",isDisabled:n,className:i=>{const l=typeof s=="function"?s(i):s;return g(u,l)},...d})});export{w as Button,w as default,h as resolveVariant};
1
+ "use client";import{jsx as f}from"react/jsx-runtime";import{forwardRef as p}from"react";import{Button as c}from"react-aria-components";import{cn as g}from"../../utils/cn.js";function h(t){switch(t){case"primary":return"solid";case"secondary":return"outline";case"tertiary":return"ghost";default:return t}}const w=p(function({className:s,isDisabled:n=!1,isLoading:e=!1,size:o="md",variant:d="solid",...a},b){const r=h(d),u=["flex items-center justify-center","rounded-md border-solid border","font-semibold text-sm","py-2 px-8","transition-all duration-200 ease-in-out","cursor-pointer disabled:cursor-not-allowed","focus:outline-2 focus:outline focus:outline-border-subtle pressed:scale-95",o==="sm"&&"h-8",o==="md"&&"h-10",o==="lg"&&"h-12",r==="ghost"&&["bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent","border-transparent hover:border-transparent focus:border-transparent disabled:border-transparent","text-fg disabled:text-fg-subtle",e&&"disabled:text-fg"],r==="link"&&["bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent","border-transparent hover:border-transparent focus:border-transparent disabled:border-transparent","inline-flex h-auto p-0 rounded-none","underline underline-offset-2","text-fg disabled:text-fg-muted hover:opacity-70",e&&"disabled:text-fg"],r==="outline"&&["bg-transparent hover:bg-transparent focus:bg-transparent disabled:bg-transparent","border-border hover:border-border-emphasis focus:border-border-emphasis disabled:border-border-subtle","text-fg disabled:text-fg-muted",e&&["disabled:border-border","disabled:text-fg"]],r==="solid"&&["bg-surface-inverse-hover hover:bg-surface-inverse focus:bg-surface-inverse disabled:bg-surface-inverse","border-surface-inverse-hover hover:border-fg focus:border-fg disabled:border-border-subtle","text-white disabled:text-fg-subtle",e&&["disabled:border-fg","disabled:text-white"]]];return f(c,{ref:b,type:"button",isDisabled:n,className:i=>{const l=typeof s=="function"?s(i):s;return g(u,l)},...a})});export{w as Button,w as default,h as resolveVariant};
@@ -1 +1 @@
1
- "use client";import{jsxs as o,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 y}from"../../ModalOverlay/ModalOverlay.js";import{Modal as N}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[l,c]=v(a),m=()=>{r(l.trim())};return o("div",{className:"relative",children:[e("input",{type:"search",value:l,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-white text-sm text-fg placeholder:text-fg-subtle focus:outline-none focus:border-border-emphasis [&::-webkit-search-cancel-button]:hidden"}),l&&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",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:l,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-gray-200",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:l}),disableIndicatorAnimation:!0,indicator:M,children:e("div",{className:"pt-2",children:e(I,{filter:t,onFilterChange:l,appliedFilters:r,variant:"sidebar"})})},t.code))});return o(h,{children:[o("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-slate-700 transition-colors mt-4 mb-8",onClick:()=>l([]),children:"Clear all filters"})]}),e("div",{className:"lg:hidden",children:e(x,{children:e(y,{isOpen:c,onOpenChange:t=>m(t),isDismissable:!0,children:e(N,{children:e(k,{size:"30rem",direction:"left","aria-label":"Filters",className:"p-5",children:({close:t})=>o("div",{className:"flex flex-col w-full h-full overflow-hidden",children:[o("div",{className:"flex items-center w-full justify-between",children:[e("span",{className:"text-xl font-bold text-slate-800",children:"Filters"}),e(f,{variant:"ghost","aria-label":"Close",onPress:()=>t(),className:"justify-start size-auto p-0",children:e(C,{})})]}),o("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:()=>l([]),onClearSearch:s&&n?()=>n(""):void 0})]}),o("div",{className:"shrink-0 pt-3 flex flex-col gap-3 border-t border-gray-200",children:[r.length>0&&e("button",{type:"button",className:"text-sm py-2 text-fg-muted hover:text-slate-700 transition-colors",onClick:()=>l([]),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 y}from"../../ModalOverlay/ModalOverlay.js";import{Modal as N}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-white 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-gray-200",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-slate-700 transition-colors mt-4 mb-8 cursor-pointer",onClick:()=>o([]),children:"Clear all filters"})]}),e("div",{className:"lg:hidden",children:e(x,{children:e(y,{isOpen:c,onOpenChange:t=>m(t),isDismissable:!0,children:e(N,{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-slate-800",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-gray-200",children:[r.length>0&&e("button",{type:"button",className:"text-sm py-2 text-fg-muted hover:text-slate-700 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 +1 @@
1
- "use client";import{jsxs as o,jsx as t}from"react/jsx-runtime";function m({searchQuery:e,hasFilters:n,onClearFilters:s,onClearSearch:l}){const i=n&&s||e&&l;return o("div",{className:"flex flex-col items-center justify-center py-16 px-4 text-center col-span-full",children:[o("svg",{xmlns:"http://www.w3.org/2000/svg",width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",className:"text-slate-300 mb-4",children:[t("circle",{cx:"11",cy:"11",r:"8"}),t("path",{d:"m21 21-4.3-4.3"})]}),t("h3",{className:"text-lg font-semibold text-slate-800 mb-2",children:"No results found"}),e&&o("p",{className:"text-sm text-fg-muted mb-4",children:["We couldn't find anything matching “",e,"”"]}),!e&&!n&&t("p",{className:"text-sm text-fg-muted mb-4",children:"No products match your current selection."}),i&&o("div",{className:"flex items-center gap-3",children:[e&&l&&t("button",{type:"button",onClick:l,className:"text-sm font-medium text-slate-700 hover:text-fg underline underline-offset-2 transition-colors",children:"Clear search"}),n&&s&&e&&l&&t("span",{className:"text-slate-300",children:"|"}),n&&s&&t("button",{type:"button",onClick:s,className:"text-sm font-medium text-slate-700 hover:text-fg underline underline-offset-2 transition-colors",children:"Clear all filters"})]}),!i&&t("p",{className:"text-xs text-fg-subtle",children:"Try a different search term or browse our categories."})]})}export{m as EmptyResults};
1
+ "use client";import{jsxs as l,jsx as t}from"react/jsx-runtime";function r({searchQuery:e,hasFilters:n,onClearFilters:s,onClearSearch:o}){const i=n&&s||e&&o;return l("div",{className:"flex flex-col items-center justify-center py-16 px-4 text-center col-span-full",children:[l("svg",{xmlns:"http://www.w3.org/2000/svg",width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",className:"text-slate-300 mb-4",children:[t("circle",{cx:"11",cy:"11",r:"8"}),t("path",{d:"m21 21-4.3-4.3"})]}),t("h3",{className:"text-lg font-semibold text-slate-800 mb-2",children:"No results found"}),e&&l("p",{className:"text-sm text-fg-muted mb-4",children:["We couldn't find anything matching “",e,"”"]}),!e&&!n&&t("p",{className:"text-sm text-fg-muted mb-4",children:"No products match your current selection."}),i&&l("div",{className:"flex items-center gap-3",children:[e&&o&&t("button",{type:"button",onClick:o,className:"text-sm font-medium text-slate-700 hover:text-fg underline underline-offset-2 transition-colors cursor-pointer",children:"Clear search"}),n&&s&&e&&o&&t("span",{className:"text-slate-300",children:"|"}),n&&s&&t("button",{type:"button",onClick:s,className:"text-sm font-medium text-slate-700 hover:text-fg underline underline-offset-2 transition-colors cursor-pointer",children:"Clear all filters"})]}),!i&&t("p",{className:"text-xs text-fg-subtle",children:"Try a different search term or browse our categories."})]})}export{r as EmptyResults};
@@ -1 +1 @@
1
- "use client";import{jsx as e}from"react/jsx-runtime";import{forwardRef as m}from"react";import{Dialog as f}from"react-aria-components";import{motion as d}from"framer-motion";import{cn as u}from"../../utils/cn.js";const s=m(({style:o,animationVariants:i,...t},n)=>{const r=typeof o=="function"?o(t):o;return e(f,{...t,ref:n,style:r})});let a=null;function c(){return typeof window>"u"?null:(a||(a=d.create(s)),a)}const l=c();function D({className:o,children:i,animationVariants:t,...n}){const r={className:u("p-8 outline-0 max-w-max w-screen absolute top-2/4 left-2/4","-translate-x-1/2 -translate-y-1/2","shadow-[0_8px_24px_rgba(0,0,0,0.1)] rounded-lg bg-white border border-solid border-border-emphasis",o),...n};return l?e(l,{...r,variants:t||{hidden:{opacity:0},visible:{opacity:1}},children:i}):e(s,{...r,children:i})}export{D as Dialog,D as default};
1
+ "use client";import{jsx as e}from"react/jsx-runtime";import{forwardRef as s}from"react";import{Dialog as f}from"react-aria-components";import{motion as d}from"framer-motion";import{cn as u}from"../../utils/cn.js";const m=s(({style:o,animationVariants:a,...t},i)=>{const r=typeof o=="function"?o(t):o;return e(f,{...t,ref:i,style:r})});let n=null;function c(){return typeof window>"u"?null:(n||(n=d.create(m)),n)}const l=c();function y({className:o,children:a,animationVariants:t,...i}){const r={className:u("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","shadow-[0_8px_24px_rgba(0,0,0,0.1)] rounded-lg bg-white border border-solid border-border-emphasis",o),...i};return l?e(l,{...r,variants:t||{hidden:{opacity:0},visible:{opacity:1}},children:a}):e(m,{...r,children:a})}export{y as Dialog,y as default};
@@ -1,3 +1,3 @@
1
1
  import { DrawerProps } from './Drawer.types';
2
- export declare function Drawer({ direction, children, className, animationVariants, ...props }: DrawerProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function Drawer({ direction, children, className, animationVariants, fullScreen, ...props }: DrawerProps): import("react/jsx-runtime").JSX.Element;
3
3
  export default Drawer;
@@ -1 +1 @@
1
- "use client";import{jsx as n}from"react/jsx-runtime";import{forwardRef as u}from"react";import{Dialog as f}from"react-aria-components";import{m as b}from"framer-motion";import{cn as m}from"../../utils/cn.js";const p={top:{hidden:{y:"-100%",transition:{ease:"easeIn"}},visible:{y:0,transition:{ease:"easeOut"}}},bottom:{hidden:{y:"100%",transition:{ease:"easeIn"}},visible:{y:0,transition:{ease:"easeOut"}}},right:{hidden:{x:"100%",transition:{ease:"easeIn"}},visible:{x:0,transition:{ease:"easeOut"}}},left:{hidden:{x:"-100%",transition:{ease:"easeIn"}},visible:{x:0,transition:{ease:"easeOut"}}}},l=u(({style:e,size:t="20rem",...o},r)=>{const a=typeof e=="function"?e(o):{...e,"--drawer-size":`${t}`};return n(f,{...o,ref:r,style:a})});let i=null;function w(){return typeof window>"u"?null:(i||(i=b.create(l)),i)}const d=w();function c({direction:e="right",children:t,className:o,animationVariants:r,...a}){const s={className:m("fixed p-8 bg-white","outline-none focus:outline-none focus-visible:outline-none","border-solid border-border-subtle","max-w-full max-h-[var(--visual-viewport-height)]",e==="top"&&"top-0 left-0 right-0 bottom-auto border-b shadow-[0_8px_24px_rgba(0,0,0,0.1)] h-[var(--drawer-size)]",e==="right"&&"top-0 left-auto bottom-0 right-0 border-l shadow-[-8px_0_24px_rgba(0,0,0,0.1)] w-[var(--drawer-size)]",e==="bottom"&&"bottom-0 left-0 right-0 top-auto border-t shadow-[0_-8px_24px_rgba(0,0,0,0.1)] h-[var(--drawer-size)]",e==="left"&&"top-0 bottom-0 left-0 right-auto border-r shadow-[8px_0_24px_rgba(0,0,0,0.1)] w-[var(--drawer-size)]",o),"data-direction":e,...a};return d?n(d,{variants:r||p[e],...s,children:t}):n(l,{...s,children:t})}export{c as Drawer,c as default};
1
+ "use client";import{jsx as n}from"react/jsx-runtime";import{forwardRef as m}from"react";import{Dialog as b}from"react-aria-components";import{m as d}from"framer-motion";import{cn as h}from"../../utils/cn.js";const p={top:{hidden:{y:"-100%",transition:{ease:"easeIn"}},visible:{y:0,transition:{ease:"easeOut"}}},bottom:{hidden:{y:"100%",transition:{ease:"easeIn"}},visible:{y:0,transition:{ease:"easeOut"}}},right:{hidden:{x:"100%",transition:{ease:"easeIn"}},visible:{x:0,transition:{ease:"easeOut"}}},left:{hidden:{x:"-100%",transition:{ease:"easeIn"}},visible:{x:0,transition:{ease:"easeOut"}}}},u=m(({style:t,size:e="20rem",...o},a)=>{const r=typeof t=="function"?t(o):{...t,"--drawer-size":`${e}`};return n(b,{...o,ref:a,style:r})});let i=null;function w(){return typeof window>"u"?null:(i||(i=d.create(u)),i)}const l=w();function y({direction:t="right",children:e,className:o,animationVariants:a,fullScreen:r=!1,...f}){const s={className:h("fixed p-8 bg-white","outline-none focus:outline-none focus-visible:outline-none","border-solid border-border-subtle",(t==="top"||t==="bottom")&&(r?"max-h-[var(--visual-viewport-height)]":"max-h-[min(var(--visual-viewport-height),88vh)]"),(t==="left"||t==="right")&&(r?"max-w-full":"max-w-[88vw]"),t==="top"&&"top-0 left-0 right-0 bottom-auto border-b shadow-[0_8px_24px_rgba(0,0,0,0.1)] h-[var(--drawer-size)]",t==="right"&&"top-0 left-auto bottom-0 right-0 border-l shadow-[-8px_0_24px_rgba(0,0,0,0.1)] w-[var(--drawer-size)]",t==="bottom"&&"bottom-0 left-0 right-0 top-auto border-t shadow-[0_-8px_24px_rgba(0,0,0,0.1)] h-[var(--drawer-size)]",t==="left"&&"top-0 bottom-0 left-0 right-auto border-r shadow-[8px_0_24px_rgba(0,0,0,0.1)] w-[var(--drawer-size)]",o),"data-direction":t,...f};return l?n(l,{variants:a||p[t],...s,children:e}):n(u,{...s,children:e})}export{y as Drawer,y as default};
@@ -7,4 +7,11 @@ export type DrawerProps = Omit<DialogProps, 'onAnimationStart'> & {
7
7
  style?: MotionStyle;
8
8
  animationVariants?: Variants;
9
9
  size?: string;
10
+ /**
11
+ * Opts out of the viewport clamp (CTAP-1877) so `size` can legitimately fill
12
+ * the screen. Without this, a `size` wider/taller than the viewport is capped
13
+ * at 88vw/88vh so a sliver of the page stays visible and tappable — full
14
+ * screen must be chosen with this prop, not arrived at by an oversized size.
15
+ */
16
+ fullScreen?: boolean;
10
17
  };
@@ -1 +1 @@
1
- "use client";import{jsxs as m,jsx as t}from"react/jsx-runtime";import{cn as a}from"../../utils/cn.js";function x({cooldownSeconds:e,isResending:n,canResend:s,onResend:d,prompt:i="Didn't get a code?",label:l="Resend code",resendingLabel:r="Sending…",availableAnnouncement:o="You can request a new code now.",className:c,buttonClassName:u}){const b=e>0?`Resend in ${e}s`:n?r:l;return m("p",{className:a("text-sm",c),children:[i," ",t("button",{type:"button",onClick:d,disabled:!s,className:a("underline disabled:no-underline disabled:opacity-50 disabled:cursor-not-allowed",u),children:b}),t("span",{"aria-live":"polite",className:"sr-only",children:e===0&&!n?o:""})]})}export{x as ResendCode,x as default};
1
+ "use client";import{jsxs as b,jsx as t}from"react/jsx-runtime";import{cn as s}from"../../utils/cn.js";function x({cooldownSeconds:e,isResending:n,canResend:a,onResend:i,prompt:r="Didn't get a code?",label:d="Resend code",resendingLabel:l="Sending…",availableAnnouncement:o="You can request a new code now.",className:c,buttonClassName:u}){const p=e>0?`Resend in ${e}s`:n?l:d;return b("p",{className:s("text-sm",c),children:[r," ",t("button",{type:"button",onClick:i,disabled:!a,className:s("underline cursor-pointer disabled:no-underline disabled:opacity-50 disabled:cursor-not-allowed",u),children:p}),t("span",{"aria-live":"polite",className:"sr-only",children:e===0&&!n?o:""})]})}export{x as ResendCode,x as default};
@@ -1 +1 @@
1
- "use client";import{jsxs as G,jsx as c}from"react/jsx-runtime";import{forwardRef as Se,useRef as g,useState as Ke,useMemo as I,isValidElement as Ve,useEffect as R}from"react";import{useFilter as we,useObjectRef as Be,useComboBox as Fe,useButton as Ne,HiddenSelect as Pe}from"react-aria";import{Section as Me,useComboBoxState as Ae}from"react-stately";import{cn as r}from"../../utils/cn.js";import{Pulse as Oe}from"../Loader/Pulse.js";import{SelectTags as Te,ChevronIcon as ke,SelectOverlay as De,SelectListBox as Le}from"./parts.js";import"react-aria-components";function S(s){return s?typeof s.rendered=="string"?s.rendered:s.textValue??"":""}function Ee(s,i){const f=i.trim().toLowerCase();let p=null,a=s.getFirstKey();for(;a!=null;){const m=s.getItem(a);if(m?.type==="item"&&(p==null&&(p=a),f.length>0&&(m.textValue??"").toLowerCase().split(/\s+/).includes(f)))return a;a=s.getKeyAfter(a)}return p}function _(s,i){const f=s.toLowerCase(),p=f.split(/\s+/);return p.includes(i)?0:f.startsWith(i)?1:p.some(a=>a.startsWith(i))?2:3}function J(s){const i=s.props;return typeof i.textValue=="string"?i.textValue:typeof i.children=="string"?i.children:""}function He(s,i){const{label:f,description:p,errorMessage:a,children:m,placeholder:k,isLoading:D,slots:L,popoverOffset:Q,className:U,classNames:l,autoComplete:K,menuTrigger:X,selectTextOnFocus:Y=!0,autoFocusFirstOption:Z=!0,searchable:je,selectionMode:$="single",selectedKey:ee,defaultSelectedKey:te,selectedKeys:V,defaultSelectedKeys:w,onSelectionChange:E,name:ne,...x}=s,u=$==="multiple",B=!!x.isDisabled,C=g(!1),h=g(null),d=x.inputValue!==void 0,[F,y]=Ke(""),N=d?x.inputValue:F,oe=I(()=>{const e=(N??"").trim().toLowerCase();if(d||typeof m=="function"||e===""||!Array.isArray(m))return m;const n=m,o=n.filter(b=>Ve(b));return o.length!==n.length||o.length===0||o.some(b=>b.type===Me)?m:[...o].sort((b,Re)=>_(J(b),e)-_(J(Re),e))},[m,d,N]),re=I(()=>u&&V?[...V]:void 0,[u,V]),se=I(()=>u&&w?[...w]:void 0,[u,w]),H={...x,label:f,children:oe,placeholder:k,isDisabled:B,menuTrigger:X??"focus",autoComplete:K,inputValue:N,...u?{selectionMode:"multiple",value:re,defaultValue:se,onChange:E}:{selectedKey:ee,defaultSelectedKey:te,onSelectionChange:e=>{if(!d){const n=e!=null?h.current?.collection.getItem(e):null;y(S(n))}E?.(e)}},onInputChange:e=>{d||y(e),x.onInputChange?.(e),!u&&e===""&&h.current?.selectedKey!=null&&h.current.setSelectedKey(null)}},{contains:le}=we({sensitivity:"base"}),t=Ae({...H,defaultFilter:le,allowsEmptyCollection:!0});R(()=>{h.current=t});const j=g(0),P=t.selectionManager.selectedKeys.size;R(()=>{u&&(P>j.current&&!d&&y(""),j.current=P)},[u,P,d]);const M=S(t.selectedItem);R(()=>{d||u||C.current||F===M||y(M)},[M,d,F,u]);const ie=g(null),W=Be(i),q=g(null),z=g(null),A=g(null),O=g(null),T=g(null),{inputProps:ue,listBoxProps:ce,labelProps:ae,buttonProps:de,descriptionProps:fe,errorMessageProps:pe}=Fe({...H,inputRef:W,listBoxRef:q,popoverRef:z,buttonRef:A},t),{buttonProps:me}=Ne(de,A),v=t.displayValidation.isInvalid,{onKeyDown:ge,onFocus:be,onBlur:he,...xe}=ue,ye=e=>{e.key==="Home"||e.key==="End"||ge?.(e)};R(()=>{const e=t.selectionManager;if(!Z||!t.isOpen||t.inputValue.length===0){T.current=null;return}const n=e.focusedKey;if(n!=null&&n!==T.current)return;const o=Ee(t.collection,t.inputValue);T.current=o??null,o!=null&&o!==n&&e.setFocusedKey(o)});const Ce=e=>{be?.(e);const n=C.current;if(C.current=!0,n||!Y)return;const o=e.target,b=o.value;b&&requestAnimationFrame(()=>{o.value===b&&o.select()})},ve=e=>{he?.(e);const n=e.currentTarget;requestAnimationFrame(()=>{document.activeElement!==n&&(C.current=!1,h.current?.close(),d||y(u?"":S(h.current?.selectedItem)))})},Ie=I(()=>new Proxy(t,{get(e,n){return n==="setValue"?o=>e.setSelectedKey(o):Reflect.get(e,n)}}),[t]);return G("div",{ref:ie,className:r("flex flex-col","w-full",U),children:[c("label",{...ae,className:r("flex","text-xs text-fg-muted",l?.label),children:f}),c(Pe,{state:Ie,triggerRef:O,label:f,name:ne,isDisabled:B,autoComplete:K}),u&&c(Te,{items:(t.selectedItems??[]).map(e=>({id:e.key,label:S(e)})),onRemove:e=>t.selectionManager.toggleSelection(e),classNames:{tagGroup:r(l?.tagGroup),tag:r(l?.tag),tagRemoveButton:r(l?.tagRemoveButton)}}),G("div",{ref:O,className:r("flex","relative","w-full",l?.comboBoxContainer),children:[c("input",{...xe,onKeyDown:ye,onFocus:Ce,onBlur:ve,ref:W,placeholder:k,autoComplete:K,className:r("border border-solid border-border","text-sm text-fg","py-0 px-2","h-10 w-full","m-0","rounded-md","bg-white","transition-all ease-in-out duration-200","hover:border-border-emphasis","focus:border-border-emphasis focus:outline focus:outline-2 focus:outline-border-subtle","disabled:bg-surface-subtle disabled:border-border",D?"disabled:text-fg":"disabled:text-fg-muted",v&&"border-danger-soft bg-danger-surface text-danger placeholder:text-fg-subtle",v&&"hover:border-danger focus:border-danger focus:outline-danger-ring",l?.input)}),D?c("div",{className:r("absolute top-2 right-2","block",l?.loader),children:L?.loadingIcon||c(Oe,{})}):c("button",{...me,ref:A,className:r("absolute top-2 right-0","block","border-none bg-none",l?.arrowButton),children:c(ke,{isOpen:t.isOpen,isDisabled:B,isInvalid:v,buttonIcon:L?.buttonIcon})}),t.isOpen&&t.collection.size>0&&c(De,{state:t,triggerRef:O,popoverRef:z,offset:Q,className:r(l?.listContainer),children:c(Le,{state:t,listBoxProps:ce,listBoxRef:q,optionVirtualFocus:!0,optionClassName:r(l?.option),sectionHeadingClassName:r(l?.sectionHeading),className:r("max-h-80","overflow-y-auto","outline-none",l?.list)})})]}),p&&c("span",{...fe,className:r("flex","text-xs","text-fg-muted",l?.description),children:p}),v&&typeof a=="string"&&c("span",{...pe,className:r("flex","text-xs","text-danger-soft",l?.errorMessage),children:a})]})}const Ye=Se(He);export{Ye as ComboBoxSelect,Ye as default};
1
+ "use client";import{jsxs as G,jsx as c}from"react/jsx-runtime";import{forwardRef as Se,useRef as g,useState as we,useMemo as I,isValidElement as Ke,useEffect as R}from"react";import{useFilter as Ve,useObjectRef as Be,useComboBox as Fe,useButton as Ne,HiddenSelect as Pe}from"react-aria";import{Section as Me,useComboBoxState as Ae}from"react-stately";import{cn as r}from"../../utils/cn.js";import{Pulse as Oe}from"../Loader/Pulse.js";import{SelectTags as Te,ChevronIcon as ke,SelectOverlay as De,SelectListBox as Le}from"./parts.js";import"react-aria-components";function S(s){return s?typeof s.rendered=="string"?s.rendered:s.textValue??"":""}function Ee(s,i){const f=i.trim().toLowerCase();let p=null,a=s.getFirstKey();for(;a!=null;){const m=s.getItem(a);if(m?.type==="item"&&(p==null&&(p=a),f.length>0&&(m.textValue??"").toLowerCase().split(/\s+/).includes(f)))return a;a=s.getKeyAfter(a)}return p}function _(s,i){const f=s.toLowerCase(),p=f.split(/\s+/);return p.includes(i)?0:f.startsWith(i)?1:p.some(a=>a.startsWith(i))?2:3}function J(s){const i=s.props;return typeof i.textValue=="string"?i.textValue:typeof i.children=="string"?i.children:""}function He(s,i){const{label:f,description:p,errorMessage:a,children:m,placeholder:k,isLoading:D,slots:L,popoverOffset:Q,className:U,classNames:l,autoComplete:w,menuTrigger:X,selectTextOnFocus:Y=!0,autoFocusFirstOption:Z=!0,searchable:je,selectionMode:$="single",selectedKey:ee,defaultSelectedKey:te,selectedKeys:K,defaultSelectedKeys:V,onSelectionChange:E,name:ne,...x}=s,u=$==="multiple",B=!!x.isDisabled,C=g(!1),h=g(null),d=x.inputValue!==void 0,[F,y]=we(""),N=d?x.inputValue:F,oe=I(()=>{const e=(N??"").trim().toLowerCase();if(d||typeof m=="function"||e===""||!Array.isArray(m))return m;const n=m,o=n.filter(b=>Ke(b));return o.length!==n.length||o.length===0||o.some(b=>b.type===Me)?m:[...o].sort((b,Re)=>_(J(b),e)-_(J(Re),e))},[m,d,N]),re=I(()=>u&&K?[...K]:void 0,[u,K]),se=I(()=>u&&V?[...V]:void 0,[u,V]),H={...x,label:f,children:oe,placeholder:k,isDisabled:B,menuTrigger:X??"focus",autoComplete:w,inputValue:N,...u?{selectionMode:"multiple",value:re,defaultValue:se,onChange:E}:{selectedKey:ee,defaultSelectedKey:te,onSelectionChange:e=>{if(!d){const n=e!=null?h.current?.collection.getItem(e):null;y(S(n))}E?.(e)}},onInputChange:e=>{d||y(e),x.onInputChange?.(e),!u&&e===""&&h.current?.selectedKey!=null&&h.current.setSelectedKey(null)}},{contains:le}=Ve({sensitivity:"base"}),t=Ae({...H,defaultFilter:le,allowsEmptyCollection:!0});R(()=>{h.current=t});const j=g(0),P=t.selectionManager.selectedKeys.size;R(()=>{u&&(P>j.current&&!d&&y(""),j.current=P)},[u,P,d]);const M=S(t.selectedItem);R(()=>{d||u||C.current||F===M||y(M)},[M,d,F,u]);const ie=g(null),W=Be(i),q=g(null),z=g(null),A=g(null),O=g(null),T=g(null),{inputProps:ue,listBoxProps:ce,labelProps:ae,buttonProps:de,descriptionProps:fe,errorMessageProps:pe}=Fe({...H,inputRef:W,listBoxRef:q,popoverRef:z,buttonRef:A},t),{buttonProps:me}=Ne(de,A),v=t.displayValidation.isInvalid,{onKeyDown:ge,onFocus:be,onBlur:he,...xe}=ue,ye=e=>{e.key==="Home"||e.key==="End"||ge?.(e)};R(()=>{const e=t.selectionManager;if(!Z||!t.isOpen||t.inputValue.length===0){T.current=null;return}const n=e.focusedKey;if(n!=null&&n!==T.current)return;const o=Ee(t.collection,t.inputValue);T.current=o??null,o!=null&&o!==n&&e.setFocusedKey(o)});const Ce=e=>{be?.(e);const n=C.current;if(C.current=!0,n||!Y)return;const o=e.target,b=o.value;b&&requestAnimationFrame(()=>{o.value===b&&o.select()})},ve=e=>{he?.(e);const n=e.currentTarget;requestAnimationFrame(()=>{document.activeElement!==n&&(C.current=!1,h.current?.close(),d||y(u?"":S(h.current?.selectedItem)))})},Ie=I(()=>new Proxy(t,{get(e,n){return n==="setValue"?o=>e.setSelectedKey(o):Reflect.get(e,n)}}),[t]);return G("div",{ref:ie,className:r("flex flex-col","w-full",U),children:[c("label",{...ae,className:r("flex","text-xs text-fg-muted",l?.label),children:f}),c(Pe,{state:Ie,triggerRef:O,label:f,name:ne,isDisabled:B,autoComplete:w}),u&&c(Te,{items:(t.selectedItems??[]).map(e=>({id:e.key,label:S(e)})),onRemove:e=>t.selectionManager.toggleSelection(e),classNames:{tagGroup:r(l?.tagGroup),tag:r(l?.tag),tagRemoveButton:r(l?.tagRemoveButton)}}),G("div",{ref:O,className:r("flex","relative","w-full",l?.comboBoxContainer),children:[c("input",{...xe,onKeyDown:ye,onFocus:Ce,onBlur:ve,ref:W,placeholder:k,autoComplete:w,className:r("border border-solid border-border","text-sm text-fg","py-0 px-2","h-10 w-full","m-0","rounded-md","bg-white","transition-all ease-in-out duration-200","hover:border-border-emphasis","focus:border-border-emphasis focus:outline focus:outline-2 focus:outline-border-subtle","disabled:bg-surface-subtle disabled:border-border",D?"disabled:text-fg":"disabled:text-fg-muted",v&&"border-danger-soft bg-danger-surface text-danger placeholder:text-fg-subtle",v&&"hover:border-danger focus:border-danger focus:outline-danger-ring",l?.input)}),D?c("div",{className:r("absolute top-2 right-2","block",l?.loader),children:L?.loadingIcon||c(Oe,{})}):c("button",{...me,ref:A,className:r("absolute top-2 right-0","block","border-none bg-none","cursor-pointer disabled:cursor-not-allowed",l?.arrowButton),children:c(ke,{isOpen:t.isOpen,isDisabled:B,isInvalid:v,buttonIcon:L?.buttonIcon})}),t.isOpen&&t.collection.size>0&&c(De,{state:t,triggerRef:O,popoverRef:z,offset:Q,className:r(l?.listContainer),children:c(Le,{state:t,listBoxProps:ce,listBoxRef:q,optionVirtualFocus:!0,optionClassName:r(l?.option),sectionHeadingClassName:r(l?.sectionHeading),className:r("max-h-80","overflow-y-auto","outline-none",l?.list)})})]}),p&&c("span",{...fe,className:r("flex","text-xs","text-fg-muted",l?.description),children:p}),v&&typeof a=="string"&&c("span",{...pe,className:r("flex","text-xs","text-danger-soft",l?.errorMessage),children:a})]})}const Ye=Se(He);export{Ye as ComboBoxSelect,Ye as default};
@@ -0,0 +1,32 @@
1
+ # BUGFIX: a `Dialog` taller than the viewport scrolls inside itself
2
+
3
+ **Date:** 2026-09-19
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** `src/components/Dialog/Dialog.tsx` — every `<Dialog>` rendered inside `<Modal>`.
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No API change. **Visual:** a dialog whose content is taller than the viewport is now capped at the viewport height minus 2rem and scrolls its own content; shorter dialogs render exactly as before.
8
+ **Ticket:** CTAP-1791 (epic CTAP-1785)
9
+
10
+ ## Problem
11
+
12
+ `Dialog` is centered with `absolute top-2/4 left-2/4 -translate-x-1/2 -translate-y-1/2` and had
13
+ no height limit. A dialog taller than the viewport therefore grew past both edges: its top half
14
+ sat above the screen and its footer, the Cancel and Update buttons, below it, with no way to
15
+ scroll to them because the overlay itself does not scroll.
16
+
17
+ CTAP-1791 put the billing address fields into the saved-card edit form, so "Update payment
18
+ method" now carries cardholder name, expiry, postal code, ten address fields and the default
19
+ control. On a laptop viewport the buttons were off screen.
20
+
21
+ ## What changed
22
+
23
+ The dialog gets `max-h-[calc(100dvh-2rem)] overflow-y-auto`. The centering is untouched, so a
24
+ capped dialog still sits in the middle with 1rem clear of each edge and its content scrolls
25
+ inside the rounded, bordered box. `dvh` follows the mobile browser chrome as it shows and hides.
26
+
27
+ ## Consumer action
28
+
29
+ None. A consumer that already wrapped its dialog content in its own scroll container keeps
30
+ working; the outer cap only engages when the content is taller than the viewport. A consumer that
31
+ passes `className` with its own `max-h-*` or `overflow-*` still wins, since `className` merges
32
+ last.
@@ -0,0 +1,90 @@
1
+ # BUGFIX: `Drawer` no longer silently becomes a full-screen takeover
2
+
3
+ **Date:** 2026-09-23
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** `src/components/Drawer/Drawer.tsx`, `Drawer.types.ts` — every `<Drawer>` whose `size` (or default) exceeds the viewport along its opening axis.
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No API change. **Visual:** a `left`/`right` drawer wider than 88% of the viewport, or a `top`/`bottom` drawer taller than 88% of it, now leaves a visible sliver of the page instead of covering it entirely. A drawer that already fit is unchanged.
8
+ **Ticket:** CTAP-1877
9
+
10
+ ## Problem
11
+
12
+ `Drawer` rendered its `size` as a custom property and capped it with `max-w-full` (left/right) or
13
+ `max-h-[var(--visual-viewport-height)]` (top/bottom). Both caps are exactly 100% of the viewport, so
14
+ a `size` wider/taller than the viewport did not render "nearly full" — it rendered **exactly** full:
15
+
16
+ ```
17
+ w-[var(--drawer-size)] max-w-full → effective width = 100vw
18
+ h-[var(--drawer-size)] max-h-[var(--visual-viewport-height)] → effective height = 100%
19
+ ```
20
+
21
+ `size="30rem"` (480px) on a 375px phone hit this on every storefront drawer that used it (header
22
+ menu, cart, add-items, collection filters). The **default** `size` (`20rem` = 320px) hit it too,
23
+ leaving only 55px at 375px and nothing at all at 320px.
24
+
25
+ A drawer with no visible edge reads as a new page rather than a layer over the current one, and it
26
+ removes the obvious place to tap to dismiss.
27
+
28
+ ## What changed
29
+
30
+ The cap is now 88% of the viewport along the drawer's opening axis, so the declared `size` is a
31
+ maximum rather than a promise:
32
+
33
+ - `left` / `right`: `max-w-[88vw]` (was `max-w-full`)
34
+ - `top` / `bottom`: `max-h-[min(var(--visual-viewport-height),88vh)]` (was `max-h-[var(--visual-viewport-height)]`)
35
+
36
+ 88vw/88vh leaves a 45–50px sliver at common phone widths and changes nothing once the drawer's own
37
+ size is already under 88% of the viewport — which every desktop width is, for every `size` this
38
+ package has shipped.
39
+
40
+ **New `fullScreen` prop** is the escape hatch: `<Drawer fullScreen>` restores the old unclamped
41
+ behavior (`max-w-full` / `max-h-[var(--visual-viewport-height)]`) for the rare drawer that
42
+ legitimately wants the whole screen. Full-screen is now something you ask for, not something an
43
+ oversized `size` gives you by accident.
44
+
45
+ ## Consumer Impact
46
+
47
+ **No action needed for a drawer whose `size` already fit its viewport.** Nothing changes for it.
48
+
49
+ **Drop any hand-rolled clamp.** If you were passing `size="min(30rem, 88vw)"` (or similar) as a
50
+ workaround, remove it — `size="30rem"` now behaves the same way natively:
51
+
52
+ ```tsx
53
+ // BEFORE
54
+ <Drawer size="min(30rem, 88vw)" direction="left" aria-label="Filters">
55
+
56
+ // AFTER
57
+ <Drawer size="30rem" direction="left" aria-label="Filters">
58
+ ```
59
+
60
+ **If a drawer genuinely needs the full screen**, add `fullScreen`:
61
+
62
+ ```tsx
63
+ <Drawer size="100vw" direction="right" fullScreen aria-label="Full-screen picker">
64
+ ```
65
+
66
+ Without `fullScreen`, a `size="100vw"` (or any value ≥ 88% of the viewport) is now clamped to 88vw —
67
+ that clamp is the point of this fix, so an existing call site that relied on `size` alone to reach
68
+ full screen needs the new prop.
69
+
70
+ The overlay's own dismiss-on-tap-outside behavior (`isDismissable` on `ModalOverlay`) is unaffected
71
+ by this change; the sliver only makes that existing behavior discoverable.
72
+
73
+ ## Verification
74
+
75
+ `Drawer.spec.tsx` — 10 tests: for each of the four directions, asserts the clamp class is present
76
+ and the old unconditional full-viewport class is absent, plus the `fullScreen` escape hatch restores
77
+ the old classes and removes the clamp. Also covers that the default `size` (`20rem`) is still subject
78
+ to the clamp, and that a `size` under the clamp still renders via `w-[var(--drawer-size)]` /
79
+ `h-[var(--drawer-size)]` (i.e. the fix does not force every drawer to exactly 88vw/88vh — it is a
80
+ ceiling, not a fixed size).
81
+
82
+ These are cancellation assertions, the same shape as `Button`'s `variant="link"` tests: they check
83
+ the old full-viewport class is **gone**, not merely that a clamp class was appended alongside it —
84
+ `tailwind-merge` collapses conflicting `max-w-*`/`max-h-*` utilities to the last one it sees, so a
85
+ fix that left both classes present would still resolve to whichever one happened to come last.
86
+
87
+ ⚠️ **Not visually verified against real touch devices.** The tests assert which Tailwind classes
88
+ survive in jsdom; they cannot show that 88vw/88vh is the right percentage for a given storefront's
89
+ breakpoints, only that some percentage other than 100% is now applied. The ticket already flags this
90
+ as a design judgment call, not an engineering one.
@@ -0,0 +1,84 @@
1
+ # BUGFIX: interactive elements show a pointer cursor under Tailwind v4
2
+
3
+ **Date:** 2026-09-23
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** `src/components/Button/Button.tsx`, `ResendCode/ResendCode.tsx`, `Select/ComboBoxSelect.tsx`, `Collection/EmptyResults/EmptyResults.tsx`, `Collection/CollectionFilters/CollectionFilters.tsx`.
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No API change. **Visual:** hovering an enabled button in any of the affected components now shows a pointer cursor instead of the default arrow. Disabled states are unchanged (still `not-allowed` where they had it, still no explicit cursor where they didn't).
8
+ **Ticket:** CTAP-1874
9
+
10
+ ## Problem
11
+
12
+ Tailwind v4's Preflight changed the `<button>` reset from `cursor: pointer` to `cursor: default`, so
13
+ a class that used to be unnecessary now is. `Button` and `ResendCode` never set `cursor-pointer`
14
+ explicitly — they relied on the browser/Tailwind v3 default — so under Tailwind v4 they drew the
15
+ arrow cursor on hover instead of a hand. `Input`'s password-reveal toggle already set
16
+ `cursor-pointer` explicitly, which is what made this an inconsistency rather than a design decision:
17
+ the library already knew to do this, just not everywhere.
18
+
19
+ The ticket asked for a grep of the rest of the package for `<button` without a cursor class rather
20
+ than stopping at the two reported components. That turned up three more raw `<button>` elements
21
+ missing it: the combobox's dropdown-arrow toggle (`ComboBoxSelect`), and the "Clear search" / "Clear
22
+ all filters" text buttons in `EmptyResults` and `CollectionFilters` (the last of these appears at
23
+ three call sites: the search-clear icon, the desktop sidebar, and the mobile filter drawer).
24
+ `Pagination`, `ButtonSelect` and `MultiButtonSelect` already had `cursor-pointer` and needed no
25
+ change.
26
+
27
+ ## What changed
28
+
29
+ Added `cursor-pointer` to the enabled state of every affected button, alongside `disabled:cursor-not-allowed`
30
+ where a disabled state exists:
31
+
32
+ | Component | Before | After |
33
+ |---|---|---|
34
+ | `Button` | no cursor class | `cursor-pointer disabled:cursor-not-allowed` |
35
+ | `ResendCode` | `disabled:cursor-not-allowed` only | `cursor-pointer disabled:cursor-not-allowed` |
36
+ | `ComboBoxSelect` (arrow toggle) | no cursor class | `cursor-pointer disabled:cursor-not-allowed` |
37
+ | `EmptyResults` (both actions) | no cursor class | `cursor-pointer` (no disabled state on these buttons) |
38
+ | `CollectionFilters` (all three call sites) | no cursor class | `cursor-pointer` (no disabled state on these buttons) |
39
+
40
+ `Button` gaining `disabled:cursor-not-allowed` is new — it had no cursor handling in either state
41
+ before this fix — and matches the pattern already established by `ResendCode` and `Input`.
42
+
43
+ ## Consumer Impact
44
+
45
+ **No action needed to upgrade.** This only adds a `cursor-pointer` (and, for `Button`, a
46
+ `disabled:cursor-not-allowed`) utility to the base class list; nothing is removed, and `className`
47
+ already merges last via `cn`/tailwind-merge, so a consumer overriding cursor at the call site
48
+ continues to win.
49
+
50
+ **Drop any hand-rolled workaround.** If you were passing a `className`/`buttonClassName` purely to
51
+ restore the pointer cursor under Tailwind v4, remove it:
52
+
53
+ ```tsx
54
+ // BEFORE
55
+ <ResendCode buttonClassName="cursor-pointer underline" ... />
56
+
57
+ // AFTER
58
+ <ResendCode ... />
59
+ ```
60
+
61
+ ## Verification
62
+
63
+ New/updated specs, run via `nx run frontend-module-ui:test`:
64
+
65
+ - `Button.spec.tsx` — `cursor-pointer` asserted for all four `variant` aliases (`primary`,
66
+ `secondary`, `tertiary`, `link`).
67
+ - `ResendCode.test.tsx` — `cursor-pointer` asserted while enabled; `disabled:cursor-not-allowed`
68
+ still asserted while disabled.
69
+ - `Select/ComboBoxSelect.spec.tsx` (new) — `cursor-pointer` on the dropdown-arrow toggle, rendered
70
+ through the public `<Select searchable>`.
71
+ - `Collection/EmptyResults/EmptyResults.spec.tsx` (new) — `cursor-pointer` on both "Clear search" and
72
+ "Clear all filters".
73
+ - `Collection/CollectionFilters/CollectionFilters.spec.tsx` (new) — `cursor-pointer` on the
74
+ search-clear icon button and the desktop "Clear all filters" button.
75
+
76
+ All ten new/changed assertions were confirmed to fail against the pre-fix code (missing
77
+ `cursor-pointer` in the rendered className) before the fix landed, then pass after.
78
+
79
+ ⚠️ **Not independently re-verified against a live Tailwind v4 build.** The tests assert the utility
80
+ class is present in the rendered `className`; they cannot show the class actually resolves to a
81
+ hand cursor in a given consumer's compiled CSS (e.g. if a consumer's Tailwind config purges
82
+ `cursor-pointer` for some reason). The mechanism reported in the ticket — Preflight's `cursor:
83
+ default` reset on `<button>` under Tailwind v4 — was not independently reproduced in a browser as
84
+ part of this fix; it was taken from the ticket's own evidence.
@@ -1,4 +1,34 @@
1
1
  [
2
+ {
3
+ "path": "2026/09/ui-2026-09-23-interactive-elements-cursor-pointer.md",
4
+ "module": "libs/frontend-modules/ui",
5
+ "package": "@clicktap/ui",
6
+ "date": "2026-09-23",
7
+ "title": "BUGFIX: interactive elements show a pointer cursor under Tailwind v4",
8
+ "classification": "BUGFIX",
9
+ "breaking": "No API change. **Visual:** hovering an enabled button in any of the affected components now shows a pointer cursor instead of the default arrow. Disabled states are unchanged (still `not-allowed` where they had it, still no explicit cursor where they didn't).",
10
+ "affects": "`src/components/Button/Button.tsx`, `ResendCode/ResendCode.tsx`, `Select/ComboBoxSelect.tsx`, `Collection/EmptyResults/EmptyResults.tsx`, `Collection/CollectionFilters/CollectionFilters.tsx`."
11
+ },
12
+ {
13
+ "path": "2026/09/ui-2026-09-23-drawer-viewport-clamp.md",
14
+ "module": "libs/frontend-modules/ui",
15
+ "package": "@clicktap/ui",
16
+ "date": "2026-09-23",
17
+ "title": "BUGFIX: `Drawer` no longer silently becomes a full-screen takeover",
18
+ "classification": "BUGFIX",
19
+ "breaking": "No API change. **Visual:** a `left`/`right` drawer wider than 88% of the viewport, or a `top`/`bottom` drawer taller than 88% of it, now leaves a visible sliver of the page instead of covering it entirely. A drawer that already fit is unchanged.",
20
+ "affects": "`src/components/Drawer/Drawer.tsx`, `Drawer.types.ts` — every `<Drawer>` whose `size` (or default) exceeds the viewport along its opening axis."
21
+ },
22
+ {
23
+ "path": "2026/09/ui-2026-09-19-dialog-scrolls-within-viewport.md",
24
+ "module": "libs/frontend-modules/ui",
25
+ "package": "@clicktap/ui",
26
+ "date": "2026-09-19",
27
+ "title": "BUGFIX: a `Dialog` taller than the viewport scrolls inside itself",
28
+ "classification": "BUGFIX",
29
+ "breaking": "No API change. **Visual:** a dialog whose content is taller than the viewport is now capped at the viewport height minus 2rem and scrolls its own content; shorter dialogs render exactly as before.",
30
+ "affects": "`src/components/Dialog/Dialog.tsx` — every `<Dialog>` rendered inside `<Modal>`."
31
+ },
2
32
  {
3
33
  "path": "2026/09/ui-2026-09-18-pin-input-autofill-and-controlled-value.md",
4
34
  "module": "libs/frontend-modules/ui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clicktap/ui",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "private": false,
5
5
  "author": "Clicktap",
6
6
  "description": "A library of React UI components and low-level hooks.",