@mortenbrudvik/waveui 0.8.0 → 0.10.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 (206) hide show
  1. package/README.md +634 -167
  2. package/dist/components/data-display/InfoButton.cjs +2 -0
  3. package/dist/components/data-display/InfoButton.cjs.map +1 -0
  4. package/dist/components/data-display/InfoButton.mjs +173 -0
  5. package/dist/components/data-display/InfoButton.mjs.map +1 -0
  6. package/dist/components/data-display/InfoLabel.cjs +1 -1
  7. package/dist/components/data-display/InfoLabel.cjs.map +1 -1
  8. package/dist/components/data-display/InfoLabel.mjs +39 -96
  9. package/dist/components/data-display/InfoLabel.mjs.map +1 -1
  10. package/dist/components/input/Calendar.cjs +2 -0
  11. package/dist/components/input/Calendar.cjs.map +1 -0
  12. package/dist/components/input/Calendar.mjs +352 -0
  13. package/dist/components/input/Calendar.mjs.map +1 -0
  14. package/dist/components/input/Calendar.views.cjs +2 -0
  15. package/dist/components/input/Calendar.views.cjs.map +1 -0
  16. package/dist/components/input/Calendar.views.mjs +194 -0
  17. package/dist/components/input/Calendar.views.mjs.map +1 -0
  18. package/dist/components/input/Checkbox.cjs +1 -1
  19. package/dist/components/input/Checkbox.cjs.map +1 -1
  20. package/dist/components/input/Checkbox.mjs +91 -75
  21. package/dist/components/input/Checkbox.mjs.map +1 -1
  22. package/dist/components/input/ColorArea.cjs +1 -1
  23. package/dist/components/input/ColorArea.cjs.map +1 -1
  24. package/dist/components/input/ColorArea.mjs +18 -18
  25. package/dist/components/input/ColorArea.mjs.map +1 -1
  26. package/dist/components/input/ColorPicker.parts.cjs +1 -1
  27. package/dist/components/input/ColorPicker.parts.cjs.map +1 -1
  28. package/dist/components/input/ColorPicker.parts.mjs +1 -1
  29. package/dist/components/input/ColorPicker.parts.mjs.map +1 -1
  30. package/dist/components/input/Combobox.cjs +1 -1
  31. package/dist/components/input/Combobox.cjs.map +1 -1
  32. package/dist/components/input/Combobox.expand.cjs +1 -1
  33. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  34. package/dist/components/input/Combobox.expand.mjs +20 -20
  35. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  36. package/dist/components/input/Combobox.mjs +219 -138
  37. package/dist/components/input/Combobox.mjs.map +1 -1
  38. package/dist/components/input/Combobox.text.cjs +2 -0
  39. package/dist/components/input/Combobox.text.cjs.map +1 -0
  40. package/dist/components/input/Combobox.text.mjs +24 -0
  41. package/dist/components/input/Combobox.text.mjs.map +1 -0
  42. package/dist/components/input/DatePicker.cjs +1 -1
  43. package/dist/components/input/DatePicker.cjs.map +1 -1
  44. package/dist/components/input/DatePicker.mjs +205 -286
  45. package/dist/components/input/DatePicker.mjs.map +1 -1
  46. package/dist/components/input/Dropdown.cjs +1 -1
  47. package/dist/components/input/Dropdown.cjs.map +1 -1
  48. package/dist/components/input/Dropdown.mjs +137 -105
  49. package/dist/components/input/Dropdown.mjs.map +1 -1
  50. package/dist/components/input/Field.cjs +1 -1
  51. package/dist/components/input/Field.cjs.map +1 -1
  52. package/dist/components/input/Field.mjs +99 -66
  53. package/dist/components/input/Field.mjs.map +1 -1
  54. package/dist/components/input/Input.cjs +1 -1
  55. package/dist/components/input/Input.cjs.map +1 -1
  56. package/dist/components/input/Input.mjs +79 -55
  57. package/dist/components/input/Input.mjs.map +1 -1
  58. package/dist/components/input/Label.cjs +1 -1
  59. package/dist/components/input/Label.cjs.map +1 -1
  60. package/dist/components/input/Label.mjs +19 -13
  61. package/dist/components/input/Label.mjs.map +1 -1
  62. package/dist/components/input/Listbox.cjs +2 -0
  63. package/dist/components/input/Listbox.cjs.map +1 -0
  64. package/dist/components/input/Listbox.mjs +134 -0
  65. package/dist/components/input/Listbox.mjs.map +1 -0
  66. package/dist/components/input/Option.cjs +1 -1
  67. package/dist/components/input/Option.cjs.map +1 -1
  68. package/dist/components/input/Option.mjs +158 -97
  69. package/dist/components/input/Option.mjs.map +1 -1
  70. package/dist/components/input/Rating.cjs +1 -1
  71. package/dist/components/input/Rating.cjs.map +1 -1
  72. package/dist/components/input/Rating.item.cjs +2 -0
  73. package/dist/components/input/Rating.item.cjs.map +1 -0
  74. package/dist/components/input/Rating.item.mjs +255 -0
  75. package/dist/components/input/Rating.item.mjs.map +1 -0
  76. package/dist/components/input/Rating.mjs +150 -151
  77. package/dist/components/input/Rating.mjs.map +1 -1
  78. package/dist/components/input/SearchBox.cjs +1 -1
  79. package/dist/components/input/SearchBox.cjs.map +1 -1
  80. package/dist/components/input/SearchBox.mjs +146 -115
  81. package/dist/components/input/SearchBox.mjs.map +1 -1
  82. package/dist/components/input/Select.cjs +1 -1
  83. package/dist/components/input/Select.cjs.map +1 -1
  84. package/dist/components/input/Select.mjs +34 -25
  85. package/dist/components/input/Select.mjs.map +1 -1
  86. package/dist/components/input/Slider.cjs +1 -1
  87. package/dist/components/input/Slider.cjs.map +1 -1
  88. package/dist/components/input/Slider.mjs +77 -54
  89. package/dist/components/input/Slider.mjs.map +1 -1
  90. package/dist/components/input/SpinButton.cjs +1 -1
  91. package/dist/components/input/SpinButton.cjs.map +1 -1
  92. package/dist/components/input/SpinButton.mjs +198 -96
  93. package/dist/components/input/SpinButton.mjs.map +1 -1
  94. package/dist/components/input/SwatchPicker.cjs +1 -1
  95. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  96. package/dist/components/input/SwatchPicker.context.cjs +2 -0
  97. package/dist/components/input/SwatchPicker.context.cjs.map +1 -0
  98. package/dist/components/input/SwatchPicker.context.mjs +35 -0
  99. package/dist/components/input/SwatchPicker.context.mjs.map +1 -0
  100. package/dist/components/input/SwatchPicker.mjs +131 -103
  101. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  102. package/dist/components/input/SwatchPicker.swatches.cjs +2 -0
  103. package/dist/components/input/SwatchPicker.swatches.cjs.map +1 -0
  104. package/dist/components/input/SwatchPicker.swatches.mjs +207 -0
  105. package/dist/components/input/SwatchPicker.swatches.mjs.map +1 -0
  106. package/dist/components/input/Switch.cjs +1 -1
  107. package/dist/components/input/Switch.cjs.map +1 -1
  108. package/dist/components/input/Switch.mjs +71 -55
  109. package/dist/components/input/Switch.mjs.map +1 -1
  110. package/dist/components/input/TagPicker.cjs +1 -1
  111. package/dist/components/input/TagPicker.cjs.map +1 -1
  112. package/dist/components/input/TagPicker.mjs +154 -123
  113. package/dist/components/input/TagPicker.mjs.map +1 -1
  114. package/dist/components/input/Textarea.cjs +1 -1
  115. package/dist/components/input/Textarea.cjs.map +1 -1
  116. package/dist/components/input/Textarea.mjs +28 -21
  117. package/dist/components/input/Textarea.mjs.map +1 -1
  118. package/dist/components/input/TimePicker.cjs +1 -1
  119. package/dist/components/input/TimePicker.cjs.map +1 -1
  120. package/dist/components/input/TimePicker.mjs +204 -199
  121. package/dist/components/input/TimePicker.mjs.map +1 -1
  122. package/dist/components/input/dateUtils.cjs +1 -1
  123. package/dist/components/input/dateUtils.cjs.map +1 -1
  124. package/dist/components/input/dateUtils.mjs +114 -96
  125. package/dist/components/input/dateUtils.mjs.map +1 -1
  126. package/dist/components/input/fieldLabel.cjs +2 -0
  127. package/dist/components/input/fieldLabel.cjs.map +1 -0
  128. package/dist/components/input/fieldLabel.mjs +17 -0
  129. package/dist/components/input/fieldLabel.mjs.map +1 -0
  130. package/dist/components/input/inputLook.cjs +2 -0
  131. package/dist/components/input/inputLook.cjs.map +1 -0
  132. package/dist/components/input/inputLook.mjs +28 -0
  133. package/dist/components/input/inputLook.mjs.map +1 -0
  134. package/dist/components/input/pickerLabels.cjs +2 -0
  135. package/dist/components/input/pickerLabels.cjs.map +1 -0
  136. package/dist/components/input/pickerLabels.mjs +16 -0
  137. package/dist/components/input/pickerLabels.mjs.map +1 -0
  138. package/dist/components/input/pickerStyles.cjs +1 -1
  139. package/dist/components/input/pickerStyles.cjs.map +1 -1
  140. package/dist/components/input/pickerStyles.mjs +42 -4
  141. package/dist/components/input/pickerStyles.mjs.map +1 -1
  142. package/dist/components/input/pickerValues.cjs +2 -0
  143. package/dist/components/input/pickerValues.cjs.map +1 -0
  144. package/dist/components/input/pickerValues.mjs +24 -0
  145. package/dist/components/input/pickerValues.mjs.map +1 -0
  146. package/dist/components/navigation/Menu.popover.cjs +1 -1
  147. package/dist/components/navigation/Menu.popover.cjs.map +1 -1
  148. package/dist/components/navigation/Menu.popover.mjs +10 -10
  149. package/dist/components/navigation/Menu.root.cjs +1 -1
  150. package/dist/components/navigation/Menu.root.cjs.map +1 -1
  151. package/dist/components/navigation/Menu.root.mjs +10 -10
  152. package/dist/components/overlays/Popover.cjs +1 -1
  153. package/dist/components/overlays/Popover.cjs.map +1 -1
  154. package/dist/components/overlays/Popover.mjs +203 -208
  155. package/dist/components/overlays/Popover.mjs.map +1 -1
  156. package/dist/components/overlays/Popover.shared.cjs +1 -1
  157. package/dist/components/overlays/Popover.shared.cjs.map +1 -1
  158. package/dist/components/overlays/Popover.shared.mjs +82 -77
  159. package/dist/components/overlays/Popover.shared.mjs.map +1 -1
  160. package/dist/components/provider/WaveProvider.cjs +1 -1
  161. package/dist/components/provider/WaveProvider.cjs.map +1 -1
  162. package/dist/components/provider/WaveProvider.mjs +25 -19
  163. package/dist/components/provider/WaveProvider.mjs.map +1 -1
  164. package/dist/components/table/useGridNavigation.cjs +1 -1
  165. package/dist/components/table/useGridNavigation.cjs.map +1 -1
  166. package/dist/components/table/useGridNavigation.mjs +67 -70
  167. package/dist/components/table/useGridNavigation.mjs.map +1 -1
  168. package/dist/hooks/useActiveDescendant.cjs +2 -0
  169. package/dist/hooks/useActiveDescendant.cjs.map +1 -0
  170. package/dist/hooks/useActiveDescendant.mjs +56 -0
  171. package/dist/hooks/useActiveDescendant.mjs.map +1 -0
  172. package/dist/hooks/useFieldControl.cjs.map +1 -1
  173. package/dist/hooks/useFieldControl.mjs.map +1 -1
  174. package/dist/hooks/useListbox.cjs +1 -1
  175. package/dist/hooks/useListbox.cjs.map +1 -1
  176. package/dist/hooks/useListbox.mjs +250 -210
  177. package/dist/hooks/useListbox.mjs.map +1 -1
  178. package/dist/hooks/useRovingGrid.cjs +2 -0
  179. package/dist/hooks/useRovingGrid.cjs.map +1 -0
  180. package/dist/hooks/useRovingGrid.mjs +69 -0
  181. package/dist/hooks/useRovingGrid.mjs.map +1 -0
  182. package/dist/index.cjs +1 -1
  183. package/dist/index.d.cts +2901 -203
  184. package/dist/index.d.ts +2901 -203
  185. package/dist/index.mjs +107 -99
  186. package/dist/lib/classNames.cjs +1 -1
  187. package/dist/lib/classNames.cjs.map +1 -1
  188. package/dist/lib/classNames.mjs +111 -67
  189. package/dist/lib/classNames.mjs.map +1 -1
  190. package/dist/lib/date.cjs +2 -0
  191. package/dist/lib/date.cjs.map +1 -0
  192. package/dist/lib/date.mjs +44 -0
  193. package/dist/lib/date.mjs.map +1 -0
  194. package/dist/lib/focus.cjs +1 -1
  195. package/dist/lib/focus.cjs.map +1 -1
  196. package/dist/lib/focus.mjs +5 -2
  197. package/dist/lib/focus.mjs.map +1 -1
  198. package/dist/lib/layers.cjs.map +1 -1
  199. package/dist/lib/layers.mjs.map +1 -1
  200. package/dist/lib/styles.cjs +1 -1
  201. package/dist/lib/styles.cjs.map +1 -1
  202. package/dist/lib/styles.mjs +21 -4
  203. package/dist/lib/styles.mjs.map +1 -1
  204. package/dist/styles.css +1 -1
  205. package/package.json +1 -1
  206. package/src/styles/tokens.css +13 -0
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/composeEventHandlers.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../hooks/useEventCallback.cjs"),a=require("../../hooks/useId.cjs"),o=require("../../lib/focus.cjs"),s=require("../../hooks/useMergedRefs.cjs"),ee=require("../../lib/aria.cjs"),te=require("../../hooks/usePresence.cjs"),ne=require("../../hooks/useDismiss.cjs"),c=require("../portal/Portal.cjs"),l=require("../../lib/styles.cjs"),u=require("../../lib/icons.cjs"),d=require("../../hooks/usePopupPosition.cjs"),f=require("../../hooks/useRestoreFocus.cjs"),p=require("../../hooks/useHoverIntent.cjs"),m=require("../overlays/Popover.shared.cjs");let h=require("react");h=e.__toESM(h,1);let g=require("react/jsx-runtime");var _={small:{size:12,margin:`-my-1`},medium:{size:16,margin:`-my-0.5`},large:{size:20,margin:`-my-px`}},v=250,y=h.createContext(null);y.displayName=`InfoNoteClassContext`;var b=({info:e,size:b,openOnHover:x=!0,id:re,type:ie=`button`,"aria-label":ae,"aria-labelledby":S,"aria-describedby":C,className:w,disabled:T,onClick:oe,onFocus:se,onBlur:ce,onKeyDown:le,onPointerDown:ue,onMouseDown:de,onPointerEnter:fe,onPointerMove:pe,onPointerLeave:me,ref:he,...E})=>{let D=b===`small`||b===`large`?b:`medium`,O=a.useId(`info-button`),k=re||O,A=h.useContext(y),j=A?.buttonId===k?A.className:void 0,M=ae||`Information`,N=a.useId(`info-note`),P=a.useId(`info`),[F,I]=h.useState(null),L=F!==null,R=h.useRef(null),z=h.useRef(null),B=h.useRef(null),[ge,_e]=h.useState(null),[V,H]=h.useState(null),U=h.useRef(F);h.useLayoutEffect(()=>{U.current=F});let W=h.useRef(!1),G=h.useRef(!1),K=h.useRef(void 0),q=h.useRef(!1),{isMounted:J,ref:ve,presenceProps:ye}=te.usePresence(L),{setReference:be,setFloating:xe,floatingProps:Se,arrowStyles:Ce,side:we}=d.usePopupPosition({open:L,side:`top`,align:`center`,offset:8,arrowRef:B}),{layerId:Y}=ne.useDismiss({open:L,onDismiss:e=>{e!==`focus-outside`&&(W.current=!0),q.current=!1,Q(),I(null)},refs:[R,z],anchorRef:R,focusOutside:!0});f.useRestoreFocus({enabled:L,container:V,triggerRef:R,onlyIfFocusInside:!0,isHoverClose:()=>q.current});let{triggerHandlers:X,surfaceHandlers:Z,cancel:Q}=p.useHoverIntent({enabled:x&&!T,open:L,openDelay:v,closeDelay:v,trigger:ge,surface:V,onOpen:()=>{q.current=!1,I(e=>e??`hover`)},onClose:()=>{q.current=!0,I(e=>e===`hover`?null:e)},canClose:()=>U.current===`hover`&&!m.isFocusInSurfaceTree(V,Y)});h.useEffect(()=>()=>clearTimeout(K.current),[]),h.useEffect(()=>{if(!V)return;let e=e=>{let t=e.relatedTarget;!t||V.contains(t)||R.current?.contains(t)||(W.current=!1)};return V.addEventListener(`focusout`,e),()=>V.removeEventListener(`focusout`,e)},[V]);let Te=i.useEventCallback(()=>{Q(),q.current=!1;let e=F===`click`;e&&(W.current=!0),I(e?null:`click`)}),Ee=i.useEventCallback(()=>{!x||G.current||W.current&&!L||(q.current=!1,Q(),I(e=>e===`click`?e:`focus`))}),De=i.useEventCallback(e=>{let t=e.currentTarget,n=e.relatedTarget;n&&V?.contains(n)||(n||t.ownerDocument.activeElement!==t)&&(W.current=!1)}),$=i.useEventCallback(()=>{G.current=!0,clearTimeout(K.current),K.current=setTimeout(()=>{K.current=void 0,G.current=!1},0)}),Oe=e=>{if(e.key!==`Tab`||e.shiftKey||!L||!V)return;let t=o.getFirstTabbable(V);t&&(e.preventDefault(),t.focus())},ke=m.usePopoverTabOrder({enabled:L,surface:V,anchorRef:R,getPreviousStop:()=>R.current}),Ae=s.useMergedRefs(he,R,_e,be),je=s.useMergedRefs(z,H,xe,ve);return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`button`,{type:ie,id:k,"data-size":D,...E,ref:Ae,disabled:T,"aria-label":M,"aria-labelledby":S,"aria-describedby":ee.joinIds(C,P),"aria-expanded":L,"aria-controls":L?N:void 0,className:t.cn(r.infoButtonClassNames.root,`inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground`,_[D].margin,`not-disabled:not-aria-disabled:hover:text-foreground`,l.focusRing,w),onClick:n.composeEventHandlers(oe,Te),onFocus:n.composeEventHandlers(se,Ee),onKeyDown:n.composeEventHandlers(le,Oe),onPointerEnter:n.composeEventHandlers(fe,X.onPointerEnter),onPointerMove:n.composeEventHandlers(pe,X.onPointerMove),onPointerLeave:n.composeEventHandlers(me,X.onPointerLeave),onBlur:n.composeEventHandlers(ce,De,{checkDefaultPrevented:!1}),onPointerDown:n.composeEventHandlers(ue,$,{checkDefaultPrevented:!1}),onMouseDown:n.composeEventHandlers(de,$,{checkDefaultPrevented:!1}),children:(0,g.jsx)(u.InfoIcon,{size:_[D].size})}),(0,g.jsx)(`span`,{id:J?void 0:P,hidden:!0,children:J?null:e}),J&&(0,g.jsx)(c.Portal,{layerId:Y,children:(0,g.jsxs)(`div`,{ref:je,id:N,role:`note`,"aria-labelledby":S||k,"data-wave-infolabel-surface":``,"data-state":L?`open`:`closed`,...ye,...Se,onKeyDown:ke,onPointerEnter:Z.onPointerEnter,onPointerLeave:Z.onPointerLeave,className:t.cn(r.infoButtonClassNames.info,j,`w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16`),children:[(0,g.jsx)(m.PopoverBeak,{ref:B,side:we,style:Ce,"data-wave-popover-arrow":``,className:r.infoButtonClassNames.beak}),(0,g.jsx)(`div`,{id:P,children:e})]})})]})};b.displayName=`InfoButton`,exports.InfoButton=b,exports.InfoNoteClassContext=y;
2
+ //# sourceMappingURL=InfoButton.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InfoButton.cjs","names":["React","useId","usePresence","usePopupPosition","useDismiss","useHoverIntent","isFocusInSurfaceTree","useEventCallback","getFirstTabbable","usePopoverTabOrder","useMergedRefs","_Fragment","joinIds","cn","infoButtonClassNames","focusRing","composeEventHandlers","InfoIcon","Portal","PopoverBeak","require_runtime","require_cn","require_composeEventHandlers","require_classNames","require_useEventCallback","require_useId","require_focus","require_useMergedRefs","require_aria","require_usePresence","require_useDismiss","require_Portal","require_styles","require_icons","require_usePopupPosition","require_useRestoreFocus","require_useHoverIntent","require_Popover_shared","react","react_jsx_runtime","GLYPH","HOVER_DELAY","InfoNoteClassContext","InfoButton","sizeProp","idProp","ariaLabelProp","ariaLabelledBy","ariaDescribedBy","rest","size","generatedId","id","noteClass","noteClassName","ariaLabel","noteId","infoId","reason","setReason","open","buttonRef","noteRef","arrowRef","buttonElement","setButtonElement","note","setNote","reasonRef","dismissedRef","pointerPressRef","pressTimerRef","hoverCloseRef","presenceRef","dismissReason","cancelHover","hoverTriggerHandlers","hoverSurfaceHandlers","openOnHover","disabled","current","layerId","onFocusOut","next","event","handleClick","closing","handleFocus","handleBlur","button","handlePointerPress","onButtonKeyDown","first","onNoteKeyDown","buttonRefs","ref","setReference","noteRefs","setFloating","className","onClick","onFocus","onKeyDown","onPointerEnter","onPointerMove","onPointerLeave","onBlur","onPointerDown","onMouseDown","isMounted","info","presenceProps","floatingProps","arrowStyles"],"sources":["../../../src/components/data-display/InfoButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { infoButtonClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { getFirstTabbable } from '../../lib/focus';\nimport { InfoIcon } from '../../lib/icons';\nimport { focusRing } from '../../lib/styles';\nimport type { CoreSize } from '../../lib/types';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useHoverIntent } from '../../hooks/useHoverIntent';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { usePresence } from '../../hooks/usePresence';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { isFocusInSurfaceTree, PopoverBeak, usePopoverTabOrder } from '../overlays/Popover.shared';\nimport { Portal } from '../portal/Portal';\n\n/** Properties for the InfoButton component. */\nexport interface InfoButtonProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n> {\n /**\n * The information the note shows: text, links or other content. It also describes the button\n * (`aria-describedby`), from the server HTML on. It is rendered once: next to the button, not\n * displayed, while the note is closed, and inside the note while it is shown.\n */\n info: React.ReactNode;\n /**\n * Size of the glyph: 12, 16 or 20px. The button box stays 24×24px at every size, with negative\n * margins above and below, so it never makes a row that centres its items (as InfoLabel does)\n * taller.\n * @default 'medium'\n */\n size?: CoreSize;\n /**\n * Open the note when a mouse pointer rests on the button (after 250 ms; it closes 250 ms after\n * the pointer has left the button and the note, touch and pen never hover), and when the button\n * receives keyboard focus. `false` opens it only on a click, and so on Enter and Space (Fluent's\n * InfoButton opens on click only). A click always toggles the note: it pins a note that hover or\n * keyboard focus opened, and closes a pinned one.\n * @default true\n */\n openOnHover?: boolean;\n /**\n * Accessible name of the button, and of the note, which is labelled like the button. Localize it\n * here. With `aria-labelledby` listing the id of your text and the button's own `id`, the name is\n * your text followed by this one (\"Billing Information\").\n * @default 'Information'\n */\n 'aria-label'?: string;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** Why the note is open: a click pins it; keyboard focus owns it; hover opens it transiently. */\ntype OpenReason = 'hover' | 'focus' | 'click';\n\n/**\n * Glyph size and negative margins per size: the 24px box never makes a row that centres its items\n * taller.\n */\nconst GLYPH: Readonly<Record<CoreSize, { size: number; margin: string }>> = {\n small: { size: 12, margin: '-my-1' },\n medium: { size: 16, margin: '-my-0.5' },\n large: { size: 20, margin: '-my-px' },\n};\n\n/**\n * Milliseconds from a mouse pointer resting on the button to the opening, and from the pointer\n * leaving the button and the note to the hover close (the 0.7 InfoLabel delays).\n */\nconst HOVER_DELAY = 250;\n\n/** What InfoLabel tells the InfoButton it renders: see {@link InfoNoteClassContext}. */\ninterface InfoNoteClass {\n /** The `id` of the InfoButton the class is for. */\n buttonId: string;\n /** The class added to that InfoButton's note. */\n className: string;\n}\n\n/**\n * Internal (P3-00, D5): the class InfoLabel adds to the note of the InfoButton it renders, its\n * 0.8 `info` part. Only the InfoButton whose `id` matches takes it, so an InfoButton inside the\n * label's `info` content takes none. Not exported from the barrels.\n */\nexport const InfoNoteClassContext = React.createContext<InfoNoteClass | null>(null);\nInfoNoteClassContext.displayName = 'InfoNoteClassContext';\n\n/**\n * A button with the info glyph that shows `info` in a note next to it: text, links or other rich\n * content, placed above the button with a beak (a toggletip).\n *\n * - **Naming.** The button is named \"Information\" (`aria-label`, which localizes it). Next to a\n * heading or other text, pass `aria-labelledby` with that text's id and the button's own `id`:\n * the button is then named \"‹text› Information\", so several info buttons on a page are told\n * apart. The note (`role=\"note\"`) is labelled like the button: by the same `aria-labelledby`\n * ids, else by the button. The button has `aria-expanded`, and `aria-controls` while the note is\n * open, but no `aria-haspopup`: a note is none of the popup kinds it names.\n * - **Opening.** A click pins the note open, and a click on a pinned note closes it. With\n * `openOnHover` (the default) a mouse pointer resting on the button opens it after 250 ms, and\n * it closes 250 ms after the pointer has left the button and the note (a pointer moving\n * diagonally from the button into the note keeps it open; touch and pen never hover); keyboard\n * focus on the button opens it too, while focus that comes from a pointer press waits for the\n * click. A hover close moves no focus, and a note that keyboard focus opened or keeps never\n * closes because of pointer movement. Opening never moves focus. Escape, a press outside and\n * focus moving outside the button and the note close it; Escape returns focus to the button when\n * it was in the note, and a dismissed note opens again on keyboard focus only once focus has\n * left both.\n * - **Tab path.** The note is portaled, but it keeps its place right after the button in the\n * keyboard order: Tab from the open button enters the note's first element (a link in it), Tab\n * past its last element continues after the button, and Shift+Tab from its first element\n * returns to the button.\n * - **One copy of `info`.** While the note is closed, `info` renders next to the button in an\n * element that is not displayed and describes the button (`aria-describedby`), so it is in the\n * server HTML and is read when the button takes focus. While the note is shown, `info` renders\n * only inside it, in an unnamed element that describes the button. `info` is never in the\n * document twice, so content with ids or effects is safe; it remounts each time the note opens\n * and closes, so state inside it starts over and its effects run again.\n * - **Structure.** The button is the root element: `ref`, `className`, `style` and every other\n * attribute go to it, the consumer's handlers first. The description and the note render next\n * to it, never inside it. Its box is 24×24px at every `size` and renders `data-size`. The note\n * mounts through the presence core: it carries `data-presence`, `data-state` (`open`, and\n * `closed` while it exits, `inert`) and the `data-wave-infolabel-surface` marker; it ships no\n * motion of its own.\n *\n * Fluent's InfoButton takes the same `info` and `size`. It opens on click only\n * (`openOnHover={false}` here), renders its note in place, and moves focus into the note; this one\n * keeps focus on the button and renders the note in a portal. Fluent's `popover` props and a\n * controlled open state are not offered.\n *\n * @example\n * <h2 id=\"billing\">Billing</h2>\n * <InfoButton\n * id=\"billing-info\"\n * aria-labelledby=\"billing billing-info\"\n * info={<>Invoices are sent monthly. <a href=\"/help/billing\">Learn more</a></>}\n * />\n */\nexport const InfoButton = ({\n info,\n size: sizeProp,\n openOnHover = true,\n id: idProp,\n type = 'button',\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n className,\n disabled,\n onClick,\n onFocus,\n onBlur,\n onKeyDown,\n onPointerDown,\n onMouseDown,\n onPointerEnter,\n onPointerMove,\n onPointerLeave,\n ref,\n ...rest\n}: InfoButtonProps) => {\n // A size from untyped code renders, and reports, the default.\n const size: CoreSize = sizeProp === 'small' || sizeProp === 'large' ? sizeProp : 'medium';\n const generatedId = useId('info-button');\n const id = idProp || generatedId;\n const noteClass = React.useContext(InfoNoteClassContext);\n const noteClassName = noteClass?.buttonId === id ? noteClass.className : undefined;\n // An empty name would leave the button and the note unnamed: the glyph is decorative.\n const ariaLabel = ariaLabelProp || 'Information';\n const noteId = useId('info-note');\n // The one copy of `info`: not displayed while the note is closed, inside the note while shown.\n const infoId = useId('info');\n const [reason, setReason] = React.useState<OpenReason | null>(null);\n const open = reason !== null;\n\n const buttonRef = React.useRef<HTMLButtonElement | null>(null);\n const noteRef = React.useRef<HTMLDivElement | null>(null);\n const arrowRef = React.useRef<HTMLDivElement | null>(null);\n // The button and the note held in state too (C-POPUPS): for the hover intent (the safe zone\n // measures both) and the focus restore.\n const [buttonElement, setButtonElement] = React.useState<HTMLButtonElement | null>(null);\n const [note, setNote] = React.useState<HTMLDivElement | null>(null);\n\n // The latest reason, for the hover close (read in the hover intent's pointer events and timers).\n const reasonRef = React.useRef(reason);\n React.useLayoutEffect(() => {\n reasonRef.current = reason;\n });\n // Set by a dismissal (Escape, outside press): keyboard focus reopens only after focus has left\n // both the button and the note (a focus restore must not reopen it). A click that closes the\n // note sets it too: in Safari a click leaves focus on <body>, and the focus restore follows. It\n // blocks only a reopening: keyboard focus still takes over a note that a click or hover opened.\n const dismissedRef = React.useRef(false);\n // A pointer press focuses the button; that focus waits for the click (0.7).\n const pointerPressRef = React.useRef(false);\n const pressTimerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // A hover close in flight, set before its request: that close moves no focus. Every other close\n // request and every opening clears it.\n const hoverCloseRef = React.useRef(false);\n\n // The note mounts and unmounts through the presence core; the dismiss layer, the focus restore\n // and the positioning below key on `open`, so they go on close, not after an exit motion.\n // Destructured: react-hooks/refs treats an object whose member is passed to `ref` as a ref.\n const { isMounted, ref: presenceRef, presenceProps } = usePresence(open);\n const { setReference, setFloating, floatingProps, arrowStyles, side } = usePopupPosition({\n open,\n side: 'top',\n align: 'center',\n offset: 8,\n arrowRef,\n });\n const { layerId } = useDismiss({\n open,\n onDismiss: (dismissReason) => {\n // Only a focusin elsewhere dismisses with 'focus-outside' (focus lost to <body> never does):\n // focus has left both then, so keyboard focus may reopen the note at once.\n if (dismissReason !== 'focus-outside') dismissedRef.current = true;\n hoverCloseRef.current = false;\n // Declared below; the layer calls this only from its document listeners, after render.\n cancelHover();\n setReason(null);\n },\n refs: [buttonRef, noteRef],\n anchorRef: buttonRef,\n focusOutside: true,\n });\n // Focus returns to the button when the note closes with focus inside it (or lost to <body>); a\n // hover close returns only focus that is inside it, so it never moves focus.\n useRestoreFocus({\n enabled: open,\n container: note,\n triggerRef: buttonRef,\n onlyIfFocusInside: true,\n isHoverClose: () => hoverCloseRef.current,\n });\n\n const {\n triggerHandlers: hoverTriggerHandlers,\n surfaceHandlers: hoverSurfaceHandlers,\n cancel: cancelHover,\n } = useHoverIntent({\n // Browsers send pointer events to a disabled button too, and the hook skips only an\n // `aria-disabled` trigger.\n enabled: openOnHover && !disabled,\n open,\n openDelay: HOVER_DELAY,\n closeDelay: HOVER_DELAY,\n trigger: buttonElement,\n surface: note,\n onOpen: () => {\n hoverCloseRef.current = false;\n setReason((current) => current ?? 'hover');\n },\n onClose: () => {\n hoverCloseRef.current = true;\n setReason((current) => (current === 'hover' ? null : current));\n },\n // Only a note opened by hover, not pinned and not keyboard-owned, closes by hover; focus\n // inside the note (or a layer opened from it) blocks it too (Phase 2 D18).\n canClose: () => reasonRef.current === 'hover' && !isFocusInSurfaceTree(note, layerId),\n });\n\n React.useEffect(() => () => clearTimeout(pressTimerRef.current), []);\n\n // A dismissal ends once focus has left both the button and the note: at the button's blur\n // (below) and at a focusout of the note. Focus the note loses to nothing is the note closing\n // (removed, or inert while it exits): that ends nothing.\n React.useEffect(() => {\n if (!note) return undefined;\n const onFocusOut = (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null;\n if (!next || note.contains(next) || buttonRef.current?.contains(next)) return;\n dismissedRef.current = false;\n };\n note.addEventListener('focusout', onFocusOut);\n return () => note.removeEventListener('focusout', onFocusOut);\n }, [note]);\n\n // The handlers below read and write refs: useEventCallback keeps them out of render (C-HOOKS).\n const handleClick = useEventCallback(() => {\n cancelHover();\n hoverCloseRef.current = false;\n // Closed, or opened by hover or keyboard focus: pinned. Pinned: closed.\n const closing = reason === 'click';\n if (closing) dismissedRef.current = true;\n setReason(closing ? null : 'click');\n });\n\n const handleFocus = useEventCallback(() => {\n // Keyboard focus opens, as hovering's keyboard counterpart.\n if (!openOnHover || pointerPressRef.current || (dismissedRef.current && !open)) return;\n hoverCloseRef.current = false;\n cancelHover();\n // A note that hover opened becomes focus-owned; a pinned one stays pinned.\n setReason((current) => (current === 'click' ? current : 'focus'));\n });\n\n const handleBlur = useEventCallback((event: React.FocusEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const next = event.relatedTarget as Node | null;\n // Tab from the button into the note keeps a dismissal.\n if (next && note?.contains(next)) return;\n // So does the window losing focus (a switch of window or tab): the button stays the document's\n // focused element, and the browser focuses it again, from nothing, when the window comes back.\n if (!next && button.ownerDocument.activeElement === button) return;\n // Focus lost to nothing in the page (a press on a spot that takes no focus) has left both.\n dismissedRef.current = false;\n });\n\n const handlePointerPress = useEventCallback(() => {\n pointerPressRef.current = true;\n // A press focuses the button (where it does) as the default action of `mousedown`, in the same\n // event turn. Forget the press after that turn: a press that does not focus the button (Safari\n // and Firefox on macOS, iOS) and ends without a click (dragged off, or a touch that becomes a\n // scroll) must not turn a later keyboard focus into pointer focus. A touch tap dispatches its\n // compatibility `mousedown` (and the focus) after the touch ends; that `mousedown` sets the\n // flag again.\n clearTimeout(pressTimerRef.current);\n pressTimerRef.current = setTimeout(() => {\n pressTimerRef.current = undefined;\n pointerPressRef.current = false;\n }, 0);\n });\n\n // Tab from the open button enters the note. The button's own handler runs before a modal focus\n // trap's document listener, which would wrap the Tab when the button is the trap's last element.\n const onButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (event.key !== 'Tab' || event.shiftKey || !open || !note) return;\n const first = getFirstTabbable(note);\n if (!first) return;\n event.preventDefault();\n first.focus();\n };\n // Tab past the note's last element continues after the button, Shift+Tab from its first element\n // returns to it, and the page order skips the note where its portal is.\n const onNoteKeyDown = usePopoverTabOrder({\n enabled: open,\n surface: note,\n anchorRef: buttonRef,\n getPreviousStop: () => buttonRef.current,\n });\n\n const buttonRefs = useMergedRefs<HTMLButtonElement>(\n ref,\n buttonRef,\n setButtonElement,\n setReference,\n );\n const noteRefs = useMergedRefs<HTMLDivElement>(noteRef, setNote, setFloating, presenceRef);\n\n return (\n <>\n <button\n type={type}\n id={id}\n data-size={size}\n {...rest}\n ref={buttonRefs}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={joinIds(ariaDescribedBy, infoId)}\n aria-expanded={open}\n aria-controls={open ? noteId : undefined}\n className={cn(\n infoButtonClassNames.root,\n 'inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground',\n GLYPH[size].margin,\n 'not-disabled:not-aria-disabled:hover:text-foreground',\n focusRing,\n className,\n )}\n onClick={composeEventHandlers(onClick, handleClick)}\n onFocus={composeEventHandlers(onFocus, handleFocus)}\n onKeyDown={composeEventHandlers(onKeyDown, onButtonKeyDown)}\n onPointerEnter={composeEventHandlers(onPointerEnter, hoverTriggerHandlers.onPointerEnter)}\n onPointerMove={composeEventHandlers(onPointerMove, hoverTriggerHandlers.onPointerMove)}\n onPointerLeave={composeEventHandlers(onPointerLeave, hoverTriggerHandlers.onPointerLeave)}\n // Bookkeeping that follows the DOM (where focus went, whether a press is under way): it\n // runs even when the consumer prevents the default.\n onBlur={composeEventHandlers(onBlur, handleBlur, { checkDefaultPrevented: false })}\n onPointerDown={composeEventHandlers(onPointerDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n onMouseDown={composeEventHandlers(onMouseDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n >\n <InfoIcon size={GLYPH[size].size} />\n </button>\n {/* The same element follows the button in every phase, so a row styled by position never\n shifts: it holds `info` while the note is not mounted, and nothing while the note does. */}\n <span id={isMounted ? undefined : infoId} hidden>\n {isMounted ? null : info}\n </span>\n {isMounted && (\n <Portal layerId={layerId}>\n <div\n ref={noteRefs}\n id={noteId}\n role=\"note\"\n aria-labelledby={ariaLabelledBy || id}\n data-wave-infolabel-surface=\"\"\n data-state={open ? 'open' : 'closed'}\n {...presenceProps}\n {...floatingProps}\n onKeyDown={onNoteKeyDown}\n onPointerEnter={hoverSurfaceHandlers.onPointerEnter}\n onPointerLeave={hoverSurfaceHandlers.onPointerLeave}\n className={cn(\n infoButtonClassNames.info,\n noteClassName,\n 'w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16',\n )}\n >\n <PopoverBeak\n ref={arrowRef}\n side={side}\n style={arrowStyles}\n data-wave-popover-arrow=\"\"\n className={infoButtonClassNames.beak}\n />\n <div id={infoId}>{info}</div>\n </div>\n </Portal>\n )}\n </>\n );\n};\nInfoButton.displayName = 'InfoButton';\n"],"mappings":"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"}
@@ -0,0 +1,173 @@
1
+ "use client";
2
+ import { cn as e } from "../../lib/cn.mjs";
3
+ import { composeEventHandlers as t } from "../../lib/composeEventHandlers.mjs";
4
+ import { infoButtonClassNames as n } from "../../lib/classNames.mjs";
5
+ import { useEventCallback as r } from "../../hooks/useEventCallback.mjs";
6
+ import { useId as i } from "../../hooks/useId.mjs";
7
+ import { getFirstTabbable as a } from "../../lib/focus.mjs";
8
+ import { useMergedRefs as o } from "../../hooks/useMergedRefs.mjs";
9
+ import { joinIds as s } from "../../lib/aria.mjs";
10
+ import { usePresence as ee } from "../../hooks/usePresence.mjs";
11
+ import { useDismiss as te } from "../../hooks/useDismiss.mjs";
12
+ import { Portal as ne } from "../portal/Portal.mjs";
13
+ import { focusRing as re } from "../../lib/styles.mjs";
14
+ import { InfoIcon as ie } from "../../lib/icons.mjs";
15
+ import { usePopupPosition as ae } from "../../hooks/usePopupPosition.mjs";
16
+ import { useRestoreFocus as oe } from "../../hooks/useRestoreFocus.mjs";
17
+ import { useHoverIntent as se } from "../../hooks/useHoverIntent.mjs";
18
+ import { PopoverBeak as ce, isFocusInSurfaceTree as c, usePopoverTabOrder as l } from "../overlays/Popover.shared.mjs";
19
+ import * as u from "react";
20
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
21
+ //#region src/components/data-display/InfoButton.tsx
22
+ var m = {
23
+ small: {
24
+ size: 12,
25
+ margin: "-my-1"
26
+ },
27
+ medium: {
28
+ size: 16,
29
+ margin: "-my-0.5"
30
+ },
31
+ large: {
32
+ size: 20,
33
+ margin: "-my-px"
34
+ }
35
+ }, h = 250, g = u.createContext(null);
36
+ g.displayName = "InfoNoteClassContext";
37
+ var _ = ({ info: _, size: v, openOnHover: y = !0, id: le, type: ue = "button", "aria-label": de, "aria-labelledby": b, "aria-describedby": fe, className: pe, disabled: x, onClick: me, onFocus: he, onBlur: ge, onKeyDown: _e, onPointerDown: ve, onMouseDown: ye, onPointerEnter: S, onPointerMove: C, onPointerLeave: w, ref: T, ...E }) => {
38
+ let D = v === "small" || v === "large" ? v : "medium", O = i("info-button"), k = le || O, A = u.useContext(g), j = A?.buttonId === k ? A.className : void 0, M = de || "Information", N = i("info-note"), P = i("info"), [F, I] = u.useState(null), L = F !== null, R = u.useRef(null), z = u.useRef(null), B = u.useRef(null), [be, xe] = u.useState(null), [V, Se] = u.useState(null), H = u.useRef(F);
39
+ u.useLayoutEffect(() => {
40
+ H.current = F;
41
+ });
42
+ let U = u.useRef(!1), W = u.useRef(!1), G = u.useRef(void 0), K = u.useRef(!1), { isMounted: q, ref: Ce, presenceProps: we } = ee(L), { setReference: Te, setFloating: Ee, floatingProps: De, arrowStyles: Oe, side: ke } = ae({
43
+ open: L,
44
+ side: "top",
45
+ align: "center",
46
+ offset: 8,
47
+ arrowRef: B
48
+ }), { layerId: J } = te({
49
+ open: L,
50
+ onDismiss: (e) => {
51
+ e !== "focus-outside" && (U.current = !0), K.current = !1, Z(), I(null);
52
+ },
53
+ refs: [R, z],
54
+ anchorRef: R,
55
+ focusOutside: !0
56
+ });
57
+ oe({
58
+ enabled: L,
59
+ container: V,
60
+ triggerRef: R,
61
+ onlyIfFocusInside: !0,
62
+ isHoverClose: () => K.current
63
+ });
64
+ let { triggerHandlers: Y, surfaceHandlers: X, cancel: Z } = se({
65
+ enabled: y && !x,
66
+ open: L,
67
+ openDelay: h,
68
+ closeDelay: h,
69
+ trigger: be,
70
+ surface: V,
71
+ onOpen: () => {
72
+ K.current = !1, I((e) => e ?? "hover");
73
+ },
74
+ onClose: () => {
75
+ K.current = !0, I((e) => e === "hover" ? null : e);
76
+ },
77
+ canClose: () => H.current === "hover" && !c(V, J)
78
+ });
79
+ u.useEffect(() => () => clearTimeout(G.current), []), u.useEffect(() => {
80
+ if (!V) return;
81
+ let e = (e) => {
82
+ let t = e.relatedTarget;
83
+ !t || V.contains(t) || R.current?.contains(t) || (U.current = !1);
84
+ };
85
+ return V.addEventListener("focusout", e), () => V.removeEventListener("focusout", e);
86
+ }, [V]);
87
+ let Ae = r(() => {
88
+ Z(), K.current = !1;
89
+ let e = F === "click";
90
+ e && (U.current = !0), I(e ? null : "click");
91
+ }), je = r(() => {
92
+ !y || W.current || U.current && !L || (K.current = !1, Z(), I((e) => e === "click" ? e : "focus"));
93
+ }), Q = r((e) => {
94
+ let t = e.currentTarget, n = e.relatedTarget;
95
+ n && V?.contains(n) || (n || t.ownerDocument.activeElement !== t) && (U.current = !1);
96
+ }), $ = r(() => {
97
+ W.current = !0, clearTimeout(G.current), G.current = setTimeout(() => {
98
+ G.current = void 0, W.current = !1;
99
+ }, 0);
100
+ }), Me = (e) => {
101
+ if (e.key !== "Tab" || e.shiftKey || !L || !V) return;
102
+ let t = a(V);
103
+ t && (e.preventDefault(), t.focus());
104
+ }, Ne = l({
105
+ enabled: L,
106
+ surface: V,
107
+ anchorRef: R,
108
+ getPreviousStop: () => R.current
109
+ }), Pe = o(T, R, xe, Te), Fe = o(z, Se, Ee, Ce);
110
+ return /* @__PURE__ */ p(d, { children: [
111
+ /* @__PURE__ */ f("button", {
112
+ type: ue,
113
+ id: k,
114
+ "data-size": D,
115
+ ...E,
116
+ ref: Pe,
117
+ disabled: x,
118
+ "aria-label": M,
119
+ "aria-labelledby": b,
120
+ "aria-describedby": s(fe, P),
121
+ "aria-expanded": L,
122
+ "aria-controls": L ? N : void 0,
123
+ className: e(n.root, "inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground", m[D].margin, "not-disabled:not-aria-disabled:hover:text-foreground", re, pe),
124
+ onClick: t(me, Ae),
125
+ onFocus: t(he, je),
126
+ onKeyDown: t(_e, Me),
127
+ onPointerEnter: t(S, Y.onPointerEnter),
128
+ onPointerMove: t(C, Y.onPointerMove),
129
+ onPointerLeave: t(w, Y.onPointerLeave),
130
+ onBlur: t(ge, Q, { checkDefaultPrevented: !1 }),
131
+ onPointerDown: t(ve, $, { checkDefaultPrevented: !1 }),
132
+ onMouseDown: t(ye, $, { checkDefaultPrevented: !1 }),
133
+ children: /* @__PURE__ */ f(ie, { size: m[D].size })
134
+ }),
135
+ /* @__PURE__ */ f("span", {
136
+ id: q ? void 0 : P,
137
+ hidden: !0,
138
+ children: q ? null : _
139
+ }),
140
+ q && /* @__PURE__ */ f(ne, {
141
+ layerId: J,
142
+ children: /* @__PURE__ */ p("div", {
143
+ ref: Fe,
144
+ id: N,
145
+ role: "note",
146
+ "aria-labelledby": b || k,
147
+ "data-wave-infolabel-surface": "",
148
+ "data-state": L ? "open" : "closed",
149
+ ...we,
150
+ ...De,
151
+ onKeyDown: Ne,
152
+ onPointerEnter: X.onPointerEnter,
153
+ onPointerLeave: X.onPointerLeave,
154
+ className: e(n.info, j, "w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16"),
155
+ children: [/* @__PURE__ */ f(ce, {
156
+ ref: B,
157
+ side: ke,
158
+ style: Oe,
159
+ "data-wave-popover-arrow": "",
160
+ className: n.beak
161
+ }), /* @__PURE__ */ f("div", {
162
+ id: P,
163
+ children: _
164
+ })]
165
+ })
166
+ })
167
+ ] });
168
+ };
169
+ _.displayName = "InfoButton";
170
+ //#endregion
171
+ export { _ as InfoButton, g as InfoNoteClassContext };
172
+
173
+ //# sourceMappingURL=InfoButton.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InfoButton.mjs","names":["useId","_Fragment","useId$1","React","GLYPH","HOVER_DELAY","InfoNoteClassContext","InfoButton","sizeProp","idProp","ariaLabelProp","ariaLabelledBy","ariaDescribedBy","rest","size","generatedId","id","noteClass","noteClassName","ariaLabel","noteId","infoId","reason","setReason","open","buttonRef","noteRef","arrowRef","buttonElement","setButtonElement","note","setNote","reasonRef","dismissedRef","pointerPressRef","pressTimerRef","hoverCloseRef","presenceRef","usePresence","usePopupPosition","useDismiss","dismissReason","cancelHover","hoverTriggerHandlers","hoverSurfaceHandlers","useHoverIntent","openOnHover","disabled","current","isFocusInSurfaceTree","layerId","onFocusOut","next","event","handleClick","useEventCallback","closing","handleFocus","handleBlur","button","handlePointerPress","onButtonKeyDown","first","getFirstTabbable","onNoteKeyDown","usePopoverTabOrder","buttonRefs","useMergedRefs","ref","setReference","noteRefs","setFloating","Fragment","joinIds","cn","infoButtonClassNames","focusRing","className","composeEventHandlers","onClick","onFocus","onKeyDown","onPointerEnter","onPointerMove","onPointerLeave","onBlur","onPointerDown","onMouseDown","InfoIcon","isMounted","info","Portal","presenceProps","floatingProps","PopoverBeak","arrowStyles"],"sources":["../../../src/components/data-display/InfoButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { infoButtonClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { getFirstTabbable } from '../../lib/focus';\nimport { InfoIcon } from '../../lib/icons';\nimport { focusRing } from '../../lib/styles';\nimport type { CoreSize } from '../../lib/types';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useHoverIntent } from '../../hooks/useHoverIntent';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { usePresence } from '../../hooks/usePresence';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { isFocusInSurfaceTree, PopoverBeak, usePopoverTabOrder } from '../overlays/Popover.shared';\nimport { Portal } from '../portal/Portal';\n\n/** Properties for the InfoButton component. */\nexport interface InfoButtonProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n> {\n /**\n * The information the note shows: text, links or other content. It also describes the button\n * (`aria-describedby`), from the server HTML on. It is rendered once: next to the button, not\n * displayed, while the note is closed, and inside the note while it is shown.\n */\n info: React.ReactNode;\n /**\n * Size of the glyph: 12, 16 or 20px. The button box stays 24×24px at every size, with negative\n * margins above and below, so it never makes a row that centres its items (as InfoLabel does)\n * taller.\n * @default 'medium'\n */\n size?: CoreSize;\n /**\n * Open the note when a mouse pointer rests on the button (after 250 ms; it closes 250 ms after\n * the pointer has left the button and the note, touch and pen never hover), and when the button\n * receives keyboard focus. `false` opens it only on a click, and so on Enter and Space (Fluent's\n * InfoButton opens on click only). A click always toggles the note: it pins a note that hover or\n * keyboard focus opened, and closes a pinned one.\n * @default true\n */\n openOnHover?: boolean;\n /**\n * Accessible name of the button, and of the note, which is labelled like the button. Localize it\n * here. With `aria-labelledby` listing the id of your text and the button's own `id`, the name is\n * your text followed by this one (\"Billing Information\").\n * @default 'Information'\n */\n 'aria-label'?: string;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** Why the note is open: a click pins it; keyboard focus owns it; hover opens it transiently. */\ntype OpenReason = 'hover' | 'focus' | 'click';\n\n/**\n * Glyph size and negative margins per size: the 24px box never makes a row that centres its items\n * taller.\n */\nconst GLYPH: Readonly<Record<CoreSize, { size: number; margin: string }>> = {\n small: { size: 12, margin: '-my-1' },\n medium: { size: 16, margin: '-my-0.5' },\n large: { size: 20, margin: '-my-px' },\n};\n\n/**\n * Milliseconds from a mouse pointer resting on the button to the opening, and from the pointer\n * leaving the button and the note to the hover close (the 0.7 InfoLabel delays).\n */\nconst HOVER_DELAY = 250;\n\n/** What InfoLabel tells the InfoButton it renders: see {@link InfoNoteClassContext}. */\ninterface InfoNoteClass {\n /** The `id` of the InfoButton the class is for. */\n buttonId: string;\n /** The class added to that InfoButton's note. */\n className: string;\n}\n\n/**\n * Internal (P3-00, D5): the class InfoLabel adds to the note of the InfoButton it renders, its\n * 0.8 `info` part. Only the InfoButton whose `id` matches takes it, so an InfoButton inside the\n * label's `info` content takes none. Not exported from the barrels.\n */\nexport const InfoNoteClassContext = React.createContext<InfoNoteClass | null>(null);\nInfoNoteClassContext.displayName = 'InfoNoteClassContext';\n\n/**\n * A button with the info glyph that shows `info` in a note next to it: text, links or other rich\n * content, placed above the button with a beak (a toggletip).\n *\n * - **Naming.** The button is named \"Information\" (`aria-label`, which localizes it). Next to a\n * heading or other text, pass `aria-labelledby` with that text's id and the button's own `id`:\n * the button is then named \"‹text› Information\", so several info buttons on a page are told\n * apart. The note (`role=\"note\"`) is labelled like the button: by the same `aria-labelledby`\n * ids, else by the button. The button has `aria-expanded`, and `aria-controls` while the note is\n * open, but no `aria-haspopup`: a note is none of the popup kinds it names.\n * - **Opening.** A click pins the note open, and a click on a pinned note closes it. With\n * `openOnHover` (the default) a mouse pointer resting on the button opens it after 250 ms, and\n * it closes 250 ms after the pointer has left the button and the note (a pointer moving\n * diagonally from the button into the note keeps it open; touch and pen never hover); keyboard\n * focus on the button opens it too, while focus that comes from a pointer press waits for the\n * click. A hover close moves no focus, and a note that keyboard focus opened or keeps never\n * closes because of pointer movement. Opening never moves focus. Escape, a press outside and\n * focus moving outside the button and the note close it; Escape returns focus to the button when\n * it was in the note, and a dismissed note opens again on keyboard focus only once focus has\n * left both.\n * - **Tab path.** The note is portaled, but it keeps its place right after the button in the\n * keyboard order: Tab from the open button enters the note's first element (a link in it), Tab\n * past its last element continues after the button, and Shift+Tab from its first element\n * returns to the button.\n * - **One copy of `info`.** While the note is closed, `info` renders next to the button in an\n * element that is not displayed and describes the button (`aria-describedby`), so it is in the\n * server HTML and is read when the button takes focus. While the note is shown, `info` renders\n * only inside it, in an unnamed element that describes the button. `info` is never in the\n * document twice, so content with ids or effects is safe; it remounts each time the note opens\n * and closes, so state inside it starts over and its effects run again.\n * - **Structure.** The button is the root element: `ref`, `className`, `style` and every other\n * attribute go to it, the consumer's handlers first. The description and the note render next\n * to it, never inside it. Its box is 24×24px at every `size` and renders `data-size`. The note\n * mounts through the presence core: it carries `data-presence`, `data-state` (`open`, and\n * `closed` while it exits, `inert`) and the `data-wave-infolabel-surface` marker; it ships no\n * motion of its own.\n *\n * Fluent's InfoButton takes the same `info` and `size`. It opens on click only\n * (`openOnHover={false}` here), renders its note in place, and moves focus into the note; this one\n * keeps focus on the button and renders the note in a portal. Fluent's `popover` props and a\n * controlled open state are not offered.\n *\n * @example\n * <h2 id=\"billing\">Billing</h2>\n * <InfoButton\n * id=\"billing-info\"\n * aria-labelledby=\"billing billing-info\"\n * info={<>Invoices are sent monthly. <a href=\"/help/billing\">Learn more</a></>}\n * />\n */\nexport const InfoButton = ({\n info,\n size: sizeProp,\n openOnHover = true,\n id: idProp,\n type = 'button',\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n className,\n disabled,\n onClick,\n onFocus,\n onBlur,\n onKeyDown,\n onPointerDown,\n onMouseDown,\n onPointerEnter,\n onPointerMove,\n onPointerLeave,\n ref,\n ...rest\n}: InfoButtonProps) => {\n // A size from untyped code renders, and reports, the default.\n const size: CoreSize = sizeProp === 'small' || sizeProp === 'large' ? sizeProp : 'medium';\n const generatedId = useId('info-button');\n const id = idProp || generatedId;\n const noteClass = React.useContext(InfoNoteClassContext);\n const noteClassName = noteClass?.buttonId === id ? noteClass.className : undefined;\n // An empty name would leave the button and the note unnamed: the glyph is decorative.\n const ariaLabel = ariaLabelProp || 'Information';\n const noteId = useId('info-note');\n // The one copy of `info`: not displayed while the note is closed, inside the note while shown.\n const infoId = useId('info');\n const [reason, setReason] = React.useState<OpenReason | null>(null);\n const open = reason !== null;\n\n const buttonRef = React.useRef<HTMLButtonElement | null>(null);\n const noteRef = React.useRef<HTMLDivElement | null>(null);\n const arrowRef = React.useRef<HTMLDivElement | null>(null);\n // The button and the note held in state too (C-POPUPS): for the hover intent (the safe zone\n // measures both) and the focus restore.\n const [buttonElement, setButtonElement] = React.useState<HTMLButtonElement | null>(null);\n const [note, setNote] = React.useState<HTMLDivElement | null>(null);\n\n // The latest reason, for the hover close (read in the hover intent's pointer events and timers).\n const reasonRef = React.useRef(reason);\n React.useLayoutEffect(() => {\n reasonRef.current = reason;\n });\n // Set by a dismissal (Escape, outside press): keyboard focus reopens only after focus has left\n // both the button and the note (a focus restore must not reopen it). A click that closes the\n // note sets it too: in Safari a click leaves focus on <body>, and the focus restore follows. It\n // blocks only a reopening: keyboard focus still takes over a note that a click or hover opened.\n const dismissedRef = React.useRef(false);\n // A pointer press focuses the button; that focus waits for the click (0.7).\n const pointerPressRef = React.useRef(false);\n const pressTimerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // A hover close in flight, set before its request: that close moves no focus. Every other close\n // request and every opening clears it.\n const hoverCloseRef = React.useRef(false);\n\n // The note mounts and unmounts through the presence core; the dismiss layer, the focus restore\n // and the positioning below key on `open`, so they go on close, not after an exit motion.\n // Destructured: react-hooks/refs treats an object whose member is passed to `ref` as a ref.\n const { isMounted, ref: presenceRef, presenceProps } = usePresence(open);\n const { setReference, setFloating, floatingProps, arrowStyles, side } = usePopupPosition({\n open,\n side: 'top',\n align: 'center',\n offset: 8,\n arrowRef,\n });\n const { layerId } = useDismiss({\n open,\n onDismiss: (dismissReason) => {\n // Only a focusin elsewhere dismisses with 'focus-outside' (focus lost to <body> never does):\n // focus has left both then, so keyboard focus may reopen the note at once.\n if (dismissReason !== 'focus-outside') dismissedRef.current = true;\n hoverCloseRef.current = false;\n // Declared below; the layer calls this only from its document listeners, after render.\n cancelHover();\n setReason(null);\n },\n refs: [buttonRef, noteRef],\n anchorRef: buttonRef,\n focusOutside: true,\n });\n // Focus returns to the button when the note closes with focus inside it (or lost to <body>); a\n // hover close returns only focus that is inside it, so it never moves focus.\n useRestoreFocus({\n enabled: open,\n container: note,\n triggerRef: buttonRef,\n onlyIfFocusInside: true,\n isHoverClose: () => hoverCloseRef.current,\n });\n\n const {\n triggerHandlers: hoverTriggerHandlers,\n surfaceHandlers: hoverSurfaceHandlers,\n cancel: cancelHover,\n } = useHoverIntent({\n // Browsers send pointer events to a disabled button too, and the hook skips only an\n // `aria-disabled` trigger.\n enabled: openOnHover && !disabled,\n open,\n openDelay: HOVER_DELAY,\n closeDelay: HOVER_DELAY,\n trigger: buttonElement,\n surface: note,\n onOpen: () => {\n hoverCloseRef.current = false;\n setReason((current) => current ?? 'hover');\n },\n onClose: () => {\n hoverCloseRef.current = true;\n setReason((current) => (current === 'hover' ? null : current));\n },\n // Only a note opened by hover, not pinned and not keyboard-owned, closes by hover; focus\n // inside the note (or a layer opened from it) blocks it too (Phase 2 D18).\n canClose: () => reasonRef.current === 'hover' && !isFocusInSurfaceTree(note, layerId),\n });\n\n React.useEffect(() => () => clearTimeout(pressTimerRef.current), []);\n\n // A dismissal ends once focus has left both the button and the note: at the button's blur\n // (below) and at a focusout of the note. Focus the note loses to nothing is the note closing\n // (removed, or inert while it exits): that ends nothing.\n React.useEffect(() => {\n if (!note) return undefined;\n const onFocusOut = (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null;\n if (!next || note.contains(next) || buttonRef.current?.contains(next)) return;\n dismissedRef.current = false;\n };\n note.addEventListener('focusout', onFocusOut);\n return () => note.removeEventListener('focusout', onFocusOut);\n }, [note]);\n\n // The handlers below read and write refs: useEventCallback keeps them out of render (C-HOOKS).\n const handleClick = useEventCallback(() => {\n cancelHover();\n hoverCloseRef.current = false;\n // Closed, or opened by hover or keyboard focus: pinned. Pinned: closed.\n const closing = reason === 'click';\n if (closing) dismissedRef.current = true;\n setReason(closing ? null : 'click');\n });\n\n const handleFocus = useEventCallback(() => {\n // Keyboard focus opens, as hovering's keyboard counterpart.\n if (!openOnHover || pointerPressRef.current || (dismissedRef.current && !open)) return;\n hoverCloseRef.current = false;\n cancelHover();\n // A note that hover opened becomes focus-owned; a pinned one stays pinned.\n setReason((current) => (current === 'click' ? current : 'focus'));\n });\n\n const handleBlur = useEventCallback((event: React.FocusEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const next = event.relatedTarget as Node | null;\n // Tab from the button into the note keeps a dismissal.\n if (next && note?.contains(next)) return;\n // So does the window losing focus (a switch of window or tab): the button stays the document's\n // focused element, and the browser focuses it again, from nothing, when the window comes back.\n if (!next && button.ownerDocument.activeElement === button) return;\n // Focus lost to nothing in the page (a press on a spot that takes no focus) has left both.\n dismissedRef.current = false;\n });\n\n const handlePointerPress = useEventCallback(() => {\n pointerPressRef.current = true;\n // A press focuses the button (where it does) as the default action of `mousedown`, in the same\n // event turn. Forget the press after that turn: a press that does not focus the button (Safari\n // and Firefox on macOS, iOS) and ends without a click (dragged off, or a touch that becomes a\n // scroll) must not turn a later keyboard focus into pointer focus. A touch tap dispatches its\n // compatibility `mousedown` (and the focus) after the touch ends; that `mousedown` sets the\n // flag again.\n clearTimeout(pressTimerRef.current);\n pressTimerRef.current = setTimeout(() => {\n pressTimerRef.current = undefined;\n pointerPressRef.current = false;\n }, 0);\n });\n\n // Tab from the open button enters the note. The button's own handler runs before a modal focus\n // trap's document listener, which would wrap the Tab when the button is the trap's last element.\n const onButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (event.key !== 'Tab' || event.shiftKey || !open || !note) return;\n const first = getFirstTabbable(note);\n if (!first) return;\n event.preventDefault();\n first.focus();\n };\n // Tab past the note's last element continues after the button, Shift+Tab from its first element\n // returns to it, and the page order skips the note where its portal is.\n const onNoteKeyDown = usePopoverTabOrder({\n enabled: open,\n surface: note,\n anchorRef: buttonRef,\n getPreviousStop: () => buttonRef.current,\n });\n\n const buttonRefs = useMergedRefs<HTMLButtonElement>(\n ref,\n buttonRef,\n setButtonElement,\n setReference,\n );\n const noteRefs = useMergedRefs<HTMLDivElement>(noteRef, setNote, setFloating, presenceRef);\n\n return (\n <>\n <button\n type={type}\n id={id}\n data-size={size}\n {...rest}\n ref={buttonRefs}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={joinIds(ariaDescribedBy, infoId)}\n aria-expanded={open}\n aria-controls={open ? noteId : undefined}\n className={cn(\n infoButtonClassNames.root,\n 'inline-flex size-6 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground',\n GLYPH[size].margin,\n 'not-disabled:not-aria-disabled:hover:text-foreground',\n focusRing,\n className,\n )}\n onClick={composeEventHandlers(onClick, handleClick)}\n onFocus={composeEventHandlers(onFocus, handleFocus)}\n onKeyDown={composeEventHandlers(onKeyDown, onButtonKeyDown)}\n onPointerEnter={composeEventHandlers(onPointerEnter, hoverTriggerHandlers.onPointerEnter)}\n onPointerMove={composeEventHandlers(onPointerMove, hoverTriggerHandlers.onPointerMove)}\n onPointerLeave={composeEventHandlers(onPointerLeave, hoverTriggerHandlers.onPointerLeave)}\n // Bookkeeping that follows the DOM (where focus went, whether a press is under way): it\n // runs even when the consumer prevents the default.\n onBlur={composeEventHandlers(onBlur, handleBlur, { checkDefaultPrevented: false })}\n onPointerDown={composeEventHandlers(onPointerDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n onMouseDown={composeEventHandlers(onMouseDown, handlePointerPress, {\n checkDefaultPrevented: false,\n })}\n >\n <InfoIcon size={GLYPH[size].size} />\n </button>\n {/* The same element follows the button in every phase, so a row styled by position never\n shifts: it holds `info` while the note is not mounted, and nothing while the note does. */}\n <span id={isMounted ? undefined : infoId} hidden>\n {isMounted ? null : info}\n </span>\n {isMounted && (\n <Portal layerId={layerId}>\n <div\n ref={noteRefs}\n id={noteId}\n role=\"note\"\n aria-labelledby={ariaLabelledBy || id}\n data-wave-infolabel-surface=\"\"\n data-state={open ? 'open' : 'closed'}\n {...presenceProps}\n {...floatingProps}\n onKeyDown={onNoteKeyDown}\n onPointerEnter={hoverSurfaceHandlers.onPointerEnter}\n onPointerLeave={hoverSurfaceHandlers.onPointerLeave}\n className={cn(\n infoButtonClassNames.info,\n noteClassName,\n 'w-max max-w-[min(20rem,calc(100vw-1rem))] rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16',\n )}\n >\n <PopoverBeak\n ref={arrowRef}\n side={side}\n style={arrowStyles}\n data-wave-popover-arrow=\"\"\n className={infoButtonClassNames.beak}\n />\n <div id={infoId}>{info}</div>\n </div>\n </Portal>\n )}\n </>\n );\n};\nInfoButton.displayName = 'InfoButton';\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/classNames.cjs"),r=require("../../hooks/useId.cjs"),i=require("../../hooks/useMergedRefs.cjs"),a=require("../../hooks/useDismiss.cjs"),o=require("../portal/Portal.cjs"),s=require("../../lib/styles.cjs"),c=require("../../lib/icons.cjs"),l=require("../../hooks/usePopupPosition.cjs");let u=require("react");u=e.__toESM(u,1);let d=require("react/jsx-runtime");var f=250,p=250,m=({label:e,info:m,infoButtonLabel:h=`Information`,className:g,ref:_,...v})=>{let y=r.useId(`info-label-description`),[b,x]=u.useState(null),S=b!==null,C=u.useRef(null),w=u.useRef(null),T=u.useRef(!1),E=u.useRef(null),D=u.useRef(null),O=u.useRef(null),k=()=>{D.current!==null&&clearTimeout(D.current),D.current=null},A=()=>{O.current!==null&&clearTimeout(O.current),O.current=null};u.useEffect(()=>()=>{E.current!==null&&clearTimeout(E.current),D.current!==null&&clearTimeout(D.current),O.current!==null&&clearTimeout(O.current)},[]);let j=()=>{k(),A(),x(null)},{setReference:M,setFloating:N,floatingProps:P}=l.usePopupPosition({open:S,side:`top`,align:`center`,offset:8}),{layerId:F}=a.useDismiss({open:S,onDismiss:j,refs:[C,w],anchorRef:C,focusOutside:!0}),I=i.useMergedRefs(C,M),L=i.useMergedRefs(w,N),R=()=>{k(),A(),O.current=setTimeout(()=>{O.current=null,x(e=>e===`hover`?null:e)},p)},z=e=>{e.pointerType!==`touch`&&(A(),!(S||D.current!==null)&&(D.current=setTimeout(()=>{D.current=null,x(e=>e??`hover`)},f)))},B=e=>{e.pointerType!==`touch`&&R()},V=()=>{T.current=!0,E.current!==null&&clearTimeout(E.current),E.current=setTimeout(()=>{E.current=null,T.current=!1},0)},H=()=>{T.current||x(e=>e===`click`?e:`focus`)},U=()=>{x(e=>e===`focus`?null:e)},W=()=>{k(),A(),x(e=>e===`click`?null:`click`)};return(0,d.jsxs)(`span`,{ref:_,className:t.cn(n.infoLabelClassNames.root,`inline-flex items-center gap-1 text-body-1`,g),...v,children:[e,(0,d.jsx)(`button`,{ref:I,type:`button`,"aria-label":h,"aria-expanded":S,"aria-describedby":y,className:t.cn(n.infoLabelClassNames.infoButton,`inline-flex size-4 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground`,`not-disabled:not-aria-disabled:hover:text-foreground`,s.focusRing),onPointerDown:V,onMouseDown:V,onPointerEnter:z,onPointerLeave:B,onFocus:H,onBlur:U,onClick:W,children:(0,d.jsx)(c.InfoIcon,{size:16})}),(0,d.jsx)(`span`,{id:y,hidden:!0,children:m}),S&&(0,d.jsx)(o.Portal,{layerId:F,children:(0,d.jsx)(`div`,{ref:L,"aria-hidden":`true`,"data-wave-infolabel-surface":``,"data-state":`open`,...P,className:t.cn(n.infoLabelClassNames.info,`w-max max-w-xs rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16`),onPointerEnter:z,onPointerLeave:B,children:m})})]})};m.displayName=`InfoLabel`,exports.InfoLabel=m;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../hooks/useId.cjs"),a=require("../input/fieldLabel.cjs"),o=require("../input/Label.cjs"),s=require("./InfoButton.cjs");let c=require("react");c=e.__toESM(c,1);let l=require("react/jsx-runtime");var u=({children:e,label:a,info:u,infoButtonLabel:d=`Information`,openOnHover:f,id:p,htmlFor:m,required:h,size:g=`medium`,weight:_,disabled:v,className:y,ref:b,...x})=>{let S=n.resolveDeprecatedProp(`InfoLabel`,e,a,`label`,`children`),C=i.useId(`info-label`),w=p||C,T=i.useId(`info-label-button`),E=c.useMemo(()=>({buttonId:T,className:r.infoLabelClassNames.info}),[T]);return(0,l.jsxs)(`span`,{ref:b,className:t.cn(r.infoLabelClassNames.root,`inline-flex items-center gap-0`,y),...x,children:[(0,l.jsx)(o.Label,{className:r.infoLabelClassNames.label,id:w,htmlFor:m,required:h,size:g,weight:_,disabled:v,children:S}),(0,l.jsx)(s.InfoNoteClassContext.Provider,{value:E,children:(0,l.jsx)(s.InfoButton,{id:T,className:r.infoLabelClassNames.infoButton,info:u,size:g,openOnHover:f,"aria-label":d,"aria-labelledby":`${w} ${T}`})})]})};u.displayName=`InfoLabel`,a.markFieldLabel(u),exports.InfoLabel=u;
2
2
  //# sourceMappingURL=InfoLabel.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"InfoLabel.cjs","names":["useId","React","usePopupPosition","useDismiss","useMergedRefs","cn","infoLabelClassNames","focusRing","InfoIcon","Portal","require_runtime","require_cn","require_classNames","require_useId","require_useMergedRefs","require_useDismiss","require_Portal","require_styles","require_icons","require_usePopupPosition","react","react_jsx_runtime","SHOW_DELAY","HIDE_DELAY","InfoLabel","props","descriptionId","openReason","setOpenReason","open","triggerRef","surfaceRef","pointerPressRef","pressTimerRef","showTimerRef","hideTimerRef","clearShowTimer","clearHideTimer","close","triggerRefs","setReference","surfaceRefs","setFloating","scheduleHide","reason","handlePointerEnter","event","handlePointerLeave","handlePointerDown","handleFocus","handleBlur","handleClick","className","label","infoButtonLabel","info","floatingProps"],"sources":["../../../src/components/data-display/InfoLabel.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { infoLabelClassNames } from '../../lib/classNames';\nimport { focusRing } from '../../lib/styles';\nimport { InfoIcon } from '../../lib/icons';\nimport { useId } from '../../hooks/useId';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { Portal } from '../portal/Portal';\n\n/** Properties for the InfoLabel component. */\nexport interface InfoLabelProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Primary label text to display. */\n label: string;\n /**\n * Additional information about the label. It describes the info button\n * (`aria-describedby`, available from the first render) and is shown in a popup on hover,\n * keyboard focus or click.\n */\n info: string;\n /**\n * Accessible name of the info button. Localise it here.\n * @default 'Information'\n */\n infoButtonLabel?: string;\n /** Ref to the root `<span>`. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/** Why the popup is open: a click pins it; hover and keyboard focus open it transiently. */\ntype OpenReason = 'hover' | 'focus' | 'click';\n\n/** Hover delay before the popup opens, and before it closes after the pointer leaves (ms). */\nconst SHOW_DELAY = 250;\nconst HIDE_DELAY = 250;\n\ntype Timer = ReturnType<typeof setTimeout>;\n\n/**\n * A label with an info button that reveals additional information (a \"toggletip\").\n *\n * - The info button is a `<button type=\"button\">` named `infoButtonLabel` (\"Information\"). The\n * info text is rendered inline in a `hidden` element and linked with `aria-describedby`, so it is\n * announced from the first render (also after server rendering), whether the popup is open or not.\n * - The visual popup is portaled only while open. It is `aria-hidden` because it duplicates the\n * description, and it holds no focusable content, so focus stays on the button.\n * - Opens on keyboard focus (not on focus that comes from a pointer press), on hover after a\n * short delay (the popup itself can be hovered) and on click. A click pins it: a click while it is\n * open because of hover or focus keeps it open, a click on a pinned popup closes it.\n * - Closes on Escape, on a press outside, when focus moves elsewhere and — unless pinned — when\n * the pointer leaves or the button loses focus (WCAG 1.4.13). A popup opened by hover that then\n * receives keyboard focus stays open while the button keeps focus, even after the pointer leaves.\n *\n * @example\n * <InfoLabel label=\"Password\" info=\"Use at least 8 characters.\" />\n */\nexport const InfoLabel = ({\n label,\n info,\n infoButtonLabel = 'Information',\n className,\n ref,\n ...props\n}: InfoLabelProps) => {\n const descriptionId = useId('info-label-description');\n const [openReason, setOpenReason] = React.useState<OpenReason | null>(null);\n const open = openReason !== null;\n\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n /**\n * Set by a pointer press on the button, so the focus that follows does not open the popup. It\n * lasts only for the event turn of the press (see `handlePointerDown`).\n */\n const pointerPressRef = React.useRef(false);\n const pressTimerRef = React.useRef<Timer | null>(null);\n const showTimerRef = React.useRef<Timer | null>(null);\n const hideTimerRef = React.useRef<Timer | null>(null);\n\n const clearShowTimer = () => {\n if (showTimerRef.current !== null) clearTimeout(showTimerRef.current);\n showTimerRef.current = null;\n };\n const clearHideTimer = () => {\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n };\n\n React.useEffect(\n () => () => {\n if (pressTimerRef.current !== null) clearTimeout(pressTimerRef.current);\n if (showTimerRef.current !== null) clearTimeout(showTimerRef.current);\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n },\n [],\n );\n\n const close = () => {\n clearShowTimer();\n clearHideTimer();\n setOpenReason(null);\n };\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open,\n side: 'top',\n align: 'center',\n offset: 8,\n });\n const { layerId } = useDismiss({\n open,\n onDismiss: close,\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating);\n\n /** Pointer left the button or the popup: a hover-opened popup closes after a delay. */\n const scheduleHide = () => {\n clearShowTimer();\n clearHideTimer();\n hideTimerRef.current = setTimeout(() => {\n hideTimerRef.current = null;\n setOpenReason((reason) => (reason === 'hover' ? null : reason));\n }, HIDE_DELAY);\n };\n\n const handlePointerEnter = (event: React.PointerEvent) => {\n if (event.pointerType === 'touch') return;\n clearHideTimer();\n if (open || showTimerRef.current !== null) return;\n showTimerRef.current = setTimeout(() => {\n showTimerRef.current = null;\n setOpenReason((reason) => reason ?? 'hover');\n }, SHOW_DELAY);\n };\n\n const handlePointerLeave = (event: React.PointerEvent) => {\n if (event.pointerType === 'touch') return;\n scheduleHide();\n };\n\n const handlePointerDown = () => {\n pointerPressRef.current = true;\n // A press focuses the button (where it does) as the default action of `mousedown`, in the same\n // event turn. Forget the press after that turn: a press that does not focus the button (Safari\n // and Firefox on macOS, iOS) and ends without a click (dragged off, or a touch that becomes a\n // scroll) must not turn a later keyboard focus into pointer focus. A touch tap dispatches its\n // compatibility `mousedown` (and the focus) after the touch ends; that `mousedown` sets the\n // flag again.\n if (pressTimerRef.current !== null) clearTimeout(pressTimerRef.current);\n pressTimerRef.current = setTimeout(() => {\n pressTimerRef.current = null;\n pointerPressRef.current = false;\n }, 0);\n };\n\n const handleFocus = () => {\n // Keyboard focus opens; focus that comes from a pointer press waits for the click.\n if (pointerPressRef.current) return;\n // A popup opened by hover becomes focus-owned, so it stays while the button has focus even\n // after the pointer leaves (WCAG 1.4.13 persistent). A pinned popup stays pinned.\n setOpenReason((reason) => (reason === 'click' ? reason : 'focus'));\n };\n\n const handleBlur = () => {\n setOpenReason((reason) => (reason === 'focus' ? null : reason));\n };\n\n const handleClick = () => {\n clearShowTimer();\n clearHideTimer();\n // Closed → pinned; open because of hover or focus → pinned; pinned → closed.\n setOpenReason((reason) => (reason === 'click' ? null : 'click'));\n };\n\n return (\n <span\n ref={ref}\n className={cn(\n infoLabelClassNames.root,\n 'inline-flex items-center gap-1 text-body-1',\n className,\n )}\n {...props}\n >\n {label}\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={infoButtonLabel}\n aria-expanded={open}\n aria-describedby={descriptionId}\n className={cn(\n infoLabelClassNames.infoButton,\n 'inline-flex size-4 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground',\n 'not-disabled:not-aria-disabled:hover:text-foreground',\n focusRing,\n )}\n onPointerDown={handlePointerDown}\n onMouseDown={handlePointerDown}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onClick={handleClick}\n >\n <InfoIcon size={16} />\n </button>\n <span id={descriptionId} hidden>\n {info}\n </span>\n {open && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n aria-hidden=\"true\"\n data-wave-infolabel-surface=\"\"\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n infoLabelClassNames.info,\n 'w-max max-w-xs rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16',\n )}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n >\n {info}\n </div>\n </Portal>\n )}\n </span>\n );\n};\n\nInfoLabel.displayName = 'InfoLabel';\n"],"mappings":"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"}
