@clicktap/ui 1.1.1 → 2.0.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.
Files changed (51) hide show
  1. package/components/Accordion/Accordion.d.ts +76 -2
  2. package/components/Accordion/Accordion.js +1 -1
  3. package/components/Accordion/index.d.ts +2 -3
  4. package/components/Accordion/index.js +1 -1
  5. package/components/Badge/Badge.js +1 -1
  6. package/components/Badge/Badge.types.d.ts +27 -2
  7. package/components/Badge/index.js +1 -1
  8. package/components/Collapsible/Collapsible.js +1 -1
  9. package/components/Collection/CollectionFilters/CollectionFilters.js +1 -1
  10. package/components/Collection/CollectionFilters/index.js +1 -1
  11. package/components/Collection/index.js +1 -1
  12. package/components/ContextMenu/ContextMenu.js +1 -1
  13. package/components/DialogTrigger/DialogTrigger.d.ts +18 -10
  14. package/components/DialogTrigger/DialogTrigger.js +1 -1
  15. package/components/DialogTrigger/index.js +1 -1
  16. package/components/Drawer/Drawer.js +1 -1
  17. package/components/Menu/Menu.js +1 -1
  18. package/components/Meter/Meter.js +1 -1
  19. package/components/NumberTicker/DigitResolver.js +1 -1
  20. package/components/NumberTicker/NumberTicker.js +1 -1
  21. package/components/Pagination/Pagination.d.ts +27 -2
  22. package/components/Pagination/Pagination.js +1 -1
  23. package/components/Pagination/index.js +1 -1
  24. package/components/Progressbar/LinearProgressbar.js +1 -1
  25. package/components/Tabs/Tab.js +1 -1
  26. package/components/Tooltip/Tooltip.d.ts +9 -1
  27. package/components/Tooltip/Tooltip.js +1 -1
  28. package/components/Tooltip/Tooltip.types.d.ts +21 -2
  29. package/components/Tooltip/index.js +1 -1
  30. package/components/UploadImage/UploadImage.js +1 -1
  31. package/docs/migrations/2026/08/ui-2026-08-14-zod-error-map.md +104 -0
  32. package/docs/migrations/2026/08/ui-2026-08-21-graphql-client-string-documents.md +45 -0
  33. package/docs/migrations/2026/08/ui-2026-08-21-graphql-peer-covered.md +26 -0
  34. package/docs/migrations/2026/08/ui-2026-08-21-heroui-removed.md +54 -0
  35. package/docs/migrations/2026/08/ui-2026-08-21-lazymotion-m-components.md +54 -0
  36. package/docs/migrations/2026/08/ui-2026-08-21-modal-triggers-wired-explicitly.md +43 -0
  37. package/docs/migrations/2026/08/ui-2026-08-21-next-image-fallback-keeps-fill.md +38 -0
  38. package/docs/migrations/2026/08/ui-2026-08-22-accordion-divider-between-items.md +30 -0
  39. package/docs/migrations/2026/08/ui-2026-08-22-accordion-divider-class-ownership.md +14 -0
  40. package/docs/migrations/2026/08/ui-2026-08-22-react-aria-moves-as-one-version-set.md +46 -0
  41. package/docs/migrations/index.json +117 -2
  42. package/next/Image.js +1 -1
  43. package/package.json +10 -38
  44. package/utils/createAuthAwareGraphqlClient.d.ts +104 -16
  45. package/utils/createAuthAwareGraphqlClient.js +1 -56
  46. package/utils/zod/configureZodErrorMap.d.ts +115 -0
  47. package/utils/zod/configureZodErrorMap.js +1 -0
  48. package/utils/zod/index.d.ts +1 -0
  49. package/utils/zod/index.js +1 -0
  50. package/utils/zod.d.ts +1 -0
  51. package/utils/zod.js +1 -0
@@ -1,3 +1,28 @@
1
- import { PaginationProps } from '@heroui/pagination';
2
- export declare function Pagination({ showControls, className, classNames, ...rest }: PaginationProps): import("react/jsx-runtime").JSX.Element;
1
+ /**
2
+ * Native pagination (CTAP-1383) — previously a styled wrapper around
3
+ * @heroui/pagination, the last @heroui consumer in this library. Standard
4
+ * boundary/sibling windowing with ellipses; heroui prop names kept for the
5
+ * call sites this repo uses (total, page/initialPage, onChange, boundaries,
6
+ * siblings, showControls, className, classNames).
7
+ */
8
+ export type PaginationProps = {
9
+ total: number;
10
+ page?: number;
11
+ initialPage?: number;
12
+ onChange?: (page: number) => void;
13
+ boundaries?: number;
14
+ siblings?: number;
15
+ showControls?: boolean;
16
+ isCompact?: boolean;
17
+ isDisabled?: boolean;
18
+ className?: string;
19
+ classNames?: {
20
+ wrapper?: string;
21
+ item?: string;
22
+ prev?: string;
23
+ next?: string;
24
+ ellipsis?: string;
25
+ };
26
+ };
27
+ export declare function Pagination({ total, page, initialPage, onChange, boundaries, siblings, showControls, isCompact, isDisabled, className, classNames, }: PaginationProps): import("react/jsx-runtime").JSX.Element;
3
28
  export default Pagination;
