@hjmds/react 1.33.2 → 1.35.0

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 (137) hide show
  1. package/dist/actions.d.ts +23 -2
  2. package/dist/actions.d.ts.map +1 -1
  3. package/dist/actions.js +61 -6
  4. package/dist/actions.js.map +1 -1
  5. package/dist/advanced-display.d.ts +35 -2
  6. package/dist/advanced-display.d.ts.map +1 -1
  7. package/dist/advanced-display.js +28 -9
  8. package/dist/advanced-display.js.map +1 -1
  9. package/dist/anchor.d.ts +3 -1
  10. package/dist/anchor.d.ts.map +1 -1
  11. package/dist/anchor.js +5 -5
  12. package/dist/anchor.js.map +1 -1
  13. package/dist/auth-screen.d.ts.map +1 -1
  14. package/dist/auth-screen.js +5 -2
  15. package/dist/auth-screen.js.map +1 -1
  16. package/dist/avatar-blobatar-motion.d.ts.map +1 -1
  17. package/dist/avatar-blobatar-motion.js +2 -1
  18. package/dist/avatar-blobatar-motion.js.map +1 -1
  19. package/dist/avatar-blobatar.d.ts.map +1 -1
  20. package/dist/avatar-blobatar.js +2 -1
  21. package/dist/avatar-blobatar.js.map +1 -1
  22. package/dist/bottom-cta.d.ts +8 -1
  23. package/dist/bottom-cta.d.ts.map +1 -1
  24. package/dist/bottom-cta.js +6 -2
  25. package/dist/bottom-cta.js.map +1 -1
  26. package/dist/carousel-motion.d.ts.map +1 -1
  27. package/dist/carousel-motion.js +11 -2
  28. package/dist/carousel-motion.js.map +1 -1
  29. package/dist/carousel.d.ts +10 -0
  30. package/dist/carousel.d.ts.map +1 -1
  31. package/dist/carousel.js +23 -7
  32. package/dist/carousel.js.map +1 -1
  33. package/dist/collapsible.d.ts +6 -0
  34. package/dist/collapsible.d.ts.map +1 -1
  35. package/dist/collapsible.js +22 -20
  36. package/dist/collapsible.js.map +1 -1
  37. package/dist/collection-rail.d.ts +4 -4
  38. package/dist/collection-rail.d.ts.map +1 -1
  39. package/dist/collection-rail.js.map +1 -1
  40. package/dist/conversation-row.d.ts.map +1 -1
  41. package/dist/conversation-row.js +4 -1
  42. package/dist/conversation-row.js.map +1 -1
  43. package/dist/data-table.d.ts +2 -0
  44. package/dist/data-table.d.ts.map +1 -1
  45. package/dist/data-table.js +2 -2
  46. package/dist/data-table.js.map +1 -1
  47. package/dist/display.d.ts +60 -3
  48. package/dist/display.d.ts.map +1 -1
  49. package/dist/display.js +33 -10
  50. package/dist/display.js.map +1 -1
  51. package/dist/evidence.d.ts +1 -1
  52. package/dist/evidence.d.ts.map +1 -1
  53. package/dist/evidence.js +2 -1
  54. package/dist/evidence.js.map +1 -1
  55. package/dist/feedback.d.ts +5 -0
  56. package/dist/feedback.d.ts.map +1 -1
  57. package/dist/feedback.js +7 -5
  58. package/dist/feedback.js.map +1 -1
  59. package/dist/floating-action-button.js +1 -1
  60. package/dist/floating-action-button.js.map +1 -1
  61. package/dist/forms.d.ts.map +1 -1
  62. package/dist/forms.js +4 -4
  63. package/dist/forms.js.map +1 -1
  64. package/dist/image-choice.js +3 -3
  65. package/dist/image-choice.js.map +1 -1
  66. package/dist/index.d.ts +5 -4
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +5 -4
  69. package/dist/index.js.map +1 -1
  70. package/dist/internal/initial-focus.d.ts +6 -0
  71. package/dist/internal/initial-focus.d.ts.map +1 -1
  72. package/dist/internal/initial-focus.js +6 -0
  73. package/dist/internal/initial-focus.js.map +1 -1
  74. package/dist/internal/pending-content.d.ts +19 -0
  75. package/dist/internal/pending-content.d.ts.map +1 -0
  76. package/dist/internal/pending-content.js +34 -0
  77. package/dist/internal/pending-content.js.map +1 -0
  78. package/dist/internal/screen-footer.d.ts +4 -2
  79. package/dist/internal/screen-footer.d.ts.map +1 -1
  80. package/dist/internal/screen-footer.js +2 -2
  81. package/dist/internal/screen-footer.js.map +1 -1
  82. package/dist/internal.d.ts +9 -1
  83. package/dist/internal.d.ts.map +1 -1
  84. package/dist/internal.js +4 -0
  85. package/dist/internal.js.map +1 -1
  86. package/dist/navigation.d.ts.map +1 -1
  87. package/dist/navigation.js +58 -4
  88. package/dist/navigation.js.map +1 -1
  89. package/dist/number-field.d.ts.map +1 -1
  90. package/dist/number-field.js +4 -3
  91. package/dist/number-field.js.map +1 -1
  92. package/dist/overlays.d.ts +4 -0
  93. package/dist/overlays.d.ts.map +1 -1
  94. package/dist/overlays.js +46 -18
  95. package/dist/overlays.js.map +1 -1
  96. package/dist/public-exports.generated.d.ts +6 -0
  97. package/dist/public-exports.generated.d.ts.map +1 -1
  98. package/dist/public-exports.generated.js +18 -0
  99. package/dist/public-exports.generated.js.map +1 -1
  100. package/dist/screen-flows.d.ts +13 -6
  101. package/dist/screen-flows.d.ts.map +1 -1
  102. package/dist/screen-flows.js +26 -15
  103. package/dist/screen-flows.js.map +1 -1
  104. package/dist/screens.d.ts +19 -6
  105. package/dist/screens.d.ts.map +1 -1
  106. package/dist/screens.js +22 -11
  107. package/dist/screens.js.map +1 -1
  108. package/dist/selection.d.ts +22 -0
  109. package/dist/selection.d.ts.map +1 -1
  110. package/dist/selection.js +32 -9
  111. package/dist/selection.js.map +1 -1
  112. package/dist/shortcut-rail.d.ts +27 -0
  113. package/dist/shortcut-rail.d.ts.map +1 -0
  114. package/dist/shortcut-rail.js +37 -0
  115. package/dist/shortcut-rail.js.map +1 -0
  116. package/dist/steps.d.ts +5 -2
  117. package/dist/steps.d.ts.map +1 -1
  118. package/dist/steps.js +3 -3
  119. package/dist/steps.js.map +1 -1
  120. package/dist/styles.css +242 -61
  121. package/dist/styles.layered.css +242 -61
  122. package/dist/task-list.d.ts +3 -1
  123. package/dist/task-list.d.ts.map +1 -1
  124. package/dist/task-list.js +2 -2
  125. package/dist/task-list.js.map +1 -1
  126. package/dist/theme.d.ts.map +1 -1
  127. package/dist/theme.js +1 -0
  128. package/dist/theme.js.map +1 -1
  129. package/dist/time-picker.d.ts +9 -0
  130. package/dist/time-picker.d.ts.map +1 -0
  131. package/dist/time-picker.js +74 -0
  132. package/dist/time-picker.js.map +1 -0
  133. package/dist/top-bar.d.ts +4 -0
  134. package/dist/top-bar.d.ts.map +1 -1
  135. package/dist/top-bar.js +2 -2
  136. package/dist/top-bar.js.map +1 -1
  137. package/package.json +26 -15