1
+ {"version":3,"file":"InfoLabel.cjs","names":["resolveDeprecatedProp","useId","React","infoLabelClassNames","cn","Label","InfoNoteClassContext","InfoButton","markFieldLabel","require_runtime","require_cn","require_dev","require_classNames","require_useId","require_fieldLabel","require_Label","require_InfoButton","react","react_jsx_runtime","InfoLabel","rest","content","children","label","generatedLabelId","labelId","id","buttonId","noteClass","className","infoButtonLabel"],"sources":["../../../src/components/data-display/InfoLabel.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { infoLabelClassNames } from '../../lib/classNames';\nimport { resolveDeprecatedProp } from '../../lib/dev';\nimport { useId } from '../../hooks/useId';\nimport { markFieldLabel } from '../input/fieldLabel';\nimport { Label, type LabelProps } from '../input/Label';\nimport { InfoButton, InfoNoteClassContext } from './InfoButton';\n\n/** Properties for the InfoLabel component. */\nexport interface InfoLabelProps\n extends\n Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'>,\n Pick<LabelProps, 'htmlFor' | 'required' | 'size' | 'weight' | 'disabled'> {\n /** The label content (Fluent's `children`). */\n children?: React.ReactNode;\n /**\n * The label content.\n * @deprecated Use `children`.\n */\n label?: React.ReactNode;\n /** The information the info button's note shows: text, links or other rich content. */\n info: React.ReactNode;\n /**\n * Accessible name of the info button: the second half of its name, after the label text\n * (\"Password Information\"). Localize it here.\n * @default 'Information'\n */\n infoButtonLabel?: string;\n /**\n * Opens the note on a mouse hover, after a short delay, and on keyboard focus, next to a click\n * (see `InfoButton`'s `openOnHover`, which this passes through unchanged).\n * @default true\n */\n openOnHover?: boolean;\n /** Ref to the root `<span>`. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * A label followed by a button that reveals more information about it (Fluent's InfoLabel): the\n * label renders in a real `<label htmlFor>`, and the button shows `info` — text, links or other\n * rich content — in a note next to it, rendered once whether the note is open or closed (see\n * `InfoButton`, which owns that note and its opening, closing and Tab order).\n *\n * - `children` is the label content; `label` is a deprecated alias (a warning fires once, and\n * `children` wins when both are given).\n * - `htmlFor`, `required`, `size`, `weight` and `disabled` route to the `<label>`; `disabled` dims\n * only the label text, and the info button stays usable.\n * - The button is named \"‹label text› ‹infoButtonLabel›\" through `aria-labelledby`, which points\n * at the `<label>` and at the button's own id (whose `aria-label` supplies the second half), so\n * several InfoLabels on a page are told apart; `infoButtonLabel` localizes that second half.\n * `openOnHover` and `size` reach the button too.\n * - No `controlRef`: InfoLabel is a label, not a form control, so C-ROUTING's composite-control\n * routing does not apply to it; a layout that needs the button's own element renders `InfoButton`\n * next to a `Label` instead.\n *\n * @example\n * <InfoLabel info=\"Use at least 8 characters.\">Password</InfoLabel>\n */\nexport const InfoLabel = ({\n children,\n label,\n info,\n infoButtonLabel = 'Information',\n openOnHover,\n id,\n htmlFor,\n required,\n size = 'medium',\n weight,\n disabled,\n className,\n ref,\n ...rest\n}: InfoLabelProps) => {\n const content = resolveDeprecatedProp('InfoLabel', children, label, 'label', 'children');\n const generatedLabelId = useId('info-label');\n // An empty `id` counts as unset, so the button's name never points at an empty id.\n const labelId = id || generatedLabelId;\n const buttonId = useId('info-label-button');\n // The note of this InfoButton carries the label's `info` class, its 0.8 popup class (P3-00 D8).\n const noteClass = React.useMemo(\n () => ({ buttonId, className: infoLabelClassNames.info }),\n [buttonId],\n );\n return (\n <span\n ref={ref}\n className={cn(infoLabelClassNames.root, 'inline-flex items-center gap-0', className)}\n {...rest}\n >\n <Label\n className={infoLabelClassNames.label}\n id={labelId}\n htmlFor={htmlFor}\n required={required}\n size={size}\n weight={weight}\n disabled={disabled}\n >\n {content}\n </Label>\n <InfoNoteClassContext.Provider value={noteClass}>\n <InfoButton\n id={buttonId}\n className={infoLabelClassNames.infoButton}\n info={info}\n size={size}\n openOnHover={openOnHover}\n aria-label={infoButtonLabel}\n aria-labelledby={`${labelId} ${buttonId}`}\n />\n </InfoNoteClassContext.Provider>\n </span>\n );\n};\n\nInfoLabel.displayName = 'InfoLabel';\nmarkFieldLabel(InfoLabel);\n"],"mappings":"yXA4DA,IAAamB,GAAa,CACxB,WACA,QACA,OACA,kBAAkB,cAClB,cACA,KACA,UACA,WACA,OAAO,SACP,SACA,WACA,YACA,MACA,GAAGC,KACiB,CACpB,IAAMC,EAAUrB,EAAAA,sBAAsB,YAAasB,EAAUC,EAAO,QAAS,UAAU,EACjFC,EAAmBvB,EAAAA,MAAM,YAAY,EAErCwB,EAAUC,GAAMF,EAChBG,EAAW1B,EAAAA,MAAM,mBAAmB,EAEpC2B,EAAY1B,EAAM,aACf,CAAE,WAAU,UAAWC,EAAAA,oBAAoB,IAAK,GACvD,CAACwB,CAAQ,CACX,EACA,OACE,EAAAT,EAAA,KAAA,CAAC,OAAD,CACO,MACL,UAAWd,EAAAA,GAAGD,EAAAA,oBAAoB,KAAM,iCAAkC0B,CAAS,EACnF,GAAIT,EAHN,SAAA,EAKE,EAAAF,EAAA,IAAA,CAACb,EAAAA,MAAD,CACE,UAAWF,EAAAA,oBAAoB,MAC/B,GAAIsB,EACK,UACC,WACJ,OACE,SACE,WAET,SAAAJ,CACI,CAAA,GACP,EAAAH,EAAA,IAAA,CAACZ,EAAAA,qBAAqB,SAAtB,CAA+B,MAAOsB,EACpC,UAAA,EAAAV,EAAA,IAAA,CAACX,EAAAA,WAAD,CACE,GAAIoB,EACJ,UAAWxB,EAAAA,oBAAoB,WACzB,OACA,OACO,cACb,aAAY2B,EACZ,kBAAiB,GAAGL,EAAQ,GAAGE,GAChC,CAAA,CAC4B,CAAA,CAC3B,GAEV,EAEA,EAAU,YAAc,YACxBnB,EAAAA,eAAeW,CAAS"}
@@ -1,105 +1,48 @@
1
1
  "use client";