@@ -1 +1 @@
1
- "use client";import{jsx as s}from"react/jsx-runtime";import{Pagination as n}from"@heroui/pagination";import{cn as e}from"../../utils/cn.js";const r=e("w-8 h-8 flex justify-center items-center cursor-pointer shadow-none","border border-border-subtle data-[disabled=true]:border-0","rounded transition-colors duration-300 focus-visible:outline-white focus-visible:outline-offset-4","hover:bg-slate-50 data-[disabled=true]:bg-surface-subtle data-[disabled=true]:text-fg-subtle","[&>svg]:w-4.5 [&>svg]:h-auto");function b({showControls:o=!0,className:i,classNames:t,...a}){return s(n,{showControls:o,className:e("text-slate-600",i),classNames:{wrapper:e("flex flex-nowrap gap-2 lg:gap-3",t?.wrapper),item:e("w-8 h-8 flex text-sm justify-center items-center cursor-pointer rounded shadow-none","border-solid border border-slate-100 bg-transparent transition-colors duration-300","data-[active=true]:bg-slate-500 data-[active=true]:text-white focus-visible:outline-white focus-visible:outline-offset-4","[&[data-hover=true]:not([data-active=true])]:border-border-subtle",t?.item),prev:e(r,t?.prev),next:e(r,t?.next),ellipsis:e("w-4 h-4.5 scale-110 translate-y-1",t?.ellipsis)},...a})}export{b as Pagination,b as default};
1
+ "use client";import{jsx as a,jsxs as N}from"react/jsx-runtime";import{useState as j}from"react";import{cn as l}from"../../utils/cn.js";import{DownArrow as v}from"../Icon/DownArrow.js";const g=l("w-8 h-8 flex justify-center items-center cursor-pointer shadow-none","border border-solid border-border-subtle data-[disabled=true]:border-0","rounded transition-colors duration-300 focus-visible:outline-white focus-visible:outline-offset-4","bg-transparent hover:bg-slate-50 data-[disabled=true]:bg-surface-subtle data-[disabled=true]:text-fg-subtle","[&>svg]:w-4.5 [&>svg]:h-auto");function M(e,h,t,d){const s=(b,n)=>Array.from({length:n-b+1},(m,o)=>b+o);if(e<=t*2+d*2+3)return s(1,e);const f=Math.max(h-d,t+2),c=Math.min(h+d,e-t-1);return[...s(1,t),f>t+2?"ellipsis":t+1,...s(f,c),c<e-t-1?"ellipsis":e-t,...s(e-t+1,e)]}function $({total:e,page:h,initialPage:t=1,onChange:d,boundaries:s=1,siblings:f=1,showControls:c=!0,isCompact:b=!1,isDisabled:n,className:m,classNames:o}){const[w,x]=j(t),r=h??w,p=i=>{const u=Math.min(Math.max(i,1),e);u!==r&&(x(u),d?.(u))};return a("nav",{"aria-label":"Pagination",className:l("text-slate-600",m),children:N("ul",{className:l("flex flex-nowrap gap-2 lg:gap-3 list-none p-0 m-0",o?.wrapper),children:[c&&a("li",{children:a("button",{type:"button","aria-label":"Previous page",disabled:n||r<=1,"data-disabled":n||r<=1||void 0,onClick:()=>p(r-1),className:l(g,o?.prev),children:a(v,{className:"rotate-90 w-4 h-4"})})}),M(e,r,s,f).map((i,u)=>i==="ellipsis"?a("li",{"aria-hidden":"true",className:l("w-4 h-4.5 scale-110 translate-y-1",o?.ellipsis),children:"…"},`e-${u}`):a("li",{children:a("button",{type:"button","aria-label":`Page ${i}`,"aria-current":i===r?"page":void 0,disabled:n,"data-active":i===r||void 0,onClick:()=>p(i),className:l(b?"w-6 h-6 text-xs":"w-8 h-8 text-sm","flex justify-center items-center cursor-pointer rounded shadow-none","border-solid border border-slate-100 bg-transparent transition-colors duration-300","data-[active=true]:bg-slate-500 data-[active=true]:text-white focus-visible:outline-white focus-visible:outline-offset-4","hover:not-data-[active=true]:border-border-subtle",o?.item),children:i})},i)),c&&a("li",{children:a("button",{type:"button","aria-label":"Next page",disabled:n||r>=e,"data-disabled":n||r>=e||void 0,onClick:()=>p(r+1),className:l(g,o?.next),children:a(v,{className:"-rotate-90 w-4 h-4"})})})]})})}export{$ as Pagination,$ as default};
@@ -1 +1 @@
1
- import{Pagination as p}from"./Pagination.js";import"react/jsx-runtime";import"@heroui/pagination";import"../../utils/cn.js";export{p as Pagination};
1
+ import{Pagination as a}from"./Pagination.js";import"react/jsx-runtime";import"react";import"../../utils/cn.js";import"../Icon/DownArrow.js";export{a as Pagination};
@@ -1 +1 @@
1
- "use client";import{jsx as t,jsxs as i,Fragment as m}from"react/jsx-runtime";import{ProgressBar as u,Label as h}from"react-aria-components";import{motion as p}from"framer-motion";import{cn as r}from"../../utils/cn.js";function g({label:a,isIndeterminate:n,showValue:l=!0,width:d,className:s,classNames:e,...c}){return t(u,{style:{width:d||"100%"},className:r("overflow-hidden",s),...c,children:({percentage:o,valueText:f})=>i(m,{children:[i("div",{className:r("flex items-center justify-between gap-2 mb-1",e?.labelWrapper),children:[t(h,{className:r("text-sm",e?.label),children:a}),l&&t("span",{className:r("text-sm",e?.value),children:f})]}),t("div",{className:r("overflow-hidden forced-color-adjust-none h-2.5 rounded will-change-transform bg-slate-300",e?.trackWrapper),children:t(p.div,{style:{width:!n&&typeof o=="number"?`${o}%`:""},...n&&{animate:{x:["-100%","250px"]},transition:{repeat:1/0,duration:1.5}},className:r("h-full bg-surface-inverse-hover rounded transition-width duration-500 ease-in-out",{"w-1/2":n,"transition-none":n},e?.track)})})]})})}export{g as LinearProgressbar,g as default};
1
+ "use client";import{jsx as t,jsxs as i,Fragment as m}from"react/jsx-runtime";import{ProgressBar as u,Label as h}from"react-aria-components";import{m as p}from"framer-motion";import{cn as r}from"../../utils/cn.js";function g({label:a,isIndeterminate:n,showValue:l=!0,width:d,className:s,classNames:e,...c}){return t(u,{style:{width:d||"100%"},className:r("overflow-hidden",s),...c,children:({percentage:o,valueText:f})=>i(m,{children:[i("div",{className:r("flex items-center justify-between gap-2 mb-1",e?.labelWrapper),children:[t(h,{className:r("text-sm",e?.label),children:a}),l&&t("span",{className:r("text-sm",e?.value),children:f})]}),t("div",{className:r("overflow-hidden forced-color-adjust-none h-2.5 rounded will-change-transform bg-slate-300",e?.trackWrapper),children:t(p.div,{style:{width:!n&&typeof o=="number"?`${o}%`:""},...n&&{animate:{x:["-100%","250px"]},transition:{repeat:1/0,duration:1.5}},className:r("h-full bg-surface-inverse-hover rounded transition-width duration-500 ease-in-out",{"w-1/2":n,"transition-none":n},e?.track)})})]})})}export{g as LinearProgressbar,g as default};
@@ -1 +1 @@
1
- "use client";import{jsx as d,jsxs as u,Fragment as c}from"react/jsx-runtime";import{useContext as b}from"react";import{Tab as f}from"react-aria-components";import{motion as m}from"framer-motion";import{cn as i}from"../../utils/cn.js";import{TabsOrientationContext as p}from"./Tabs.context.js";function x({orientation:o,variant:e="base",className:t,children:l,...r}){return d(f,{className:i("flex items-center relative z-10","text-sm cursor-pointer forced-color-adjust-none","transition-all duration-300 ease-in-out","justify-center","disabled:opacity-50","selected:outline-0 hover:outline-0 hover:text-fg-muted",e==="underline"&&o==="vertical"&&"justify-start","py-2 px-3",e==="underline"&&o==="horizontal"&&"p-3",e==="underline"&&o==="vertical"&&"py-3 pr-3 pl-0",(e==="underline"||e==="outline"||e==="enclosed")&&"text-slate-800",e==="solid"&&"text-fg-subtle",(e==="underline"||e==="outline")&&"selected:text-slate-800",(e==="solid"||e==="enclosed")&&"selected:text-slate-100",t),...r,children:l})}function w({variant:o="base",children:e,className:t,classNames:l,...r}){const s=b(p);return d(x,{orientation:s,variant:o,className:t,...r,children:n=>u(c,{children:[typeof e=="function"?e({defaultChildren:void 0,...n}):e,(n.isFocusVisible||n.isSelected)&&d(m.span,{className:i("absolute z-0",o==="solid"&&"inset-0 rounded-lg bg-slate-600 mix-blend-color",o==="outline"&&"inset-0 rounded-lg border-solid border-2 border-surface-inverse-hover",o==="underline"&&"rounded-lg bg-surface-inverse-hover mix-blend-color bottom-0",o==="underline"&&s==="horizontal"&&"left-0 w-full h-px",o==="underline"&&s==="vertical"&&"right-0 w-px h-full",o==="enclosed"&&"inset-0 border-solid border-1 border-surface-inverse-hover -mb-px border-b-0 rounded-t-lg rounded-b-none bg-white mix-blend-difference",o==="base"&&"hidden",l?.overflow),layoutId:o,transition:{type:"spring",bounce:.2,duration:.75}})]})})}export{x as BaseTab,w as Tab,w as default};
1
+ "use client";import{jsx as d,jsxs as u,Fragment as c}from"react/jsx-runtime";import{useContext as b}from"react";import{Tab as f}from"react-aria-components";import{m}from"framer-motion";import{cn as i}from"../../utils/cn.js";import{TabsOrientationContext as p}from"./Tabs.context.js";function x({orientation:o,variant:e="base",className:l,children:t,...r}){return d(f,{className:i("flex items-center relative z-10","text-sm cursor-pointer forced-color-adjust-none","transition-all duration-300 ease-in-out","justify-center","disabled:opacity-50","selected:outline-0 hover:outline-0 hover:text-fg-muted",e==="underline"&&o==="vertical"&&"justify-start","py-2 px-3",e==="underline"&&o==="horizontal"&&"p-3",e==="underline"&&o==="vertical"&&"py-3 pr-3 pl-0",(e==="underline"||e==="outline"||e==="enclosed")&&"text-slate-800",e==="solid"&&"text-fg-subtle",(e==="underline"||e==="outline")&&"selected:text-slate-800",(e==="solid"||e==="enclosed")&&"selected:text-slate-100",l),...r,children:t})}function w({variant:o="base",children:e,className:l,classNames:t,...r}){const s=b(p);return d(x,{orientation:s,variant:o,className:l,...r,children:n=>u(c,{children:[typeof e=="function"?e({defaultChildren:void 0,...n}):e,(n.isFocusVisible||n.isSelected)&&d(m.span,{className:i("absolute z-0",o==="solid"&&"inset-0 rounded-lg bg-slate-600 mix-blend-color",o==="outline"&&"inset-0 rounded-lg border-solid border-2 border-surface-inverse-hover",o==="underline"&&"rounded-lg bg-surface-inverse-hover mix-blend-color bottom-0",o==="underline"&&s==="horizontal"&&"left-0 w-full h-px",o==="underline"&&s==="vertical"&&"right-0 w-px h-full",o==="enclosed"&&"inset-0 border-solid border-1 border-surface-inverse-hover -mb-px border-b-0 rounded-t-lg rounded-b-none bg-white mix-blend-difference",o==="base"&&"hidden",t?.overflow),layoutId:o,transition:{type:"spring",bounce:.2,duration:.75}})]})})}export{x as BaseTab,w as Tab,w as default};
@@ -1,2 +1,10 @@
1
- export declare const Tooltip: import('react').ForwardRefExoticComponent<Omit<import('@heroui/tooltip').TooltipProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
1
+ import { TooltipProps } from './Tooltip.types';
2
+ /**
3
+ * Native tooltip on react-aria-components (CTAP-1383) — previously a styled
4
+ * wrapper around @heroui/tooltip, which pulled the @heroui system into any
5
+ * chunk that used it. RAC is already this library's base. Same call-site API:
6
+ * `<Tooltip content={...} offset={12}>{trigger}</Tooltip>` with an arbitrary
7
+ * trigger node (Focusable makes non-interactive children hoverable/focusable).
8
+ */
9
+ export declare function Tooltip({ children, content, placement, offset, showArrow, isDisabled, delay, classNames, }: TooltipProps): import("react/jsx-runtime").JSX.Element;
2
10
  export default Tooltip;