@@ -1 +1 @@
1
- {"version":3,"file":"avatar-blobatar-motion.js","sourceRoot":"","sources":["../src/avatar-blobatar-motion.tsx"],"names":[],"mappings":";AAAA,OAAM,EAAC,SAAS,EAAC,MAAM,EAAC,QAAQ,EAAgB,MAAI,OAAO,CAAC;AAAA,OAAM,EAAC,QAAQ,EAAC,MAAI,iBAAiB,CAAC;AAAA,OAAM,EAAC,IAAI,EAAC,KAAK,EAAC,GAAG,EAAC,SAAS,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,EAAC,MAAI,qBAAqB,CAAC;AAAA,OAAM,qBAAqB,CAAC;AAC7M,OAAM,EAAC,qBAAqB,EAAuD,MAAI,yCAAyC,CAAC;AAAA,OAAM,EAAC,WAAW,EAAC,MAAI,eAAe,CAAC;AACxK,MAAM,WAAW,GAAC,EAAC,IAAI,EAAC,KAAK,EAAC,GAAG,EAAC,SAAS,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,EAAC,CAAC;AAClE,SAAS,OAAO,CAAC,EAAC,IAAI,EAAC,OAAO,EAA6C;IAAE,MAAM,IAAI,GAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAAA,MAAK,EAAC,WAAW,EAAC,GAAC,WAAW,EAAE,CAAC;IAAA,MAAM,IAAI,GAAC,MAAM,CAAkB,IAAI,CAAC,CAAC;IAAA,MAAK,CAAC,OAAO,EAAC,UAAU,CAAC,GAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAAA,MAAK,CAAC,UAAU,EAAC,aAAa,CAAC,GAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1R,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QAChE,8FAA8F;QAC9F,MAAM,QAAQ,GAAG,OAAO,oBAAoB,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;QACxK,IAAI,IAAI,CAAC,OAAO;YAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO,GAAG,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACrG,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,MAAM,MAAM,GAAC,IAAI,CAAC,MAAM,IAAE,IAAI,CAAC,OAAO,IAAE,OAAO,IAAE,UAAU,IAAE,CAAC,WAAW,CAAC,aAAa,CAAC;IACxF,OAAO,eAAM,GAAG,EAAE,IAAI,iBAAc,MAAM,EAAC,KAAK,EAAE,EAAC,OAAO,EAAC,OAAO,EAAC,YAAE,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,CAAA,CAAC,CAAA,QAAQ,CAAA,CAAC,CAAA,KAAK,EAAE,GAAG,EAAC,EAAE,iBAAa,MAAM,GAAE,GAAO,CAAC;AAC5O,CAAC;AACD,4EAA4E;AAC5E,MAAM,UAAU,8BAA8B,CAAC,OAA6B,IAA6C,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAA,OAAM,CAAC,EAAC,IAAI,EAAC,EAAC,EAAE,CAAA,KAAC,OAAO,IAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,CAAA,CAAC","sourcesContent":["import{useEffect,useRef,useState,type ReactNode}from'react';import{Blobatar}from'@blobatar/react';import{idle,happy,sad,surprised,wink,sleepy,thinking}from'blobatar/expression';import'blobatar/motion.css';\nimport{resolveBlobatarMotion,type BlobatarMotionOptions,type AvatarFallbackContext}from'@hjmds/design-contracts/avatar-fallback';import{useHjmTheme}from'./provider.js';\nconst expressions={idle,happy,sad,surprised,wink,sleepy,thinking};\nfunction Artwork({size,options}:{size:number;options:BlobatarMotionOptions}){const spec=resolveBlobatarMotion(options);const{environment}=useHjmTheme();const host=useRef<HTMLSpanElement>(null);const[visible,setVisible]=useState(false);const[foreground,setForeground]=useState(false);\n useEffect(() => {\n const update = () => setForeground(!document.hidden);\n update(); document.addEventListener('visibilitychange', update);\n // Without viewport observation, keep decorative artwork static instead of failing the avatar.\n const observer = typeof IntersectionObserver === 'undefined' ? undefined : new IntersectionObserver(entries => setVisible(entries.some(entry => entry.isIntersecting)));\n if (host.current) observer?.observe(host.current);\n return () => { observer?.disconnect(); document.removeEventListener('visibilitychange', update); };\n }, []);\n const active=spec.active&&spec.visible&&visible&&foreground&&!environment.reducedMotion;\n return <span ref={host} aria-hidden=\"true\" style={{display:'block'}}><Blobatar name={spec.seed} size={size} normalize={false} expression={expressions[spec.expression]} animate={active?'always':false} alt=\"\" aria-hidden=\"true\"/></span>;\n}\n/** Optional animated entry. Static avatar imports never load motion CSS. */\nexport function createAnimatedBlobatarFallback(options:BlobatarMotionOptions):(context:AvatarFallbackContext)=>ReactNode{resolveBlobatarMotion(options);return({size})=><Artwork size={size} options={options}/>;}\n"]}
1
+ {"version":3,"file":"avatar-blobatar-motion.js","sourceRoot":"","sources":["../src/avatar-blobatar-motion.tsx"],"names":[],"mappings":"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","sourcesContent":["import{useEffect,useRef,useState,type ReactNode}from'react';import{Blobatar}from'@blobatar/react';import{idle,happy,sad,surprised,wink,sleepy,thinking}from'blobatar/expression';import'blobatar/motion.css';\nimport{resolveBlobatarMotion,type BlobatarMotionOptions,type AvatarFallbackContext}from'@hjmds/design-contracts/avatar-fallback';import{useHjmTheme}from'./provider.js';\nconst expressions={idle,happy,sad,surprised,wink,sleepy,thinking};\nfunction Artwork({size,options}:{size:number;options:BlobatarMotionOptions}){const spec=resolveBlobatarMotion(options);const{environment}=useHjmTheme();const host=useRef<HTMLSpanElement>(null);const[visible,setVisible]=useState(false);const[foreground,setForeground]=useState(false);\n useEffect(() => {\n const update = () => setForeground(!document.hidden);\n update(); document.addEventListener('visibilitychange', update);\n // Without viewport observation, keep decorative artwork static instead of failing the avatar.\n const observer = typeof IntersectionObserver === 'undefined' ? undefined : new IntersectionObserver(entries => setVisible(entries.some(entry => entry.isIntersecting)));\n if (host.current) observer?.observe(host.current);\n return () => { observer?.disconnect(); document.removeEventListener('visibilitychange', update); };\n }, []);\n // Omitted animate is the SDK's static default; only the active branch requests inline SVG motion.\n const active=spec.active&&spec.visible&&visible&&foreground&&!environment.reducedMotion;\n return <span ref={host} aria-hidden=\"true\" style={{display:'block'}}><Blobatar name={spec.seed} size={size} normalize={false} expression={expressions[spec.expression]} {...(active?{animate:'always' as const}:{})} alt=\"\" aria-hidden=\"true\"/></span>;\n}\n/** Optional animated entry. Static avatar imports never load motion CSS. */\nexport function createAnimatedBlobatarFallback(options:BlobatarMotionOptions):(context:AvatarFallbackContext)=>ReactNode{resolveBlobatarMotion(options);return({size})=><Artwork size={size} options={options}/>;}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"avatar-blobatar.d.ts","sourceRoot":"","sources":["../src/avatar-blobatar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,qBAAqB,EAAE,KAAK,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAC5I,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,YAAY,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,SAAS,CAKtH"}
1
+ {"version":3,"file":"avatar-blobatar.d.ts","sourceRoot":"","sources":["../src/avatar-blobatar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,qBAAqB,EAAE,KAAK,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAC5I,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,YAAY,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,SAAS,CAMtH"}
@@ -5,6 +5,7 @@ import { resolveBlobatarFallback } from "@hjmds/design-contracts/avatar-fallback
5
5
  /** Optional, local artwork. Avatar owns naming and image-error recovery. */
6
6
  export function createBlobatarFallback(options) {
7
7
  const { seed, expression } = resolveBlobatarFallback(options);
8
- return ({ size }) => _jsx(Blobatar, { name: seed, size: size, normalize: false, animate: false, ...(expression === "happy" ? { expression: happy } : {}), alt: "", "aria-hidden": "true", style: { display: "block" } });
8
+ // Omit animate for the documented static default; blobatar 2.7 intersects its false branch with Animate.
9
+ return ({ size }) => _jsx(Blobatar, { name: seed, size: size, normalize: false, ...(expression === "happy" ? { expression: happy } : {}), alt: "", "aria-hidden": "true", style: { display: "block" } });
9
10
  }
10
11
  //# sourceMappingURL=avatar-blobatar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"avatar-blobatar.js","sourceRoot":"","sources":["../src/avatar-blobatar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAA4D,MAAM,yCAAyC,CAAC;AAG5I,4EAA4E;AAC5E,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC9D,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,KACjF,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EACzD,GAAG,EAAC,EAAE,iBAAa,MAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAI,CAAC;AAC9D,CAAC","sourcesContent":["import { Blobatar } from \"@blobatar/react\";\nimport { happy } from \"blobatar/expression\";\nimport { resolveBlobatarFallback, type AvatarFallbackContext, type BlobatarFallbackOptions } from \"@hjmds/design-contracts/avatar-fallback\";\nimport type { ReactNode } from \"react\";\nexport type { BlobatarFallbackOptions } from \"@hjmds/design-contracts/avatar-fallback\";\n/** Optional, local artwork. Avatar owns naming and image-error recovery. */\nexport function createBlobatarFallback(options: BlobatarFallbackOptions): (context: AvatarFallbackContext) => ReactNode {\n const { seed, expression } = resolveBlobatarFallback(options);\n return ({ size }) => <Blobatar name={seed} size={size} normalize={false} animate={false}\n {...(expression === \"happy\" ? { expression: happy } : {})}\n alt=\"\" aria-hidden=\"true\" style={{ display: \"block\" }} />;\n}\n"]}
1
+ {"version":3,"file":"avatar-blobatar.js","sourceRoot":"","sources":["../src/avatar-blobatar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAA4D,MAAM,yCAAyC,CAAC;AAG5I,4EAA4E;AAC5E,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC9D,yGAAyG;IACzG,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,KACjE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EACzD,GAAG,EAAC,EAAE,iBAAa,MAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAI,CAAC;AAC9D,CAAC","sourcesContent":["import { Blobatar } from \"@blobatar/react\";\nimport { happy } from \"blobatar/expression\";\nimport { resolveBlobatarFallback, type AvatarFallbackContext, type BlobatarFallbackOptions } from \"@hjmds/design-contracts/avatar-fallback\";\nimport type { ReactNode } from \"react\";\nexport type { BlobatarFallbackOptions } from \"@hjmds/design-contracts/avatar-fallback\";\n/** Optional, local artwork. Avatar owns naming and image-error recovery. */\nexport function createBlobatarFallback(options: BlobatarFallbackOptions): (context: AvatarFallbackContext) => ReactNode {\n const { seed, expression } = resolveBlobatarFallback(options);\n // Omit animate for the documented static default; blobatar 2.7 intersects its false branch with Animate.\n return ({ size }) => <Blobatar name={seed} size={size} normalize={false}\n {...(expression === \"happy\" ? { expression: happy } : {})}\n alt=\"\" aria-hidden=\"true\" style={{ display: \"block\" }} />;\n}\n"]}
@@ -3,13 +3,20 @@ import { type ButtonSize, type ButtonTone } from "./actions.js";
3
3
  import type { HjmCompositionStyleProp } from "./composition-style.js";
4
4
  export type BottomCTAAction = Readonly<{
5
5
  label: string;
6
- onClick: MouseEventHandler<HTMLButtonElement>;
6
+ /** A regular button requires a handler; submit/reset may use the browser's native form action. */
7
+ onClick?: MouseEventHandler<HTMLButtonElement>;
8
+ type?: "button" | "submit" | "reset";
9
+ /** HTML form owner, including a form in a Sheet body while this action is in its footer. */
10
+ form?: string;
7
11
  accessibilityLabel?: string;
8
12
  disabled?: boolean;
9
13
  loading?: boolean;
10
14
  loadingLabel?: string;
11
15
  size?: ButtonSize;
12
16
  tone?: ButtonTone;
17
+ /** Decorative Button adornments; label remains the action name. */
18
+ leading?: ReactNode;
19
+ trailing?: ReactNode;
13
20
  /**
14
21
  * Ref to this action's button. The component `ref` names the bar; a dialog or overlay opened from the primary
15
22
  * action needs the button itself to return focus to on close (2026-10-18 Utilverse display board).
@@ -1 +1 @@
1
- {"version":3,"file":"bottom-cta.d.ts","sourceRoot":"","sources":["../src/bottom-cta.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA8C,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAC1I,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAIxE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;CACpC,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC;IACvF,aAAa,EAAE,eAAe,CAAC;IAC/B,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAeH,wGAAwG;AACxG,eAAO,MAAM,SAAS;mBAzBL,eAAe;sBACZ,eAAe,GAAG,SAAS;kBAC/B,MAAM;yBACC,MAAM;qBACV,MAAM;IACvB,4FAA4F;eACjF,MAAM,GAAG,QAAQ;IAC5B,gGAAgG;kBAClF,uBAAuB;mDA2CrC,CAAC"}
1
+ {"version":3,"file":"bottom-cta.d.ts","sourceRoot":"","sources":["../src/bottom-cta.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA8C,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAC1I,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAIxE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,OAAO,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrC,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,mEAAmE;IACnE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;CACpC,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC;IACvF,aAAa,EAAE,eAAe,CAAC;IAC/B,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAiBH,wGAAwG;AACxG,eAAO,MAAM,SAAS;mBA3BL,eAAe;sBACZ,eAAe,GAAG,SAAS;kBAC/B,MAAM;yBACC,MAAM;qBACV,MAAM;IACvB,4FAA4F;eACjF,MAAM,GAAG,QAAQ;IAC5B,gGAAgG;kBAClF,uBAAuB;mDA8CrC,CAAC"}
@@ -9,10 +9,13 @@ import { ScreenFooterContext } from "./internal/screen-footer.js";
9
9
  function Action({ action, tone }) {
10
10
  if (!action.label.trim())
11
11
  throw new TypeError("BottomCTA action label must not be empty");
12
- return _jsx(Button, { ref: action.buttonRef, onClick: action.onClick, tone: action.tone ?? tone, size: action.size ?? "medium", disabled: action.disabled, loading: action.loading ?? false, "aria-label": action.loading ? action.loadingLabel ?? action.accessibilityLabel ?? action.label : action.accessibilityLabel, children: action.label });
12
+ const type = action.type ?? "button";
13
+ if (type === "button" && !action.onClick)
14
+ throw new TypeError("BottomCTA button action requires onClick");
15
+ return _jsx(Button, { ref: action.buttonRef, type: type, form: action.form, onClick: action.onClick, tone: action.tone ?? tone, size: action.size ?? "medium", disabled: action.disabled, loading: action.loading ?? false, leading: action.leading, trailing: action.trailing, "aria-label": action.loading ? action.loadingLabel ?? action.accessibilityLabel ?? action.label : action.accessibilityLabel, children: action.label });
13
16
  }
14
17
  function isAction(value) {
15
- return typeof value === "object" && value !== null && "label" in value && "onClick" in value;
18
+ return typeof value === "object" && value !== null && "label" in value && typeof value.label === "string";
16
19
  }
17
20
  /** One primary action with optional supporting copy and a secondary action, matching Native's slots. */
18
21
  export const BottomCTA = forwardRef(function BottomCTA({ primaryAction, secondaryAction, description, accessibilityLabel, safeAreaBottom = 0, position = "flow", className, style, layoutStyle, ...props }, ref) {
@@ -28,6 +31,7 @@ export const BottomCTA = forwardRef(function BottomCTA({ primaryAction, secondar
28
31
  "--hjm-bottom-cta-gap": `${bottomCtaRecipe.gap}px`,
29
32
  "--hjm-bottom-cta-content-max-width": `${bottomCtaRecipe.contentMaxWidth}px`,
30
33
  "--hjm-bottom-cta-safe-area": `${safeAreaBottom}px`, ...style, ...layoutStyle,
34
+ "--hjm-bottom-cta-parent-padding": inScreenFooter?.bottomPadding ?? "0px",
31
35
  }, children: [description ? _jsx("p", { className: "hjm-bottom-cta__description", children: description }) : null, _jsxs("div", { className: "hjm-bottom-cta__actions", children: [secondaryAction == null ? null : _jsx("div", { className: "hjm-bottom-cta__secondary", children: isAction(secondaryAction) ? _jsx(Action, { action: secondaryAction, tone: "secondary" }) : secondaryAction }), _jsx("div", { className: "hjm-bottom-cta__primary", children: _jsx(Action, { action: primaryAction, tone: "primary" }) })] })] });
32
36
  });
33
37
  //# sourceMappingURL=bottom-cta.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bottom-cta.js","sourceRoot":"","sources":["../src/bottom-cta.tsx"],"names":[],"mappings":"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","sourcesContent":["import { bottomCtaRecipe } from \"@hjmds/design-contracts/recipes\";\nimport { isLargeTextScale } from \"@hjmds/design-contracts/components/design-system-provider\";\nimport { forwardRef, useContext, type CSSProperties, type HTMLAttributes, type MouseEventHandler, type ReactNode, type Ref } from \"react\";\nimport { Button, type ButtonSize, type ButtonTone } from \"./actions.js\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport { ScreenFooterContext } from \"./internal/screen-footer.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type BottomCTAAction = Readonly<{\n label: string;\n onClick: MouseEventHandler<HTMLButtonElement>;\n accessibilityLabel?: string;\n disabled?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n tone?: ButtonTone;\n /**\n * Ref to this action's button. The component `ref` names the bar; a dialog or overlay opened from the primary\n * action needs the button itself to return focus to on close (2026-10-18 Utilverse display board).\n */\n buttonRef?: Ref<HTMLButtonElement>;\n}>;\n\nexport type BottomCTAProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\"> & Readonly<{\n primaryAction: BottomCTAAction;\n secondaryAction?: BottomCTAAction | ReactNode;\n description?: string;\n accessibilityLabel?: string;\n safeAreaBottom?: number;\n /** Sticky remains in document flow; fixed overlays would need a measured content spacer. */\n position?: \"flow\" | \"sticky\";\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nfunction Action({ action, tone }: { action: BottomCTAAction; tone: ButtonTone }) {\n if (!action.label.trim()) throw new TypeError(\"BottomCTA action label must not be empty\");\n return <Button ref={action.buttonRef} onClick={action.onClick} tone={action.tone ?? tone} size={action.size ?? \"medium\"}\n disabled={action.disabled} loading={action.loading ?? false}\n aria-label={action.loading ? action.loadingLabel ?? action.accessibilityLabel ?? action.label : action.accessibilityLabel}>\n {action.label}\n </Button>;\n}\n\nfunction isAction(value: BottomCTAAction | ReactNode): value is BottomCTAAction {\n return typeof value === \"object\" && value !== null && \"label\" in value && \"onClick\" in value;\n}\n\n/** One primary action with optional supporting copy and a secondary action, matching Native's slots. */\nexport const BottomCTA = forwardRef<HTMLDivElement, BottomCTAProps>(function BottomCTA({\n primaryAction, secondaryAction, description, accessibilityLabel, safeAreaBottom = 0,\n position = \"flow\", className, style, layoutStyle, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n const inScreenFooter = useContext(ScreenFooterContext);\n if (!Number.isFinite(safeAreaBottom) || safeAreaBottom < 0) throw new RangeError(\"BottomCTA safeAreaBottom must be non-negative\");\n return <div {...props} ref={ref} role=\"group\" aria-label={accessibilityLabel}\n className={classNames(\"hjm-bottom-cta\", className)} data-position={position}\n data-placement={inScreenFooter ? \"screen-footer\" : undefined}\n data-large-text={isLargeTextScale(theme?.environment.textScale ?? 1)}\n style={{\n \"--hjm-bottom-cta-min-height\": `${bottomCtaRecipe.minHeight}px`,\n \"--hjm-bottom-cta-padding-inline\": `${bottomCtaRecipe.paddingHorizontal}px`,\n \"--hjm-bottom-cta-padding-top\": `${bottomCtaRecipe.paddingTop}px`,\n \"--hjm-bottom-cta-padding-bottom\": `${bottomCtaRecipe.paddingBottom}px`,\n \"--hjm-bottom-cta-gap\": `${bottomCtaRecipe.gap}px`,\n \"--hjm-bottom-cta-content-max-width\": `${bottomCtaRecipe.contentMaxWidth}px`,\n \"--hjm-bottom-cta-safe-area\": `${safeAreaBottom}px`, ...style, ...layoutStyle,\n } as CSSProperties}>\n {description ? <p className=\"hjm-bottom-cta__description\">{description}</p> : null}\n <div className=\"hjm-bottom-cta__actions\">\n {secondaryAction == null ? null : <div className=\"hjm-bottom-cta__secondary\">{isAction(secondaryAction) ? <Action action={secondaryAction} tone=\"secondary\" /> : secondaryAction}</div>}\n <div className=\"hjm-bottom-cta__primary\"><Action action={primaryAction} tone=\"primary\" /></div>\n </div>\n </div>;\n});\n"]}
1
+ {"version":3,"file":"bottom-cta.js","sourceRoot":"","sources":["../src/bottom-cta.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAE,UAAU,EAA6F,MAAM,OAAO,CAAC;AAC1I,OAAO,EAAE,MAAM,EAAoC,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAsClE,SAAS,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAiD;IAC7E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAC1F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC;IACrC,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAC1G,OAAO,KAAC,MAAM,IAAC,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,QAAQ,EACpJ,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,gBACnG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,YACxH,MAAM,CAAC,KAAK,GACN,CAAC;AACZ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAkC;IAClD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC;AAC5G,CAAC;AAED,wGAAwG;AACxG,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAiC,SAAS,SAAS,CAAC,EACrF,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAE,cAAc,GAAG,CAAC,EACnF,QAAQ,GAAG,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,KAAK,EAC3D,EAAE,GAAG;IACJ,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,cAAc,GAAG,UAAU,CAAC,mBAAmB,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,+CAA+C,CAAC,CAAC;IAClI,OAAO,kBAAS,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAC,OAAO,gBAAa,kBAAkB,EAC1E,SAAS,EAAE,UAAU,CAAC,gBAAgB,EAAE,SAAS,CAAC,mBAAiB,QAAQ,oBAC3D,cAAc,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,qBAC3C,gBAAgB,CAAC,KAAK,EAAE,WAAW,CAAC,SAAS,IAAI,CAAC,CAAC,EACpE,KAAK,EAAE;YACL,6BAA6B,EAAE,GAAG,eAAe,CAAC,SAAS,IAAI;YAC/D,iCAAiC,EAAE,GAAG,eAAe,CAAC,iBAAiB,IAAI;YAC3E,8BAA8B,EAAE,GAAG,eAAe,CAAC,UAAU,IAAI;YACjE,iCAAiC,EAAE,GAAG,eAAe,CAAC,aAAa,IAAI;YACvE,sBAAsB,EAAE,GAAG,eAAe,CAAC,GAAG,IAAI;YAClD,oCAAoC,EAAE,GAAG,eAAe,CAAC,eAAe,IAAI;YAC5E,4BAA4B,EAAE,GAAG,cAAc,IAAI,EAAE,GAAG,KAAK,EAAE,GAAG,WAAW;YAC7E,iCAAiC,EAAE,cAAc,EAAE,aAAa,IAAI,KAAK;SACzD,aACjB,WAAW,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,6BAA6B,YAAE,WAAW,GAAK,CAAC,CAAC,CAAC,IAAI,EAClF,eAAK,SAAS,EAAC,yBAAyB,aACrC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,2BAA2B,YAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAC,WAAW,GAAG,CAAC,CAAC,CAAC,eAAe,GAAO,EACvL,cAAK,SAAS,EAAC,yBAAyB,YAAC,KAAC,MAAM,IAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAC,SAAS,GAAG,GAAM,IAC3F,IACF,CAAC;AACT,CAAC,CAAC,CAAC","sourcesContent":["import { bottomCtaRecipe } from \"@hjmds/design-contracts/recipes\";\nimport { isLargeTextScale } from \"@hjmds/design-contracts/components/design-system-provider\";\nimport { forwardRef, useContext, type CSSProperties, type HTMLAttributes, type MouseEventHandler, type ReactNode, type Ref } from \"react\";\nimport { Button, type ButtonSize, type ButtonTone } from \"./actions.js\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport { ScreenFooterContext } from \"./internal/screen-footer.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type BottomCTAAction = Readonly<{\n label: string;\n /** A regular button requires a handler; submit/reset may use the browser's native form action. */\n onClick?: MouseEventHandler<HTMLButtonElement>;\n type?: \"button\" | \"submit\" | \"reset\";\n /** HTML form owner, including a form in a Sheet body while this action is in its footer. */\n form?: string;\n accessibilityLabel?: string;\n disabled?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n tone?: ButtonTone;\n /** Decorative Button adornments; label remains the action name. */\n leading?: ReactNode;\n trailing?: ReactNode;\n /**\n * Ref to this action's button. The component `ref` names the bar; a dialog or overlay opened from the primary\n * action needs the button itself to return focus to on close (2026-10-18 Utilverse display board).\n */\n buttonRef?: Ref<HTMLButtonElement>;\n}>;\n\nexport type BottomCTAProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\"> & Readonly<{\n primaryAction: BottomCTAAction;\n secondaryAction?: BottomCTAAction | ReactNode;\n description?: string;\n accessibilityLabel?: string;\n safeAreaBottom?: number;\n /** Sticky remains in document flow; fixed overlays would need a measured content spacer. */\n position?: \"flow\" | \"sticky\";\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nfunction Action({ action, tone }: { action: BottomCTAAction; tone: ButtonTone }) {\n if (!action.label.trim()) throw new TypeError(\"BottomCTA action label must not be empty\");\n const type = action.type ?? \"button\";\n if (type === \"button\" && !action.onClick) throw new TypeError(\"BottomCTA button action requires onClick\");\n return <Button ref={action.buttonRef} type={type} form={action.form} onClick={action.onClick} tone={action.tone ?? tone} size={action.size ?? \"medium\"}\n disabled={action.disabled} loading={action.loading ?? false} leading={action.leading} trailing={action.trailing}\n aria-label={action.loading ? action.loadingLabel ?? action.accessibilityLabel ?? action.label : action.accessibilityLabel}>\n {action.label}\n </Button>;\n}\n\nfunction isAction(value: BottomCTAAction | ReactNode): value is BottomCTAAction {\n return typeof value === \"object\" && value !== null && \"label\" in value && typeof value.label === \"string\";\n}\n\n/** One primary action with optional supporting copy and a secondary action, matching Native's slots. */\nexport const BottomCTA = forwardRef<HTMLDivElement, BottomCTAProps>(function BottomCTA({\n primaryAction, secondaryAction, description, accessibilityLabel, safeAreaBottom = 0,\n position = \"flow\", className, style, layoutStyle, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n const inScreenFooter = useContext(ScreenFooterContext);\n if (!Number.isFinite(safeAreaBottom) || safeAreaBottom < 0) throw new RangeError(\"BottomCTA safeAreaBottom must be non-negative\");\n return <div {...props} ref={ref} role=\"group\" aria-label={accessibilityLabel}\n className={classNames(\"hjm-bottom-cta\", className)} data-position={position}\n data-placement={inScreenFooter ? \"screen-footer\" : undefined}\n data-large-text={isLargeTextScale(theme?.environment.textScale ?? 1)}\n style={{\n \"--hjm-bottom-cta-min-height\": `${bottomCtaRecipe.minHeight}px`,\n \"--hjm-bottom-cta-padding-inline\": `${bottomCtaRecipe.paddingHorizontal}px`,\n \"--hjm-bottom-cta-padding-top\": `${bottomCtaRecipe.paddingTop}px`,\n \"--hjm-bottom-cta-padding-bottom\": `${bottomCtaRecipe.paddingBottom}px`,\n \"--hjm-bottom-cta-gap\": `${bottomCtaRecipe.gap}px`,\n \"--hjm-bottom-cta-content-max-width\": `${bottomCtaRecipe.contentMaxWidth}px`,\n \"--hjm-bottom-cta-safe-area\": `${safeAreaBottom}px`, ...style, ...layoutStyle,\n \"--hjm-bottom-cta-parent-padding\": inScreenFooter?.bottomPadding ?? \"0px\",\n } as CSSProperties}>\n {description ? <p className=\"hjm-bottom-cta__description\">{description}</p> : null}\n <div className=\"hjm-bottom-cta__actions\">\n {secondaryAction == null ? null : <div className=\"hjm-bottom-cta__secondary\">{isAction(secondaryAction) ? <Action action={secondaryAction} tone=\"secondary\" /> : secondaryAction}</div>}\n <div className=\"hjm-bottom-cta__primary\"><Action action={primaryAction} tone=\"primary\" /></div>\n </div>\n </div>;\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"carousel-motion.d.ts","sourceRoot":"","sources":["../src/carousel-motion.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yDAAyD,CAAC;AAC5F,OAAO,EAA0D,KAAK,6BAA6B,EAAE,MAAM,6CAA6C,CAAC;AAKzJ,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3F,+EAA+E;IAC/E,qBAAqB,CAAC,EAAE,6BAA6B,CAAC;IACtD,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IACpG,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC;AACF,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAgDxD"}
1
+ {"version":3,"file":"carousel-motion.d.ts","sourceRoot":"","sources":["../src/carousel-motion.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yDAAyD,CAAC;AAC5F,OAAO,EAA0D,KAAK,6BAA6B,EAAE,MAAM,6CAA6C,CAAC;AAKzJ,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3F,+EAA+E;IAC/E,qBAAqB,CAAC,EAAE,6BAA6B,CAAC;IACtD,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IACpG,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC;AACF,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAwDxD"}
@@ -27,6 +27,8 @@ export function CarouselMotion(props) {
27
27
  watchFocus: false, duration: environment.reducedMotion ? 0 : 25 });
28
28
  const latest = useRef(props);
29
29
  latest.current = props;
30
+ const latestIndex = useRef(index);
31
+ latestIndex.current = index;
30
32
  const syncing = useRef(false);
31
33
  useEffect(() => {
32
34
  if (!api)
@@ -37,8 +39,15 @@ export function CarouselMotion(props) {
37
39
  if (!syncing.current && item && item.id !== current.currentKey)
38
40
  current.onCurrentKeyChange(item.id);
39
41
  };
40
- api.on("select", select);
41
- return () => { api.off("select", select); };
42
+ // A zero-width/modal viewport can collapse snaps before its first measurement.
43
+ // Re-measurement must restore the controlled key, not retain that collapsed engine index.
44
+ const reInit = () => {
45
+ syncing.current = true;
46
+ api.scrollTo(latestIndex.current, true);
47
+ syncing.current = false;
48
+ };
49
+ api.on("select", select).on("reInit", reInit);
50
+ return () => { api.off("select", select).off("reInit", reInit); };
42
51
  }, [api]);
43
52
  useEffect(() => { syncing.current = true; api?.scrollTo(index, environment.reducedMotion); syncing.current = false; }, [api, index, environment.reducedMotion]);
44
53
  return _jsxs("section", { "aria-label": props.label, "aria-roledescription": "carousel", style: props.layoutStyle, children: [_jsx("div", { ref: viewport, style: { overflow: "hidden" }, children: _jsx("div", { style: { display: "flex", touchAction: "pan-y pinch-zoom" }, children: props.slides.map((slide, i) => _jsx("div", { role: "group", "aria-roledescription": "slide", "aria-label": resolved[i].accessibleName, "aria-hidden": resolved[i].inert, inert: resolved[i].inert, style: { flex: "0 0 100%", minWidth: 0 }, children: props.renderSlide(slide) }, slide.id)) }) }), props.navigation === "counter" ? _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [_jsx(IconButton, { label: props.previousLabel, tone: "ghost", disabled: index === 0, onClick: () => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, "previous")), children: _jsx(Icon, { name: "chevronStart" }) }), _jsxs(Text, { variant: "caption", "aria-live": "polite", children: [index + 1, "/", props.slides.length] }), _jsx(IconButton, { label: props.nextLabel, tone: "ghost", disabled: index === props.slides.length - 1, onClick: () => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, "next")), children: _jsx(Icon, { name: "chevronEnd" }) })] }) : _jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--hjm-space-sm)", marginBlockStart: "var(--hjm-space-sm)" }, children: [_jsx(IconButton, { label: props.previousLabel, tone: "ghost", disabled: index === 0, onClick: () => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, "previous")), children: _jsx(Icon, { name: "chevronStart" }) }), _jsx(IconButton, { label: props.nextLabel, tone: "ghost", disabled: index === props.slides.length - 1, onClick: () => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, "next")), children: _jsx(Icon, { name: "chevronEnd" }) })] })] });
@@ -1 +1 @@
1
- {"version":3,"file":"carousel-motion.js","sourceRoot":"","sources":["../src/carousel-motion.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport useEmblaCarousel from \"embla-carousel-react\";\nimport type { SortableItem } from \"@hjmds/design-contracts/components/interaction-adapters\";\nimport { resolveCarouselDescriptor, getCarouselNavigationTarget, type ComposeCarouselAccessibleName } from \"@hjmds/design-contracts/components/carousel\";\nimport { IconButton } from \"./actions.js\";\nimport { Icon } from \"./supplemental-display.js\";\nimport { Text } from \"./layout.js\";\nimport { useHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type CarouselMotionProps = {\n navigation?: \"buttons\" | \"counter\";\n slides: readonly SortableItem[]; currentKey: string; onCurrentKeyChange(key: string): void;\n /** Optional localized position/name composer shared with the base Carousel. */\n composeAccessibleName?: ComposeCarouselAccessibleName;\n renderSlide(item: SortableItem): ReactNode; label: string; previousLabel: string; nextLabel: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n};\nexport function CarouselMotion(props: CarouselMotionProps) {\n const descriptor = { slides: props.slides, currentKey: props.currentKey };\n // Share finite keyed selection and names with Carousel; the optional peer owns\n // only swipe/motion. No autoplay is implied by this controlled presentation.\n const resolved = resolveCarouselDescriptor(descriptor, {\n composeAccessibleName: props.composeAccessibleName ?? ((info) => info.label),\n });\n const index = resolved.findIndex(slide => slide.current);\n if (![props.label, props.previousLabel, props.nextLabel].every(label => label.trim())) {\n throw new TypeError(\"CarouselMotion labels must not be empty\");\n }\n const { environment } = useHjmTheme();\n // Embla deep-compares options and reInits on change; a live startIndex restarted the engine at the target on\n // every selection, so navigation snapped instead of animating (2026-09-30 review). Only the first index seeds it.\n const [startIndex] = useState(index);\n const [viewport, api] = useEmblaCarousel({ loop: false, direction: environment.direction, startIndex,\n // HJM owns selection; upstream focus scrolling must not independently change slides.\n // Embla's default 25 is a physics duration factor, not an HJM millisecond token.\n watchFocus: false, duration: environment.reducedMotion ? 0 : 25 });\n const latest = useRef(props); latest.current = props;\n const syncing = useRef(false);\n useEffect(() => {\n if (!api) return;\n const select = () => {\n const current = latest.current;\n const item = current.slides[api.selectedScrollSnap()];\n if (!syncing.current && item && item.id !== current.currentKey) current.onCurrentKeyChange(item.id);\n };\n api.on(\"select\", select);\n return () => { api.off(\"select\", select); };\n }, [api]);\n useEffect(() => { syncing.current = true; api?.scrollTo(index, environment.reducedMotion); syncing.current = false; }, [api, index, environment.reducedMotion]);\n return <section aria-label={props.label} aria-roledescription=\"carousel\" style={props.layoutStyle}>\n <div ref={viewport} style={{ overflow: \"hidden\" }}><div style={{ display: \"flex\", touchAction: \"pan-y pinch-zoom\" }}>\n {props.slides.map((slide, i) => <div key={slide.id} role=\"group\" aria-roledescription=\"slide\" aria-label={resolved[i]!.accessibleName} aria-hidden={resolved[i]!.inert} inert={resolved[i]!.inert}\n style={{ flex: \"0 0 100%\", minWidth: 0 }}>{props.renderSlide(slide)}</div>)}\n </div></div>\n {/* Tool actions: icon buttons named by the localized labels, matching Carousel and CollectionRail. Logical\n chevrons mirror with the reading direction (2026-10-17 composition review). */}\n {props.navigation === \"counter\" ? <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <IconButton label={props.previousLabel} tone=\"ghost\" disabled={index === 0} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"previous\"))}><Icon name=\"chevronStart\" /></IconButton>\n <Text variant=\"caption\" aria-live=\"polite\">{index + 1}/{props.slides.length}</Text>\n <IconButton label={props.nextLabel} tone=\"ghost\" disabled={index === props.slides.length - 1} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"next\"))}><Icon name=\"chevronEnd\" /></IconButton>\n </div> : <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: \"var(--hjm-space-sm)\", marginBlockStart: \"var(--hjm-space-sm)\" }}>\n <IconButton label={props.previousLabel} tone=\"ghost\" disabled={index === 0} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"previous\"))}><Icon name=\"chevronStart\" /></IconButton>\n <IconButton label={props.nextLabel} tone=\"ghost\" disabled={index === props.slides.length - 1} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"next\"))}><Icon name=\"chevronEnd\" /></IconButton>\n </div>}\n </section>;\n}\n"]}
1
+ {"version":3,"file":"carousel-motion.js","sourceRoot":"","sources":["../src/carousel-motion.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AACpE,OAAO,gBAAgB,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,yBAAyB,EAAE,2BAA2B,EAAsC,MAAM,6CAA6C,CAAC;AACzJ,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAY5C,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,MAAM,UAAU,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;IAC1E,+EAA+E;IAC/E,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,yBAAyB,CAAC,UAAU,EAAE;QACrD,qBAAqB,EAAE,KAAK,CAAC,qBAAqB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;KAC7E,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAAC;IACtC,6GAA6G;IAC7G,kHAAkH;IAClH,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,EAAE,UAAU;QAClG,qFAAqF;QACrF,iFAAiF;QACjF,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;IACrD,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC,UAAU;gBAAE,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtG,CAAC,CAAC;QACF,+EAA+E;QAC/E,0FAA0F;QAC1F,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;QAC1B,CAAC,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9C,OAAO,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACV,SAAS,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC;IAChK,OAAO,iCAAqB,KAAK,CAAC,KAAK,0BAAuB,UAAU,EAAC,KAAK,EAAE,KAAK,CAAC,WAAW,aAC/F,cAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAE,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAChH,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,cAAoB,IAAI,EAAC,OAAO,0BAAsB,OAAO,gBAAa,QAAQ,CAAC,CAAC,CAAE,CAAC,cAAc,iBAAe,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,EAC/L,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,YAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAD3B,KAAK,CAAC,EAAE,CAC0B,CAAC,GACzE,GAAM,EAGX,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,eAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,aACtH,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE,IAAI,EAAC,OAAO,EAAC,QAAQ,EAAE,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,YAAE,KAAC,IAAI,IAAC,IAAI,EAAC,cAAc,GAAG,GAAa,EACnN,MAAC,IAAI,IAAC,OAAO,EAAC,SAAS,eAAW,QAAQ,aAAE,KAAK,GAAG,CAAC,OAAG,KAAK,CAAC,MAAM,CAAC,MAAM,IAAQ,EACnF,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAC,OAAO,EAAC,QAAQ,EAAE,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,YAAE,KAAC,IAAI,IAAC,IAAI,EAAC,YAAY,GAAG,GAAa,IAC3N,CAAC,CAAC,CAAC,eAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,aACvI,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE,IAAI,EAAC,OAAO,EAAC,QAAQ,EAAE,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,YAAE,KAAC,IAAI,IAAC,IAAI,EAAC,cAAc,GAAG,GAAa,EACnN,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAC,OAAO,EAAC,QAAQ,EAAE,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,YAAE,KAAC,IAAI,IAAC,IAAI,EAAC,YAAY,GAAG,GAAa,IAC3N,IACE,CAAC;AACb,CAAC","sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport useEmblaCarousel from \"embla-carousel-react\";\nimport type { SortableItem } from \"@hjmds/design-contracts/components/interaction-adapters\";\nimport { resolveCarouselDescriptor, getCarouselNavigationTarget, type ComposeCarouselAccessibleName } from \"@hjmds/design-contracts/components/carousel\";\nimport { IconButton } from \"./actions.js\";\nimport { Icon } from \"./supplemental-display.js\";\nimport { Text } from \"./layout.js\";\nimport { useHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type CarouselMotionProps = {\n navigation?: \"buttons\" | \"counter\";\n slides: readonly SortableItem[]; currentKey: string; onCurrentKeyChange(key: string): void;\n /** Optional localized position/name composer shared with the base Carousel. */\n composeAccessibleName?: ComposeCarouselAccessibleName;\n renderSlide(item: SortableItem): ReactNode; label: string; previousLabel: string; nextLabel: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n};\nexport function CarouselMotion(props: CarouselMotionProps) {\n const descriptor = { slides: props.slides, currentKey: props.currentKey };\n // Share finite keyed selection and names with Carousel; the optional peer owns\n // only swipe/motion. No autoplay is implied by this controlled presentation.\n const resolved = resolveCarouselDescriptor(descriptor, {\n composeAccessibleName: props.composeAccessibleName ?? ((info) => info.label),\n });\n const index = resolved.findIndex(slide => slide.current);\n if (![props.label, props.previousLabel, props.nextLabel].every(label => label.trim())) {\n throw new TypeError(\"CarouselMotion labels must not be empty\");\n }\n const { environment } = useHjmTheme();\n // Embla deep-compares options and reInits on change; a live startIndex restarted the engine at the target on\n // every selection, so navigation snapped instead of animating (2026-09-30 review). Only the first index seeds it.\n const [startIndex] = useState(index);\n const [viewport, api] = useEmblaCarousel({ loop: false, direction: environment.direction, startIndex,\n // HJM owns selection; upstream focus scrolling must not independently change slides.\n // Embla's default 25 is a physics duration factor, not an HJM millisecond token.\n watchFocus: false, duration: environment.reducedMotion ? 0 : 25 });\n const latest = useRef(props); latest.current = props;\n const latestIndex = useRef(index); latestIndex.current = index;\n const syncing = useRef(false);\n useEffect(() => {\n if (!api) return;\n const select = () => {\n const current = latest.current;\n const item = current.slides[api.selectedScrollSnap()];\n if (!syncing.current && item && item.id !== current.currentKey) current.onCurrentKeyChange(item.id);\n };\n // A zero-width/modal viewport can collapse snaps before its first measurement.\n // Re-measurement must restore the controlled key, not retain that collapsed engine index.\n const reInit = () => {\n syncing.current = true;\n api.scrollTo(latestIndex.current, true);\n syncing.current = false;\n };\n api.on(\"select\", select).on(\"reInit\", reInit);\n return () => { api.off(\"select\", select).off(\"reInit\", reInit); };\n }, [api]);\n useEffect(() => { syncing.current = true; api?.scrollTo(index, environment.reducedMotion); syncing.current = false; }, [api, index, environment.reducedMotion]);\n return <section aria-label={props.label} aria-roledescription=\"carousel\" style={props.layoutStyle}>\n <div ref={viewport} style={{ overflow: \"hidden\" }}><div style={{ display: \"flex\", touchAction: \"pan-y pinch-zoom\" }}>\n {props.slides.map((slide, i) => <div key={slide.id} role=\"group\" aria-roledescription=\"slide\" aria-label={resolved[i]!.accessibleName} aria-hidden={resolved[i]!.inert} inert={resolved[i]!.inert}\n style={{ flex: \"0 0 100%\", minWidth: 0 }}>{props.renderSlide(slide)}</div>)}\n </div></div>\n {/* Tool actions: icon buttons named by the localized labels, matching Carousel and CollectionRail. Logical\n chevrons mirror with the reading direction (2026-10-17 composition review). */}\n {props.navigation === \"counter\" ? <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <IconButton label={props.previousLabel} tone=\"ghost\" disabled={index === 0} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"previous\"))}><Icon name=\"chevronStart\" /></IconButton>\n <Text variant=\"caption\" aria-live=\"polite\">{index + 1}/{props.slides.length}</Text>\n <IconButton label={props.nextLabel} tone=\"ghost\" disabled={index === props.slides.length - 1} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"next\"))}><Icon name=\"chevronEnd\" /></IconButton>\n </div> : <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: \"var(--hjm-space-sm)\", marginBlockStart: \"var(--hjm-space-sm)\" }}>\n <IconButton label={props.previousLabel} tone=\"ghost\" disabled={index === 0} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"previous\"))}><Icon name=\"chevronStart\" /></IconButton>\n <IconButton label={props.nextLabel} tone=\"ghost\" disabled={index === props.slides.length - 1} onClick={() => props.onCurrentKeyChange(getCarouselNavigationTarget(descriptor, \"next\"))}><Icon name=\"chevronEnd\" /></IconButton>\n </div>}\n </section>;\n}\n"]}
@@ -15,6 +15,8 @@ export type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, "children" | "o
15
15
  composeAccessibleName: ComposeCarouselAccessibleName;
16
16
  labels: CarouselLabels;
17
17
  autoplay?: CarouselAutoplayConfig;
18
+ /** Available content height in a bounded host. Navigation stays outside the scrolling slide. */
19
+ slideMaxHeight?: number;
18
20
  /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */
19
21
  layoutStyle?: HjmCompositionStyleProp;
20
22
  }>;
@@ -24,6 +26,10 @@ export type PageIndicatorProps = Omit<HTMLAttributes<HTMLDivElement>, "children"
24
26
  count: number;
25
27
  /** Zero-based current page; the product pager owns it. */
26
28
  currentIndex: number;
29
+ /** Limit visible dots while retaining absolute page names and navigation. */
30
+ maxVisibleDots?: number;
31
+ /** Position-only dots when separate pager controls own navigation. Default true retains 44pt dot targets. */
32
+ interactive?: boolean;
27
33
  /** Requested page from a dot or an arrow key. It can repeat the current index. */
28
34
  onCurrentIndexChange: (index: number) => void;
29
35
  composeAccessibleName: ComposePageIndicatorAccessibleName;
@@ -40,6 +46,10 @@ export declare const PageIndicator: import("react").ForwardRefExoticComponent<Om
40
46
  count: number;
41
47
  /** Zero-based current page; the product pager owns it. */
42
48
  currentIndex: number;
49
+ /** Limit visible dots while retaining absolute page names and navigation. */
50
+ maxVisibleDots?: number;
51
+ /** Position-only dots when separate pager controls own navigation. Default true retains 44pt dot targets. */
52
+ interactive?: boolean;
43
53
  /** Requested page from a dot or an arrow key. It can repeat the current index. */
44
54
  onCurrentIndexChange: (index: number) => void;
45
55
  composeAccessibleName: ComposePageIndicatorAccessibleName;
@@ -1 +1 @@
1
- {"version":3,"file":"carousel.d.ts","sourceRoot":"","sources":["../src/carousel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACtH,OAAO,EAGL,KAAK,uBAAuB,EAAE,KAAK,iBAAiB,EAAE,KAAK,sBAAsB,EACjF,KAAK,6BAA6B,EAAE,KAAK,kCAAkC,EAC5E,MAAM,6CAA6C,CAAC;AAMrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC7H,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IACvH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC3C,WAAW,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,SAAS,CAAC;IAC3D,qBAAqB,EAAE,6BAA6B,CAAC;IACrD,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC;IACxG,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,qBAAqB,EAAE,kCAAkC,CAAC;IAC1D,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAeH;;;GAGG;AACH,eAAO,MAAM,aAAa;IA7BxB,iFAAiF;WAC1E,MAAM;WACN,MAAM;IACb,0DAA0D;kBAC5C,MAAM;IACpB,kFAAkF;0BAC5D,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;2BACtB,kCAAkC;IACzD,gGAAgG;kBAClF,uBAAuB;mDAuCrC,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,0GAmGnB,CAAC"}
1
+ {"version":3,"file":"carousel.d.ts","sourceRoot":"","sources":["../src/carousel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACtH,OAAO,EAGL,KAAK,uBAAuB,EAAE,KAAK,iBAAiB,EAAE,KAAK,sBAAsB,EACjF,KAAK,6BAA6B,EAAE,KAAK,kCAAkC,EAC5E,MAAM,6CAA6C,CAAC;AAMrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC7H,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IACvH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC3C,WAAW,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,SAAS,CAAC;IAC3D,qBAAqB,EAAE,6BAA6B,CAAC;IACrD,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,gGAAgG;IAChG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC;IACxG,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6GAA6G;IAC7G,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kFAAkF;IAClF,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,qBAAqB,EAAE,kCAAkC,CAAC;IAC1D,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAeH;;;GAGG;AACH,eAAO,MAAM,aAAa;IAjCxB,iFAAiF;WAC1E,MAAM;WACN,MAAM;IACb,0DAA0D;kBAC5C,MAAM;IACpB,6EAA6E;qBAC5D,MAAM;IACvB,6GAA6G;kBAC/F,OAAO;IACrB,kFAAkF;0BAC5D,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;2BACtB,kCAAkC;IACzD,gGAAgG;kBAClF,uBAAuB;mDAsDrC,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,0GAsGnB,CAAC"}
package/dist/carousel.js CHANGED
@@ -14,24 +14,38 @@ function PageDots({ dots, onSelect, dotClassName, keys }) {
14
14
  * Carousel's position dots for a product-owned pager (scroll-snap row, virtualized pager). Arrow keys on a dot move
15
15
  * one page in reading direction, as in Carousel's controls.
16
16
  */
17
- export const PageIndicator = forwardRef(function PageIndicator({ label, count, currentIndex, onCurrentIndexChange, composeAccessibleName, className, onKeyDown, layoutStyle, ...props }, ref) {
17
+ export const PageIndicator = forwardRef(function PageIndicator({ label, count, currentIndex, maxVisibleDots, interactive = true, onCurrentIndexChange, composeAccessibleName, className, onKeyDown, onFocusCapture, layoutStyle, ...props }, ref) {
18
18
  const theme = useOptionalHjmTheme();
19
+ const host = useRef(null);
20
+ const lastFocused = useRef(null);
21
+ useLayoutEffect(() => {
22
+ // Moving the bounded window can remove the focused dot. Recover within the same control only when
23
+ // removal left focus on body; a user who moved elsewhere keeps that focus.
24
+ if (interactive && lastFocused.current && !lastFocused.current.isConnected && document.activeElement === document.body) {
25
+ host.current?.querySelector('.hjm-page-indicator__dot[data-current="true"]')?.focus();
26
+ }
27
+ }, [interactive, count, currentIndex, maxVisibleDots]);
19
28
  if (!label.trim())
20
29
  throw new TypeError("PageIndicator label must not be empty");
21
- const descriptor = { count, currentIndex };
30
+ const descriptor = { count, currentIndex, ...(maxVisibleDots === undefined ? {} : { maxVisibleDots }) };
22
31
  const dots = resolvePageIndicator(descriptor, composeAccessibleName);
23
- return _jsx("div", { ...props, ref: ref, style: { ...props.style, ...layoutStyle }, role: "group", "aria-label": label, className: classNames("hjm-page-indicator", className), onKeyDown: (event) => {
32
+ const current = dots.find(dot => dot.current);
33
+ return _jsx("div", { ...props, ref: node => { host.current = node; assignRef(ref, node); }, style: { ...props.style, ...layoutStyle }, role: interactive ? "group" : "img", "aria-label": interactive ? label : current.accessibleName, "data-interactive": interactive, className: classNames("hjm-page-indicator", className), onFocusCapture: event => {
34
+ lastFocused.current = event.target instanceof HTMLElement ? event.target : null;
35
+ onFocusCapture?.(event);
36
+ }, onKeyDown: (event) => {
24
37
  onKeyDown?.(event);
25
- if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey)
38
+ if (!interactive || event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey)
26
39
  return;
27
40
  if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
28
41
  return;
29
42
  event.preventDefault();
30
43
  onCurrentIndexChange(getPageIndicatorTarget(descriptor, (event.key === "ArrowRight") !== (theme?.environment.direction === "rtl") ? "next" : "previous"));
31
- }, children: _jsx(PageDots, { dots: dots, onSelect: onCurrentIndexChange, dotClassName: "hjm-page-indicator__dot" }) });
44
+ }, children: interactive ? _jsx(PageDots, { dots: dots, onSelect: onCurrentIndexChange, dotClassName: "hjm-page-indicator__dot" })
45
+ : dots.map(dot => _jsx("span", { "aria-hidden": "true", "data-current": dot.current, className: "hjm-page-indicator__marker", children: _jsx("span", {}) }, dot.index)) });
32
46
  });
33
47
  /** Finite keyed cards. Hidden slides stay mounted but cannot receive focus. */
34
- export const Carousel = forwardRef(function Carousel({ label, slides, renderSlide, composeAccessibleName, labels, autoplay, currentKey, defaultCurrentKey, onCurrentKeyChange, className, onFocusCapture, onMouseEnter, onMouseLeave, onKeyDown, layoutStyle, ...props }, ref) {
48
+ export const Carousel = forwardRef(function Carousel({ label, slides, renderSlide, composeAccessibleName, labels, autoplay, currentKey, defaultCurrentKey, onCurrentKeyChange, className, onFocusCapture, onMouseEnter, onMouseLeave, onKeyDown, layoutStyle, slideMaxHeight, ...props }, ref) {
35
49
  const theme = useOptionalHjmTheme();
36
50
  const [storedCurrent, setCurrent] = useControllableState({
37
51
  ...(currentKey === undefined ? {} : { value: currentKey }),
@@ -59,6 +73,8 @@ export const Carousel = forwardRef(function Carousel({ label, slides, renderSlid
59
73
  const resolved = resolveCarouselDescriptor(descriptor, { composeAccessibleName });
60
74
  if (!label.trim() || Object.values(labels).some((value) => !value.trim()))
61
75
  throw new TypeError("Carousel labels must not be empty");
76
+ if (slideMaxHeight !== undefined && (!Number.isFinite(slideMaxHeight) || slideMaxHeight <= 0))
77
+ throw new TypeError("Carousel slideMaxHeight must be finite and positive");
62
78
  const [rotating, setRotating] = useState(autoplay !== undefined);
63
79
  const [hovered, setHovered] = useState(false);
64
80
  const [hidden, setHidden] = useState(false);
@@ -108,6 +124,6 @@ export const Carousel = forwardRef(function Carousel({ label, slides, renderSlid
108
124
  if (next && last)
109
125
  setCurrent(slides[0].id);
110
126
  setRotating(next);
111
- }, children: rotationRequested ? labels.pause : labels.resume }) : null, _jsx("div", { className: "hjm-carousel__track", "aria-live": playing ? "off" : "polite", "aria-atomic": false, children: resolved.map((slide) => _jsx("div", { className: "hjm-carousel__slide", role: "group", "aria-roledescription": "slide", "aria-label": slide.accessibleName, hidden: slide.inert, inert: slide.inert, children: renderSlide(slide) }, slide.id)) }), _jsxs("div", { className: "hjm-carousel__controls", "data-carousel-controls": true, role: "group", "aria-label": labels.navigation, children: [_jsx(IconButton, { label: labels.previous, tone: "ghost", "aria-disabled": current === slides[0].id, onClick: () => move("previous"), children: _jsx(Icon, { name: "chevronStart" }) }), _jsx("div", { className: "hjm-carousel__dots", children: _jsx(PageDots, { dotClassName: "hjm-carousel__dot", keys: slides.map((slide) => slide.id), onSelect: (index) => select(slides[index].id), dots: resolvePageIndicator({ count: slides.length, currentIndex: resolved.findIndex((slide) => slide.current) }, ({ position }) => resolved[position - 1].accessibleName) }) }), _jsx(IconButton, { label: labels.next, tone: "ghost", "aria-disabled": last, onClick: () => move("next"), children: _jsx(Icon, { name: "chevronEnd" }) })] })] });
127
+ }, children: rotationRequested ? labels.pause : labels.resume }) : null, _jsx("div", { className: "hjm-carousel__track", "aria-live": playing ? "off" : "polite", "aria-atomic": false, children: resolved.map((slide) => _jsx("div", { className: "hjm-carousel__slide", role: "group", "aria-roledescription": "slide", "aria-label": slide.accessibleName, hidden: slide.inert, inert: slide.inert, tabIndex: slideMaxHeight === undefined ? undefined : 0, style: slideMaxHeight === undefined ? undefined : { maxHeight: slideMaxHeight, overflowY: "auto" }, children: renderSlide(slide) }, slide.id)) }), _jsxs("div", { className: "hjm-carousel__controls", "data-carousel-controls": true, role: "group", "aria-label": labels.navigation, children: [_jsx(IconButton, { label: labels.previous, tone: "ghost", "aria-disabled": current === slides[0].id, onClick: () => move("previous"), children: _jsx(Icon, { name: "chevronStart" }) }), _jsx("div", { className: "hjm-carousel__dots", children: _jsx(PageDots, { dotClassName: "hjm-carousel__dot", keys: slides.map((slide) => slide.id), onSelect: (index) => select(slides[index].id), dots: resolvePageIndicator({ count: slides.length, currentIndex: resolved.findIndex((slide) => slide.current) }, ({ position }) => resolved[position - 1].accessibleName) }) }), _jsx(IconButton, { label: labels.next, tone: "ghost", "aria-disabled": last, onClick: () => move("next"), children: _jsx(Icon, { name: "chevronEnd" }) })] })] });
112
128
  });
113
129
  //# sourceMappingURL=carousel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"carousel.js","sourceRoot":"","sources":["../src/carousel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState, forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport {\n resolveCarouselDescriptor, getCarouselNavigationTarget, isCarouselAutoplayActive,\n resolvePageIndicator, getPageIndicatorTarget,\n type CarouselSlideDescriptor, type CarouselSelection, type CarouselAutoplayConfig,\n type ComposeCarouselAccessibleName, type ComposePageIndicatorAccessibleName, type ResolvedPageIndicatorDot,\n} from \"@hjmds/design-contracts/components/carousel\";\nimport { Button, IconButton } from \"./actions.js\";\nimport { Icon } from \"./supplemental-display.js\";\nimport { isLargeTextScale } from \"@hjmds/design-contracts/components/design-system-provider\";\nimport { assignRef, classNames, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type CarouselLabels = Readonly<{ previous: string; next: string; pause: string; resume: string; navigation: string }>;\nexport type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\" | \"onChange\"> & CarouselSelection & Readonly<{\n label: string;\n slides: readonly CarouselSlideDescriptor[];\n renderSlide: (slide: CarouselSlideDescriptor) => ReactNode;\n composeAccessibleName: ComposeCarouselAccessibleName;\n labels: CarouselLabels;\n autoplay?: CarouselAutoplayConfig;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nexport type PageIndicatorProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\" | \"onChange\"> & Readonly<{\n /** Name of the paged region (e.g. \"Home pages\"); the group's accessible name. */\n label: string;\n count: number;\n /** Zero-based current page; the product pager owns it. */\n currentIndex: number;\n /** Requested page from a dot or an arrow key. It can repeat the current index. */\n onCurrentIndexChange: (index: number) => void;\n composeAccessibleName: ComposePageIndicatorAccessibleName;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\n/** The dot buttons shared by Carousel and PageIndicator, so both rows keep one markup and one recipe. */\nfunction PageDots({ dots, onSelect, dotClassName, keys }: Readonly<{\n dots: readonly ResolvedPageIndicatorDot[];\n onSelect: (index: number) => void;\n dotClassName: string;\n /** Carousel keys dots by slide id so removing a slide removes its own (possibly focused) dot. */\n keys?: readonly string[];\n}>) {\n return <>{dots.map((dot) => <button key={keys?.[dot.index] ?? dot.index} type=\"button\" aria-label={dot.accessibleName}\n aria-disabled={dot.current} data-current={dot.current} className={dotClassName}\n onClick={() => onSelect(dot.index)}><span aria-hidden=\"true\" /></button>)}</>;\n}\n\n/**\n * Carousel's position dots for a product-owned pager (scroll-snap row, virtualized pager). Arrow keys on a dot move\n * one page in reading direction, as in Carousel's controls.\n */\nexport const PageIndicator = forwardRef<HTMLDivElement, PageIndicatorProps>(function PageIndicator({\n label, count, currentIndex, onCurrentIndexChange, composeAccessibleName, className, onKeyDown, layoutStyle, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n if (!label.trim()) throw new TypeError(\"PageIndicator label must not be empty\");\n const descriptor = { count, currentIndex };\n const dots = resolvePageIndicator(descriptor, composeAccessibleName);\n return <div {...props} ref={ref} style={{ ...props.style, ...layoutStyle }} role=\"group\" aria-label={label}\n className={classNames(\"hjm-page-indicator\", className)}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n onCurrentIndexChange(getPageIndicatorTarget(descriptor,\n (event.key === \"ArrowRight\") !== (theme?.environment.direction === \"rtl\") ? \"next\" : \"previous\"));\n }}>\n <PageDots dots={dots} onSelect={onCurrentIndexChange} dotClassName=\"hjm-page-indicator__dot\" />\n </div>;\n});\n\n/** Finite keyed cards. Hidden slides stay mounted but cannot receive focus. */\nexport const Carousel = forwardRef<HTMLDivElement, CarouselProps>(function Carousel({\n label, slides, renderSlide, composeAccessibleName, labels, autoplay,\n currentKey, defaultCurrentKey, onCurrentKeyChange, className,\n onFocusCapture, onMouseEnter, onMouseLeave, onKeyDown, layoutStyle, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n const [storedCurrent, setCurrent] = useControllableState({\n ...(currentKey === undefined ? {} : { value: currentKey }),\n defaultValue: defaultCurrentKey ?? slides[0]?.id ?? \"\",\n ...(onCurrentKeyChange === undefined ? {} : { onChange: onCurrentKeyChange }),\n });\n const previousIndex = useRef<number | null>(null);\n const host = useRef<HTMLDivElement | null>(null);\n const lastFocused = useRef<HTMLElement | null>(null);\n // A host can delete the current uncontrolled slide after mount. Keep its place\n // in the collection (or the new last slide); explicit invalid keys still fail validation.\n const recovering = currentKey === undefined && previousIndex.current !== null && !slides.some(slide => slide.id === storedCurrent);\n const current = recovering ? slides[Math.min(previousIndex.current!, slides.length - 1)]?.id ?? storedCurrent : storedCurrent;\n useLayoutEffect(() => {\n previousIndex.current = slides.findIndex(slide => slide.id === current);\n if (recovering) {\n setCurrent(current);\n // A removed dot/card can strand focus on body; retained controls keep their focus.\n if (lastFocused.current && !lastFocused.current.isConnected && document.activeElement === document.body) {\n host.current?.querySelector<HTMLButtonElement>('.hjm-carousel__dot[data-current=\"true\"]')?.focus();\n }\n }\n }, [current, recovering, slides, setCurrent]);\n const descriptor = { slides, currentKey: current, ...(autoplay ? { autoplay } : {}) };\n const resolved = resolveCarouselDescriptor(descriptor, { composeAccessibleName });\n if (!label.trim() || Object.values(labels).some((value) => !value.trim())) throw new TypeError(\"Carousel labels must not be empty\");\n const [rotating, setRotating] = useState(autoplay !== undefined);\n const [hovered, setHovered] = useState(false);\n const [hidden, setHidden] = useState(false);\n const pointerRotation = useRef<boolean | null>(null);\n const last = current === slides.at(-1)!.id;\n const playing = isCarouselAutoplayActive(autoplay, {\n reducedMotion: theme?.environment.reducedMotion ?? false,\n paused: !rotating || hovered || hidden || last,\n });\n useEffect(() => {\n const update = () => setHidden(document.hidden);\n update(); document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n useEffect(() => {\n if (!playing || !autoplay) return;\n const timer = setTimeout(() => setCurrent(getCarouselNavigationTarget(descriptor, \"next\")), autoplay.intervalMs);\n return () => clearTimeout(timer);\n }, [playing, autoplay?.intervalMs, current, slides, setCurrent]);\n function select(key: string) { setRotating(false); if (key !== current) setCurrent(key); }\n function move(intent: \"next\" | \"previous\") { select(getCarouselNavigationTarget(descriptor, intent)); }\n const rotationRequested = rotating && !last && !theme?.environment.reducedMotion;\n return <div {...props} style={{ ...props.style, ...layoutStyle }} ref={node => { host.current = node; assignRef(ref, node); }} role=\"region\" aria-label={label} aria-roledescription=\"carousel\"\n className={classNames(\"hjm-carousel\", className)} data-large-text={isLargeTextScale(theme?.environment.textScale ?? 1)}\n onFocusCapture={(event) => {\n lastFocused.current = event.target instanceof HTMLElement ? event.target : null;\n // APG: leaving focus must not silently restart rotation. Explicit resume is required.\n if (!event.currentTarget.contains(event.relatedTarget)) setRotating(false);\n onFocusCapture?.(event);\n }}\n onMouseEnter={(event) => { setHovered(true); onMouseEnter?.(event); }}\n onMouseLeave={(event) => { setHovered(false); onMouseLeave?.(event); }}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n // Do not steal editing/navigation keys from links, inputs or widgets inside a card.\n if (!(event.target instanceof Element) || !event.target.closest(\"[data-carousel-controls]\")) return;\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n move((event.key === \"ArrowRight\") !== (theme?.environment.direction === \"rtl\") ? \"next\" : \"previous\");\n }}>\n {autoplay ? <Button tone=\"ghost\" className=\"hjm-carousel__rotation\" disabled={theme?.environment.reducedMotion}\n onPointerDown={() => { pointerRotation.current = !rotationRequested; }}\n onPointerCancel={() => { pointerRotation.current = null; }}\n onClick={(event) => {\n // An abandoned pointer gesture must not override a later keyboard activation.\n const next = event.detail > 0 ? pointerRotation.current ?? !rotationRequested : !rotationRequested;\n pointerRotation.current = null;\n if (next && last) setCurrent(slides[0]!.id);\n setRotating(next);\n }}>{rotationRequested ? labels.pause : labels.resume}</Button> : null}\n <div className=\"hjm-carousel__track\" aria-live={playing ? \"off\" : \"polite\"} aria-atomic={false}>\n {resolved.map((slide) => <div key={slide.id} className=\"hjm-carousel__slide\" role=\"group\"\n aria-roledescription=\"slide\" aria-label={slide.accessibleName} hidden={slide.inert} inert={slide.inert}>\n {renderSlide(slide)}\n </div>)}\n </div>\n <div className=\"hjm-carousel__controls\" data-carousel-controls role=\"group\" aria-label={labels.navigation}>\n {/* Previous/next are tool actions: icon buttons named by the localized label (2026-10-17 review E9). */}\n <IconButton label={labels.previous} tone=\"ghost\" aria-disabled={current === slides[0]!.id} onClick={() => move(\"previous\")}><Icon name=\"chevronStart\" /></IconButton>\n <div className=\"hjm-carousel__dots\">\n <PageDots dotClassName=\"hjm-carousel__dot\" keys={slides.map((slide) => slide.id)} onSelect={(index) => select(slides[index]!.id)}\n dots={resolvePageIndicator({ count: slides.length, currentIndex: resolved.findIndex((slide) => slide.current) },\n ({ position }) => resolved[position - 1]!.accessibleName)} />\n </div>\n <IconButton label={labels.next} tone=\"ghost\" aria-disabled={last} onClick={() => move(\"next\")}><Icon name=\"chevronEnd\" /></IconButton>\n </div>\n </div>;\n});\n"]}
1
+ {"version":3,"file":"carousel.js","sourceRoot":"","sources":["../src/carousel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState, forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport {\n resolveCarouselDescriptor, getCarouselNavigationTarget, isCarouselAutoplayActive,\n resolvePageIndicator, getPageIndicatorTarget,\n type CarouselSlideDescriptor, type CarouselSelection, type CarouselAutoplayConfig,\n type ComposeCarouselAccessibleName, type ComposePageIndicatorAccessibleName, type ResolvedPageIndicatorDot,\n} from \"@hjmds/design-contracts/components/carousel\";\nimport { Button, IconButton } from \"./actions.js\";\nimport { Icon } from \"./supplemental-display.js\";\nimport { isLargeTextScale } from \"@hjmds/design-contracts/components/design-system-provider\";\nimport { assignRef, classNames, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type CarouselLabels = Readonly<{ previous: string; next: string; pause: string; resume: string; navigation: string }>;\nexport type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\" | \"onChange\"> & CarouselSelection & Readonly<{\n label: string;\n slides: readonly CarouselSlideDescriptor[];\n renderSlide: (slide: CarouselSlideDescriptor) => ReactNode;\n composeAccessibleName: ComposeCarouselAccessibleName;\n labels: CarouselLabels;\n autoplay?: CarouselAutoplayConfig;\n /** Available content height in a bounded host. Navigation stays outside the scrolling slide. */\n slideMaxHeight?: number;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nexport type PageIndicatorProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\" | \"onChange\"> & Readonly<{\n /** Name of the paged region (e.g. \"Home pages\"); the group's accessible name. */\n label: string;\n count: number;\n /** Zero-based current page; the product pager owns it. */\n currentIndex: number;\n /** Limit visible dots while retaining absolute page names and navigation. */\n maxVisibleDots?: number;\n /** Position-only dots when separate pager controls own navigation. Default true retains 44pt dot targets. */\n interactive?: boolean;\n /** Requested page from a dot or an arrow key. It can repeat the current index. */\n onCurrentIndexChange: (index: number) => void;\n composeAccessibleName: ComposePageIndicatorAccessibleName;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\n/** The dot buttons shared by Carousel and PageIndicator, so both rows keep one markup and one recipe. */\nfunction PageDots({ dots, onSelect, dotClassName, keys }: Readonly<{\n dots: readonly ResolvedPageIndicatorDot[];\n onSelect: (index: number) => void;\n dotClassName: string;\n /** Carousel keys dots by slide id so removing a slide removes its own (possibly focused) dot. */\n keys?: readonly string[];\n}>) {\n return <>{dots.map((dot) => <button key={keys?.[dot.index] ?? dot.index} type=\"button\" aria-label={dot.accessibleName}\n aria-disabled={dot.current} data-current={dot.current} className={dotClassName}\n onClick={() => onSelect(dot.index)}><span aria-hidden=\"true\" /></button>)}</>;\n}\n\n/**\n * Carousel's position dots for a product-owned pager (scroll-snap row, virtualized pager). Arrow keys on a dot move\n * one page in reading direction, as in Carousel's controls.\n */\nexport const PageIndicator = forwardRef<HTMLDivElement, PageIndicatorProps>(function PageIndicator({\n label, count, currentIndex, maxVisibleDots, interactive = true, onCurrentIndexChange, composeAccessibleName, className, onKeyDown, onFocusCapture, layoutStyle, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n const host = useRef<HTMLDivElement | null>(null);\n const lastFocused = useRef<HTMLElement | null>(null);\n useLayoutEffect(() => {\n // Moving the bounded window can remove the focused dot. Recover within the same control only when\n // removal left focus on body; a user who moved elsewhere keeps that focus.\n if (interactive && lastFocused.current && !lastFocused.current.isConnected && document.activeElement === document.body) {\n host.current?.querySelector<HTMLButtonElement>('.hjm-page-indicator__dot[data-current=\"true\"]')?.focus();\n }\n }, [interactive, count, currentIndex, maxVisibleDots]);\n if (!label.trim()) throw new TypeError(\"PageIndicator label must not be empty\");\n const descriptor = { count, currentIndex, ...(maxVisibleDots === undefined ? {} : { maxVisibleDots }) };\n const dots = resolvePageIndicator(descriptor, composeAccessibleName);\n const current = dots.find(dot => dot.current)!;\n return <div {...props} ref={node => { host.current = node; assignRef(ref, node); }} style={{ ...props.style, ...layoutStyle }} role={interactive ? \"group\" : \"img\"} aria-label={interactive ? label : current.accessibleName} data-interactive={interactive}\n className={classNames(\"hjm-page-indicator\", className)}\n onFocusCapture={event => {\n lastFocused.current = event.target instanceof HTMLElement ? event.target : null;\n onFocusCapture?.(event);\n }}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (!interactive || event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n onCurrentIndexChange(getPageIndicatorTarget(descriptor,\n (event.key === \"ArrowRight\") !== (theme?.environment.direction === \"rtl\") ? \"next\" : \"previous\"));\n }}>\n {interactive ? <PageDots dots={dots} onSelect={onCurrentIndexChange} dotClassName=\"hjm-page-indicator__dot\" />\n : dots.map(dot => <span key={dot.index} aria-hidden=\"true\" data-current={dot.current} className=\"hjm-page-indicator__marker\"><span /></span>)}\n </div>;\n});\n\n/** Finite keyed cards. Hidden slides stay mounted but cannot receive focus. */\nexport const Carousel = forwardRef<HTMLDivElement, CarouselProps>(function Carousel({\n label, slides, renderSlide, composeAccessibleName, labels, autoplay,\n currentKey, defaultCurrentKey, onCurrentKeyChange, className,\n onFocusCapture, onMouseEnter, onMouseLeave, onKeyDown, layoutStyle, slideMaxHeight, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n const [storedCurrent, setCurrent] = useControllableState({\n ...(currentKey === undefined ? {} : { value: currentKey }),\n defaultValue: defaultCurrentKey ?? slides[0]?.id ?? \"\",\n ...(onCurrentKeyChange === undefined ? {} : { onChange: onCurrentKeyChange }),\n });\n const previousIndex = useRef<number | null>(null);\n const host = useRef<HTMLDivElement | null>(null);\n const lastFocused = useRef<HTMLElement | null>(null);\n // A host can delete the current uncontrolled slide after mount. Keep its place\n // in the collection (or the new last slide); explicit invalid keys still fail validation.\n const recovering = currentKey === undefined && previousIndex.current !== null && !slides.some(slide => slide.id === storedCurrent);\n const current = recovering ? slides[Math.min(previousIndex.current!, slides.length - 1)]?.id ?? storedCurrent : storedCurrent;\n useLayoutEffect(() => {\n previousIndex.current = slides.findIndex(slide => slide.id === current);\n if (recovering) {\n setCurrent(current);\n // A removed dot/card can strand focus on body; retained controls keep their focus.\n if (lastFocused.current && !lastFocused.current.isConnected && document.activeElement === document.body) {\n host.current?.querySelector<HTMLButtonElement>('.hjm-carousel__dot[data-current=\"true\"]')?.focus();\n }\n }\n }, [current, recovering, slides, setCurrent]);\n const descriptor = { slides, currentKey: current, ...(autoplay ? { autoplay } : {}) };\n const resolved = resolveCarouselDescriptor(descriptor, { composeAccessibleName });\n if (!label.trim() || Object.values(labels).some((value) => !value.trim())) throw new TypeError(\"Carousel labels must not be empty\");\n if (slideMaxHeight !== undefined && (!Number.isFinite(slideMaxHeight) || slideMaxHeight <= 0)) throw new TypeError(\"Carousel slideMaxHeight must be finite and positive\");\n const [rotating, setRotating] = useState(autoplay !== undefined);\n const [hovered, setHovered] = useState(false);\n const [hidden, setHidden] = useState(false);\n const pointerRotation = useRef<boolean | null>(null);\n const last = current === slides.at(-1)!.id;\n const playing = isCarouselAutoplayActive(autoplay, {\n reducedMotion: theme?.environment.reducedMotion ?? false,\n paused: !rotating || hovered || hidden || last,\n });\n useEffect(() => {\n const update = () => setHidden(document.hidden);\n update(); document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n useEffect(() => {\n if (!playing || !autoplay) return;\n const timer = setTimeout(() => setCurrent(getCarouselNavigationTarget(descriptor, \"next\")), autoplay.intervalMs);\n return () => clearTimeout(timer);\n }, [playing, autoplay?.intervalMs, current, slides, setCurrent]);\n function select(key: string) { setRotating(false); if (key !== current) setCurrent(key); }\n function move(intent: \"next\" | \"previous\") { select(getCarouselNavigationTarget(descriptor, intent)); }\n const rotationRequested = rotating && !last && !theme?.environment.reducedMotion;\n return <div {...props} style={{ ...props.style, ...layoutStyle }} ref={node => { host.current = node; assignRef(ref, node); }} role=\"region\" aria-label={label} aria-roledescription=\"carousel\"\n className={classNames(\"hjm-carousel\", className)} data-large-text={isLargeTextScale(theme?.environment.textScale ?? 1)}\n onFocusCapture={(event) => {\n lastFocused.current = event.target instanceof HTMLElement ? event.target : null;\n // APG: leaving focus must not silently restart rotation. Explicit resume is required.\n if (!event.currentTarget.contains(event.relatedTarget)) setRotating(false);\n onFocusCapture?.(event);\n }}\n onMouseEnter={(event) => { setHovered(true); onMouseEnter?.(event); }}\n onMouseLeave={(event) => { setHovered(false); onMouseLeave?.(event); }}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n // Do not steal editing/navigation keys from links, inputs or widgets inside a card.\n if (!(event.target instanceof Element) || !event.target.closest(\"[data-carousel-controls]\")) return;\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n move((event.key === \"ArrowRight\") !== (theme?.environment.direction === \"rtl\") ? \"next\" : \"previous\");\n }}>\n {autoplay ? <Button tone=\"ghost\" className=\"hjm-carousel__rotation\" disabled={theme?.environment.reducedMotion}\n onPointerDown={() => { pointerRotation.current = !rotationRequested; }}\n onPointerCancel={() => { pointerRotation.current = null; }}\n onClick={(event) => {\n // An abandoned pointer gesture must not override a later keyboard activation.\n const next = event.detail > 0 ? pointerRotation.current ?? !rotationRequested : !rotationRequested;\n pointerRotation.current = null;\n if (next && last) setCurrent(slides[0]!.id);\n setRotating(next);\n }}>{rotationRequested ? labels.pause : labels.resume}</Button> : null}\n <div className=\"hjm-carousel__track\" aria-live={playing ? \"off\" : \"polite\"} aria-atomic={false}>\n {resolved.map((slide) => <div key={slide.id} className=\"hjm-carousel__slide\" role=\"group\"\n aria-roledescription=\"slide\" aria-label={slide.accessibleName} hidden={slide.inert} inert={slide.inert}\n tabIndex={slideMaxHeight === undefined ? undefined : 0}\n style={slideMaxHeight === undefined ? undefined : { maxHeight: slideMaxHeight, overflowY: \"auto\" }}>\n {renderSlide(slide)}\n </div>)}\n </div>\n <div className=\"hjm-carousel__controls\" data-carousel-controls role=\"group\" aria-label={labels.navigation}>\n {/* Previous/next are tool actions: icon buttons named by the localized label (2026-10-17 review E9). */}\n <IconButton label={labels.previous} tone=\"ghost\" aria-disabled={current === slides[0]!.id} onClick={() => move(\"previous\")}><Icon name=\"chevronStart\" /></IconButton>\n <div className=\"hjm-carousel__dots\">\n <PageDots dotClassName=\"hjm-carousel__dot\" keys={slides.map((slide) => slide.id)} onSelect={(index) => select(slides[index]!.id)}\n dots={resolvePageIndicator({ count: slides.length, currentIndex: resolved.findIndex((slide) => slide.current) },\n ({ position }) => resolved[position - 1]!.accessibleName)} />\n </div>\n <IconButton label={labels.next} tone=\"ghost\" aria-disabled={last} onClick={() => move(\"next\")}><Icon name=\"chevronEnd\" /></IconButton>\n </div>\n </div>;\n});\n"]}
@@ -4,6 +4,12 @@ import type { HjmCompositionStyleProp } from "./composition-style.js";
4
4
  export type CollapsibleProps = CollapsibleOpenState & Readonly<{
5
5
  /** The control's label; it also tells the user what will appear. */
6
6
  trigger: ReactNode;
7
+ /** Accessible action name when the trigger is artwork or has a shorter visible label. */
8
+ triggerLabel?: string;
9
+ /** Static heading beside the disclosure action, outside the button's accessible name. */
10
+ title?: ReactNode;
11
+ /** While content is being prepared, replace the action with one labeled spinner. */
12
+ pendingLabel?: string;
7
13
  children: ReactNode;
8
14
  disabled?: boolean;
9
15
  /** Inline tools stay expanded without a disclosure trigger. */
@@ -1 +1 @@
1
- {"version":3,"file":"collapsible.d.ts","sourceRoot":"","sources":["../src/collapsible.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAA4E,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GACjD,QAAQ,CAAC;IACP,oEAAoE;IACpE,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;IACvC,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEL,eAAO,MAAM,WAAW,6GA6DtB,CAAC;AAEH,8GAA8G;AAC9G,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,+BAItE"}
1
+ {"version":3,"file":"collapsible.d.ts","sourceRoot":"","sources":["../src/collapsible.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAA4E,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIjH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GACjD,QAAQ,CAAC;IACP,oEAAoE;IACpE,OAAO,EAAE,SAAS,CAAC;IACnB,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;IACvC,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEL,eAAO,MAAM,WAAW,6GAmEtB,CAAC;AAEH,8GAA8G;AAC9G,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,+BAItE"}
@@ -3,7 +3,8 @@ import { collapsibleRecipe, validateCollapsibleOpenState, } from "@hjmds/design-
3
3
  import { forwardRef, useId, useLayoutEffect, useRef, useState } from "react";
4
4
  import { assignRef, classNames } from "./internal.js";
5
5
  import { Icon } from "./supplemental-display.js";
6
- export const Collapsible = forwardRef(function Collapsible({ trigger, children, disabled = false, presentation = "disclosure", keepMounted = false, marker = "end", className, layoutStyle, ...openState }, forwardedRef) {
6
+ import { Spinner } from "./internal/spinner.js";
7
+ export const Collapsible = forwardRef(function Collapsible({ trigger, triggerLabel, title, pendingLabel, children, disabled = false, presentation = "disclosure", keepMounted = false, marker = "end", className, layoutStyle, ...openState }, forwardedRef) {
7
8
  validateCollapsibleOpenState(openState);
8
9
  const controlled = openState.open !== undefined;
9
10
  const [internalOpen, setInternalOpen] = useState(openState.defaultOpen ?? false);
@@ -12,33 +13,34 @@ export const Collapsible = forwardRef(function Collapsible({ trigger, children,
12
13
  const button = useRef(null);
13
14
  const content = useRef(null);
14
15
  const focusedContent = useRef(false);
15
- const wasOpen = useRef(open);
16
+ const contentOpen = open && pendingLabel === undefined;
17
+ const wasOpen = useRef(contentOpen);
16
18
  useLayoutEffect(() => {
17
19
  // A controlled close can remove or hide the currently focused input without
18
20
  // activating the trigger. Recover only that focus, never an outside control.
19
21
  const active = host.current?.ownerDocument.activeElement;
20
- if (wasOpen.current && !open && focusedContent.current &&
22
+ if (wasOpen.current && !contentOpen && focusedContent.current &&
21
23
  (content.current?.contains(active ?? null) || active === host.current?.ownerDocument.body)) {
22
- (disabled ? host.current : button.current)?.focus();
24
+ (disabled || pendingLabel !== undefined ? host.current : button.current)?.focus();
23
25
  }
24
- if (!open)
26
+ if (!contentOpen)
25
27
  focusedContent.current = false;
26
- wasOpen.current = open;
27
- }, [open, disabled]);
28
+ wasOpen.current = contentOpen;
29
+ }, [contentOpen, disabled, pendingLabel]);
28
30
  const id = `${useId().replaceAll(":", "")}-collapsible`;
29
- return (_jsxs("div", { ref: node => { host.current = node; assignRef(forwardedRef, node); }, tabIndex: -1, className: classNames("hjm-collapsible", className), "data-state": open ? "open" : "closed", style: { ...layoutStyle, "--hjm-collapsible-gap": `${collapsibleRecipe.gap}px` }, children: [_jsxs("button", { ref: button, hidden: presentation === "inline", type: "button", className: "hjm-collapsible__trigger", "aria-expanded": open, "aria-controls": `${id}-content`, disabled: disabled, onClick: event => {
30
- if (disabled || presentation === "inline")
31
- return;
32
- // Safari does not focus buttons on pointer activation; keep the closing
33
- // disclosure as the keyboard origin before its focused content disappears.
34
- if (open)
35
- event.currentTarget.focus();
36
- const next = !open;
37
- if (!controlled)
38
- setInternalOpen(next);
39
- openState.onOpenChange?.(next);
40
- }, children: [trigger, marker === "end" ? _jsx(CollapsibleMarker, { open: open }) : null] }), open || keepMounted ? (_jsx("div", { ref: content, onFocusCapture: () => { focusedContent.current = true; }, onBlurCapture: event => { if (event.relatedTarget instanceof Node && !event.currentTarget.contains(event.relatedTarget))
41
- focusedContent.current = false; }, hidden: !open, id: `${id}-content`, role: "region", className: "hjm-collapsible__content", children: children })) : null] }));
31
+ return (_jsxs("div", { ref: node => { host.current = node; assignRef(forwardedRef, node); }, tabIndex: -1, className: classNames("hjm-collapsible", className), "data-state": open ? "open" : "closed", "aria-busy": pendingLabel === undefined ? undefined : true, style: { ...layoutStyle, "--hjm-collapsible-gap": `${collapsibleRecipe.gap}px` }, children: [_jsxs("div", { className: "hjm-collapsible__header", "data-titled": title == null ? undefined : true, hidden: presentation === "inline", children: [title == null ? null : _jsx("div", { className: "hjm-collapsible__title", children: title }), pendingLabel === undefined ? _jsxs("button", { ref: button, hidden: presentation === "inline", type: "button", className: "hjm-collapsible__trigger", "aria-label": triggerLabel, "aria-expanded": open, "aria-controls": `${id}-content`, disabled: disabled, onClick: event => {
32
+ if (disabled || presentation === "inline")
33
+ return;
34
+ // Safari does not focus buttons on pointer activation; keep the closing
35
+ // disclosure as the keyboard origin before its focused content disappears.
36
+ if (open)
37
+ event.currentTarget.focus();
38
+ const next = !open;
39
+ if (!controlled)
40
+ setInternalOpen(next);
41
+ openState.onOpenChange?.(next);
42
+ }, children: [trigger, marker === "end" ? _jsx(CollapsibleMarker, { open: open }) : null] }) : _jsx("span", { className: "hjm-collapsible__pending", children: _jsx(Spinner, { label: pendingLabel, size: "small" }) })] }), contentOpen || keepMounted ? (_jsx("div", { ref: content, onFocusCapture: () => { focusedContent.current = true; }, onBlurCapture: event => { if (event.relatedTarget instanceof Node && !event.currentTarget.contains(event.relatedTarget))
43
+ focusedContent.current = false; }, hidden: !contentOpen, id: `${id}-content`, role: "region", className: "hjm-collapsible__content", children: children })) : null] }));
42
44
  });
43
45
  /** The decorative open/closed mark; the expanded state itself travels through the trigger's aria-expanded. */
44
46
  export function CollapsibleMarker({ open }) {