2
2
  import { cn as e } from "../../lib/cn.mjs";
3
- import { infoLabelClassNames as t } from "../../lib/classNames.mjs";
4
- import { useId as n } from "../../hooks/useId.mjs";
5
- import { useMergedRefs as r } from "../../hooks/useMergedRefs.mjs";
6
- import { useDismiss as i } from "../../hooks/useDismiss.mjs";
7
- import { Portal as a } from "../portal/Portal.mjs";
8
- import { focusRing as o } from "../../lib/styles.mjs";
9
- import { InfoIcon as s } from "../../lib/icons.mjs";
10
- import { usePopupPosition as c } from "../../hooks/usePopupPosition.mjs";
11
- import * as l from "react";
12
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
3
+ import { resolveDeprecatedProp as t } from "../../lib/dev.mjs";
4
+ import { infoLabelClassNames as n } from "../../lib/classNames.mjs";
5
+ import { useId as r } from "../../hooks/useId.mjs";
6
+ import { markFieldLabel as i } from "../input/fieldLabel.mjs";
7
+ import { Label as a } from "../input/Label.mjs";
8
+ import { InfoButton as o, InfoNoteClassContext as s } from "./InfoButton.mjs";
9
+ import * as c from "react";
10
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
13
11
  //#region src/components/data-display/InfoLabel.tsx