@@ -1 +1 @@
1
- "use client";import{jsx as n}from"react/jsx-runtime";import{forwardRef as f}from"react";import{Tooltip as b}from"@heroui/tooltip";import{cn as t}from"../../utils/cn.js";const i=f(function({classNames:e,placement:a,showArrow:o=!1,...r},l){return n(b,{classNames:{base:[t(["z-0 relative bg-transparent outline-none","focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-500","before:absolute before:rotate-45 before:w-2.5 before:h-2.5 before:rounded-sm before:bg-slate-500 before:shadow-sm","before:hidden data-[arrow=true]:before:block","data-[placement=bottom]:before:-top-1 data-[placement=bottom]:before:left-1/2 data-[placement=bottom]:before:rotate-45 data-[placement=bottom]:before:-translate-x-1/2","data-[placement=bottom-end]:before:-top-1 data-[placement=bottom-end]:before:left-1/2 data-[placement=bottom-end]:before:rotate-45 data-[placement=bottom-end]:before:-translate-x-1/2","data-[placement=bottom-start]:before:-top-1 data-[placement=bottom-start]:before:left-1/2 data-[placement=bottom-start]:before:rotate-45 data-[placement=bottom-start]:before:-translate-x-1/2","data-[placement=left]:before:-right-1 data-[placement=left]:before:top-1/2 data-[placement=left]:before:-translate-y-1/2","data-[placement=left-end]:before:-right-1 data-[placement=left-end]:before:top-1/2 data-[placement=left-end]:before:-translate-y-1/2","data-[placement=left-start]:before:-right-1 data-[placement=left-start]:before:top-1/2 data-[placement=left-start]:before:-translate-y-1/2","data-[placement=right]:before:-left-1 data-[placement=right]:before:top-1/2 data-[placement=right]:before:-translate-y-1/2","data-[placement=right-end]:before:-left-1 data-[placement=right-end]:before:top-1/2 data-[placement=right-end]:before:-translate-y-1/2","data-[placement=right-start]:before:-left-1 data-[placement=right-start]:before:top-1/2 data-[placement=right-start]:before:-translate-y-1/2","data-[placement=top]:before:-bottom-1 data-[placement=top]:before:left-1/2 data-[placement=top]:before:rotate-45 data-[placement=top]:before:-translate-x-1/2","data-[placement=top-end]:before:-bottom-1 data-[placement=top-end]:before:left-1/2 data-[placement=top-end]:before:rotate-45 data-[placement=top-end]:before:-translate-x-1/2","data-[placement=top-start]:before:-bottom-1 data-[placement=top-start]:before:left-1/2 data-[placement=top-start]:before:rotate-45 data-[placement=top-start]:before:-translate-x-1/2",e?.base])],content:[t(["inline-flex flex-col items-center justify-center outline-none","w-full py-1 px-2.5 z-10 box-border bg-slate-500 rounded-md shadow-sm","text-sm text-white subpixel-antialiased",e?.content])]},placement:a,showArrow:o,...r,ref:l})});export{i as Tooltip,i as default};
1
+ "use client";import{jsxs as l,jsx as e}from"react/jsx-runtime";import{TooltipTrigger as d,Focusable as f,Tooltip as m,OverlayArrow as b}from"react-aria-components";import{cn as t}from"../../utils/cn.js";function v({children:i,content:n,placement:s="top",offset:r=8,showArrow:a=!1,isDisabled:u,delay:c=500,classNames:o}){return l(d,{delay:c,isDisabled:u,children:[e(f,{children:e("span",{role:"button",tabIndex:0,className:"inline-flex outline-none",children:i})}),l(m,{placement:s,offset:r,className:t("z-50 outline-none","focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-500",o?.base),children:[a&&e(b,{children:e("span",{className:"block rotate-45 w-2.5 h-2.5 -translate-y-1/2 rounded-sm bg-slate-500 shadow-sm"})}),e("div",{className:t("inline-flex flex-col items-center justify-center outline-none","w-full py-1 px-2.5 box-border bg-slate-500 rounded-md shadow-sm","text-sm text-white subpixel-antialiased",o?.content),children:n})]})]})}export{v as Tooltip,v as default};
@@ -1,2 +1,21 @@
1
- import { TooltipProps as UITooltipProps } from '@heroui/tooltip';
2
- export type TooltipProps = UITooltipProps;
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * Structural props (CTAP-1383) — previously re-exported from @heroui/tooltip.
4
+ * Covers the surface this repo uses; heroui prop names kept.
5
+ */
6
+ export type TooltipProps = {
7
+ children: ReactNode;
8
+ /** Tooltip body. */
9
+ content?: ReactNode;
10
+ placement?: 'top' | 'bottom' | 'left' | 'right' | 'top start' | 'top end' | 'bottom start' | 'bottom end';
11
+ /** Distance from the trigger, px. */
12
+ offset?: number;
13
+ showArrow?: boolean;
14
+ isDisabled?: boolean;
15
+ /** ms before showing (heroui: delay). */
16
+ delay?: number;
17
+ classNames?: {
18
+ base?: string;
19
+ content?: string;
20
+ };
21
+ };
@@ -1 +1 @@
1
- import{Tooltip as e}from"./Tooltip.js";import"react/jsx-runtime";import"react";import"@heroui/tooltip";import"../../utils/cn.js";export{e as Tooltip};
1
+ import{Tooltip as m}from"./Tooltip.js";import"react/jsx-runtime";import"react-aria-components";import"../../utils/cn.js";export{m as Tooltip};
@@ -1 +1 @@
1
- "use client";import{jsxs as l,Fragment as u,jsx as e}from"react/jsx-runtime";import{forwardRef as y,useState as v}from"react";import{motion as j}from"framer-motion";import{FieldError as V}from"react-aria-components";import{cn as r}from"../../utils/cn.js";import _ from"../../hooks/useIsClient.js";import{Skeleton as p}from"../Skeleton/Skeleton.js";function k({hasTitle:a,className:s}){return l("div",{className:"w-full h-full flex flex-col gap-4",children:[a&&e(p,{className:"w-1/2 h-8 mx-auto rounded-md z-20"}),e(p,{className:r("w-full h-56 rounded-md z-20 relative",s)})]})}const H=y(({title:a,description:s="Preview will display here.",fileExtension:m,actionTitle:g="select file",variant:i,errorMessage:h,defaultImagePath:n,classNames:f,...c},b)=>{const x=_(),[t,w]=v({src:null,alt:null}),C=!!h,N=m?m.split(",").map(o=>`image/${o.trim()}`).join(", "):"image/*";return l("div",{className:r("w-full h-full","flex flex-col gap-4"),children:[x?l(u,{children:[a&&e("h6",{className:r("text-center","text-2xl","m-0 mb-4"),children:a}),l("div",{className:r("w-full h-full","flex flex-col justify-between gap-5 items-center",["bg-surface-subtle",i==="base"&&"bg-transparent"],"rounded-md",[i==="base"&&"rounded-none"],["p-6 lg:p-7",i==="base"&&"p-0 lg:p-0"]),children:[l("label",{htmlFor:`${c?.name}-upload-file`,"aria-label":"image upload",onChange:o=>{const{target:{files:d}}=o;d&&w({src:URL.createObjectURL(d[0]),alt:d[0].name})},className:r("inline-flex justify-center items-center","uppercase text-base border-border-subtle","bg-transparent","border-1 border-solid","px-4 py-0","rounded-2xl","cursor-pointer","w-full h-9",["max-w-48",i==="base"&&"max-w-none"],"my-0 mx-auto","has-[:disabled]:bg-transparent has-[:disabled]:text-fg-strong","hover:bg-slate-100s hover:text-black",f?.label),children:[g,e("input",{id:`${c?.name}-upload-file`,accept:N,type:"file",hidden:!0,...c,ref:b,className:"hidden"})]}),n&&!t.src?e("div",{className:"flex justify-center items-center w-60",children:e("a",{href:n,target:"_blank",rel:"noreferrer",children:e("img",{src:n,alt:"",className:"w-full h-auto"})})}):l(u,{children:[e(j.div,{initial:{opacity:0},animate:{opacity:1},transition:{type:"spring",duration:3,bounce:0},className:"flex justify-center items-center w-52 h-auto",children:t.src?e("img",{src:t.src,height:96,width:200,alt:t.alt||"",className:"max-h-full object-contain"}):l("svg",{xmlns:"http://www.w3.org/2000/svg",width:"31",height:"55",viewBox:"0 0 31 55",fill:"none",children:[e("g",{clipPath:"url(#clip0_15607_32844)",children:e("path",{d:"M15.9521 0.721924C24.2431 0.721924 30.9521 7.36392 30.9521 15.5719V43.9219C30.9521 49.8889 26.0703 54.7219 20.0431 54.7219C14.0158 54.7219 9.13397 49.8889 9.13397 43.9219V20.9719C9.13397 17.2459 12.1885 14.2219 15.9521 14.2219C19.7158 14.2219 22.7703 17.2459 22.7703 20.9719V41.2219H17.3158V20.7289C17.3158 19.2439 14.5885 19.2439 14.5885 20.7289V43.9219C14.5885 46.8919 17.0431 49.3219 20.0431 49.3219C23.0431 49.3219 25.4976 46.8919 25.4976 43.9219V15.5719C25.4976 10.3609 21.2158 6.12192 15.9521 6.12192C10.6885 6.12192 6.40669 10.3609 6.40669 15.5719V41.2219H0.952148V15.5719C0.952148 7.36392 7.66124 0.721924 15.9521 0.721924Z",fill:"#646464"})}),e("defs",{children:e("clipPath",{id:"clip0_15607_32844",children:e("rect",{width:"30",height:"54",fill:"white",transform:"translate(0.952148 0.721924)"})})})]})},t.src),e("p",{className:"m-0 text-fg-strong text-center",children:t.alt||s})]})]})]}):e(k,{hasTitle:!!a,className:f?.skeleton}),C&&e(V,{className:r("-mt-2","self-start"),children:h})]})});export{H as UploadImage,H as default};
1
+ "use client";import{jsxs as l,Fragment as u,jsx as e}from"react/jsx-runtime";import{forwardRef as y,useState as v}from"react";import{m as j}from"framer-motion";import{FieldError as V}from"react-aria-components";import{cn as r}from"../../utils/cn.js";import _ from"../../hooks/useIsClient.js";import{Skeleton as p}from"../Skeleton/Skeleton.js";function k({hasTitle:a,className:s}){return l("div",{className:"w-full h-full flex flex-col gap-4",children:[a&&e(p,{className:"w-1/2 h-8 mx-auto rounded-md z-20"}),e(p,{className:r("w-full h-56 rounded-md z-20 relative",s)})]})}const H=y(({title:a,description:s="Preview will display here.",fileExtension:m,actionTitle:g="select file",variant:i,errorMessage:h,defaultImagePath:n,classNames:f,...c},b)=>{const x=_(),[t,w]=v({src:null,alt:null}),C=!!h,N=m?m.split(",").map(o=>`image/${o.trim()}`).join(", "):"image/*";return l("div",{className:r("w-full h-full","flex flex-col gap-4"),children:[x?l(u,{children:[a&&e("h6",{className:r("text-center","text-2xl","m-0 mb-4"),children:a}),l("div",{className:r("w-full h-full","flex flex-col justify-between gap-5 items-center",["bg-surface-subtle",i==="base"&&"bg-transparent"],"rounded-md",[i==="base"&&"rounded-none"],["p-6 lg:p-7",i==="base"&&"p-0 lg:p-0"]),children:[l("label",{htmlFor:`${c?.name}-upload-file`,"aria-label":"image upload",onChange:o=>{const{target:{files:d}}=o;d&&w({src:URL.createObjectURL(d[0]),alt:d[0].name})},className:r("inline-flex justify-center items-center","uppercase text-base border-border-subtle","bg-transparent","border-1 border-solid","px-4 py-0","rounded-2xl","cursor-pointer","w-full h-9",["max-w-48",i==="base"&&"max-w-none"],"my-0 mx-auto","has-[:disabled]:bg-transparent has-[:disabled]:text-fg-strong","hover:bg-slate-100s hover:text-black",f?.label),children:[g,e("input",{id:`${c?.name}-upload-file`,accept:N,type:"file",hidden:!0,...c,ref:b,className:"hidden"})]}),n&&!t.src?e("div",{className:"flex justify-center items-center w-60",children:e("a",{href:n,target:"_blank",rel:"noreferrer",children:e("img",{src:n,alt:"",className:"w-full h-auto"})})}):l(u,{children:[e(j.div,{initial:{opacity:0},animate:{opacity:1},transition:{type:"spring",duration:3,bounce:0},className:"flex justify-center items-center w-52 h-auto",children:t.src?e("img",{src:t.src,height:96,width:200,alt:t.alt||"",className:"max-h-full object-contain"}):l("svg",{xmlns:"http://www.w3.org/2000/svg",width:"31",height:"55",viewBox:"0 0 31 55",fill:"none",children:[e("g",{clipPath:"url(#clip0_15607_32844)",children:e("path",{d:"M15.9521 0.721924C24.2431 0.721924 30.9521 7.36392 30.9521 15.5719V43.9219C30.9521 49.8889 26.0703 54.7219 20.0431 54.7219C14.0158 54.7219 9.13397 49.8889 9.13397 43.9219V20.9719C9.13397 17.2459 12.1885 14.2219 15.9521 14.2219C19.7158 14.2219 22.7703 17.2459 22.7703 20.9719V41.2219H17.3158V20.7289C17.3158 19.2439 14.5885 19.2439 14.5885 20.7289V43.9219C14.5885 46.8919 17.0431 49.3219 20.0431 49.3219C23.0431 49.3219 25.4976 46.8919 25.4976 43.9219V15.5719C25.4976 10.3609 21.2158 6.12192 15.9521 6.12192C10.6885 6.12192 6.40669 10.3609 6.40669 15.5719V41.2219H0.952148V15.5719C0.952148 7.36392 7.66124 0.721924 15.9521 0.721924Z",fill:"#646464"})}),e("defs",{children:e("clipPath",{id:"clip0_15607_32844",children:e("rect",{width:"30",height:"54",fill:"white",transform:"translate(0.952148 0.721924)"})})})]})},t.src),e("p",{className:"m-0 text-fg-strong text-center",children:t.alt||s})]})]})]}):e(k,{hasTitle:!!a,className:f?.skeleton}),C&&e(V,{className:r("-mt-2","self-start"),children:h})]})});export{H as UploadImage,H as default};
@@ -0,0 +1,104 @@
1
+ # A shared zod 4 error map, so every app stops hand-rolling `config/zod.ts`
2
+
3
+ **Date:** 2026-08-14
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** any consumer on **zod 4**. Nothing changes for a zod 3 app, or for one not using zod
6
+ at all.
7
+ **Classification:** IMPROVEMENT
8
+ **Breaking:** No. Purely additive — one new subpath. `@clicktap/ui` gains **no** dependency on zod,
9
+ so nothing about your install changes.
10
+
11
+ ## Why this exists
12
+
13
+ zod 4 dropped zod 3's readable defaults. Verified against zod 4.4.3:
14
+
15
+ ```
16
+ undefined against z.string() -> "Invalid input: expected string, received undefined"
17
+ z.string().min(8) -> "Too small: expected string to have >=8 characters"
18
+ ```
19
+
20
+ Both are developer output. Neither should reach a visitor.
21
+
22
+ **The first cannot be fixed with per-field messages, and that is the part that catches people out.**
23
+ react-hook-form leaves an untouched field as `undefined` (inputs render `value ?? ''`), so
24
+ `.min(1, '…')` and `.nonempty({ message })` only ever see an empty string — their message never
25
+ applies to the missing case. Submitting an untouched form is the common path, so nearly every field
26
+ in every app is exposed. It has to be fixed globally.
27
+
28
+ ## Usage
29
+
30
+ ```ts
31
+ import { z } from 'zod';
32
+ import { configureZodErrorMap } from '@clicktap/ui/utils/zod';
33
+
34
+ configureZodErrorMap(z);
35
+ ```
36
+
37
+ **You pass `z` in, and that is deliberate.** This package does not depend on zod — not on a version
38
+ and not on a copy. The map has to land on the zod instance your schemas were built with, and an app
39
+ on zod 3 must be able to install `@clicktap/ui` without a stray zod 4 appearing in its type graph.
40
+ That last one is not hypothetical: adding zod 4 to this library, even only as a dev dependency,
41
+ made `@hookform/resolvers`' `zodResolver` resolve zod 4's types against the reference app's zod 3
42
+ schemas and produced 29 typecheck errors across it. Passing the namespace makes the instance
43
+ explicit and costs one argument.
44
+
45
+ Call it **once per module registry**. In a Next.js app that is two places, because the server and
46
+ browser bundles are separate registries and a module evaluated in one is not evaluated in the other:
47
+
48
+ ```ts
49
+ // app/layout.tsx — SSR and server actions
50
+ // app/providers.tsx — 'use client', the browser
51
+ configureZodErrorMap(z);
52
+ ```
53
+
54
+ It is a plain idempotent function rather than a side-effecting import on purpose: calling it twice
55
+ is harmless, and an explicit call is not something a bundler can tree-shake away. That also answers
56
+ the "structure it so there is only one import point" question — there is no structure that can
57
+ collapse two module registries into one, so the API is at least honest about needing both calls.
58
+
59
+ ### Customising
60
+
61
+ ```ts
62
+ configureZodErrorMap(z, {
63
+ required: 'Required.',
64
+ constraints: { stringTooSmall: (min) => `At least ${min}, please.` },
65
+ });
66
+
67
+ configureZodErrorMap(z, { constraints: false }); // only rewrite the missing-value case
68
+ ```
69
+
70
+ ## Precedence — per-check messages still win
71
+
72
+ The map returns `undefined` for anything it does not handle, so zod falls back to whatever message
73
+ is attached at the schema. Covered by tests:
74
+
75
+ | schema | input | message |
76
+ | --- | --- | --- |
77
+ | `z.string().min(8, 'Pick a longer password.')` | `'abc'` | `Pick a longer password.` |
78
+ | `z.string().min(8)` | `'abc'` | `Must be at least 8 characters.` |
79
+ | `z.string().min(1, 'Enter your name.')` | `''` | `Enter your name.` |
80
+ | `z.string().min(1, 'Enter your name.')` | `undefined` | `This field is required.` |
81
+ | `z.email()` | `'nope'` | `Invalid email address` (zod's own, already readable) |
82
+
83
+ The fourth row is the one worth staring at: the field's own message loses to the global map,
84
+ because the field was never filled in and its `.min(1)` check never ran.
85
+
86
+ ## Migration checklist
87
+
88
+ - [ ] **Delete your hand-rolled `config/zod.ts`** and call `configureZodErrorMap(z)` from the same
89
+ two places the side-effect import was made.
90
+ - [ ] Nothing to install — you supply your own `z`.
91
+ - [ ] If you are on zod 3, ignore this entirely.
92
+
93
+ ## Note for maintainers
94
+
95
+ The behaviour above was verified against a real zod 4.4.3 before the API was settled; the unit tests
96
+ then drive the map with issue fixtures captured from that run, since the package carries no zod to
97
+ test against.
98
+
99
+ An earlier attempt imported zod directly and declared it an optional peer. Two things killed it, in
100
+ order of discovery: the library build **inlined zod's own global-config implementation**
101
+ (`globalThis.__zod_globalConfig` and the assignment around it) rather than importing it — visible
102
+ only by reading `dist/`, because the in-repo import resolves through the tsconfig path alias and
103
+ looks identical either way — and installing zod 4 here to test against broke the zod-3 reference
104
+ app's typecheck. Taking the namespace as an argument removes both problems at once.
@@ -0,0 +1,45 @@
1
+ # GraphQL Client Executes String Documents — graphql-request Removed
2
+
3
+ **Date:** 2026-08-21
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** `utils/createAuthAwareGraphqlClient` (public API)
6
+ **Classification:** COMPATIBILITY (breaking)
7
+ **Breaking:** Yes. AST `TypedDocumentNode` documents are no longer accepted; the `GraphQLClient` re-export is gone; `ClientError` is now this module's own class.
8
+ **Ticket:** CTAP-1383
9
+
10
+ ## Problem
11
+
12
+ The auth-aware client wrapped `graphql-request`, whose static imports put the whole
13
+ `graphql` package (parser, lexer, printer, AST machinery) plus `cross-fetch` into every
14
+ consumer page's initial JS — ~30 KB gz that existed only to turn an AST back into the
15
+ query text codegen started from.
16
+
17
+ ## Changes
18
+
19
+ - `createAuthAwareGraphqlClient()` executes documents as **text**: one JSON POST via
20
+ `fetch`. Same call signatures (`request(url, document, variables?, requestHeaders?)`
21
+ and `.withOptions({ …, signal })`), same partial-response semantics (HTTP 200 with
22
+ `data` + `errors` returns the partial data), same 401/403/5xx events on
23
+ `authAwareEvents`, same dev field-error logging.
24
+ - **Documents must be strings** — codegen `TypedDocumentString`
25
+ (`documentMode: 'string'`) or plain strings. The new `GraphqlDocument<TData,
26
+ TVariables>` type carries result/variable typing structurally, so response-type
27
+ inference at call sites is unchanged.
28
+ - **`ClientError` is now defined here**, shape-compatible with `graphql-request`'s
29
+ (`response.status/data/errors`, `request.query/variables`). `instanceof ClientError`
30
+ against this module's export now always matches — the dual-package hazard the old
31
+ re-export warned about is gone. The identity-agnostic `getRequestErrorStatus` from
32
+ `utils/error` continues to work untouched.
33
+ - **Removed:** the `GraphQLClient` re-export (nothing to re-export), and the
34
+ `graphql-request` dependency (dev + peer).
35
+
36
+ ## Migration
37
+
38
+ 1. Switch codegen to string documents: `presetConfig`/`config` → `documentMode:
39
+ 'string'` (client-preset ≥ 4 for `TypedDocumentString`), regenerate.
40
+ 2. Replace `TypedDocumentNode`/`RequestDocument` types at wrapper boundaries with
41
+ `GraphqlDocument` / `GraphqlVariables` from this module; `GraphQLClientRequestHeaders`
42
+ with the platform `HeadersInit`.
43
+ 3. Sites that imported `ClientError` from `graphql-request` import it from here.
44
+ 4. A call that must send `FormData` or custom cors/credentials keeps using
45
+ `createAuthAwareFetch()` (unchanged) and `String(document)` for the query text.
@@ -0,0 +1,26 @@
1
+ # `graphql` Peer Is Covered — No More Double Bundle
2
+
3
+ **Date:** 2026-08-21
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** consumers' bundle size (~12 KB gz of duplicated `graphql`)
6
+ **Classification:** BUGFIX (dependency resolution)
7
+ **Breaking:** No. `graphql ^16.6.0` is an optional peer, matching `@clicktap/state`.
8
+ **Ticket:** CTAP-1383
9
+
10
+ ## Problem
11
+
12
+ The lib declared `graphql-request` (dev + optional peer) but not `graphql` itself.
13
+ With `auto-install-peers`, pnpm resolved `graphql-request`'s `graphql ^16` peer to the
14
+ **newest** matching version (16.14.0) while the app and `@clicktap/state` pinned 16.12.0.
15
+ Two full copies of the `graphql` package — parser, lexer, printer — shipped in every
16
+ page's initial JS. (The uncovered-peer-is-a-floating-install trap.)
17
+
18
+ ## Changes
19
+
20
+ `graphql` is now declared: `16.12.0` in devDependencies, `^16.6.0` as an optional peer
21
+ (mirroring `@clicktap/state`). The whole runtime graph resolves a single `graphql@16.12.0`.
22
+
23
+ ## What to check
24
+
25
+ After `pnpm install`, `ls node_modules/.pnpm | grep '^graphql@'` in a consuming app
26
+ should show one runtime version reachable from the frontend graph.
@@ -0,0 +1,54 @@
1
+ # The @heroui Dependency Is Gone — Accordion, Tooltip, Pagination, Badge Are Native
2
+
3
+ **Date:** 2026-08-21
4
+ **Package:** `@clicktap/ui`
5
+ **Ticket:** CTAP-1383 (epic CTAP-1380)
6
+ **Affects:** Accordion, Tooltip, Pagination, Badge. Peer/dev dependencies on `@heroui/*` removed.
7
+ **Classification:** COMPATIBILITY
8
+ **Breaking:** API-compatible for every usage shape in the reference repo (inventoried below). Consumers importing `@heroui`/`@nextui-org` types directly must switch to this package's exports.
9
+
10
+ ## Why
11
+
12
+ Four components wrapped `@heroui/*` while supplying ALL of their own visual
13
+ styling — the wrapper bought two DOM elements and a positioning engine, and
14
+ cost the whole @heroui system + theme (~25 KiB gz) in the initial JS of every
15
+ page, anchored by the PLP filter sidebar's Accordion. This library's actual
16
+ base is react-aria-components; the rewrites cost nothing new:
17
+
18
+ - **Accordion** → RAC `DisclosureGroup`/`Disclosure`. Height animates with
19
+ the CSS grid-rows trick (no animation runtime).
20
+ - **Tooltip** → RAC `TooltipTrigger`/`Tooltip`/`OverlayArrow` (`Focusable`
21
+ makes arbitrary trigger nodes hoverable).
22
+ - **Pagination** → native boundary/sibling windowing.
23
+ - **Badge** → the two wrapper elements, inline.
24
+
25
+ ## Preserved API surface (inventoried from real call sites)
26
+
27
+ - Accordion: `selectionMode`, `defaultSelectedKeys` (incl. `"all"`),
28
+ `defaultExpandedKeys`, controlled `selectedKeys` + `onSelectionChange`,
29
+ `disallowEmptySelection`, `hideIndicator` (group and per-item),
30
+ `showDivider`/`dividerProps` (an `<hr>` between items — `[&_hr]:hidden`
31
+ keeps working), `itemClasses`/per-item `classNames` slots, per-item
32
+ `title`/`subtitle`/`startContent`/`indicator` (a component still receives
33
+ `{ isOpen }` — import `AccordionItemIndicatorProps` from HERE now),
34
+ `disableIndicatorAnimation`, `onPress`, and the `data-open` attribute on
35
+ heading/trigger/indicator that `data-[open=true]:` selectors style.
36
+ - Tooltip: `content`, `offset`, `placement`, `showArrow`, `delay`,
37
+ `isDisabled`, `classNames.base/content`, arbitrary child as trigger.
38
+ - Pagination: `total`, `page`/`initialPage`, `onChange`, `boundaries`,
39
+ `siblings`, `showControls`, `isCompact`, `classNames` slots incl.
40
+ `data-[active=true]`/`data-[disabled=true]` styling hooks.
41
+ - Badge: unchanged names (`content`, `isInvisible`, `placement`, `shape`,
42
+ `size`, `variant`, `showOutline`, `isDot`, `classNames`).
43
+
44
+ ## What you need to do
45
+
46
+ - Remove `@heroui/*` and `@nextui-org/*` from your app's dependencies if they
47
+ existed only for these components or their types; import the types from
48
+ this package instead (`AccordionItemIndicatorProps` moved here).
49
+ - If you used heroui props OUTSIDE the surface above, check the component
50
+ source — the compat layer is deliberate, not exhaustive.
51
+ - Verified on the reference storefront: PLP filter accordion (expand/
52
+ collapse/apply, all-open default, indicator rotation), checkout accordions
53
+ (controlled single-selection payment list, promo/gift-card items), cart
54
+ badge, dividers, `data-open` styling. ~54 KiB gz off the initial bundle.
@@ -0,0 +1,54 @@
1
+ # Animated Components Render `m.*` — Apps Must Provide LazyMotion Features
2
+
3
+ **Date:** 2026-08-21
4
+ **Package:** `@clicktap/ui`
5
+ **Ticket:** CTAP-1383 (epic CTAP-1380)
6
+ **Affects:** every animated component (ModalOverlay, Drawer, Dialog, Menu, ContextMenu, Tabs, Badge, Collapsible, NumberTicker, Meter, Progressbar, UploadImage).
7
+ **Classification:** COMPATIBILITY
8
+ **Breaking:** Animations no-op (components still render and function) until the consuming app mounts a `LazyMotion` provider.
9
+
10
+ ## What changed
11
+
12
+ Every framer-motion element in this library is now `m.*` instead of
13
+ `motion.*`. Under framer's LazyMotion pattern, `m` components carry no
14
+ animation runtime of their own — the feature set loads from a provider the
15
+ APP mounts, as an async chunk.
16
+
17
+ Why: `motion.*` imports statically bundle the full framer runtime (~38 KiB
18
+ gz). Because overlay primitives (ModalOverlay, Drawer, Badge) sit in every
19
+ storefront's layout, that runtime shipped in the initial JS of every page.
20
+ With `m.*`, the initial bundle carries only framer's slim core and the
21
+ features arrive async — measured ~0.2-0.5s of simulated-mobile LCP on the
22
+ reference storefront.
23
+
24
+ ## What you need to do
25
+
26
+ Mount a `LazyMotion` provider above everything that renders these components
27
+ (both root layouts in the reference app):
28
+
29
+ ```tsx
30
+ 'use client';
31
+ import { LazyMotion } from 'framer-motion';
32
+
33
+ const loadFeatures = () => import('./motionFeatures').then((m) => m.default);
34
+ // motionFeatures.ts: import { domMax } from 'framer-motion'; export default domMax;
35
+
36
+ export function MotionProvider({ children }) {
37
+ return <LazyMotion features={loadFeatures}>{children}</LazyMotion>;
38
+ }
39
+ ```
40
+
41
+ Two load-bearing details:
42
+
43
+ - the features import lives in its OWN file — inlining `domMax` into the
44
+ provider keeps it in the provider's (initial) chunk, defeating the point;
45
+ - use `domMax` if anything in your tree uses layout animations
46
+ (`layout`/`layoutId` props); `domAnimation` otherwise.
47
+
48
+ Without a provider nothing errors: components mount, function, and simply
49
+ appear without transitions. If your drawers/dialogs "stopped animating"
50
+ after this release, the missing provider is why.
51
+
52
+ Do not use `strict` on the provider if any code in the tree still renders
53
+ full `motion.*` — full-motion elements are self-contained and coexist fine
54
+ without strict.
@@ -0,0 +1,43 @@
1
+ # Modal Triggers Are Wired Explicitly; Overlay Animation Back on `motion`
2
+
3
+ **Date:** 2026-08-21
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** DialogTrigger, ModalOverlay, Dialog
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No. Same component APIs; consumer `onPress` on the trigger child still runs (chained).
8
+ **Ticket:** CTAP-1383
9
+
10
+ ## Problems
11
+
12
+ 1. **Trigger-driven modals never opened** — every uncontrolled `DialogTrigger` modal
13
+ (account address/payment create-edit-delete, delete confirmations) rendered an inert
14
+ button: react-aria-components wires its trigger through `PressResponderContext`, and in
15
+ this app's production bundles that registration silently never reaches the Button (no
16
+ aria attributes applied, press never toggles state) — with a verified-single instance
17
+ of react-aria-components, @react-aria/interactions, react, and framer-motion in the
18
+ bundle, so it is NOT the dual-package hazard CTAP-1210 fixed. Every working modal in
19
+ the app (FilterButton, RegionSelectAction, CartDrawer) works because it passes an
20
+ explicit `onPress`/controlled `isOpen`. Evidence says this has been broken on master
21
+ too (the same missing aria attributes on QA's trigger buttons).
22
+ 2. **`m.create` broke controlled modals** — the CTAP-1383 LazyMotion conversion switched
23
+ `motion.create(...)` to `m.create(...)` in ModalOverlay/Dialog. In production browser
24
+ bundles the m-created overlay never mounts its portal (works in jsdom; browser-only).
25
+ Verified by A/B builds: identical code with `motion.create` opens, `m.create` does not.
26
+
27
+ ## Changes
28
+
29
+ - `DialogTrigger` no longer wraps react-aria-components' DialogTrigger. It owns a
30
+ `useOverlayTriggerState`, provides it on `OverlayTriggerStateContext` (which both our
31
+ ModalOverlay and react-aria's read), and clones the first child with an explicit
32
+ `onPress` that toggles the state — the mechanism the rest of the app already proves
33
+ out. A consumer-supplied `onPress` is chained before the toggle. The `useDialogTrigger`
34
+ shim is unchanged.
35
+ - `ModalOverlay`/`Dialog` are back on `motion.create` (costs ~18 KB gz of initial JS vs
36
+ the m form — the price of modals that open). Root-causing why `m.create` + react-aria
37
+ overlays fail in production bundles is a follow-up; if solved, the m form can return.
38
+
39
+ ## What to check
40
+
41
+ Any `DialogTrigger` + `Button` + `ModalOverlay` combination opens on press — uncontrolled
42
+ (account pages) and controlled (PLP filter) alike. Dialog `aria-label`s are now the only
43
+ labelling source (the react-aria trigger-id fallback is gone) — our dialogs all pass one.
@@ -0,0 +1,38 @@
1
+ # next Image: the optimizer-failure fallback keeps fill semantics
2
+
3
+ **Date:** 2026-08-21
4
+ **Package:** `@clicktap/ui`
5
+ **Ticket:** CTAP-1376 (found during the CTAP-1375 QA incident)
6
+ **Affects:** `@clicktap/ui/next/Image` — every `fill`-mode usage. No consumer code change.
7
+ **Classification:** BUGFIX
8
+ **Breaking:** No
9
+
10
+ ## The failure this removes
11
+
12
+ When `next/image` optimization fails, this wrapper bypasses it and renders the
13
+ raw src as a plain `<img>` (the `onError` debounce path and the render-error
14
+ boundary). Both forwarded `className`, `style` and `width`/`height` — but a
15
+ `fill` image has no width/height props, and its positioning
16
+ (`position:absolute; inset:0; height/width:100%`) is inline style **NextImage
17
+ itself adds**, which the fallback bypassed along with the optimizer.
18
+
19
+ So a fill image whose optimizer fetch failed degraded catastrophically instead
20
+ of gracefully: before the raw src loaded it sat as a broken icon at its
21
+ container's top-left; once loaded it rendered at NATURAL size — a 2880px
22
+ category hero escaping its `relative overflow-hidden` banner and filling
23
+ multiple viewports. With an environment-wide optimizer failure (CTAP-1375:
24
+ media URLs pointing at an unreachable host) that was every banner on the site.
25
+
26
+ ## The fix
27
+
28
+ When the props include `fill`, both fallback `<img>`s re-apply the same inline
29
+ styles NextImage would have (`position:absolute; height/width:100%;
30
+ left/top/right/bottom:0`), merged under the caller's `style`. `object-fit`
31
+ still arrives via the forwarded `className` (`object-cover` etc.). Sized
32
+ images (width/height props) are byte-for-byte unchanged.
33
+
34
+ ## What you need to do
35
+
36
+ Nothing — take the release. If you have ever worked around this with wrapper
37
+ divs or manual absolute positioning around a fill-mode `Image` specifically
38
+ for its error state, that can come off.
@@ -0,0 +1,30 @@
1
+ # Accordion Divider Renders BETWEEN Items; react-aria Stack Aligned
2
+
3
+ **Date:** 2026-08-22
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** Accordion consumers using `dividerProps` as an inter-item spacer (checkout payment methods); every react-aria-components consumer
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No (peer ranges raised: react-aria ^3.48, react-aria-components ^1.17, react-stately ^3.46).
8
+ **Ticket:** CTAP-1383
9
+
10
+ ## Problems
11
+
12
+ 1. **Divider inside the item box.** The CTAP-1383 Accordion rewrite rendered the
13
+ between-items `<hr>` INSIDE each Disclosure — inside the consumer's bordered
14
+ `itemClasses.base` box. The checkout payment list styles the divider as an invisible
15
+ `h-4` spacer meant as a GAP between boxes, so every payment method carried ~32px of
16
+ dead space under its title. heroui rendered dividers between items; the rewrite now
17
+ does too (a sibling after each Disclosure).
18
+ 2. **Mixed react-aria internals.** The repo pinned react-aria 3.46 / react-aria-components
19
+ 1.15 while pnpm resolved newer @react-aria internals into their closures. Production
20
+ bundles with that mix showed a family of RAC-internal failures jsdom never caught:
21
+ Table rows losing their cells on hover (account orders emptying), and the
22
+ PressResponder trigger wiring never reaching Buttons. Dev/peer versions now align on
23
+ the coherent set: react-aria 3.48.0, react-aria-components 1.17.0, react-stately
24
+ 3.46.0 — verified to fix the Table hover failure outright.
25
+
26
+ ## What to check
27
+
28
+ Payment methods render as compact bordered rows (divider = gap, not in-box padding).
29
+ Account orders survive hovering/navigating rows. Bordered/splitted Accordion variants
30
+ keep their internal spacing (those style the divider as a visible rule, unaffected).
@@ -0,0 +1,14 @@
1
+ # Accordion `dividerProps.className` Owns the Divider Outright
2
+
3
+ **Date:** 2026-08-22
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** Accordion consumers styling the inter-item divider
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No.
8
+ **Ticket:** CTAP-1383
9
+
10
+ A consumer-supplied `dividerProps.className` now replaces the default divider classes
11
+ (spacing and color) instead of stacking on top of them. The checkout payment list
12
+ styles the divider as an invisible `h-4` spacer; with the default `my-2` still applied
13
+ the gap between items doubled to 32px. Consumers that pass no className keep the
14
+ default `my-2` rule.
@@ -0,0 +1,46 @@
1
+ # react-aria Moves as One Version Set
2
+
3
+ **Date:** 2026-08-22
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** every consumer of react-aria-components — dependency policy, not an API change
6
+ **Classification:** COMPATIBILITY
7
+ **Read before updating:** never bump `react-aria`, `react-aria-components`, or `react-stately` independently — skewed internals fail silently, and only in production bundles.
8
+ **Ticket:** CTAP-1383
9
+
10
+ ## The finding
11
+
12
+ The repo pinned `react-aria-components@1.15.1` / `react-aria@3.46.0` while pnpm resolved
13
+ NEWER `@react-aria/*` subpackages into their closures (an ordinary lockfile drift — the
14
+ packages install fine, typecheck fine, and every jest suite passes). The mixed internals
15
+ produced a family of failures that exist **only in production browser bundles**:
16
+
17
+ - **Uncontrolled `DialogTrigger` modals were dead.** The PressResponder trigger wiring
18
+ never reached the Button: the trigger props arrived structurally but valueless, presses
19
+ never toggled the overlay state. Every account modal (address/payment create, edit,
20
+ delete confirmations) silently did nothing — on master too.
21
+ - **`Table` rows lost their cells on hover.** Sweeping the pointer across the account
22
+ orders table emptied every `<tr>` (row elements remain, children gone) — the "my orders
23
+ disappear when I hover them" report. Click-through and back-navigation compounded it.
24
+ - Likely related, unproven: `m.create()`-wrapped RAC overlays never mounting their portal
25
+ (see the modal-triggers guide) — same only-in-production signature.
26
+
27
+ None of this reproduces in jsdom. All package instances were verified single-copy in the
28
+ bundle (this is NOT the CTAP-1210 dual-package hazard), which is what makes the skew so
29
+ hard to see: the failure lives in version-mismatched internals of one coherent-looking
30
+ install.
31
+
32
+ ## The rule
33
+
34
+ `react-aria`, `react-aria-components`, and `react-stately` are released together and must
35
+ be adopted together. This package's devDependencies pin one coherent set (currently
36
+ 3.48.0 / 1.17.0 / 3.46.0) and its peer ranges floor consumers at the same set; the
37
+ reference app pins the identical versions. When bumping, take the trio from the same
38
+ react-aria release announcement and re-run the production-bundle checks below —
39
+ aligning to the coherent set fixed the Table failure outright with no code change.
40
+
41
+ ## How to verify (jsdom cannot)
42
+
43
+ Probe a production build in a real browser: open an uncontrolled DialogTrigger modal
44
+ (account → Add Address), hover-sweep the orders table rows and count `[role="row"]`
45
+ children, and diff `.pnpm` instance DIRS — key on the full directory name, not
46
+ `name@version`, or two peer-hash instances of the same version collapse into one.