14
- var f = 250, p = 250, m = ({ label: m, info: h, infoButtonLabel: g = "Information", className: _, ref: v, ...y }) => {
15
- let b = n("info-label-description"), [x, S] = l.useState(null), C = x !== null, w = l.useRef(null), T = l.useRef(null), E = l.useRef(!1), D = l.useRef(null), O = l.useRef(null), k = l.useRef(null), A = () => {
16
- O.current !== null && clearTimeout(O.current), O.current = null;
17
- }, j = () => {
18
- k.current !== null && clearTimeout(k.current), k.current = null;
19
- };
20
- l.useEffect(() => () => {
21
- D.current !== null && clearTimeout(D.current), O.current !== null && clearTimeout(O.current), k.current !== null && clearTimeout(k.current);
22
- }, []);
23
- let M = () => {
24
- A(), j(), S(null);
25
- }, { setReference: N, setFloating: P, floatingProps: F } = c({
26
- open: C,
27
- side: "top",
28
- align: "center",
29
- offset: 8
30
- }), { layerId: I } = i({
31
- open: C,
32
- onDismiss: M,
33
- refs: [w, T],
34
- anchorRef: w,
35
- focusOutside: !0
36
- }), L = r(w, N), R = r(T, P), z = () => {
37
- A(), j(), k.current = setTimeout(() => {
38
- k.current = null, S((e) => e === "hover" ? null : e);
39
- }, p);
40
- }, B = (e) => {
41
- e.pointerType !== "touch" && (j(), !(C || O.current !== null) && (O.current = setTimeout(() => {
42
- O.current = null, S((e) => e ?? "hover");
43
- }, f)));
44
- }, V = (e) => {
45
- e.pointerType !== "touch" && z();
46
- }, H = () => {
47
- E.current = !0, D.current !== null && clearTimeout(D.current), D.current = setTimeout(() => {
48
- D.current = null, E.current = !1;
49
- }, 0);
50
- }, U = () => {
51
- E.current || S((e) => e === "click" ? e : "focus");
52
- }, W = () => {
53
- S((e) => e === "focus" ? null : e);
54
- }, G = () => {
55
- A(), j(), S((e) => e === "click" ? null : "click");
56
- };
57
- return /* @__PURE__ */ d("span", {
58
- ref: v,
59
- className: e(t.root, "inline-flex items-center gap-1 text-body-1", _),
60
- ...y,
61
- children: [
62
- m,
63
- /* @__PURE__ */ u("button", {
64
- ref: L,
65
- type: "button",
66
- "aria-label": g,
67
- "aria-expanded": C,
68
- "aria-describedby": b,
69
- className: e(t.infoButton, "inline-flex size-4 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground", "not-disabled:not-aria-disabled:hover:text-foreground", o),
70
- onPointerDown: H,
71
- onMouseDown: H,
72
- onPointerEnter: B,
73
- onPointerLeave: V,
74
- onFocus: U,
75
- onBlur: W,
76
- onClick: G,
77
- children: /* @__PURE__ */ u(s, { size: 16 })
78
- }),
79
- /* @__PURE__ */ u("span", {
80
- id: b,
81
- hidden: !0,
82
- children: h
83
- }),
84
- C && /* @__PURE__ */ u(a, {
85
- layerId: I,
86
- children: /* @__PURE__ */ u("div", {
87
- ref: R,
88
- "aria-hidden": "true",
89
- "data-wave-infolabel-surface": "",
90
- "data-state": "open",
91
- ...F,
92
- className: e(t.info, "w-max max-w-xs rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16"),
93
- onPointerEnter: B,
94
- onPointerLeave: V,
95
- children: h
96
- })
12
+ var d = ({ children: i, label: d, info: f, infoButtonLabel: p = "Information", openOnHover: m, id: h, htmlFor: g, required: _, size: v = "medium", weight: y, disabled: b, className: x, ref: S, ...C }) => {
13
+ let w = t("InfoLabel", i, d, "label", "children"), T = r("info-label"), E = h || T, D = r("info-label-button"), O = c.useMemo(() => ({
14
+ buttonId: D,
15
+ className: n.info
16
+ }), [D]);
17
+ return /* @__PURE__ */ u("span", {
18
+ ref: S,
19
+ className: e(n.root, "inline-flex items-center gap-0", x),
20
+ ...C,
21
+ children: [/* @__PURE__ */ l(a, {
22
+ className: n.label,
23
+ id: E,
24
+ htmlFor: g,
25
+ required: _,
26
+ size: v,
27
+ weight: y,
28
+ disabled: b,
29
+ children: w
30
+ }), /* @__PURE__ */ l(s.Provider, {
31
+ value: O,
32
+ children: /* @__PURE__ */ l(o, {
33
+ id: D,
34
+ className: n.infoButton,
35
+ info: f,
36
+ size: v,
37
+ openOnHover: m,
38
+ "aria-label": p,
39
+ "aria-labelledby": `${E} ${D}`
97
40
  })
98
- ]
41
+ })]
99
42
  });
100
43
  };
101
- m.displayName = "InfoLabel";
44
+ d.displayName = "InfoLabel", i(d);
102
45
  //#endregion
103
- export { m as InfoLabel };
46
+ export { d as InfoLabel };
104
47
 
105
48
  //# sourceMappingURL=InfoLabel.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"InfoLabel.mjs","names":["useId","useId$1","React","SHOW_DELAY","HIDE_DELAY","InfoLabel","props","descriptionId","openReason","setOpenReason","open","triggerRef","surfaceRef","pointerPressRef","pressTimerRef","showTimerRef","hideTimerRef","clearShowTimer","clearHideTimer","close","usePopupPosition","useDismiss","triggerRefs","useMergedRefs","setReference","surfaceRefs","setFloating","scheduleHide","reason","handlePointerEnter","event","handlePointerLeave","handlePointerDown","handleFocus","handleBlur","handleClick","cn","infoLabelClassNames","className","label","infoButtonLabel","focusRing","InfoIcon","info","Portal","floatingProps"],"sources":["../../../src/components/data-display/InfoLabel.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { infoLabelClassNames } from '../../lib/classNames';\nimport { focusRing } from '../../lib/styles';\nimport { InfoIcon } from '../../lib/icons';\nimport { useId } from '../../hooks/useId';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { Portal } from '../portal/Portal';\n\n/** Properties for the InfoLabel component. */\nexport interface InfoLabelProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Primary label text to display. */\n label: string;\n /**\n * Additional information about the label. It describes the info button\n * (`aria-describedby`, available from the first render) and is shown in a popup on hover,\n * keyboard focus or click.\n */\n info: string;\n /**\n * Accessible name of the info button. Localise it here.\n * @default 'Information'\n */\n infoButtonLabel?: string;\n /** Ref to the root `<span>`. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/** Why the popup is open: a click pins it; hover and keyboard focus open it transiently. */\ntype OpenReason = 'hover' | 'focus' | 'click';\n\n/** Hover delay before the popup opens, and before it closes after the pointer leaves (ms). */\nconst SHOW_DELAY = 250;\nconst HIDE_DELAY = 250;\n\ntype Timer = ReturnType<typeof setTimeout>;\n\n/**\n * A label with an info button that reveals additional information (a \"toggletip\").\n *\n * - The info button is a `<button type=\"button\">` named `infoButtonLabel` (\"Information\"). The\n * info text is rendered inline in a `hidden` element and linked with `aria-describedby`, so it is\n * announced from the first render (also after server rendering), whether the popup is open or not.\n * - The visual popup is portaled only while open. It is `aria-hidden` because it duplicates the\n * description, and it holds no focusable content, so focus stays on the button.\n * - Opens on keyboard focus (not on focus that comes from a pointer press), on hover after a\n * short delay (the popup itself can be hovered) and on click. A click pins it: a click while it is\n * open because of hover or focus keeps it open, a click on a pinned popup closes it.\n * - Closes on Escape, on a press outside, when focus moves elsewhere and — unless pinned — when\n * the pointer leaves or the button loses focus (WCAG 1.4.13). A popup opened by hover that then\n * receives keyboard focus stays open while the button keeps focus, even after the pointer leaves.\n *\n * @example\n * <InfoLabel label=\"Password\" info=\"Use at least 8 characters.\" />\n */\nexport const InfoLabel = ({\n label,\n info,\n infoButtonLabel = 'Information',\n className,\n ref,\n ...props\n}: InfoLabelProps) => {\n const descriptionId = useId('info-label-description');\n const [openReason, setOpenReason] = React.useState<OpenReason | null>(null);\n const open = openReason !== null;\n\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n /**\n * Set by a pointer press on the button, so the focus that follows does not open the popup. It\n * lasts only for the event turn of the press (see `handlePointerDown`).\n */\n const pointerPressRef = React.useRef(false);\n const pressTimerRef = React.useRef<Timer | null>(null);\n const showTimerRef = React.useRef<Timer | null>(null);\n const hideTimerRef = React.useRef<Timer | null>(null);\n\n const clearShowTimer = () => {\n if (showTimerRef.current !== null) clearTimeout(showTimerRef.current);\n showTimerRef.current = null;\n };\n const clearHideTimer = () => {\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n };\n\n React.useEffect(\n () => () => {\n if (pressTimerRef.current !== null) clearTimeout(pressTimerRef.current);\n if (showTimerRef.current !== null) clearTimeout(showTimerRef.current);\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n },\n [],\n );\n\n const close = () => {\n clearShowTimer();\n clearHideTimer();\n setOpenReason(null);\n };\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open,\n side: 'top',\n align: 'center',\n offset: 8,\n });\n const { layerId } = useDismiss({\n open,\n onDismiss: close,\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating);\n\n /** Pointer left the button or the popup: a hover-opened popup closes after a delay. */\n const scheduleHide = () => {\n clearShowTimer();\n clearHideTimer();\n hideTimerRef.current = setTimeout(() => {\n hideTimerRef.current = null;\n setOpenReason((reason) => (reason === 'hover' ? null : reason));\n }, HIDE_DELAY);\n };\n\n const handlePointerEnter = (event: React.PointerEvent) => {\n if (event.pointerType === 'touch') return;\n clearHideTimer();\n if (open || showTimerRef.current !== null) return;\n showTimerRef.current = setTimeout(() => {\n showTimerRef.current = null;\n setOpenReason((reason) => reason ?? 'hover');\n }, SHOW_DELAY);\n };\n\n const handlePointerLeave = (event: React.PointerEvent) => {\n if (event.pointerType === 'touch') return;\n scheduleHide();\n };\n\n const handlePointerDown = () => {\n pointerPressRef.current = true;\n // A press focuses the button (where it does) as the default action of `mousedown`, in the same\n // event turn. Forget the press after that turn: a press that does not focus the button (Safari\n // and Firefox on macOS, iOS) and ends without a click (dragged off, or a touch that becomes a\n // scroll) must not turn a later keyboard focus into pointer focus. A touch tap dispatches its\n // compatibility `mousedown` (and the focus) after the touch ends; that `mousedown` sets the\n // flag again.\n if (pressTimerRef.current !== null) clearTimeout(pressTimerRef.current);\n pressTimerRef.current = setTimeout(() => {\n pressTimerRef.current = null;\n pointerPressRef.current = false;\n }, 0);\n };\n\n const handleFocus = () => {\n // Keyboard focus opens; focus that comes from a pointer press waits for the click.\n if (pointerPressRef.current) return;\n // A popup opened by hover becomes focus-owned, so it stays while the button has focus even\n // after the pointer leaves (WCAG 1.4.13 persistent). A pinned popup stays pinned.\n setOpenReason((reason) => (reason === 'click' ? reason : 'focus'));\n };\n\n const handleBlur = () => {\n setOpenReason((reason) => (reason === 'focus' ? null : reason));\n };\n\n const handleClick = () => {\n clearShowTimer();\n clearHideTimer();\n // Closed → pinned; open because of hover or focus → pinned; pinned → closed.\n setOpenReason((reason) => (reason === 'click' ? null : 'click'));\n };\n\n return (\n <span\n ref={ref}\n className={cn(\n infoLabelClassNames.root,\n 'inline-flex items-center gap-1 text-body-1',\n className,\n )}\n {...props}\n >\n {label}\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={infoButtonLabel}\n aria-expanded={open}\n aria-describedby={descriptionId}\n className={cn(\n infoLabelClassNames.infoButton,\n 'inline-flex size-4 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-muted-foreground',\n 'not-disabled:not-aria-disabled:hover:text-foreground',\n focusRing,\n )}\n onPointerDown={handlePointerDown}\n onMouseDown={handlePointerDown}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onClick={handleClick}\n >\n <InfoIcon size={16} />\n </button>\n <span id={descriptionId} hidden>\n {info}\n </span>\n {open && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n aria-hidden=\"true\"\n data-wave-infolabel-surface=\"\"\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n infoLabelClassNames.info,\n 'w-max max-w-xs rounded-md border border-border bg-background px-3 py-2 text-caption-1 text-foreground shadow-16',\n )}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n >\n {info}\n </div>\n </Portal>\n )}\n </span>\n );\n};\n\nInfoLabel.displayName = 'InfoLabel';\n"],"mappings":"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"}
1
+ {"version":3,"file":"InfoLabel.mjs","names":["useId","useId$1","React","InfoLabel","rest","content","resolveDeprecatedProp","children","label","generatedLabelId","labelId","id","buttonId","noteClass","infoLabelClassNames","cn","className","Label","InfoNoteClassContext","InfoButton","infoButtonLabel","markFieldLabel"],"sources":["../../../src/components/data-display/InfoLabel.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { infoLabelClassNames } from '../../lib/classNames';\nimport { resolveDeprecatedProp } from '../../lib/dev';\nimport { useId } from '../../hooks/useId';\nimport { markFieldLabel } from '../input/fieldLabel';\nimport { Label, type LabelProps } from '../input/Label';\nimport { InfoButton, InfoNoteClassContext } from './InfoButton';\n\n/** Properties for the InfoLabel component. */\nexport interface InfoLabelProps\n extends\n Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'>,\n Pick<LabelProps, 'htmlFor' | 'required' | 'size' | 'weight' | 'disabled'> {\n /** The label content (Fluent's `children`). */\n children?: React.ReactNode;\n /**\n * The label content.\n * @deprecated Use `children`.\n */\n label?: React.ReactNode;\n /** The information the info button's note shows: text, links or other rich content. */\n info: React.ReactNode;\n /**\n * Accessible name of the info button: the second half of its name, after the label text\n * (\"Password Information\"). Localize it here.\n * @default 'Information'\n */\n infoButtonLabel?: string;\n /**\n * Opens the note on a mouse hover, after a short delay, and on keyboard focus, next to a click\n * (see `InfoButton`'s `openOnHover`, which this passes through unchanged).\n * @default true\n */\n openOnHover?: boolean;\n /** Ref to the root `<span>`. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * A label followed by a button that reveals more information about it (Fluent's InfoLabel): the\n * label renders in a real `<label htmlFor>`, and the button shows `info` — text, links or other\n * rich content — in a note next to it, rendered once whether the note is open or closed (see\n * `InfoButton`, which owns that note and its opening, closing and Tab order).\n *\n * - `children` is the label content; `label` is a deprecated alias (a warning fires once, and\n * `children` wins when both are given).\n * - `htmlFor`, `required`, `size`, `weight` and `disabled` route to the `<label>`; `disabled` dims\n * only the label text, and the info button stays usable.\n * - The button is named \"‹label text› ‹infoButtonLabel›\" through `aria-labelledby`, which points\n * at the `<label>` and at the button's own id (whose `aria-label` supplies the second half), so\n * several InfoLabels on a page are told apart; `infoButtonLabel` localizes that second half.\n * `openOnHover` and `size` reach the button too.\n * - No `controlRef`: InfoLabel is a label, not a form control, so C-ROUTING's composite-control\n * routing does not apply to it; a layout that needs the button's own element renders `InfoButton`\n * next to a `Label` instead.\n *\n * @example\n * <InfoLabel info=\"Use at least 8 characters.\">Password</InfoLabel>\n */\nexport const InfoLabel = ({\n children,\n label,\n info,\n infoButtonLabel = 'Information',\n openOnHover,\n id,\n htmlFor,\n required,\n size = 'medium',\n weight,\n disabled,\n className,\n ref,\n ...rest\n}: InfoLabelProps) => {\n const content = resolveDeprecatedProp('InfoLabel', children, label, 'label', 'children');\n const generatedLabelId = useId('info-label');\n // An empty `id` counts as unset, so the button's name never points at an empty id.\n const labelId = id || generatedLabelId;\n const buttonId = useId('info-label-button');\n // The note of this InfoButton carries the label's `info` class, its 0.8 popup class (P3-00 D8).\n const noteClass = React.useMemo(\n () => ({ buttonId, className: infoLabelClassNames.info }),\n [buttonId],\n );\n return (\n <span\n ref={ref}\n className={cn(infoLabelClassNames.root, 'inline-flex items-center gap-0', className)}\n {...rest}\n >\n <Label\n className={infoLabelClassNames.label}\n id={labelId}\n htmlFor={htmlFor}\n required={required}\n size={size}\n weight={weight}\n disabled={disabled}\n >\n {content}\n </Label>\n <InfoNoteClassContext.Provider value={noteClass}>\n <InfoButton\n id={buttonId}\n className={infoLabelClassNames.infoButton}\n info={info}\n size={size}\n openOnHover={openOnHover}\n aria-label={infoButtonLabel}\n aria-labelledby={`${labelId} ${buttonId}`}\n />\n </InfoNoteClassContext.Provider>\n </span>\n );\n};\n\nInfoLabel.displayName = 'InfoLabel';\nmarkFieldLabel(InfoLabel);\n"],"mappings":";;;;;;;;;;;AA4DA,IAAaG,KAAa,EACxB,aACA,UACA,SACA,qBAAkB,eAClB,gBACA,OACA,YACA,aACA,UAAO,UACP,WACA,aACA,cACA,QACA,GAAGC,QACiB;CACpB,IAAMC,IAAUC,EAAsB,aAAaC,GAAUC,GAAO,SAAS,UAAU,GACjFC,IAAmBT,EAAM,YAAY,GAErCU,IAAUC,KAAMF,GAChBG,IAAWZ,EAAM,mBAAmB,GAEpCa,IAAYX,EAAM,eACf;EAAE;EAAU,WAAWY,EAAoB;CAAK,IACvD,CAACF,CAAQ,CACX;CACA,OACE,kBAAC,QAAD;EACO;EACL,WAAWG,EAAGD,EAAoB,MAAM,kCAAkCE,CAAS;EACnF,GAAIZ;EAHN,UAAA,CAKE,kBAACa,GAAD;GACE,WAAWH,EAAoB;GAC/B,IAAIJ;GACK;GACC;GACJ;GACE;GACE;GAET,UAAAL;EACI,CAAA,GACP,kBAACa,EAAqB,UAAtB;GAA+B,OAAOL;GACpC,UAAA,kBAACM,GAAD;IACE,IAAIP;IACJ,WAAWE,EAAoB;IACzB;IACA;IACO;IACb,cAAYM;IACZ,mBAAiB,GAAGV,EAAQ,GAAGE;GAChC,CAAA;EAC4B,CAAA,CAC3B;;AAEV;AAEA,EAAU,cAAc,aACxBS,EAAelB,CAAS"}