@dynamic-framework/ui-react 2.9.0 → 2.11.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 (478) hide show
  1. package/dist/DPortalStack-6zJm6JoD.js +14 -0
  2. package/dist/DPortalStack-6zJm6JoD.js.map +1 -0
  3. package/dist/DPortalStack-D1-GGU4C.js +12 -0
  4. package/dist/DPortalStack-D1-GGU4C.js.map +1 -0
  5. package/dist/api.json +7429 -0
  6. package/dist/css/dynamic-ui.css +95 -26
  7. package/dist/css/dynamic-ui.min.css +2 -2
  8. package/dist/esm/components/DAlert/DAlert.js +16 -0
  9. package/dist/esm/components/DAlert/DAlert.js.map +1 -0
  10. package/dist/esm/components/DAlert/index.js +6 -0
  11. package/dist/esm/components/DAlert/index.js.map +1 -0
  12. package/dist/esm/components/DAvatar/DAvatar.js +23 -0
  13. package/dist/esm/components/DAvatar/DAvatar.js.map +1 -0
  14. package/dist/esm/components/DAvatar/index.js +6 -0
  15. package/dist/esm/components/DAvatar/index.js.map +1 -0
  16. package/dist/esm/components/DBadge/DBadge.js +31 -0
  17. package/dist/esm/components/DBadge/DBadge.js.map +1 -0
  18. package/dist/esm/components/DBadge/index.js +6 -0
  19. package/dist/esm/components/DBadge/index.js.map +1 -0
  20. package/dist/esm/components/DBox/DBox.js +9 -0
  21. package/dist/esm/components/DBox/DBox.js.map +1 -0
  22. package/dist/esm/components/DBox/index.js +6 -0
  23. package/dist/esm/components/DBox/index.js.map +1 -0
  24. package/dist/esm/components/DBoxFile/DBoxFile.js +26 -0
  25. package/dist/esm/components/DBoxFile/DBoxFile.js.map +1 -0
  26. package/dist/esm/components/DBoxFile/index.js +6 -0
  27. package/dist/esm/components/DBoxFile/index.js.map +1 -0
  28. package/dist/esm/components/DBoxFile/useDBoxFile.js +236 -0
  29. package/dist/esm/components/DBoxFile/useDBoxFile.js.map +1 -0
  30. package/dist/esm/components/DBoxFile/utils.js +127 -0
  31. package/dist/esm/components/DBoxFile/utils.js.map +1 -0
  32. package/dist/esm/components/DButton/DButton.js +84 -0
  33. package/dist/esm/components/DButton/DButton.js.map +1 -0
  34. package/dist/esm/components/DButton/index.js +6 -0
  35. package/dist/esm/components/DButton/index.js.map +1 -0
  36. package/dist/esm/components/DButtonIcon/DButtonIcon.js +55 -0
  37. package/dist/esm/components/DButtonIcon/DButtonIcon.js.map +1 -0
  38. package/dist/esm/components/DButtonIcon/index.js +6 -0
  39. package/dist/esm/components/DButtonIcon/index.js.map +1 -0
  40. package/dist/esm/components/DButtonIcon/warnMissingAccessibleName.js +40 -0
  41. package/dist/esm/components/DButtonIcon/warnMissingAccessibleName.js.map +1 -0
  42. package/dist/esm/components/DCard/DCard.js +17 -0
  43. package/dist/esm/components/DCard/DCard.js.map +1 -0
  44. package/dist/esm/components/DCard/components/DCardBody.js +9 -0
  45. package/dist/esm/components/DCard/components/DCardBody.js.map +1 -0
  46. package/dist/esm/components/DCard/components/DCardFooter.js +9 -0
  47. package/dist/esm/components/DCard/components/DCardFooter.js.map +1 -0
  48. package/dist/esm/components/DCard/components/DCardHeader.js +9 -0
  49. package/dist/esm/components/DCard/components/DCardHeader.js.map +1 -0
  50. package/dist/esm/components/DCard/index.js +9 -0
  51. package/dist/esm/components/DCard/index.js.map +1 -0
  52. package/dist/esm/components/DCarousel/DCarousel.js +39 -0
  53. package/dist/esm/components/DCarousel/DCarousel.js.map +1 -0
  54. package/dist/esm/components/DCarousel/components/DCarouselSlide.js +12 -0
  55. package/dist/esm/components/DCarousel/components/DCarouselSlide.js.map +1 -0
  56. package/dist/esm/components/DCarousel/index.js +7 -0
  57. package/dist/esm/components/DCarousel/index.js.map +1 -0
  58. package/dist/esm/components/DChip/DChip.js +18 -0
  59. package/dist/esm/components/DChip/DChip.js.map +1 -0
  60. package/dist/esm/components/DChip/index.js +6 -0
  61. package/dist/esm/components/DChip/index.js.map +1 -0
  62. package/dist/esm/components/DCollapse/DCollapse.js +30 -0
  63. package/dist/esm/components/DCollapse/DCollapse.js.map +1 -0
  64. package/dist/esm/components/DCollapse/index.js +6 -0
  65. package/dist/esm/components/DCollapse/index.js.map +1 -0
  66. package/dist/esm/components/DConfirmModal/DConfirmModalContainer.js +59 -0
  67. package/dist/esm/components/DConfirmModal/DConfirmModalContainer.js.map +1 -0
  68. package/dist/esm/components/DConfirmModal/DConfirmModalUI.js +39 -0
  69. package/dist/esm/components/DConfirmModal/DConfirmModalUI.js.map +1 -0
  70. package/dist/esm/components/DConfirmModal/confirmModalStore.js +38 -0
  71. package/dist/esm/components/DConfirmModal/confirmModalStore.js.map +1 -0
  72. package/dist/esm/components/DCreditCard/DCreditCard.js +15 -0
  73. package/dist/esm/components/DCreditCard/DCreditCard.js.map +1 -0
  74. package/dist/esm/components/DCreditCard/index.js +6 -0
  75. package/dist/esm/components/DCreditCard/index.js.map +1 -0
  76. package/dist/esm/components/DCurrencyText/DCurrencyText.js +10 -0
  77. package/dist/esm/components/DCurrencyText/DCurrencyText.js.map +1 -0
  78. package/dist/esm/components/DCurrencyText/index.js +6 -0
  79. package/dist/esm/components/DCurrencyText/index.js.map +1 -0
  80. package/dist/esm/components/DDataStateWrapper/DDataStateWrapper.js +42 -0
  81. package/dist/esm/components/DDataStateWrapper/DDataStateWrapper.js.map +1 -0
  82. package/dist/esm/components/DDataStateWrapper/components/EmptyState.js +10 -0
  83. package/dist/esm/components/DDataStateWrapper/components/EmptyState.js.map +1 -0
  84. package/dist/esm/components/DDataStateWrapper/components/ErrorState.js +10 -0
  85. package/dist/esm/components/DDataStateWrapper/components/ErrorState.js.map +1 -0
  86. package/dist/esm/components/DDataStateWrapper/components/LoadingState.js +8 -0
  87. package/dist/esm/components/DDataStateWrapper/components/LoadingState.js.map +1 -0
  88. package/dist/esm/components/DDataStateWrapper/index.js +9 -0
  89. package/dist/esm/components/DDataStateWrapper/index.js.map +1 -0
  90. package/dist/esm/components/DDatePicker/DDatePicker.js +54 -0
  91. package/dist/esm/components/DDatePicker/DDatePicker.js.map +1 -0
  92. package/dist/esm/components/DDatePicker/components/DDatePickerHeaderSelector.js +50 -0
  93. package/dist/esm/components/DDatePicker/components/DDatePickerHeaderSelector.js.map +1 -0
  94. package/dist/esm/components/DDatePicker/components/DDatePickerInput.js +16 -0
  95. package/dist/esm/components/DDatePicker/components/DDatePickerInput.js.map +1 -0
  96. package/dist/esm/components/DDatePicker/components/DDatePickerTime.js +11 -0
  97. package/dist/esm/components/DDatePicker/components/DDatePickerTime.js.map +1 -0
  98. package/dist/esm/components/DDatePicker/index.js +6 -0
  99. package/dist/esm/components/DDatePicker/index.js.map +1 -0
  100. package/dist/esm/components/DDropdown/DDropdown.js +315 -0
  101. package/dist/esm/components/DDropdown/DDropdown.js.map +1 -0
  102. package/dist/esm/components/DDropdown/index.js +6 -0
  103. package/dist/esm/components/DDropdown/index.js.map +1 -0
  104. package/dist/esm/components/DErrorBoundary/DErrorBoundary.js +22 -0
  105. package/dist/esm/components/DErrorBoundary/DErrorBoundary.js.map +1 -0
  106. package/dist/esm/components/DErrorBoundary/components/DefaultErrorBoundary.js +10 -0
  107. package/dist/esm/components/DErrorBoundary/components/DefaultErrorBoundary.js.map +1 -0
  108. package/dist/esm/components/DErrorBoundary/index.js +7 -0
  109. package/dist/esm/components/DErrorBoundary/index.js.map +1 -0
  110. package/dist/esm/components/DIcon/DIcon.js +21 -0
  111. package/dist/esm/components/DIcon/DIcon.js.map +1 -0
  112. package/dist/esm/components/DIcon/index.js +6 -0
  113. package/dist/esm/components/DIcon/index.js.map +1 -0
  114. package/dist/esm/components/DIconBase/DIconBase.js +146 -0
  115. package/dist/esm/components/DIconBase/DIconBase.js.map +1 -0
  116. package/dist/esm/components/DIconBase/index.js +6 -0
  117. package/dist/esm/components/DIconBase/index.js.map +1 -0
  118. package/dist/esm/components/DIconBase/resolveIconFromRegistry.js +20 -0
  119. package/dist/esm/components/DIconBase/resolveIconFromRegistry.js.map +1 -0
  120. package/dist/esm/components/DIconBase/warnUnknownIcon.js +59 -0
  121. package/dist/esm/components/DIconBase/warnUnknownIcon.js.map +1 -0
  122. package/dist/esm/components/DInput/DInput.js +95 -0
  123. package/dist/esm/components/DInput/DInput.js.map +1 -0
  124. package/dist/esm/components/DInput/index.js +6 -0
  125. package/dist/esm/components/DInput/index.js.map +1 -0
  126. package/dist/esm/components/DInputCheck/DInputCheck.js +94 -0
  127. package/dist/esm/components/DInputCheck/DInputCheck.js.map +1 -0
  128. package/dist/esm/components/DInputCheck/index.js +6 -0
  129. package/dist/esm/components/DInputCheck/index.js.map +1 -0
  130. package/dist/esm/components/DInputCounter/DInputCounter.js +107 -0
  131. package/dist/esm/components/DInputCounter/DInputCounter.js.map +1 -0
  132. package/dist/esm/components/DInputCounter/index.js +6 -0
  133. package/dist/esm/components/DInputCounter/index.js.map +1 -0
  134. package/dist/esm/components/DInputCurrency/DInputCurrency.js +25 -0
  135. package/dist/esm/components/DInputCurrency/DInputCurrency.js.map +1 -0
  136. package/dist/esm/components/DInputCurrency/index.js +6 -0
  137. package/dist/esm/components/DInputCurrency/index.js.map +1 -0
  138. package/dist/esm/components/DInputMask/DInputMask.js +13 -0
  139. package/dist/esm/components/DInputMask/DInputMask.js.map +1 -0
  140. package/dist/esm/components/DInputMask/index.js +6 -0
  141. package/dist/esm/components/DInputMask/index.js.map +1 -0
  142. package/dist/esm/components/DInputPassword/DInputPassword.js +24 -0
  143. package/dist/esm/components/DInputPassword/DInputPassword.js.map +1 -0
  144. package/dist/esm/components/DInputPassword/index.js +6 -0
  145. package/dist/esm/components/DInputPassword/index.js.map +1 -0
  146. package/dist/esm/components/DInputPhone/DInputPhone.js +108 -0
  147. package/dist/esm/components/DInputPhone/DInputPhone.js.map +1 -0
  148. package/dist/esm/components/DInputPhone/index.js +6 -0
  149. package/dist/esm/components/DInputPhone/index.js.map +1 -0
  150. package/dist/esm/components/DInputPin/DInputPin.js +88 -0
  151. package/dist/esm/components/DInputPin/DInputPin.js.map +1 -0
  152. package/dist/esm/components/DInputPin/index.js +6 -0
  153. package/dist/esm/components/DInputPin/index.js.map +1 -0
  154. package/dist/esm/components/DInputRange/DInputRange.js +60 -0
  155. package/dist/esm/components/DInputRange/DInputRange.js.map +1 -0
  156. package/dist/esm/components/DInputRange/index.js +6 -0
  157. package/dist/esm/components/DInputRange/index.js.map +1 -0
  158. package/dist/esm/components/DInputSearch/DInputSearch.js +57 -0
  159. package/dist/esm/components/DInputSearch/DInputSearch.js.map +1 -0
  160. package/dist/esm/components/DInputSearch/index.js +6 -0
  161. package/dist/esm/components/DInputSearch/index.js.map +1 -0
  162. package/dist/esm/components/DInputSelect/DInputSelect.js +104 -0
  163. package/dist/esm/components/DInputSelect/DInputSelect.js.map +1 -0
  164. package/dist/esm/components/DInputSelect/index.js +6 -0
  165. package/dist/esm/components/DInputSelect/index.js.map +1 -0
  166. package/dist/esm/components/DInputSwitch/DInputSwitch.js +43 -0
  167. package/dist/esm/components/DInputSwitch/DInputSwitch.js.map +1 -0
  168. package/dist/esm/components/DInputSwitch/index.js +6 -0
  169. package/dist/esm/components/DInputSwitch/index.js.map +1 -0
  170. package/dist/esm/components/DLayout/DLayout.js +22 -0
  171. package/dist/esm/components/DLayout/DLayout.js.map +1 -0
  172. package/dist/esm/components/DLayout/components/DLayoutPane.js +16 -0
  173. package/dist/esm/components/DLayout/components/DLayoutPane.js.map +1 -0
  174. package/dist/esm/components/DLayout/index.js +7 -0
  175. package/dist/esm/components/DLayout/index.js.map +1 -0
  176. package/dist/esm/components/DListGroup/DListGroup.js +39 -0
  177. package/dist/esm/components/DListGroup/DListGroup.js.map +1 -0
  178. package/dist/esm/components/DListGroup/ListGroupContext.js +11 -0
  179. package/dist/esm/components/DListGroup/ListGroupContext.js.map +1 -0
  180. package/dist/esm/components/DListGroup/components/DListGroupItem.js +59 -0
  181. package/dist/esm/components/DListGroup/components/DListGroupItem.js.map +1 -0
  182. package/dist/esm/components/DListGroup/components/warnInvalidListMarkup.js +30 -0
  183. package/dist/esm/components/DListGroup/components/warnInvalidListMarkup.js.map +1 -0
  184. package/dist/esm/components/DListGroup/index.js +7 -0
  185. package/dist/esm/components/DListGroup/index.js.map +1 -0
  186. package/dist/esm/components/DModal/DModal.js +37 -0
  187. package/dist/esm/components/DModal/DModal.js.map +1 -0
  188. package/dist/esm/components/DModal/components/DModalBody.js +9 -0
  189. package/dist/esm/components/DModal/components/DModalBody.js.map +1 -0
  190. package/dist/esm/components/DModal/components/DModalFooter.js +14 -0
  191. package/dist/esm/components/DModal/components/DModalFooter.js.map +1 -0
  192. package/dist/esm/components/DModal/components/DModalHeader.js +15 -0
  193. package/dist/esm/components/DModal/components/DModalHeader.js.map +1 -0
  194. package/dist/esm/components/DModal/index.js +9 -0
  195. package/dist/esm/components/DModal/index.js.map +1 -0
  196. package/dist/esm/components/DOffcanvas/DOffcanvas.js +82 -0
  197. package/dist/esm/components/DOffcanvas/DOffcanvas.js.map +1 -0
  198. package/dist/esm/components/DOffcanvas/components/DOffcanvasBody.js +9 -0
  199. package/dist/esm/components/DOffcanvas/components/DOffcanvasBody.js.map +1 -0
  200. package/dist/esm/components/DOffcanvas/components/DOffcanvasFooter.js +14 -0
  201. package/dist/esm/components/DOffcanvas/components/DOffcanvasFooter.js.map +1 -0
  202. package/dist/esm/components/DOffcanvas/components/DOffcanvasHeader.js +15 -0
  203. package/dist/esm/components/DOffcanvas/components/DOffcanvasHeader.js.map +1 -0
  204. package/dist/esm/components/DOffcanvas/index.js +9 -0
  205. package/dist/esm/components/DOffcanvas/index.js.map +1 -0
  206. package/dist/esm/components/DOtp/DOtp.js +26 -0
  207. package/dist/esm/components/DOtp/DOtp.js.map +1 -0
  208. package/dist/esm/components/DOtp/components/OtpCountdown.js +12 -0
  209. package/dist/esm/components/DOtp/components/OtpCountdown.js.map +1 -0
  210. package/dist/esm/components/DOtp/index.js +6 -0
  211. package/dist/esm/components/DOtp/index.js.map +1 -0
  212. package/dist/esm/components/DPaginator/DPaginator.js +12 -0
  213. package/dist/esm/components/DPaginator/DPaginator.js.map +1 -0
  214. package/dist/esm/components/DPaginator/index.js +6 -0
  215. package/dist/esm/components/DPaginator/index.js.map +1 -0
  216. package/dist/esm/components/DPasswordStrengthMeter/DPasswordStrengthMeter.js +27 -0
  217. package/dist/esm/components/DPasswordStrengthMeter/DPasswordStrengthMeter.js.map +1 -0
  218. package/dist/esm/components/DPasswordStrengthMeter/PasswordCheckItem.js +10 -0
  219. package/dist/esm/components/DPasswordStrengthMeter/PasswordCheckItem.js.map +1 -0
  220. package/dist/esm/components/DPasswordStrengthMeter/PasswordCheckList.js +41 -0
  221. package/dist/esm/components/DPasswordStrengthMeter/PasswordCheckList.js.map +1 -0
  222. package/dist/esm/components/DPasswordStrengthMeter/PasswordStrength.js +23 -0
  223. package/dist/esm/components/DPasswordStrengthMeter/PasswordStrength.js.map +1 -0
  224. package/dist/esm/components/DPasswordStrengthMeter/index.js +6 -0
  225. package/dist/esm/components/DPasswordStrengthMeter/index.js.map +1 -0
  226. package/dist/esm/components/DPopover/DPopover.js +49 -0
  227. package/dist/esm/components/DPopover/DPopover.js.map +1 -0
  228. package/dist/esm/components/DPopover/index.js +6 -0
  229. package/dist/esm/components/DPopover/index.js.map +1 -0
  230. package/dist/esm/components/DProgress/DProgress.js +22 -0
  231. package/dist/esm/components/DProgress/DProgress.js.map +1 -0
  232. package/dist/esm/components/DProgress/index.js +6 -0
  233. package/dist/esm/components/DProgress/index.js.map +1 -0
  234. package/dist/esm/components/DSelect/DSelect.js +88 -0
  235. package/dist/esm/components/DSelect/DSelect.js.map +1 -0
  236. package/dist/esm/components/DSelect/components/DSelectClearIndicator.js +12 -0
  237. package/dist/esm/components/DSelect/components/DSelectClearIndicator.js.map +1 -0
  238. package/dist/esm/components/DSelect/components/DSelectDropdownIndicator.js +12 -0
  239. package/dist/esm/components/DSelect/components/DSelectDropdownIndicator.js.map +1 -0
  240. package/dist/esm/components/DSelect/components/DSelectLoadingIndicator.js +12 -0
  241. package/dist/esm/components/DSelect/components/DSelectLoadingIndicator.js.map +1 -0
  242. package/dist/esm/components/DSelect/components/DSelectMultiValueRemove.js +12 -0
  243. package/dist/esm/components/DSelect/components/DSelectMultiValueRemove.js.map +1 -0
  244. package/dist/esm/components/DSelect/components/DSelectOptionCheck.js +14 -0
  245. package/dist/esm/components/DSelect/components/DSelectOptionCheck.js.map +1 -0
  246. package/dist/esm/components/DSelect/components/DSelectOptionEmoji.js +14 -0
  247. package/dist/esm/components/DSelect/components/DSelectOptionEmoji.js.map +1 -0
  248. package/dist/esm/components/DSelect/components/DSelectOptionIcon.js +15 -0
  249. package/dist/esm/components/DSelect/components/DSelectOptionIcon.js.map +1 -0
  250. package/dist/esm/components/DSelect/components/DSelectPlaceholder.js +14 -0
  251. package/dist/esm/components/DSelect/components/DSelectPlaceholder.js.map +1 -0
  252. package/dist/esm/components/DSelect/components/DSelectSingleValueEmoji.js +15 -0
  253. package/dist/esm/components/DSelect/components/DSelectSingleValueEmoji.js.map +1 -0
  254. package/dist/esm/components/DSelect/components/DSelectSingleValueEmojiText.js +15 -0
  255. package/dist/esm/components/DSelect/components/DSelectSingleValueEmojiText.js.map +1 -0
  256. package/dist/esm/components/DSelect/components/DSelectSingleValueIconText.js +16 -0
  257. package/dist/esm/components/DSelect/components/DSelectSingleValueIconText.js.map +1 -0
  258. package/dist/esm/components/DSelect/createAriaGuidance.js +57 -0
  259. package/dist/esm/components/DSelect/createAriaGuidance.js.map +1 -0
  260. package/dist/esm/components/DSelect/index.js +6 -0
  261. package/dist/esm/components/DSelect/index.js.map +1 -0
  262. package/dist/esm/components/DStepper/DStepper.js +13 -0
  263. package/dist/esm/components/DStepper/DStepper.js.map +1 -0
  264. package/dist/esm/components/DStepper/index.js +6 -0
  265. package/dist/esm/components/DStepper/index.js.map +1 -0
  266. package/dist/esm/components/DStepperDesktop/DStepperDesktop.js +27 -0
  267. package/dist/esm/components/DStepperDesktop/DStepperDesktop.js.map +1 -0
  268. package/dist/esm/components/DStepperDesktop/index.js +6 -0
  269. package/dist/esm/components/DStepperDesktop/index.js.map +1 -0
  270. package/dist/esm/components/DStepperMobile/DStepperMobile.js +37 -0
  271. package/dist/esm/components/DStepperMobile/DStepperMobile.js.map +1 -0
  272. package/dist/esm/components/DStepperMobile/index.js +6 -0
  273. package/dist/esm/components/DStepperMobile/index.js.map +1 -0
  274. package/dist/esm/components/DTabs/DTabs.js +106 -0
  275. package/dist/esm/components/DTabs/DTabs.js.map +1 -0
  276. package/dist/esm/components/DTabs/TabContext.js +14 -0
  277. package/dist/esm/components/DTabs/TabContext.js.map +1 -0
  278. package/dist/esm/components/DTabs/components/DTabContent.js +14 -0
  279. package/dist/esm/components/DTabs/components/DTabContent.js.map +1 -0
  280. package/dist/esm/components/DTabs/components/DTabsProvider.js +22 -0
  281. package/dist/esm/components/DTabs/components/DTabsProvider.js.map +1 -0
  282. package/dist/esm/components/DTabs/index.js +9 -0
  283. package/dist/esm/components/DTabs/index.js.map +1 -0
  284. package/dist/esm/components/DTimeline/DTimeline.js +12 -0
  285. package/dist/esm/components/DTimeline/DTimeline.js.map +1 -0
  286. package/dist/esm/components/DTimeline/index.js +6 -0
  287. package/dist/esm/components/DTimeline/index.js.map +1 -0
  288. package/dist/esm/components/DToast/DToast.js +20 -0
  289. package/dist/esm/components/DToast/DToast.js.map +1 -0
  290. package/dist/esm/components/DToast/components/DToastBody.js +9 -0
  291. package/dist/esm/components/DToast/components/DToastBody.js.map +1 -0
  292. package/dist/esm/components/DToast/components/DToastHeader.js +9 -0
  293. package/dist/esm/components/DToast/components/DToastHeader.js.map +1 -0
  294. package/dist/esm/components/DToast/index.js +8 -0
  295. package/dist/esm/components/DToast/index.js.map +1 -0
  296. package/dist/esm/components/DToastContainer/DToastContainer.js +9 -0
  297. package/dist/esm/components/DToastContainer/DToastContainer.js.map +1 -0
  298. package/dist/esm/components/DToastContainer/index.js +7 -0
  299. package/dist/esm/components/DToastContainer/index.js.map +1 -0
  300. package/dist/esm/components/DToastContainer/useDToast.js +58 -0
  301. package/dist/esm/components/DToastContainer/useDToast.js.map +1 -0
  302. package/dist/esm/components/DTooltip/DTooltip.js +51 -0
  303. package/dist/esm/components/DTooltip/DTooltip.js.map +1 -0
  304. package/dist/esm/components/DTooltip/index.js +6 -0
  305. package/dist/esm/components/DTooltip/index.js.map +1 -0
  306. package/dist/esm/components/DVoucher/DVoucher.js +55 -0
  307. package/dist/esm/components/DVoucher/DVoucher.js.map +1 -0
  308. package/dist/esm/components/DVoucher/hooks/useScreenshot.js +30 -0
  309. package/dist/esm/components/DVoucher/hooks/useScreenshot.js.map +1 -0
  310. package/dist/esm/components/DVoucher/hooks/useScreenshotDownload.js +30 -0
  311. package/dist/esm/components/DVoucher/hooks/useScreenshotDownload.js.map +1 -0
  312. package/dist/esm/components/DVoucher/hooks/useScreenshotWebShare.js +27 -0
  313. package/dist/esm/components/DVoucher/hooks/useScreenshotWebShare.js.map +1 -0
  314. package/dist/esm/components/DVoucher/index.js +9 -0
  315. package/dist/esm/components/DVoucher/index.js.map +1 -0
  316. package/dist/esm/components/config.js +14 -0
  317. package/dist/esm/components/config.js.map +1 -0
  318. package/dist/esm/components/internal/DFormLabel.js +46 -0
  319. package/dist/esm/components/internal/DFormLabel.js.map +1 -0
  320. package/dist/esm/contexts/DContext.js +124 -0
  321. package/dist/esm/contexts/DContext.js.map +1 -0
  322. package/dist/esm/contexts/DPortalContext.js +163 -0
  323. package/dist/esm/contexts/DPortalContext.js.map +1 -0
  324. package/dist/esm/contexts/portal/DPortalStack.js +12 -0
  325. package/dist/esm/contexts/portal/DPortalStack.js.map +1 -0
  326. package/dist/esm/hooks/useConfirmModal.js +63 -0
  327. package/dist/esm/hooks/useConfirmModal.js.map +1 -0
  328. package/dist/esm/hooks/useControlledState.js +51 -0
  329. package/dist/esm/hooks/useControlledState.js.map +1 -0
  330. package/dist/esm/hooks/useCountdown.js +45 -0
  331. package/dist/esm/hooks/useCountdown.js.map +1 -0
  332. package/dist/esm/hooks/useDisableBodyScrollEffect.js +52 -0
  333. package/dist/esm/hooks/useDisableBodyScrollEffect.js.map +1 -0
  334. package/dist/esm/hooks/useDisableInputWheel.js +17 -0
  335. package/dist/esm/hooks/useDisableInputWheel.js.map +1 -0
  336. package/dist/esm/hooks/useFormatCurrency.js +16 -0
  337. package/dist/esm/hooks/useFormatCurrency.js.map +1 -0
  338. package/dist/esm/hooks/useInputCurrency.js +130 -0
  339. package/dist/esm/hooks/useInputCurrency.js.map +1 -0
  340. package/dist/esm/hooks/useItemSelection.js +35 -0
  341. package/dist/esm/hooks/useItemSelection.js.map +1 -0
  342. package/dist/esm/hooks/useMediaBreakpointUp.js +69 -0
  343. package/dist/esm/hooks/useMediaBreakpointUp.js.map +1 -0
  344. package/dist/esm/hooks/useMediaQuery.js +11 -0
  345. package/dist/esm/hooks/useMediaQuery.js.map +1 -0
  346. package/dist/esm/hooks/useOtp.js +107 -0
  347. package/dist/esm/hooks/useOtp.js.map +1 -0
  348. package/dist/esm/hooks/usePortal.js +26 -0
  349. package/dist/esm/hooks/usePortal.js.map +1 -0
  350. package/dist/esm/hooks/useProvidedRefOrCreate.js +24 -0
  351. package/dist/esm/hooks/useProvidedRefOrCreate.js.map +1 -0
  352. package/dist/esm/hooks/useResponsiveProp.js +61 -0
  353. package/dist/esm/hooks/useResponsiveProp.js.map +1 -0
  354. package/dist/esm/hooks/useStackState.js +40 -0
  355. package/dist/esm/hooks/useStackState.js.map +1 -0
  356. package/dist/esm/icons/coreIcons.js +55 -0
  357. package/dist/esm/icons/coreIcons.js.map +1 -0
  358. package/dist/esm/index.js +99 -0
  359. package/dist/esm/index.js.map +1 -0
  360. package/dist/esm/utils/attr-accept.js +38 -0
  361. package/dist/esm/utils/attr-accept.js.map +1 -0
  362. package/dist/esm/utils/buildUrl.js +57 -0
  363. package/dist/esm/utils/buildUrl.js.map +1 -0
  364. package/dist/esm/utils/changeQueryString.js +19 -0
  365. package/dist/esm/utils/changeQueryString.js.map +1 -0
  366. package/dist/esm/utils/configureI18n.js +20 -0
  367. package/dist/esm/utils/configureI18n.js.map +1 -0
  368. package/dist/esm/utils/formatCurrency.js +8 -0
  369. package/dist/esm/utils/formatCurrency.js.map +1 -0
  370. package/dist/esm/utils/getCssVariable.js +7 -0
  371. package/dist/esm/utils/getCssVariable.js.map +1 -0
  372. package/dist/esm/utils/getKeyboardFocusableElements.js +11 -0
  373. package/dist/esm/utils/getKeyboardFocusableElements.js.map +1 -0
  374. package/dist/esm/utils/getQueryString.js +8 -0
  375. package/dist/esm/utils/getQueryString.js.map +1 -0
  376. package/dist/esm/utils/hasLabelContent.js +48 -0
  377. package/dist/esm/utils/hasLabelContent.js.map +1 -0
  378. package/dist/esm/utils/isTextLabel.js +23 -0
  379. package/dist/esm/utils/isTextLabel.js.map +1 -0
  380. package/dist/esm/utils/labelClickGuard.js +61 -0
  381. package/dist/esm/utils/labelClickGuard.js.map +1 -0
  382. package/dist/esm/utils/mediaQuery.js +13 -0
  383. package/dist/esm/utils/mediaQuery.js.map +1 -0
  384. package/dist/esm/utils/sanitizeHref.js +67 -0
  385. package/dist/esm/utils/sanitizeHref.js.map +1 -0
  386. package/dist/esm/utils/validatePhoneNumber.js +14 -0
  387. package/dist/esm/utils/validatePhoneNumber.js.map +1 -0
  388. package/dist/esm/utils/warnLabelUsage.js +57 -0
  389. package/dist/esm/utils/warnLabelUsage.js.map +1 -0
  390. package/dist/icons-core.json +33 -0
  391. package/dist/index.esm.js +1151 -184
  392. package/dist/index.esm.js.map +1 -1
  393. package/dist/index.js +1151 -182
  394. package/dist/index.js.map +1 -1
  395. package/dist/types/components/DAlert/DAlert.d.ts +16 -2
  396. package/dist/types/components/DButtonIcon/DButtonIcon.d.ts +9 -1
  397. package/dist/types/components/DButtonIcon/warnMissingAccessibleName.d.ts +18 -0
  398. package/dist/types/components/DDataStateWrapper/DDataStateWrapper.d.ts +29 -4
  399. package/dist/types/components/DDataStateWrapper/index.d.ts +1 -1
  400. package/dist/types/components/DErrorBoundary/DErrorBoundary.d.ts +6 -1
  401. package/dist/types/components/DErrorBoundary/components/DefaultErrorBoundary.d.ts +3 -1
  402. package/dist/types/components/DIcon/DIcon.d.ts +8 -0
  403. package/dist/types/components/DIconBase/DIconBase.d.ts +43 -1
  404. package/dist/types/components/DIconBase/resolveIconFromRegistry.d.ts +14 -0
  405. package/dist/types/components/DIconBase/warnUnknownIcon.d.ts +13 -0
  406. package/dist/types/components/DInput/DInput.d.ts +11 -2
  407. package/dist/types/components/DInputCheck/DInputCheck.d.ts +28 -3
  408. package/dist/types/components/DInputCounter/DInputCounter.d.ts +15 -3
  409. package/dist/types/components/DInputCurrency/DInputCurrency.d.ts +10 -3
  410. package/dist/types/components/DInputPhone/DInputPhone.d.ts +11 -2
  411. package/dist/types/components/DInputPin/DInputPin.d.ts +12 -2
  412. package/dist/types/components/DInputRange/DInputRange.d.ts +16 -3
  413. package/dist/types/components/DInputSelect/DInputSelect.d.ts +18 -3
  414. package/dist/types/components/DInputSwitch/DInputSwitch.d.ts +28 -3
  415. package/dist/types/components/DListGroup/DListGroup.d.ts +12 -1
  416. package/dist/types/components/DListGroup/ListGroupContext.d.ts +8 -0
  417. package/dist/types/components/DListGroup/components/DListGroupItem.d.ts +6 -1
  418. package/dist/types/components/DListGroup/components/warnInvalidListMarkup.d.ts +13 -0
  419. package/dist/types/components/DPasswordStrengthMeter/DPasswordStrengthMeter.d.ts +18 -3
  420. package/dist/types/components/DProgress/DProgress.d.ts +13 -1
  421. package/dist/types/components/DSelect/DSelect.d.ts +23 -3
  422. package/dist/types/components/DSelect/createAriaGuidance.d.ts +21 -0
  423. package/dist/types/components/DTabs/DTabs.d.ts +13 -2
  424. package/dist/types/components/DTabs/TabContext.d.ts +5 -0
  425. package/dist/types/components/DTabs/components/DTabsProvider.d.ts +11 -0
  426. package/dist/types/components/DTabs/index.d.ts +1 -0
  427. package/dist/types/components/DToast/DToast.d.ts +10 -3
  428. package/dist/types/components/DToastContainer/useDToast.d.ts +8 -1
  429. package/dist/types/components/config.d.ts +10 -0
  430. package/dist/types/components/index.d.ts +2 -2
  431. package/dist/types/components/interface.d.ts +7 -0
  432. package/dist/types/components/internal/DFormLabel.d.ts +30 -0
  433. package/dist/types/contexts/portal/DPortalStack.d.ts +13 -0
  434. package/dist/types/hooks/useControlledState.d.ts +17 -0
  435. package/dist/types/hooks/useInputCurrency.d.ts +26 -1
  436. package/dist/types/hooks/useMediaBreakpointUp.d.ts +10 -0
  437. package/dist/types/icons/coreIcons.d.ts +24 -0
  438. package/dist/types/icons/index.d.ts +2 -0
  439. package/dist/types/index.d.ts +1 -0
  440. package/dist/types/utils/hasLabelContent.d.ts +30 -0
  441. package/dist/types/utils/isTextLabel.d.ts +17 -0
  442. package/dist/types/utils/labelClickGuard.d.ts +20 -0
  443. package/dist/types/utils/warnLabelUsage.d.ts +43 -0
  444. package/package.json +10 -5
  445. package/src/style/abstracts/variables/_body.scss +2 -2
  446. package/src/style/abstracts/variables/_chips.scss +2 -0
  447. package/src/style/abstracts/variables/_colors.scss +2 -2
  448. package/src/style/base/_buttons.scss +4 -1
  449. package/src/style/base/_input-group.scss +5 -2
  450. package/src/style/base/_list-group.scss +55 -0
  451. package/src/style/components/_d-chip.scss +7 -2
  452. package/src/style/components/_d-tabs.scss +3 -0
  453. package/src/style/helpers/_color-bg.scss +5 -0
  454. package/src/style/root/_root.scss +9 -0
  455. package/dist/types/components/DVoucher/hooks/useScreenshotDownload.spec.d.ts +0 -1
  456. package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.spec.d.ts +0 -1
  457. package/dist/types/hooks/tests/useCountdown.spec.d.ts +0 -1
  458. package/dist/types/hooks/tests/useDisableBodyScrollEffect.spec.d.ts +0 -1
  459. package/dist/types/hooks/tests/useDisableInputWheel.spec.d.ts +0 -1
  460. package/dist/types/hooks/tests/useFormatCurrency.spec.d.ts +0 -1
  461. package/dist/types/hooks/tests/useInputCurrency.spec.d.ts +0 -1
  462. package/dist/types/hooks/tests/useItemSelection.spec.d.ts +0 -1
  463. package/dist/types/hooks/tests/useMediaBreakpointUp.spec.d.ts +0 -1
  464. package/dist/types/hooks/tests/useMediaQuery.spec.d.ts +0 -1
  465. package/dist/types/hooks/tests/usePortal.spec.d.ts +0 -1
  466. package/dist/types/hooks/tests/useProvidedRefOrCreate.spec.d.ts +0 -1
  467. package/dist/types/hooks/tests/useStackState.spec.d.ts +0 -1
  468. package/dist/types/utils/tests/attr-accept.spec.d.ts +0 -1
  469. package/dist/types/utils/tests/buildUrl.spec.d.ts +0 -1
  470. package/dist/types/utils/tests/changeQueryString.spec.d.ts +0 -1
  471. package/dist/types/utils/tests/configureI18n.spec.d.ts +0 -1
  472. package/dist/types/utils/tests/formatCurrency.spec.d.ts +0 -1
  473. package/dist/types/utils/tests/getCssVariable.spec.d.ts +0 -1
  474. package/dist/types/utils/tests/getKeyboardFocusableElements.spec.d.ts +0 -1
  475. package/dist/types/utils/tests/getQueryString.spec.d.ts +0 -1
  476. package/dist/types/utils/tests/mediaQuery.spec.d.ts +0 -1
  477. package/dist/types/utils/tests/sanitizeHref.spec.d.ts +0 -1
  478. package/dist/types/utils/tests/validatePhoneNumber.spec.d.ts +0 -1
package/dist/index.js CHANGED
@@ -7,9 +7,9 @@ var tslib = require('tslib');
7
7
  var LucideIcons = require('lucide-react');
8
8
  var reactIs = require('react-is');
9
9
  var reactDom = require('react-dom');
10
- var framerMotion = require('framer-motion');
11
10
  var fileSelector = require('file-selector');
12
11
  var reactSplide = require('@splidejs/react-splide');
12
+ var framerMotion = require('framer-motion');
13
13
  var currency = require('currency.js');
14
14
  var DatePicker = require('react-datepicker');
15
15
  var dateFns = require('date-fns');
@@ -45,6 +45,16 @@ function _interopNamespaceDefault(e) {
45
45
  var LucideIcons__namespace = /*#__PURE__*/_interopNamespaceDefault(LucideIcons);
46
46
 
47
47
  const PREFIX_BS = 'bs-';
48
+ /**
49
+ * Default icon-font family configured by `DContextProvider`.
50
+ *
51
+ * `DIconBase` compares the family it receives against these values to tell an
52
+ * unconfigured consumer apart from one that deliberately opted into an icon
53
+ * font (Material Symbols, Bootstrap Icons). Only the former gets the
54
+ * development warning for an unresolved icon name.
55
+ */
56
+ const DEFAULT_ICON_FAMILY_CLASS = 'bi';
57
+ const DEFAULT_ICON_FAMILY_PREFIX = 'bi-';
48
58
 
49
59
  /* eslint-disable no-lonely-if */
50
60
  function useDisableBodyScrollEffect(disable) {
@@ -161,11 +171,30 @@ function getKeyboardFocusableElements(container) {
161
171
  ].filter((element) => !element.hasAttribute('disabled'));
162
172
  }
163
173
 
174
+ const loadPortalStack = () => Promise.resolve().then(function () { return require('./DPortalStack-6zJm6JoD.js'); }).then((module) => module.default);
164
175
  const DPortalContext = React.createContext(undefined);
165
176
  function DPortalContextProvider({ portalName, children, availablePortals, }) {
166
177
  const { created } = usePortal(portalName);
167
178
  const [stack, { push, pop }] = useStackState([]);
179
+ const [PortalStack, setPortalStack] = React.useState(null);
180
+ const pending = React.useRef([]);
181
+ const ready = React.useRef(false);
168
182
  useDisableBodyScrollEffect(Boolean(stack.length));
183
+ // Loads framer-motion off the critical path only when portals are configured,
184
+ // so the first openPortal does not wait for it.
185
+ const hasPortals = Object.keys(availablePortals !== null && availablePortals !== void 0 ? availablePortals : {}).length > 0;
186
+ React.useEffect(() => {
187
+ if (!hasPortals)
188
+ return;
189
+ loadPortalStack()
190
+ .then((Stack) => {
191
+ ready.current = true;
192
+ setPortalStack(() => Stack);
193
+ })
194
+ .catch(() => {
195
+ // openPortal retries the import and reports the failure.
196
+ });
197
+ }, [hasPortals]);
169
198
  const openPortal = React.useCallback(
170
199
  // eslint-disable-next-line prefer-arrow-callback
171
200
  function openPortalImpl(name, payload) {
@@ -181,10 +210,30 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
181
210
  }
182
211
  // K is a specific member of keyof T & string so the object satisfies
183
212
  // InternalStackItem<T>, but TS can't verify generic-over-union assignability.
184
- push({ name, Component, payload });
213
+ const item = { name, Component, payload };
214
+ if (ready.current) {
215
+ push(item);
216
+ }
217
+ else {
218
+ // Not loaded yet: the portal enters the stack together with its renderer.
219
+ pending.current.push(item);
220
+ loadPortalStack().then((Stack) => {
221
+ ready.current = true;
222
+ setPortalStack(() => Stack);
223
+ pending.current.splice(0).forEach(push);
224
+ }).catch((error) => {
225
+ pending.current = [];
226
+ // eslint-disable-next-line no-console
227
+ console.error('[DPortalContext] Could not load the portal stack', error);
228
+ });
229
+ }
185
230
  (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.blur();
186
231
  }, [availablePortals, push]);
187
232
  const closePortal = React.useCallback(() => {
233
+ if (pending.current.length > 0) {
234
+ pending.current.pop();
235
+ return;
236
+ }
188
237
  // pop() is safe on empty stacks, so close remains idempotent.
189
238
  pop();
190
239
  }, [pop]);
@@ -241,13 +290,10 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
241
290
  window.removeEventListener('keydown', keyEvent);
242
291
  };
243
292
  }, [handleClose, portalName, stack.length]);
244
- return (jsxRuntime.jsxs(DPortalContext.Provider, { value: value, children: [children, created && reactDom.createPortal(
293
+ return (jsxRuntime.jsxs(DPortalContext.Provider, { value: value, children: [children, created && PortalStack && reactDom.createPortal(
245
294
  // eslint-disable-next-line max-len
246
295
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
247
- jsxRuntime.jsx("div", { onClick: ({ target }) => handleClose(target), onKeyDown: () => { }, children: jsxRuntime.jsx(framerMotion.AnimatePresence, { children: stack.flatMap(({ Component, name, payload, }) => [
248
- jsxRuntime.jsx(framerMotion.motion.div, { className: "backdrop", initial: { opacity: 0 }, animate: { opacity: 0.5 }, exit: { opacity: 0, transition: { delay: 0.3 } }, transition: { duration: 0.15, ease: 'linear' } }, `${name}-backdrop`),
249
- jsxRuntime.jsx(Component, { name: name, payload: payload }, name),
250
- ]) }) }), document.getElementById(portalName))] }));
296
+ jsxRuntime.jsx("div", { onClick: ({ target }) => handleClose(target), onKeyDown: () => { }, children: jsxRuntime.jsx(PortalStack, { stack: stack }) }), document.getElementById(portalName))] }));
251
297
  }
252
298
  /**
253
299
  * Hook to open/close registered portals (modals, offcanvas, etc.).
@@ -326,8 +372,8 @@ const DEFAULT_STATE = {
326
372
  decimal: '.',
327
373
  },
328
374
  icon: {
329
- familyClass: 'bi',
330
- familyPrefix: 'bi-',
375
+ familyClass: DEFAULT_ICON_FAMILY_CLASS,
376
+ familyPrefix: DEFAULT_ICON_FAMILY_PREFIX,
331
377
  materialStyle: false,
332
378
  },
333
379
  iconRegistry: undefined,
@@ -432,6 +478,79 @@ function useDContext() {
432
478
  return React.useContext(DContext);
433
479
  }
434
480
 
481
+ /**
482
+ * Resolves a string icon name against the registry supplied through
483
+ * `DContextProvider`'s `iconRegistry` prop.
484
+ *
485
+ * This is the single implementation shared by `DIcon` and `DIconBase`, so both
486
+ * public entry points — and every Dynamic UI component that renders an icon
487
+ * through them — resolve names identically.
488
+ *
489
+ * A non-string `icon` is returned untouched: a component passed directly needs
490
+ * no lookup. A name missing from the registry is also returned untouched, so
491
+ * the caller can fall through to lucide-react and then to the icon-font family.
492
+ */
493
+ function resolveIconFromRegistry(icon, iconRegistry) {
494
+ if (typeof icon !== 'string')
495
+ return icon;
496
+ return (iconRegistry === null || iconRegistry === void 0 ? void 0 : iconRegistry[icon]) || icon;
497
+ }
498
+
499
+ /**
500
+ * Names already reported, so a name repeated across renders or across many
501
+ * instances only warns once per page load.
502
+ */
503
+ const warnedIconNames = new Set();
504
+ /**
505
+ * True when the icon-font family is the one `DContextProvider` defaults to.
506
+ *
507
+ * The warning is limited to that case so a consumer who configured a different
508
+ * font — Material Symbols, for instance — is not told about names that font
509
+ * resolves perfectly well.
510
+ *
511
+ * It cannot tell "never configured a font" apart from "configured exactly the
512
+ * defaults", and those defaults are also the standard Bootstrap Icons setup, so
513
+ * a consumer who deliberately uses Bootstrap Icons is warned too. The message
514
+ * says so; silencing that case would need a new context flag, which is beyond
515
+ * what this helper should decide.
516
+ */
517
+ function isDefaultIconFamily(familyClass, familyPrefix) {
518
+ return familyClass === DEFAULT_ICON_FAMILY_CLASS
519
+ && familyPrefix === DEFAULT_ICON_FAMILY_PREFIX;
520
+ }
521
+ /**
522
+ * Warns, once per name and only outside production builds, that an icon name
523
+ * resolved to neither the registry nor lucide-react and is falling back to the
524
+ * default icon font, which this package no longer ships.
525
+ *
526
+ * Whether that fallback renders depends on the host app: if it loads the font
527
+ * itself the `<i>` shows the glyph, and if it does not, nothing appears. The
528
+ * message states both possibilities instead of assuming the second.
529
+ *
530
+ * The `process.env.NODE_ENV` guard is what bundlers constant-fold, so the whole
531
+ * call — and this module — drops out of a consumer's production bundle.
532
+ */
533
+ function warnUnknownIcon(name, familyClass, familyPrefix) {
534
+ if (!isDefaultIconFamily(familyClass, familyPrefix))
535
+ return;
536
+ if (warnedIconNames.has(name))
537
+ return;
538
+ warnedIconNames.add(name);
539
+ const fallbackClass = `${DEFAULT_ICON_FAMILY_CLASS} ${DEFAULT_ICON_FAMILY_PREFIX}${name}`;
540
+ // eslint-disable-next-line no-console
541
+ console.warn(`[Dynamic UI] Icon "${name}" is not in the icon registry and is not a `
542
+ + 'lucide-react export, so it falls back to the default icon font: '
543
+ + `<i class="${fallbackClass}">. That renders only if your app loads that `
544
+ + 'font, which this package stopped shipping in v2.0.0. '
545
+ + 'If the icon is missing, pass a PascalCase lucide-react name such as '
546
+ + '"Home", or register the component with '
547
+ + '<DContextProvider iconRegistry={{ MyIcon }}>. '
548
+ + `If you use the "${DEFAULT_ICON_FAMILY_CLASS}" font on purpose, this is a `
549
+ + 'false positive: the warning fires whenever the configured family equals '
550
+ + 'the package default, which is also the standard Bootstrap Icons setup. '
551
+ + 'It never appears in production builds.');
552
+ }
553
+
435
554
  function subscribeToMediaQuery(query, callback) {
436
555
  const mediaQueryList = window.matchMedia(query);
437
556
  mediaQueryList.addEventListener('change', callback);
@@ -449,9 +568,43 @@ function useMediaQuery(mediaQuery, useListener = false) {
449
568
  return React.useSyncExternalStore(useListener ? (cb) => subscribeToMediaQuery(mediaQuery, cb) : noop, () => (mediaQuery ? checkMediaQuery(mediaQuery) : true), () => false);
450
569
  }
451
570
 
452
- function useMediaBreakpointUp(breakpoint, useListener = false) {
571
+ const BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
572
+ // Breakpoints read from the CSS on first use and shared by every component
573
+ // that resolves responsive props: one computed-style read for the page,
574
+ // instead of one per breakpoint per render. `dynamic-ui.css` has to be loaded
575
+ // before rendering; if it isn't, the empty result is kept too, so a
576
+ // misconfigured tree doesn't pay the read on every render (responsive props
577
+ // then keep their default value and useResponsiveProp warns in development).
578
+ let cssBreakpoints = null;
579
+ function readCssBreakpoint(breakpoint) {
580
+ if (cssBreakpoints)
581
+ return cssBreakpoints[breakpoint];
582
+ if (typeof document === 'undefined')
583
+ return '';
584
+ const style = getComputedStyle(document.documentElement);
585
+ const read = Object.fromEntries(BREAKPOINTS.map((name) => [
586
+ name,
587
+ style.getPropertyValue(`--${PREFIX_BS}breakpoint-${name}`).trim(),
588
+ ]));
589
+ cssBreakpoints = read;
590
+ return read[breakpoint];
591
+ }
592
+ /**
593
+ * Pixel value of a breakpoint. `DContextProvider` reads them from the CSS and
594
+ * shares them through the context; outside of it (a tree without the
595
+ * provider, or its first render) they are read from the same CSS variables
596
+ * here, so responsive props resolve either way.
597
+ */
598
+ function useBreakpointValue(breakpoint) {
453
599
  const { breakpoints } = useDContext();
454
- const mediaQuery = React.useMemo(() => (`(min-width: ${breakpoints[breakpoint]})`), [breakpoint, breakpoints]);
600
+ const fromContext = breakpoints[breakpoint];
601
+ return React.useMemo(() => fromContext || readCssBreakpoint(breakpoint), [fromContext, breakpoint]);
602
+ }
603
+ function useMediaBreakpointUp(breakpoint, useListener = false) {
604
+ const value = useBreakpointValue(breakpoint);
605
+ // Without a breakpoint the query can't match; `not all` keeps it false
606
+ // instead of an invalid `(min-width: )`.
607
+ const mediaQuery = value ? `(min-width: ${value})` : 'not all';
455
608
  return useMediaQuery(mediaQuery, useListener);
456
609
  }
457
610
  function useMediaBreakpointUpXs(useListener = false) {
@@ -473,6 +626,7 @@ function useMediaBreakpointUpXxl(useListener = false) {
473
626
  return useMediaBreakpointUp('xxl', useListener);
474
627
  }
475
628
 
629
+ let warnedMissingBreakpoints = false;
476
630
  /**
477
631
  * React hook to resolve a responsive property value based on the current viewport breakpoint.
478
632
  *
@@ -498,7 +652,16 @@ function useResponsiveProp(useListener = false) {
498
652
  const bpLgUp = useMediaBreakpointUpLg(useListener);
499
653
  const bpXlUp = useMediaBreakpointUpXl(useListener);
500
654
  const bpXxlUp = useMediaBreakpointUpXxl(useListener);
655
+ // `xs` is `0` and can't tell a missing variable apart, so `sm` is checked.
656
+ const hasBreakpoints = !!useBreakpointValue('sm');
501
657
  const responsivePropValue = React.useCallback((prop) => {
658
+ if (process.env.NODE_ENV !== 'production' && !hasBreakpoints && !warnedMissingBreakpoints) {
659
+ warnedMissingBreakpoints = true;
660
+ // eslint-disable-next-line no-console
661
+ console.warn(`[Dynamic UI] The --${PREFIX_BS}breakpoint-* CSS variables are not available, so a `
662
+ + 'responsive prop (an object by breakpoint) falls back to its default value. Load '
663
+ + 'dynamic-ui.css before rendering. It never appears in production builds.');
664
+ }
502
665
  // Pick the highest matched breakpoint value that is defined in prop
503
666
  if (prop.xxl !== undefined && bpXxlUp)
504
667
  return prop.xxl;
@@ -514,14 +677,38 @@ function useResponsiveProp(useListener = false) {
514
677
  return prop.xs;
515
678
  // Fallback: return undefined if no breakpoint matches
516
679
  return undefined;
517
- }, [bpSmUp, bpMdUp, bpLgUp, bpXlUp, bpXxlUp, bpXsUp]);
680
+ }, [bpSmUp, bpMdUp, bpLgUp, bpXlUp, bpXxlUp, bpXsUp, hasBreakpoints]);
518
681
  return { responsivePropValue };
519
682
  }
520
683
 
521
684
  function isIconComponent(value) {
522
685
  return typeof value !== 'string' && reactIs.isValidElementType(value);
523
686
  }
524
- function DIconBase({ icon, color, style, className, size, useListenerSize = false, hasCircle = false, materialStyle = false, familyClass, familyPrefix, strokeWidth = 2, dataAttributes, }) {
687
+ /**
688
+ * Renders an icon, resolving the `icon` prop in this order:
689
+ *
690
+ * 1. **Icon registry** — a string name found in `DContextProvider`'s
691
+ * `iconRegistry` renders the component registered under it. A component
692
+ * passed directly skips this step and renders as-is.
693
+ * 2. **lucide-react** — a PascalCase name exported by lucide-react renders that
694
+ * icon. When `materialStyle` is on, this step is skipped and the name is
695
+ * emitted as the text content of the icon-font element instead.
696
+ * 3. **Icon-font family** — anything left over renders as
697
+ * `<i class="{familyClass} {familyPrefix}{name}">`, the legacy path for
698
+ * Material Symbols and Bootstrap Icons. Outside production builds, a name
699
+ * that reaches this step while the family is still the package default logs
700
+ * a one-time warning.
701
+ *
702
+ * A name that resolves nowhere and has no family configured renders `?`.
703
+ *
704
+ * Every path is hidden from the accessibility tree by default (`aria-hidden`),
705
+ * since icons are overwhelmingly decorative. See `ariaHidden` / `ariaLabel` to
706
+ * override that for an icon that carries meaning on its own.
707
+ */
708
+ function DIconBase({ icon: iconProp, color, style, className, size, useListenerSize = false, hasCircle = false, materialStyle = false, familyClass, familyPrefix, strokeWidth = 2, ariaHidden, ariaLabel, dataAttributes, }) {
709
+ const { iconRegistry } = useDContext();
710
+ // The registry wins over every other source, including Material icons.
711
+ const icon = resolveIconFromRegistry(iconProp, iconRegistry);
525
712
  // If materialStyle is true, use Material Design icons (legacy)
526
713
  const isStringIcon = typeof icon === 'string';
527
714
  const useMaterialIcons = materialStyle && isStringIcon;
@@ -543,6 +730,61 @@ function DIconBase({ icon, color, style, className, size, useListenerSize = fals
543
730
  }, [responsivePropValue, size]);
544
731
  const generateStyleVariables = React.useMemo(() => (Object.assign(Object.assign(Object.assign({}, resolvedSize && { [`--${PREFIX_BS}icon-component-size`]: resolvedSize }), hasCircle && { [`--${PREFIX_BS}icon-component-padding`]: `calc(var(--${PREFIX_BS}icon-component-size, 24px) * 0.4)` }), style)), [resolvedSize, hasCircle, style]);
545
732
  const generateClasses = React.useMemo(() => (Object.assign(Object.assign(Object.assign({ 'd-icon': true }, className && { [className]: true }), { 'd-icon-has-circle': hasCircle }), color && { [`d-icon-color-${color}`]: true })), [className, hasCircle, color]);
733
+ /**
734
+ * Decorative by default. `ariaHidden` and `ariaLabel` are not symmetric:
735
+ * hiding is absolute, so `ariaHidden` wins over a name, but `ariaHidden={false}`
736
+ * and `ariaLabel` agree — both expose the icon — and the name is honoured.
737
+ */
738
+ const accessibilityProps = React.useMemo(() => {
739
+ // Hiding is absolute: it wins over a name.
740
+ if (ariaHidden === true)
741
+ return { 'aria-hidden': true };
742
+ // A name exposes the icon, which is also what `ariaHidden={false}` asks for.
743
+ if (ariaLabel)
744
+ return { role: 'img', 'aria-label': ariaLabel };
745
+ // Exposed with no name: an explicit opt-out of the default, warned about below.
746
+ if (ariaHidden === false)
747
+ return {};
748
+ return { 'aria-hidden': true };
749
+ }, [ariaHidden, ariaLabel]);
750
+ /**
751
+ * `dataAttributes` is typed to `data-*`, but casting through it was the only
752
+ * way to reach the DOM before `ariaHidden` existed, so it still wins over the
753
+ * computed values — and therefore decides the effective state below.
754
+ */
755
+ const domAttributes = React.useMemo(() => (Object.assign(Object.assign({}, accessibilityProps), dataAttributes)), [accessibilityProps, dataAttributes]);
756
+ /**
757
+ * lucide-react hides its own `<svg>` unless it receives an a11y prop, and a
758
+ * registry component may do the same, which would keep the graphic out of the
759
+ * tree even when this wrapper is not hidden. Only matters when the icon is
760
+ * exposed without a name: under `role="img"` the wrapper is a leaf, so a
761
+ * hidden child changes nothing.
762
+ *
763
+ * A registry component that hardcodes `aria-hidden` on its own `<svg>` instead
764
+ * of spreading props still wins — nothing here can reach inside it.
765
+ */
766
+ const isExposedWithoutName = React.useMemo(() => {
767
+ const attributes = domAttributes;
768
+ const hidden = attributes['aria-hidden'];
769
+ return hidden !== true && hidden !== 'true' && !attributes.role;
770
+ }, [domAttributes]);
771
+ /**
772
+ * Diagnostics read the effective state, so a `dataAttributes` override that
773
+ * hides the icon after all is not reported as exposing it. Memoised like the
774
+ * values above, so re-rendering with the same props does not repeat them.
775
+ */
776
+ React.useMemo(() => {
777
+ if (process.env.NODE_ENV === 'production')
778
+ return;
779
+ if (ariaHidden === true && ariaLabel) {
780
+ // eslint-disable-next-line no-console
781
+ console.warn(`DIcon: ariaLabel "${ariaLabel}" is ignored because ariaHidden is true. Drop ariaHidden to expose the name.`);
782
+ }
783
+ if (ariaHidden === false && isExposedWithoutName) {
784
+ // eslint-disable-next-line no-console
785
+ console.warn('DIcon: ariaHidden={false} without an ariaLabel exposes an unnamed graphic to assistive technology. Pass ariaLabel to name it.');
786
+ }
787
+ }, [ariaHidden, ariaLabel, isExposedWithoutName]);
546
788
  const iconSize = React.useMemo(() => {
547
789
  if (resolvedSize) {
548
790
  const numSize = parseInt(resolvedSize, 10);
@@ -552,43 +794,46 @@ function DIconBase({ icon, color, style, className, size, useListenerSize = fals
552
794
  }, [resolvedSize]);
553
795
  // Render Material Design icon (legacy support)
554
796
  if (useMaterialIcons) {
555
- return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: isStringIcon ? icon : null })));
797
+ return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, domAttributes, { children: isStringIcon ? icon : null })));
556
798
  }
557
799
  if (isIconComponent(icon)) {
558
- return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: React.createElement(icon, {
559
- width: resolvedSize || 24,
560
- height: resolvedSize || 24,
561
- strokeWidth,
562
- }) })));
800
+ return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, domAttributes, { children: React.createElement(icon, Object.assign({ width: resolvedSize || 24, height: resolvedSize || 24, strokeWidth }, isExposedWithoutName && { 'aria-hidden': false })) })));
563
801
  }
564
802
  // Render Lucide icon
565
803
  if (!LucideIcon) {
566
- if (isStringIcon && familyClass && familyPrefix) {
567
- return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass, `${familyPrefix}${icon}`), style: generateStyleVariables }, dataAttributes)));
804
+ if (typeof icon === 'string' && familyClass && familyPrefix) {
805
+ if (process.env.NODE_ENV !== 'production') {
806
+ warnUnknownIcon(icon, familyClass, familyPrefix);
807
+ }
808
+ return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass, `${familyPrefix}${icon}`), style: generateStyleVariables }, domAttributes)));
568
809
  }
569
810
  // eslint-disable-next-line no-console
570
811
  console.warn(`Icon "${String(icon)}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
571
- return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: "?" })));
812
+ return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, domAttributes, { children: "?" })));
572
813
  }
573
- return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: jsxRuntime.jsx(LucideIcon, { size: iconSize || 24, strokeWidth: strokeWidth }) })));
814
+ return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, domAttributes, { children: jsxRuntime.jsx(LucideIcon, Object.assign({ size: iconSize || 24, strokeWidth: strokeWidth }, isExposedWithoutName && { 'aria-hidden': false })) })));
574
815
  }
575
816
 
817
+ /**
818
+ * Thin wrapper over `DIconBase` that fills the icon-font configuration
819
+ * (`familyClass`, `familyPrefix`, `materialStyle`) from `DContextProvider`
820
+ * whenever the caller does not pass it explicitly.
821
+ *
822
+ * Name resolution itself — registry, then lucide-react, then the icon-font
823
+ * family — lives in `DIconBase`, so both components behave identically.
824
+ */
576
825
  function DIcon(_a) {
577
826
  var { icon, familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = tslib.__rest(_a, ["icon", "familyClass", "familyPrefix", "materialStyle"]);
578
- const { icon: { familyClass, familyPrefix, materialStyle, }, iconRegistry, } = useDContext();
579
- const registryIcon = typeof icon === 'string'
580
- ? iconRegistry === null || iconRegistry === void 0 ? void 0 : iconRegistry[icon]
581
- : undefined;
582
- const resolvedIcon = registryIcon || icon;
583
- return (jsxRuntime.jsx(DIconBase, Object.assign({ icon: resolvedIcon, familyClass: propFamilyClass !== null && propFamilyClass !== void 0 ? propFamilyClass : familyClass, familyPrefix: propFamilyPrefix !== null && propFamilyPrefix !== void 0 ? propFamilyPrefix : familyPrefix, materialStyle: propMaterialStyle !== null && propMaterialStyle !== void 0 ? propMaterialStyle : materialStyle }, props)));
827
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
828
+ return (jsxRuntime.jsx(DIconBase, Object.assign({ icon: icon, familyClass: propFamilyClass !== null && propFamilyClass !== void 0 ? propFamilyClass : familyClass, familyPrefix: propFamilyPrefix !== null && propFamilyPrefix !== void 0 ? propFamilyPrefix : familyPrefix, materialStyle: propMaterialStyle !== null && propMaterialStyle !== void 0 ? propMaterialStyle : materialStyle }, props)));
584
829
  }
585
830
 
586
- function DAlert({ color = 'success', icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle, showClose, onClose, children, id, className, style, dataAttributes, }) {
831
+ function DAlert({ color = 'success', role = 'alert', icon: iconProp, showIcon = true, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle, showClose, closeAriaLabel = 'Close', onClose, children, id, className, style, dataAttributes, }) {
587
832
  const { icon: { materialStyle, familyClass, familyPrefix, }, iconMap: { alert, xLg, }, } = useDContext();
588
833
  const icon = React.useMemo(() => iconProp || alert[color], [alert, iconProp, color]);
589
834
  const iconClose = React.useMemo(() => (iconCloseProp || xLg), [iconCloseProp, xLg]);
590
835
  const generateClasses = React.useMemo(() => (Object.assign({ alert: true, [`alert-${color}`]: true, 'fade show': !!showClose }, className && { [className]: true })), [color, showClose, className]);
591
- return (jsxRuntime.jsxs("div", Object.assign({ className: classNames(generateClasses), style: style, role: "alert", id: id }, dataAttributes, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "alert-icon", icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })), jsxRuntime.jsx("div", { className: "alert-text", children: children }), showClose && (jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: iconClose, familyClass: iconCloseFamilyClass !== null && iconCloseFamilyClass !== void 0 ? iconCloseFamilyClass : familyClass, familyPrefix: iconCloseFamilyPrefix !== null && iconCloseFamilyPrefix !== void 0 ? iconCloseFamilyPrefix : familyPrefix, materialStyle: iconCloseMaterialStyle !== null && iconCloseMaterialStyle !== void 0 ? iconCloseMaterialStyle : materialStyle }) }))] })));
836
+ return (jsxRuntime.jsxs("div", Object.assign({ className: classNames(generateClasses), style: style }, role !== 'none' && { role }, { id: id }, dataAttributes, { children: [showIcon && icon && (jsxRuntime.jsx(DIcon, { className: "alert-icon", icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })), jsxRuntime.jsx("div", { className: "alert-text", children: children }), showClose && (jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": closeAriaLabel, onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: iconClose, familyClass: iconCloseFamilyClass !== null && iconCloseFamilyClass !== void 0 ? iconCloseFamilyClass : familyClass, familyPrefix: iconCloseFamilyPrefix !== null && iconCloseFamilyPrefix !== void 0 ? iconCloseFamilyPrefix : familyPrefix, materialStyle: iconCloseMaterialStyle !== null && iconCloseMaterialStyle !== void 0 ? iconCloseMaterialStyle : materialStyle }) }))] })));
592
837
  }
593
838
 
594
839
  function DAvatar({ id, size, image, name: nameProp, useNameAsInitials = false, className, style, dataAttributes, }) {
@@ -654,6 +899,223 @@ function useProvidedRefOrCreate(providedRef) {
654
899
  return providedRef !== null && providedRef !== void 0 ? providedRef : createdRef;
655
900
  }
656
901
 
902
+ /**
903
+ * Descendants the browser already exempts from a label's click forwarding.
904
+ *
905
+ * Per the HTML spec a label does nothing for events targeted at its interactive
906
+ * content descendants, so these need — and must get — no help from us: calling
907
+ * `preventDefault` on a click targeting a link, or on an image map area, would
908
+ * cancel the navigation it was meant to perform.
909
+ *
910
+ * The list mirrors the spec's interactive content, image maps included, since
911
+ * one of those carrying a `tabindex` would otherwise fall through to the
912
+ * pseudo-interactive branch below and be suppressed by mistake.
913
+ */
914
+ const NATIVE_INTERACTIVE = 'a[href], area[href], button, input, select, textarea, details, summary, img[usemap], object[usemap], audio[controls], video[controls], embed, iframe';
915
+ /**
916
+ * Descendants that act like controls without being interactive content.
917
+ *
918
+ * A tooltip or modal trigger built from a `span` with a role and a tabindex is
919
+ * the common case. Browsers do not exempt these, so a click on one activates
920
+ * the labelled control as well.
921
+ */
922
+ const PSEUDO_INTERACTIVE = '[role="button"], [role="link"], [role="checkbox"], [role="switch"], [role="menuitem"], [tabindex]';
923
+ /**
924
+ * Stops a click on a non-native trigger inside a `<label>` from also activating
925
+ * the labelled control.
926
+ *
927
+ * Verified against Chromium, which forwards the click for every one of the
928
+ * `PSEUDO_INTERACTIVE` selectors above while exempting the native ones. Note
929
+ * that jsdom does not reproduce this: its `isInteractiveContent` counts any
930
+ * element carrying a `tabindex` as interactive content, so a jsdom test passes
931
+ * whether or not this guard exists. That is why the guard is unit-tested
932
+ * directly rather than through a rendered component.
933
+ *
934
+ * Native interactive content returns early rather than falling through, since
935
+ * `preventDefault` on those clicks would cancel the link navigation or the
936
+ * image map the label is meant to let through.
937
+ *
938
+ * `DFormLabel` wires this in the capture phase so a trigger that stops
939
+ * propagation cannot skip it.
940
+ */
941
+ function labelClickGuard(event) {
942
+ const label = event.currentTarget;
943
+ const target = event.target;
944
+ if (!(target === null || target === void 0 ? void 0 : target.closest))
945
+ return;
946
+ /**
947
+ * `closest` walks the whole ancestor chain, so a match may sit above the
948
+ * label — a card with a `role` or a `tabindex` wrapping the control and its
949
+ * label, say. Acting on one of those would suppress the plain label text's
950
+ * own click and leave the control unresponsive, so only matches inside the
951
+ * label count.
952
+ */
953
+ const inside = (match) => (!!match && match !== label && label.contains(match));
954
+ if (inside(target.closest(NATIVE_INTERACTIVE)))
955
+ return;
956
+ if (inside(target.closest(PSEUDO_INTERACTIVE))) {
957
+ event.preventDefault();
958
+ }
959
+ }
960
+
961
+ /**
962
+ * The `<label>` every form control renders, with the click guard already wired.
963
+ *
964
+ * Internal: it exists so the guard — and the lint exception it needs — lives in
965
+ * one place instead of being repeated across every input component.
966
+ *
967
+ * The guard runs in the capture phase, not on bubble: a tooltip or modal
968
+ * trigger that calls `stopPropagation()` in its own handler — a common pattern —
969
+ * would otherwise keep the guard from ever running, and the browser would go on
970
+ * to activate the labelled control. Confirmed in Chromium, where the same
971
+ * trigger toggles the control with a bubble-phase guard and does not with this
972
+ * one. Capturing changes nothing else: `preventDefault` applies to the default
973
+ * action, which is evaluated after propagation either way.
974
+ *
975
+ * The `jsx-a11y` rules below assume a click handler is being used to make a
976
+ * non-interactive element interactive, which then owes the keyboard an
977
+ * equivalent. This handler does the opposite: it suppresses a pointer-only
978
+ * behaviour, the click a label forwards to its control, and adds no way to
979
+ * activate anything. Keyboard support for a nested trigger is the consumer's:
980
+ * `<a href>` carries its own, while a `span` with a role and a tabindex is
981
+ * focusable but inert until they handle Enter and Space themselves.
982
+ */
983
+ function DFormLabel({ htmlFor, className, children }) {
984
+ /**
985
+ * React 19 renders a bigint child; React 18 throws on one, and this package
986
+ * supports both (`react: >=18 <20`). The label helpers count bigint as text
987
+ * because that is what it draws as, so it is turned into its string form here
988
+ * — the one place every control's label passes through — rather than pushing
989
+ * a version check onto consumers.
990
+ *
991
+ * Only a bigint at the top level: one buried inside an array or a fragment is
992
+ * the consumer's own React child, and normalising it would mean walking and
993
+ * rebuilding the whole subtree.
994
+ */
995
+ const content = typeof children === 'bigint' ? String(children) : children;
996
+ return (
997
+ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events,
998
+ jsx-a11y/no-noninteractive-element-interactions */
999
+ jsxRuntime.jsx("label", { htmlFor: htmlFor, className: className, onClickCapture: labelClickGuard, children: content }));
1000
+ }
1001
+
1002
+ /**
1003
+ * Whether a `label` node has something to render.
1004
+ *
1005
+ * The render paths cannot test `label` for truthiness: since the prop widened
1006
+ * to `ReactNode` it also accepts `0`, which React renders as the text "0" and
1007
+ * which `isTextLabel` counts as a text label, yet which a truthiness check
1008
+ * would drop — leaving the control visibly unlabelled.
1009
+ *
1010
+ * Absent (`undefined`, `null`), empty (`''`) and boolean labels render nothing,
1011
+ * so they are the ones that stand for "no label". The empty string matters
1012
+ * because it is the default several components fall back to.
1013
+ *
1014
+ * Arrays are inspected rather than taken at face value: `label={items.map(...)}`
1015
+ * over an empty list yields `[]`, while a list of skipped entries yields
1016
+ * `[false, null]`. Both render nothing, so treating them as content would leave
1017
+ * an empty `<label>` on the page — and make the naming warning fire over a
1018
+ * label that was never there.
1019
+ *
1020
+ * Only arrays, though, even though `ReactNode` admits any iterable. Reading a
1021
+ * one-shot iterator — a generator — would exhaust it here, and the component
1022
+ * goes on to render the same value, so the label would vanish precisely because
1023
+ * we checked for it. An array can be walked as often as needed; anything else
1024
+ * is taken on faith.
1025
+ *
1026
+ * Anything else — an element, a portal, a generator — counts as content: what
1027
+ * it renders is only knowable by rendering it, and an element that draws
1028
+ * nothing visible is the consumer's decision rather than an absent label.
1029
+ */
1030
+ function hasLabelContent(label) {
1031
+ if (label === undefined || label === null || typeof label === 'boolean') {
1032
+ return false;
1033
+ }
1034
+ if (typeof label === 'string') {
1035
+ return label !== '';
1036
+ }
1037
+ if (React.isValidElement(label)) {
1038
+ return true;
1039
+ }
1040
+ if (Array.isArray(label)) {
1041
+ return label.some(hasLabelContent);
1042
+ }
1043
+ return true;
1044
+ }
1045
+
1046
+ /**
1047
+ * Narrows a `label` node to the plain-text case.
1048
+ *
1049
+ * `label` accepts any `ReactNode` so patterns like a terms-and-conditions link
1050
+ * or an info trigger can live next to the field name. Anything that is not text
1051
+ * cannot be forwarded to a string-only attribute (`aria-label`, `placeholder`)
1052
+ * nor laid out by Bootstrap's `form-floating`, so those paths check this first.
1053
+ *
1054
+ * Numbers and bigints count as text: React renders them as their string form
1055
+ * and so does `String()`, which is what the string-only attributes end up
1056
+ * receiving. `bigint` is part of `ReactNode` under these React types, so
1057
+ * leaving it out made a label React draws as text get treated as a rich node.
1058
+ * React 18 cannot render one, which `DFormLabel` handles by rendering its
1059
+ * string form — the classification stays true for every supported React.
1060
+ */
1061
+ function isTextLabel(label) {
1062
+ return typeof label === 'string'
1063
+ || typeof label === 'number'
1064
+ || typeof label === 'bigint';
1065
+ }
1066
+
1067
+ /**
1068
+ * Components already reported, keyed by component and reason, so the same
1069
+ * mistake only warns once per page load however often it re-renders or however
1070
+ * many instances exist.
1071
+ */
1072
+ const warnedLabels = new Set();
1073
+ function warnOnce(key, message) {
1074
+ if (warnedLabels.has(key))
1075
+ return;
1076
+ warnedLabels.add(key);
1077
+ // eslint-disable-next-line no-console
1078
+ console.warn(`[Dynamic UI] ${message} It never appears in production builds.`);
1079
+ }
1080
+ /**
1081
+ * Warns, only outside production builds, about the two ways a non-text `label`
1082
+ * goes wrong.
1083
+ *
1084
+ * A text label doubles as the control's accessible name. A `ReactNode` one does
1085
+ * not: the name becomes whatever the subtree happens to compute to, which for a
1086
+ * label carrying a link or an icon trigger reads as the wrong thing or as
1087
+ * nothing at all — so an explicit name is needed. Which prop carries it differs
1088
+ * per component, hence `accessibleNameProp`: naming the wrong one would send
1089
+ * the developer to a prop their component rejects.
1090
+ *
1091
+ * `form-floating` is the second case: it animates a single line of text between
1092
+ * the placeholder and the label position, so a node with its own height or its
1093
+ * own interactive children breaks the layout rather than the semantics.
1094
+ *
1095
+ * Labels the render paths drop are skipped through the same `hasLabelContent`
1096
+ * they use, so `label={condition && <span />}` with a false condition warns
1097
+ * about nothing: there is no label on the page to name.
1098
+ *
1099
+ * The `process.env.NODE_ENV` guard belongs at the call site — that is what
1100
+ * bundlers constant-fold, dropping this module from a consumer's production
1101
+ * bundle.
1102
+ */
1103
+ function warnLabelUsage({ component, label, hasAccessibleName, accessibleNameProp, floatingLabel = false, }) {
1104
+ if (!hasLabelContent(label) || isTextLabel(label))
1105
+ return;
1106
+ if (!hasAccessibleName) {
1107
+ warnOnce(`${component}:name`, `${component}: a non-text "label" does not give the control a reliable accessible name. `
1108
+ + `Pass ${accessibleNameProp} with the plain-text name of the field, since the label `
1109
+ + 'subtree may include links, icons or markup that read as the wrong name or as none at all.');
1110
+ }
1111
+ if (floatingLabel) {
1112
+ warnOnce(`${component}:floating`, `${component}: "floatingLabel" expects a text "label". `
1113
+ + 'The floating layout animates a single line of text, so a node with its own height '
1114
+ + 'or its own interactive children overflows the control. '
1115
+ + 'Use the default label layout for rich labels.');
1116
+ }
1117
+ }
1118
+
657
1119
  function DInput(_a, ref) {
658
1120
  var { id: idProp, style, className, label = '', disabled = false, loading = false, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, iconStart, iconStartDisabled, iconStartFamilyClass, iconStartFamilyPrefix, iconStartAriaLabel, iconStartTabIndex, iconStartMaterialStyle, iconEnd, iconEndDisabled, iconEndFamilyClass, iconEndFamilyPrefix, iconEndAriaLabel, iconEndTabIndex, iconEndMaterialStyle, hint, size, invalid = false, valid = false, floatingLabel = false, inputStart, inputEnd, value, placeholder = '', dataAttributes, readonly, onChange, onIconStartClick, onIconEndClick } = _a, inputProps = tslib.__rest(_a, ["id", "style", "className", "label", "disabled", "loading", "iconFamilyClass", "iconFamilyPrefix", "iconMaterialStyle", "iconStart", "iconStartDisabled", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartAriaLabel", "iconStartTabIndex", "iconStartMaterialStyle", "iconEnd", "iconEndDisabled", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndAriaLabel", "iconEndTabIndex", "iconEndMaterialStyle", "hint", "size", "invalid", "valid", "floatingLabel", "inputStart", "inputEnd", "value", "placeholder", "dataAttributes", "readonly", "onChange", "onIconStartClick", "onIconEndClick"]);
659
1121
  const inputRef = useProvidedRefOrCreate(ref);
@@ -693,7 +1155,7 @@ function DInput(_a, ref) {
693
1155
  const inputComponent = React.useMemo(() => (jsxRuntime.jsx("input", Object.assign({ ref: inputRef, id: id, className: classNames('form-control', {
694
1156
  'is-invalid': invalid,
695
1157
  'is-valid': valid,
696
- }), disabled: disabled || loading, readOnly: readonly, value: value, onChange: handleOnChange }, (floatingLabel || placeholder) && { placeholder: floatingLabel ? '' : placeholder }, ariaDescribedby && { 'aria-describedby': ariaDescribedby }, inputProps))), [
1158
+ }), disabled: disabled || loading, readOnly: readonly, value: value, onChange: handleOnChange }, (floatingLabel || placeholder) && { placeholder: floatingLabel ? '' : placeholder }, ariaDescribedby && { 'aria-describedby': ariaDescribedby }, invalid && { 'aria-invalid': true }, inputProps))), [
697
1159
  ariaDescribedby,
698
1160
  disabled,
699
1161
  handleOnChange,
@@ -708,7 +1170,7 @@ function DInput(_a, ref) {
708
1170
  value,
709
1171
  readonly,
710
1172
  ]);
711
- const labelComponent = React.useMemo(() => (jsxRuntime.jsx("label", { htmlFor: id, children: label })), [
1173
+ const labelComponent = React.useMemo(() => (jsxRuntime.jsx(DFormLabel, { htmlFor: id, children: label })), [
712
1174
  id,
713
1175
  label,
714
1176
  ]);
@@ -718,11 +1180,20 @@ function DInput(_a, ref) {
718
1180
  }
719
1181
  return inputComponent;
720
1182
  }, [floatingLabel, inputComponent, labelComponent]);
721
- return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [label && !floatingLabel && labelComponent, jsxRuntime.jsxs("div", { className: classNames({
1183
+ if (process.env.NODE_ENV !== 'production') {
1184
+ warnLabelUsage({
1185
+ component: 'DInput',
1186
+ label,
1187
+ hasAccessibleName: !!inputProps['aria-label'] || !!inputProps['aria-labelledby'],
1188
+ accessibleNameProp: 'aria-label',
1189
+ floatingLabel,
1190
+ });
1191
+ }
1192
+ return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [hasLabelContent(label) && !floatingLabel && labelComponent, jsxRuntime.jsxs("div", { className: classNames({
722
1193
  [`input-group-${size}`]: !!size,
723
1194
  'input-group': true,
724
1195
  'has-validation': invalid || valid,
725
- }), children: [!!inputStart && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputStart`, children: inputStart })), iconStart && (onIconStartClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: handleOnIconStartClick, disabled: disabled || loading || iconStartDisabled, "aria-label": iconStartAriaLabel || (typeof iconStart === 'string' ? iconStart : 'start icon'), tabIndex: iconStartTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Start`, "aria-hidden": "true", tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle }) }))), dynamicComponent, (iconEnd && !loading) && (onIconEndClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: handleOnIconEndClick, disabled: disabled || loading || iconEndDisabled, "aria-label": iconEndAriaLabel || (typeof iconEnd === 'string' ? iconEnd : 'end icon'), tabIndex: iconEndTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}End`, "aria-hidden": "true", tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) }))), loading && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Loading`, children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", "data-testid": "loading-spinner", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) })), !!inputEnd && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputEnd`, children: inputEnd }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
1196
+ }), children: [!!inputStart && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputStart`, children: inputStart })), iconStart && (onIconStartClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: handleOnIconStartClick, disabled: disabled || loading || iconStartDisabled, "aria-label": iconStartAriaLabel || (typeof iconStart === 'string' ? iconStart : 'start icon'), tabIndex: iconStartTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Start`, tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle }) }))), dynamicComponent, (iconEnd && !loading) && (onIconEndClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: handleOnIconEndClick, disabled: disabled || loading || iconEndDisabled, "aria-label": iconEndAriaLabel || (typeof iconEnd === 'string' ? iconEnd : 'end icon'), tabIndex: iconEndTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}End`, tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) }))), loading && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Loading`, children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", "data-testid": "loading-spinner", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) })), !!inputEnd && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputEnd`, children: inputEnd }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
726
1197
  }
727
1198
  const ForwardedDInput = React.forwardRef(DInput);
728
1199
  ForwardedDInput.displayName = 'DInput';
@@ -1205,8 +1676,46 @@ const DButton = React.forwardRef((props, ref) => {
1205
1676
  });
1206
1677
  DButton.displayName = 'DButton';
1207
1678
 
1679
+ /**
1680
+ * Controls already reported, keyed by kind and icon, so the same unlabelled one
1681
+ * only warns once per page load however often it re-renders or however many
1682
+ * instances exist.
1683
+ */
1684
+ const warnedControls = new Set();
1685
+ /**
1686
+ * Warns, once per icon and only outside production builds, that an icon-only
1687
+ * button has no accessible name.
1688
+ *
1689
+ * The control has no text to fall back on and its icon is hidden from the
1690
+ * accessibility tree, so the name has to be given explicitly. `aria-label` is
1691
+ * the direct way; on a button `aria-labelledby` and `title` count too, since
1692
+ * `rest` reaches the DOM there, while an anchor drops them — hence `kind`, so
1693
+ * the advice matches what the element can actually receive.
1694
+ *
1695
+ * A legacy icon-font icon used to leak its ligature as the name by accident —
1696
+ * never a good one, being unlocalised and often meaningless ("more_vert") — and
1697
+ * no longer does, so the omission is now visible instead of silent.
1698
+ *
1699
+ * The `process.env.NODE_ENV` guard is what bundlers constant-fold, so the whole
1700
+ * call — and this module — drops out of a consumer's production bundle.
1701
+ */
1702
+ function warnMissingAccessibleName(icon, kind) {
1703
+ const key = `${kind}:${icon}`;
1704
+ if (warnedControls.has(key))
1705
+ return;
1706
+ warnedControls.add(key);
1707
+ const alternatives = kind === 'button'
1708
+ ? 'On a button, aria-labelledby or title work too. '
1709
+ : 'A link renders from the href branch, which does not forward aria-labelledby or title, so aria-label is the only option. ';
1710
+ // eslint-disable-next-line no-console
1711
+ console.warn(`[Dynamic UI] DButtonIcon: the "${icon}" ${kind} has no accessible name. `
1712
+ + 'Pass aria-label, since an icon-only control has no text to fall back on '
1713
+ + `and its icon is hidden from the accessibility tree. ${alternatives}`
1714
+ + 'It never appears in production builds.');
1715
+ }
1716
+
1208
1717
  function DButtonIcon(_a) {
1209
- var { id, icon, size, className, variant, state, loadingAriaLabel, iconMaterialStyle, disabled = false, color = 'primary', loading = false, href, target, rel, stopPropagationEnabled = true, style, iconFamilyClass, iconFamilyPrefix, dataAttributes, onClick, 'aria-label': ariaLabelProp } = _a, rest = tslib.__rest(_a, ["id", "icon", "size", "className", "variant", "state", "loadingAriaLabel", "iconMaterialStyle", "disabled", "color", "loading", "href", "target", "rel", "stopPropagationEnabled", "style", "iconFamilyClass", "iconFamilyPrefix", "dataAttributes", "onClick", 'aria-label']);
1718
+ var { id, icon, size, iconSize, className, variant, state, loadingAriaLabel, iconMaterialStyle, disabled = false, color = 'primary', loading = false, href, target, rel, stopPropagationEnabled = true, style, iconFamilyClass, iconFamilyPrefix, dataAttributes, onClick, 'aria-label': ariaLabelProp } = _a, rest = tslib.__rest(_a, ["id", "icon", "size", "iconSize", "className", "variant", "state", "loadingAriaLabel", "iconMaterialStyle", "disabled", "color", "loading", "href", "target", "rel", "stopPropagationEnabled", "style", "iconFamilyClass", "iconFamilyPrefix", "dataAttributes", "onClick", 'aria-label']);
1210
1719
  const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
1211
1720
  const generateClasses = React.useMemo(() => {
1212
1721
  const variantClass = !variant || variant === 'solid'
@@ -1214,6 +1723,9 @@ function DButtonIcon(_a) {
1214
1723
  : `btn-${variant}-${color}`;
1215
1724
  return Object.assign(Object.assign(Object.assign({ 'btn d-button-icon': true, [variantClass]: true }, size && { [`btn-${size}`]: true }), (state && state !== 'disabled') && { [state]: true }), { loading });
1216
1725
  }, [variant, color, size, state, loading]);
1726
+ // A responsive iconSize has to follow viewport changes, so DIcon only
1727
+ // listens to breakpoints when it gets an object; a plain string needs none.
1728
+ const useIconSizeListener = typeof iconSize === 'object';
1217
1729
  const isDisabled = React.useMemo(() => (state === 'disabled' || loading || disabled), [state, loading, disabled]);
1218
1730
  const clickHandler = React.useCallback((event) => {
1219
1731
  if (stopPropagationEnabled) {
@@ -1228,14 +1740,23 @@ function DButtonIcon(_a) {
1228
1740
  const ariaLabel = React.useMemo(() => (loading
1229
1741
  ? loadingAriaLabel || ariaLabelProp
1230
1742
  : ariaLabelProp), [loading, loadingAriaLabel, ariaLabelProp]);
1743
+ /**
1744
+ * `aria-labelledby` and `title` also name a control, but they travel in `rest`,
1745
+ * which only the button branch spreads — an anchor drops them, so there the
1746
+ * button really is unnamed and the warning still applies.
1747
+ */
1748
+ const hasAccessibleName = React.useMemo(() => !!ariaLabel || (!href && (!!rest['aria-labelledby'] || !!rest.title)), [ariaLabel, href, rest]);
1749
+ if (process.env.NODE_ENV !== 'production' && !hasAccessibleName) {
1750
+ warnMissingAccessibleName(icon, href ? 'link' : 'button');
1751
+ }
1231
1752
  if (href) {
1232
1753
  return (jsxRuntime.jsx("a", Object.assign({ id: id, href: href, target: target, rel: rel, className: classNames(generateClasses, className), style: style, onClick: clickHandler, "aria-label": ariaLabel, "aria-disabled": isDisabled }, dataAttributes, { children: loading
1233
1754
  ? (jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }))
1234
- : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })) })));
1755
+ : (jsxRuntime.jsx(DIcon, { icon: icon, size: iconSize, useListenerSize: useIconSizeListener, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })) })));
1235
1756
  }
1236
1757
  return (jsxRuntime.jsx("button", Object.assign({ className: classNames(generateClasses, className), style: style, disabled: state === 'disabled' || loading, onClick: clickHandler, "aria-label": ariaLabel }, dataAttributes, rest, { children: loading
1237
1758
  ? (jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }))
1238
- : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })) })));
1759
+ : (jsxRuntime.jsx(DIcon, { icon: icon, size: iconSize, useListenerSize: useIconSizeListener, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })) })));
1239
1760
  }
1240
1761
 
1241
1762
  function DCardHeader({ className, style, children, }) {
@@ -1431,24 +1952,102 @@ function DSelectPlaceholder(_a) {
1431
1952
  return (jsxRuntime.jsx(Select.components.Placeholder, Object.assign({ innerProps: innerProps, selectProps: selectProps }, props, { children: children })));
1432
1953
  }
1433
1954
 
1955
+ /**
1956
+ * Name of the elements an `aria-labelledby` points at, joined as the accessible
1957
+ * name computation does. Read when the message is built rather than when the
1958
+ * select renders, since the referenced elements may render after it.
1959
+ *
1960
+ * Each element contributes its own `aria-label` when it has one, and its text
1961
+ * otherwise. That covers the targets a select is labelled by in practice — a
1962
+ * heading, a span, a labelled region — without shipping the full accessible
1963
+ * name algorithm; names derived from descendants' `aria-label`, `alt` or
1964
+ * hidden content are not resolved, so pass `ariaLiveMessages` for those.
1965
+ */
1966
+ function resolveLabelledBy(labelledBy) {
1967
+ if (typeof document === 'undefined')
1968
+ return '';
1969
+ return labelledBy
1970
+ .split(/\s+/)
1971
+ .map((ref) => {
1972
+ var _a, _b;
1973
+ const element = document.getElementById(ref);
1974
+ return ((_a = element === null || element === void 0 ? void 0 : element.getAttribute('aria-label')) === null || _a === void 0 ? void 0 : _a.trim()) || ((_b = element === null || element === void 0 ? void 0 : element.textContent) === null || _b === void 0 ? void 0 : _b.trim());
1975
+ })
1976
+ .filter(Boolean)
1977
+ .join(' ');
1978
+ }
1979
+ /**
1980
+ * Builds react-select's `guidance` live message around the control's name.
1981
+ *
1982
+ * react-select announces "<aria-label> is focused" on the first focus of the
1983
+ * input, falling back to "Select" when there is no `aria-label`. Neither a text
1984
+ * `label` nor `aria-labelledby` reaches that message, so the announcement would
1985
+ * disagree with the name assistive technology exposes.
1986
+ *
1987
+ * The name follows the accessible name precedence: `aria-labelledby`, then
1988
+ * `aria-label`, then the `<label>`. react-select does not export its default
1989
+ * messages, so this mirrors them for every context and only swaps the name.
1990
+ */
1991
+ function createAriaGuidance({ labelledBy, label }) {
1992
+ return ({ 'aria-label': ariaLabel, context, isSearchable, isMulti, tabSelectsValue, isInitialFocus, }) => {
1993
+ switch (context) {
1994
+ case 'menu':
1995
+ return `Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu${tabSelectsValue ? ', press Tab to select the option and exit the menu' : ''}.`;
1996
+ case 'input': {
1997
+ if (!isInitialFocus)
1998
+ return '';
1999
+ const name = (labelledBy && resolveLabelledBy(labelledBy)) || ariaLabel || label || 'Select';
2000
+ return `${name} is focused ${isSearchable ? ',type to refine list' : ''}, press Down to open the menu, ${isMulti ? ' press left to focus selected values' : ''}`;
2001
+ }
2002
+ case 'value':
2003
+ return 'Use left and right to toggle between focused values, press Backspace to remove the currently focused value';
2004
+ default:
2005
+ return '';
2006
+ }
2007
+ };
2008
+ }
2009
+
1434
2010
  function DSelect$1(_a) {
1435
- var { id: idProp, className, style, label, hint, iconFamilyClass, iconFamilyPrefix, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartAriaLabel, iconStartTabIndex, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndAriaLabel, iconEndTabIndex, invalid, valid, menuWithMaxContent = false, disabled, clearable, loading, floatingLabel = false, rtl, searchable, multi, components, defaultValue, placeholder, onIconStartClick, onIconEndClick, dataAttributes, ariaLabel = 'Search for an option' } = _a, props = tslib.__rest(_a, ["id", "className", "style", "label", "hint", "iconFamilyClass", "iconFamilyPrefix", "iconStart", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartAriaLabel", "iconStartTabIndex", "iconEnd", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndAriaLabel", "iconEndTabIndex", "invalid", "valid", "menuWithMaxContent", "disabled", "clearable", "loading", "floatingLabel", "rtl", "searchable", "multi", "components", "defaultValue", "placeholder", "onIconStartClick", "onIconEndClick", "dataAttributes", "ariaLabel"]);
2011
+ var { id: idProp, inputId: inputIdProp, className, style, label, hint, iconFamilyClass, iconFamilyPrefix, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartAriaLabel, iconStartTabIndex, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndAriaLabel, iconEndTabIndex, invalid, valid, menuWithMaxContent = false, disabled, clearable, loading, floatingLabel = false, rtl, searchable, multi, components, defaultValue, placeholder, onIconStartClick, onIconEndClick, dataAttributes, ariaLabel, ariaLiveMessages } = _a, props = tslib.__rest(_a, ["id", "inputId", "className", "style", "label", "hint", "iconFamilyClass", "iconFamilyPrefix", "iconStart", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartAriaLabel", "iconStartTabIndex", "iconEnd", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndAriaLabel", "iconEndTabIndex", "invalid", "valid", "menuWithMaxContent", "disabled", "clearable", "loading", "floatingLabel", "rtl", "searchable", "multi", "components", "defaultValue", "placeholder", "onIconStartClick", "onIconEndClick", "dataAttributes", "ariaLabel", "ariaLiveMessages"]);
1436
2012
  const innerId = React.useId();
1437
2013
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
2014
+ // The `<label>` must point at the element react-select renders the input
2015
+ // with, so an explicit `inputId` wins over `id` for both.
2016
+ const inputId = inputIdProp || id;
2017
+ // A text label or `aria-labelledby` names the control without going through
2018
+ // `aria-label`, which is all react-select's focus announcement reads, so it
2019
+ // is handed those names explicitly. Messages passed by the consumer still
2020
+ // take precedence.
2021
+ const textLabel = hasLabelContent(label) && isTextLabel(label) ? String(label) : undefined;
2022
+ const labelledBy = props['aria-labelledby'];
2023
+ const liveMessages = React.useMemo(() => (textLabel || labelledBy
2024
+ ? Object.assign({ guidance: createAriaGuidance({ labelledBy, label: textLabel }) }, ariaLiveMessages) : ariaLiveMessages), [textLabel, labelledBy, ariaLiveMessages]);
1438
2025
  const handleOnIconStartClick = React.useCallback(() => {
1439
2026
  onIconStartClick === null || onIconStartClick === void 0 ? void 0 : onIconStartClick(defaultValue);
1440
2027
  }, [onIconStartClick, defaultValue]);
1441
2028
  const handleOnIconEndClick = React.useCallback(() => {
1442
2029
  onIconEndClick === null || onIconEndClick === void 0 ? void 0 : onIconEndClick(defaultValue);
1443
2030
  }, [onIconEndClick, defaultValue]);
2031
+ if (process.env.NODE_ENV !== 'production') {
2032
+ warnLabelUsage({
2033
+ component: 'DSelect',
2034
+ label,
2035
+ // `{...props}` is spread after `ariaLabel`, so a native `aria-label` in it
2036
+ // is the one that reaches the input, even when it is `undefined`.
2037
+ hasAccessibleName: !!('aria-label' in props ? props['aria-label'] : ariaLabel)
2038
+ || !!labelledBy,
2039
+ accessibleNameProp: 'ariaLabel',
2040
+ floatingLabel,
2041
+ });
2042
+ }
1444
2043
  return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('d-select', className, {
1445
2044
  'd-select-floating': floatingLabel,
1446
2045
  disabled: disabled || loading,
1447
- }), style: style }, dataAttributes, { children: [label && (jsxRuntime.jsx("label", { htmlFor: id, children: label })), jsxRuntime.jsxs("div", { className: classNames({
2046
+ }), style: style }, dataAttributes, { children: [hasLabelContent(label) && (jsxRuntime.jsx(DFormLabel, { htmlFor: inputId, children: label })), jsxRuntime.jsxs("div", { className: classNames({
1448
2047
  'input-group': true,
1449
2048
  'has-validation': invalid,
1450
2049
  disabled: disabled || loading,
1451
- }), children: [iconStart && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: handleOnIconStartClick, disabled: disabled || loading, "aria-label": iconStartAriaLabel, tabIndex: iconStartTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix }) })), jsxRuntime.jsx(Select, Object.assign({ id: `${id}Container`, inputId: id, "aria-label": ariaLabel, styles: {
2050
+ }), children: [iconStart && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: handleOnIconStartClick, disabled: disabled || loading, "aria-label": iconStartAriaLabel, tabIndex: iconStartTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix }) })), jsxRuntime.jsx(Select, Object.assign({ id: `${id}Container`, inputId: inputId, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (hasLabelContent(label) || props['aria-labelledby'] ? undefined : 'Search for an option'), ariaLiveMessages: liveMessages, styles: {
1452
2051
  control: (base) => (Object.assign(Object.assign({}, base), { minHeight: 'unset' })),
1453
2052
  container: (base) => (Object.assign(Object.assign({}, base), { flex: 1 })),
1454
2053
  menu: (base) => (Object.assign(Object.assign({}, base), { width: menuWithMaxContent ? 'max-context' : '100%', zIndex: 1000 })),
@@ -1643,10 +2242,40 @@ function formatValue(value, currencyOptions) {
1643
2242
  }
1644
2243
  return currency(value, Object.assign(Object.assign({}, currencyOptions), { symbol: '' })).format();
1645
2244
  }
1646
- function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue) {
2245
+ /**
2246
+ * State and handlers for a currency input: a formatted string while the field
2247
+ * is idle and the raw number while it is being edited.
2248
+ *
2249
+ * `minValue`/`maxValue` bound the value. With `clamp` (default `true`) an
2250
+ * out-of-range value is brought into range whenever it is not being typed:
2251
+ * on mount, when `value` or the bounds change, and on blur. Every time the
2252
+ * clamp changes the value, `onChange` receives the clamped number, so the
2253
+ * input and the consumer's state never disagree. While the field is focused
2254
+ * the typed number is reported as is, so a partial entry is not rewritten.
2255
+ *
2256
+ * With `clamp: false` the value is never changed: the bounds only feed
2257
+ * `isOverMax` / `isUnderMin`, so the consumer can show its own message (e.g.
2258
+ * "You exceeded the limit") and keep the entered amount.
2259
+ *
2260
+ * Controlled usage follows the same contract as a native controlled input:
2261
+ * reflect `onChange` into `value` in the same event (`setState` in the
2262
+ * handler). The hook keeps showing `value` whenever it isn't being edited, so
2263
+ * a consumer can reject a change by keeping `value` as it was. Deferring the
2264
+ * update (a timer, awaiting a request) makes the hook see a stale `value` in
2265
+ * between: the field can flash the previous amount and a clamp can be
2266
+ * reported again when the deferred value arrives.
2267
+ */
2268
+ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue, clamp = true) {
1647
2269
  const inputRef = useProvidedRefOrCreate(ref);
2270
+ const onChangeRef = React.useRef(onChange);
2271
+ onChangeRef.current = onChange;
2272
+ // Last clamp reported through `onChange`, as the value it came from and the
2273
+ // value it produced. StrictMode replays effects, and a consumer may reflect
2274
+ // `onChange` asynchronously, both before `value` catches up: the same clamp
2275
+ // must not be reported twice.
2276
+ const lastReportedClampRef = React.useRef(null);
1648
2277
  const clampValue = React.useCallback((newValue) => {
1649
- if (newValue === undefined) {
2278
+ if (newValue === undefined || !clamp) {
1650
2279
  return newValue;
1651
2280
  }
1652
2281
  let clampedValue = newValue;
@@ -1657,10 +2286,9 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
1657
2286
  clampedValue = Math.min(clampedValue, maxValue);
1658
2287
  }
1659
2288
  return clampedValue;
1660
- }, [minValue, maxValue]);
2289
+ }, [minValue, maxValue, clamp]);
1661
2290
  const [innerType, setInnerType] = React.useState('text');
1662
- const [innerNumber, setInnerNumber] = React.useState(clampValue(value));
1663
- const [innerString, setInnerString] = React.useState(formatValue(clampValue(value), currencyOptions));
2291
+ const [innerNumber, setInnerNumber] = React.useState(() => clampValue(value));
1664
2292
  const handleOnFocus = React.useCallback((event) => {
1665
2293
  event.stopPropagation();
1666
2294
  setInnerType('number');
@@ -1672,35 +2300,60 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
1672
2300
  const clampedNumber = clampValue(innerNumber);
1673
2301
  if (clampedNumber !== innerNumber) {
1674
2302
  setInnerNumber(clampedNumber);
1675
- setInnerString(formatValue(clampedNumber, currencyOptions));
2303
+ // Recorded before notifying, so the sync effect that runs when editing
2304
+ // ends doesn't report the same clamp again while `value` still holds the
2305
+ // typed number.
2306
+ lastReportedClampRef.current = { from: value, to: clampedNumber };
1676
2307
  onChange === null || onChange === void 0 ? void 0 : onChange(clampedNumber);
1677
2308
  }
1678
2309
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
1679
- }, [onBlur, innerNumber, clampValue, currencyOptions, onChange]);
2310
+ }, [onBlur, innerNumber, clampValue, onChange, value]);
1680
2311
  const handleOnChange = React.useCallback((newValue) => {
1681
2312
  const newNumber = (newValue === undefined || newValue === '') ? undefined : Number(newValue);
1682
2313
  if (newNumber !== innerNumber) {
1683
2314
  setInnerNumber(newNumber);
1684
- setInnerString(formatValue(newNumber, currencyOptions));
1685
2315
  onChange === null || onChange === void 0 ? void 0 : onChange(newNumber);
1686
2316
  }
1687
- }, [currencyOptions, onChange, innerNumber]);
1688
- const isMountedRef = React.useRef(false);
2317
+ }, [onChange, innerNumber]);
2318
+ const isEditing = innerType === 'number';
2319
+ // Keep the inner value in sync with `value` and the bounds. While the user
2320
+ // is typing, the consumer echoes the typed number back through `value`, so
2321
+ // it is taken as is; otherwise it is clamped, and a clamp that changes it is
2322
+ // reported through `onChange`.
1689
2323
  React.useEffect(() => {
1690
- if (!isMountedRef.current) {
1691
- isMountedRef.current = true;
1692
- return;
2324
+ var _a;
2325
+ const nextNumber = isEditing ? value : clampValue(value);
2326
+ if (nextNumber !== innerNumber) {
2327
+ setInnerNumber(nextNumber);
2328
+ }
2329
+ if (nextNumber !== value) {
2330
+ const last = lastReportedClampRef.current;
2331
+ if (!last || last.from !== value || last.to !== nextNumber) {
2332
+ lastReportedClampRef.current = { from: value, to: nextNumber };
2333
+ (_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, nextNumber);
2334
+ }
1693
2335
  }
1694
- if (value !== innerNumber) {
1695
- setInnerNumber(value);
1696
- setInnerString(formatValue(value, currencyOptions));
2336
+ else {
2337
+ lastReportedClampRef.current = null;
1697
2338
  }
1698
- }, [value, currencyOptions, innerNumber]);
1699
- const innerValue = React.useMemo(() => { var _a; return (innerType === 'number' ? (_a = innerNumber === null || innerNumber === void 0 ? void 0 : innerNumber.toString()) !== null && _a !== void 0 ? _a : '' : innerString !== null && innerString !== void 0 ? innerString : ''); }, [innerType, innerNumber, innerString]);
2339
+ // `innerNumber` is read, not tracked: the effect reacts to the consumer's
2340
+ // value and to the bounds, not to its own updates.
2341
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2342
+ }, [value, clampValue, isEditing]);
2343
+ // Derived, not stored: the formatted text always matches the current number
2344
+ // and options, even when both change in the same render. It depends on the
2345
+ // options object itself, so a new custom `format` function is honored too;
2346
+ // formatting is cheap and doesn't feed the synchronization effect.
2347
+ const innerString = React.useMemo(() => formatValue(innerNumber, currencyOptions), [innerNumber, currencyOptions]);
2348
+ const innerValue = React.useMemo(() => { var _a; return (innerType === 'number' ? (_a = innerNumber === null || innerNumber === void 0 ? void 0 : innerNumber.toString()) !== null && _a !== void 0 ? _a : '' : innerString); }, [innerType, innerNumber, innerString]);
2349
+ const isOverMax = maxValue !== undefined && innerNumber !== undefined && innerNumber > maxValue;
2350
+ const isUnderMin = minValue !== undefined && innerNumber !== undefined && innerNumber < minValue;
1700
2351
  return {
1701
2352
  inputRef,
1702
2353
  innerValue,
1703
2354
  innerType,
2355
+ isOverMax,
2356
+ isUnderMin,
1704
2357
  handleOnFocus,
1705
2358
  handleOnChange,
1706
2359
  handleOnBlur,
@@ -1951,38 +2604,142 @@ function useOtp({ action, otpSize = 6, seconds = 15, }) {
1951
2604
  };
1952
2605
  }
1953
2606
 
2607
+ /**
2608
+ * Backs a value that the consumer may or may not drive, so a control works in
2609
+ * both modes without a copy of the state drifting away from the props.
2610
+ *
2611
+ * A control counts as controlled only when the consumer passes the value *and*
2612
+ * an `onChange` to push it back. The value on its own has always meant "start
2613
+ * here" in this library — `<DInputCheck type="radio" checked />` inside a radio
2614
+ * group, for one — so treating it as controlled would silently freeze those
2615
+ * controls, and React emits no warning to say why: the components always attach
2616
+ * their own `onChange` handler.
2617
+ *
2618
+ * @param value the value coming from props, or `undefined` when not passed.
2619
+ * @param isControlled whether the consumer can drive the value back.
2620
+ * @param initialValue what to start from when no value is passed.
2621
+ * @returns the value to render, and a setter to call on every user change.
2622
+ */
2623
+ function useControlledState(value, isControlled, initialValue) {
2624
+ const [internalValue, setInternalValue] = React.useState(value === undefined ? initialValue : value);
2625
+ // The internal copy follows `value` in both modes.
2626
+ //
2627
+ // Uncontrolled, `value` without an `onChange` still means "start here", and a
2628
+ // later flip of it from outside must still land — but between flips the
2629
+ // control has to keep moving on its own, which is why this depends on `value`
2630
+ // alone and not on `internalValue`.
2631
+ //
2632
+ // Controlled, the copy is not what gets rendered, but it is what the control
2633
+ // falls back to if the consumer later stops passing a value — `value={locked
2634
+ // ? undefined : x}`, say. Keeping it current means that hand-off carries on
2635
+ // from where the control is instead of snapping back to the mount-time seed.
2636
+ React.useEffect(() => {
2637
+ if (value === undefined) {
2638
+ return;
2639
+ }
2640
+ setInternalValue(value);
2641
+ }, [value]);
2642
+ const setValue = React.useCallback((next) => {
2643
+ if (isControlled) {
2644
+ return;
2645
+ }
2646
+ setInternalValue(next);
2647
+ }, [isControlled]);
2648
+ return [
2649
+ isControlled && value !== undefined ? value : internalValue,
2650
+ setValue,
2651
+ ];
2652
+ }
2653
+
1954
2654
  function DInputCounter(_a, ref) {
1955
- var { minValue, maxValue, value = minValue, invalid, iconStart: iconStartProp, iconEnd: iconEndProp, iconStartAriaLabel = 'decrease action', iconEndAriaLabel = 'increase action', style, onChange } = _a, props = tslib.__rest(_a, ["minValue", "maxValue", "value", "invalid", "iconStart", "iconEnd", "iconStartAriaLabel", "iconEndAriaLabel", "style", "onChange"]);
2655
+ var { minValue, maxValue, value, defaultValue, invalid, iconStart: iconStartProp, iconEnd: iconEndProp, iconStartAriaLabel = 'decrease action', iconEndAriaLabel = 'increase action', style, onChange } = _a, props = tslib.__rest(_a, ["minValue", "maxValue", "value", "defaultValue", "invalid", "iconStart", "iconEnd", "iconStartAriaLabel", "iconEndAriaLabel", "style", "onChange"]);
1956
2656
  const { handleOnWheel, } = useDisableInputWheel(ref);
1957
2657
  const inputRef = useProvidedRefOrCreate(ref);
1958
- const [internalIsInvalid, setInternalIsInvalid] = React.useState(false);
1959
- const [internalValue, setInternalValue] = React.useState(value);
2658
+ // See `useControlledState` for why `onChange` takes part in this decision.
2659
+ const isControlled = value !== undefined && onChange !== undefined;
2660
+ const [currentValue, setCurrentValue] = useControlledState(value, isControlled, defaultValue !== null && defaultValue !== void 0 ? defaultValue : minValue);
2661
+ // The step handlers move *from* the current value rather than replacing it,
2662
+ // so they need the latest one even when several clicks land in the same React
2663
+ // batch and no render has happened in between — the functional update this
2664
+ // replaced handled that on its own. Written on every render so it follows the
2665
+ // props, and by `commitValue` so a second step in the same batch starts from
2666
+ // where the first left off instead of collapsing into it.
2667
+ const currentValueRef = React.useRef(currentValue);
2668
+ currentValueRef.current = currentValue;
2669
+ // `onChange` used to be called from an effect watching the internal value,
2670
+ // which made controlling the counter impossible: a controlled counter never
2671
+ // moves that value, so the effect never fired. Reporting from the handlers
2672
+ // instead works in both modes.
2673
+ const commitValue = React.useCallback((newValue) => {
2674
+ // Clicking at a bound, or typing the value that is already there, is not a
2675
+ // change. The effect this replaced watched the value itself and so stayed
2676
+ // quiet too; without this a batch of clicks against `minValue` would report
2677
+ // the same number once per click.
2678
+ if (newValue === currentValueRef.current) {
2679
+ return;
2680
+ }
2681
+ // Only the uncontrolled path moves the ref ahead of a render. There the
2682
+ // component owns the value, so a second step in the same batch has to build
2683
+ // on the first. Controlled, the prop is the truth and every step proposes
2684
+ // from it: a parent that rejects without re-rendering — `onChange={() => {}}`,
2685
+ // or a setter returning the previous state — never re-runs the assignment
2686
+ // above, so a speculative ref would climb away from the value on screen and
2687
+ // each further click would report a number further from it.
2688
+ if (!isControlled) {
2689
+ currentValueRef.current = newValue;
2690
+ }
2691
+ setCurrentValue(newValue);
2692
+ onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
2693
+ }, [isControlled, setCurrentValue, onChange]);
2694
+ // Consumers have always been handed the starting value through `onChange` on
2695
+ // mount, and some seed their state with it, so that one call stays. The ref
2696
+ // keeps it to the first run: the old effect also re-fired on every render
2697
+ // that passed a fresh inline `onChange`. Nothing is marked as reported until
2698
+ // there is a handler to report to, so a counter mounted without `onChange`
2699
+ // still hands over its starting value once one is attached.
2700
+ const hasReportedInitialValue = React.useRef(false);
1960
2701
  React.useEffect(() => {
1961
- setInternalValue(value);
1962
- }, [value]);
2702
+ if (hasReportedInitialValue.current || !onChange) {
2703
+ return;
2704
+ }
2705
+ hasReportedInitialValue.current = true;
2706
+ onChange(currentValue);
2707
+ }, [onChange, currentValue]);
2708
+ // `value` used to default to `minValue`, so a counter left to its own devices
2709
+ // re-seeded — and reported — whenever `minValue` moved. Consumers use it as a
2710
+ // live bound, so that stays; an explicit `value` or `defaultValue` opts out,
2711
+ // exactly as passing `value` did before. The ref keeps this to real changes,
2712
+ // so mounting still reports once and a click is never undone.
2713
+ const hasExplicitStartingValue = value !== undefined || defaultValue !== undefined;
2714
+ const previousMinValue = React.useRef(minValue);
1963
2715
  React.useEffect(() => {
1964
- onChange === null || onChange === void 0 ? void 0 : onChange(Number(internalValue));
1965
- }, [onChange, internalValue]);
2716
+ if (previousMinValue.current === minValue) {
2717
+ return;
2718
+ }
2719
+ previousMinValue.current = minValue;
2720
+ if (hasExplicitStartingValue) {
2721
+ return;
2722
+ }
2723
+ commitValue(minValue);
2724
+ }, [hasExplicitStartingValue, minValue, commitValue]);
1966
2725
  const handleOnChange = React.useCallback((newValue) => {
1967
- setInternalValue(Number(newValue || '0'));
1968
- }, []);
2726
+ commitValue(Number(newValue || '0'));
2727
+ }, [commitValue]);
1969
2728
  const handleOnIconStartClick = React.useCallback(() => {
1970
- setInternalValue((prevInternalValue) => Math.max(prevInternalValue - 1, minValue));
1971
- }, [minValue]);
2729
+ commitValue(Math.max(currentValueRef.current - 1, minValue));
2730
+ }, [commitValue, minValue]);
1972
2731
  const handleOnIconEndClick = React.useCallback(() => {
1973
- setInternalValue((prevInternalValue) => Math.min(prevInternalValue + 1, maxValue));
1974
- }, [maxValue]);
2732
+ commitValue(Math.min(currentValueRef.current + 1, maxValue));
2733
+ }, [commitValue, maxValue]);
1975
2734
  const generateStyleVariables = React.useMemo(() => (Object.assign(Object.assign({}, style), { [`--${PREFIX_BS}form-control-component-text-align`]: 'center' })), [style]);
1976
- const valueString = React.useMemo(() => (internalValue.toString()), [internalValue]);
1977
- React.useEffect(() => {
1978
- setInternalIsInvalid(!(internalValue >= minValue && internalValue <= maxValue));
1979
- }, [internalValue, minValue, maxValue]);
2735
+ const valueString = React.useMemo(() => (currentValue.toString()), [currentValue]);
2736
+ const internalIsInvalid = React.useMemo(() => (!(currentValue >= minValue && currentValue <= maxValue)), [currentValue, minValue, maxValue]);
1980
2737
  const { iconMap: { input } } = useDContext();
1981
2738
  const iconEnd = React.useMemo(() => iconEndProp || input.increase, [iconEndProp, input.increase]);
1982
2739
  const iconStart = React.useMemo(() => iconStartProp || input.decrease, [iconStartProp, input.decrease]);
1983
- return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: valueString, style: generateStyleVariables, iconStart: iconStart, iconEnd: iconEnd, invalid: internalIsInvalid || invalid, type: "number", onChange: handleOnChange, onWheel: handleOnWheel, onIconStartClick: handleOnIconStartClick, onIconEndClick: handleOnIconEndClick, iconStartAriaLabel: iconStartAriaLabel, iconEndAriaLabel: iconEndAriaLabel }, internalValue === minValue && {
2740
+ return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: valueString, style: generateStyleVariables, iconStart: iconStart, iconEnd: iconEnd, invalid: internalIsInvalid || invalid, type: "number", onChange: handleOnChange, onWheel: handleOnWheel, onIconStartClick: handleOnIconStartClick, onIconEndClick: handleOnIconEndClick, iconStartAriaLabel: iconStartAriaLabel, iconEndAriaLabel: iconEndAriaLabel }, currentValue === minValue && {
1984
2741
  iconStartDisabled: true,
1985
- }, internalValue === maxValue && {
2742
+ }, currentValue === maxValue && {
1986
2743
  iconEndDisabled: true,
1987
2744
  }, props)));
1988
2745
  }
@@ -1990,11 +2747,16 @@ const ForwardedDInputCounter = React.forwardRef(DInputCounter);
1990
2747
  ForwardedDInputCounter.displayName = 'DInputCounter';
1991
2748
 
1992
2749
  function DInputCurrency(_a, ref) {
1993
- var { value, minValue, maxValue, currencyCode, onFocus, onBlur, onChange } = _a, props = tslib.__rest(_a, ["value", "minValue", "maxValue", "currencyCode", "onFocus", "onBlur", "onChange"]);
2750
+ var { value, minValue, maxValue, clamp = true, currencyCode, onFocus, onBlur, onChange, invalid: invalidProp, valid: validProp } = _a, props = tslib.__rest(_a, ["value", "minValue", "maxValue", "clamp", "currencyCode", "onFocus", "onBlur", "onChange", "invalid", "valid"]);
1994
2751
  const { currency: currencyOptions } = useDContext();
1995
2752
  const { handleOnWheel, } = useDisableInputWheel(ref);
1996
- const { inputRef, innerValue, innerType, handleOnFocus, handleOnChange, handleOnBlur, } = useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue);
1997
- return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: innerValue, onChange: handleOnChange, inputMode: "decimal", type: innerType, onFocus: handleOnFocus, onBlur: handleOnBlur, onWheel: handleOnWheel, inputStart: (jsxRuntime.jsx("span", { slot: "input-start", className: "d-input-currency-symbol", children: currencyCode || currencyOptions.symbol })) }, props)));
2753
+ const { inputRef, innerValue, innerType, isOverMax, isUnderMin, handleOnFocus, handleOnChange, handleOnBlur, } = useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue, clamp);
2754
+ const outOfRange = !clamp && (isOverMax || isUnderMin);
2755
+ // One validation state: an explicit `invalid` wins; otherwise an
2756
+ // out-of-range value is invalid and can't also be shown as valid.
2757
+ const invalid = invalidProp !== null && invalidProp !== void 0 ? invalidProp : outOfRange;
2758
+ const valid = invalid ? false : validProp;
2759
+ return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: innerValue, onChange: handleOnChange, inputMode: "decimal", type: innerType, onFocus: handleOnFocus, onBlur: handleOnBlur, onWheel: handleOnWheel, invalid: invalid, valid: valid, inputStart: (jsxRuntime.jsx("span", { slot: "input-start", className: "d-input-currency-symbol", children: currencyCode || currencyOptions.symbol })) }, props)));
1998
2760
  }
1999
2761
  const ForwardedDInputCurrency = React.forwardRef(DInputCurrency);
2000
2762
  ForwardedDInputCurrency.displayName = 'DInputCurrency';
@@ -2081,7 +2843,7 @@ const DEFAULT_VALIDATION_MESSAGES = {
2081
2843
  notMatch: 'The password confirmation and the new password do not match.',
2082
2844
  };
2083
2845
  const DEFAULT_ENABLED_CHECKS = ['uppercase', 'lowercase', 'number', 'specialChar'];
2084
- function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '', name, disabled = false, invalid = false, validationMessages = DEFAULT_VALIDATION_MESSAGES, enabledChecks = DEFAULT_ENABLED_CHECKS, className, style, dataAttributes, onChange, readonly = false, }) {
2846
+ function DPasswordStrengthMeter({ id, label = 'Password', 'aria-label': ariaLabel, placeholder, value = '', name, disabled = false, invalid = false, validationMessages = DEFAULT_VALIDATION_MESSAGES, enabledChecks = DEFAULT_ENABLED_CHECKS, className, style, dataAttributes, onChange, readonly = false, }) {
2085
2847
  const [password, setPassword] = React.useState(value);
2086
2848
  React.useEffect(() => {
2087
2849
  setPassword(value);
@@ -2090,17 +2852,31 @@ function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '
2090
2852
  setPassword(newValue);
2091
2853
  onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
2092
2854
  };
2093
- return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsxRuntime.jsx(ForwardedDInputPassword, { id: id, label: label, placeholder: placeholder, value: password, name: name, disabled: disabled, invalid: invalid, onChange: handleChange, readonly: readonly }), jsxRuntime.jsx(PasswordChecksList, { password: password, validationMessages: validationMessages, enabledChecks: enabledChecks })] })));
2855
+ return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsxRuntime.jsx(ForwardedDInputPassword, { id: id, label: label, "aria-label": ariaLabel, placeholder: placeholder, value: password, name: name, disabled: disabled, invalid: invalid, onChange: handleChange, readonly: readonly }), jsxRuntime.jsx(PasswordChecksList, { password: password, validationMessages: validationMessages, enabledChecks: enabledChecks })] })));
2094
2856
  }
2095
2857
 
2096
2858
  function DInputCheck(_a) {
2097
- var { id: idProp, type, name, label, ariaLabel, checked = false, disabled = false, invalid = false, valid = false, indeterminate, inputClassName, value, hint, onChange, className, style, dataAttributes } = _a, props = tslib.__rest(_a, ["id", "type", "name", "label", "ariaLabel", "checked", "disabled", "invalid", "valid", "indeterminate", "inputClassName", "value", "hint", "onChange", "className", "style", "dataAttributes"]);
2859
+ var { id: idProp, type, name, label, ariaLabel, checked, defaultChecked, disabled = false, invalid = false, valid = false, indeterminate, inputClassName, value, hint, onChange, className, style, dataAttributes } = _a, props = tslib.__rest(_a, ["id", "type", "name", "label", "ariaLabel", "checked", "defaultChecked", "disabled", "invalid", "valid", "indeterminate", "inputClassName", "value", "hint", "onChange", "className", "style", "dataAttributes"]);
2098
2860
  const innerRef = React.useRef(null);
2861
+ // See `useControlledState` for why `onChange` takes part in this decision.
2862
+ const isControlled = checked !== undefined && onChange !== undefined;
2099
2863
  const innerId = React.useId();
2100
2864
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
2101
2865
  const handleChange = React.useCallback((event) => {
2102
2866
  onChange === null || onChange === void 0 ? void 0 : onChange(event);
2103
- }, [onChange]);
2867
+ // Controlled only. Activating a checkbox clears the DOM `indeterminate`
2868
+ // flag, and it has no HTML attribute for React to restore the way it
2869
+ // restores `checked` when the parent rejects the change, so the mixed state
2870
+ // would be gone after the first click — the effect below only re-runs when
2871
+ // the prop moves. Uncontrolled keeps the browser's behaviour, where the
2872
+ // click owns the state and `indeterminate` was only the starting look.
2873
+ // Reapplied after `onChange` so a handler reading
2874
+ // `event.target.indeterminate` still sees what the browser left, and a
2875
+ // parent that does move the prop wins through that effect.
2876
+ if (isControlled && innerRef.current) {
2877
+ innerRef.current.indeterminate = type === 'checkbox' && Boolean(indeterminate);
2878
+ }
2879
+ }, [onChange, isControlled, indeterminate, type]);
2104
2880
  const ariaDescribedby = React.useMemo(() => ([
2105
2881
  !!hint && `${id}Hint`,
2106
2882
  ]
@@ -2114,15 +2890,24 @@ function DInputCheck(_a) {
2114
2890
  innerRef.current.indeterminate = type === 'checkbox' && Boolean(indeterminate);
2115
2891
  }
2116
2892
  }, [indeterminate, type]);
2893
+ // Legacy path only: a `checked` with no `onChange` behind it still lands on
2894
+ // the element, but through the DOM, so the input stays uncontrolled and both
2895
+ // clicking it and the native radio-group behaviour keep working.
2117
2896
  React.useEffect(() => {
2118
- if (innerRef.current) {
2119
- innerRef.current.checked = checked;
2897
+ if (isControlled || checked === undefined || !innerRef.current) {
2898
+ return;
2120
2899
  }
2121
- }, [checked]);
2122
- const inputComponent = React.useMemo(() => (jsxRuntime.jsx("input", Object.assign({ ref: innerRef, onChange: handleChange, className: classNames('form-check-input', {
2900
+ innerRef.current.checked = checked;
2901
+ }, [isControlled, checked]);
2902
+ const inputComponent = React.useMemo(() => (jsxRuntime.jsx("input", Object.assign({ ref: innerRef }, isControlled
2903
+ ? { checked }
2904
+ : defaultChecked !== undefined && { defaultChecked }, { onChange: handleChange, className: classNames('form-check-input', {
2123
2905
  'is-invalid': invalid,
2124
2906
  'is-valid': valid,
2125
2907
  }, inputClassName), style: style, id: id, disabled: disabled, type: type, name: name, value: value, "aria-label": ariaLabel }, ariaDescribedby && { 'aria-describedby': ariaDescribedby }, props))), [
2908
+ isControlled,
2909
+ checked,
2910
+ defaultChecked,
2126
2911
  handleChange,
2127
2912
  invalid,
2128
2913
  valid,
@@ -2137,10 +2922,21 @@ function DInputCheck(_a) {
2137
2922
  ariaDescribedby,
2138
2923
  props,
2139
2924
  ]);
2140
- if (!label) {
2925
+ if (process.env.NODE_ENV !== 'production') {
2926
+ warnLabelUsage({
2927
+ component: 'DInputCheck',
2928
+ label,
2929
+ // `{...props}` is spread after `aria-label={ariaLabel}`, so a native
2930
+ // `aria-label` wins — including when it is explicitly undefined.
2931
+ hasAccessibleName: !!('aria-label' in props ? props['aria-label'] : ariaLabel)
2932
+ || !!props['aria-labelledby'],
2933
+ accessibleNameProp: 'ariaLabel',
2934
+ });
2935
+ }
2936
+ if (!hasLabelContent(label)) {
2141
2937
  return inputComponent;
2142
2938
  }
2143
- return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('form-check', className) }, dataAttributes, { children: [inputComponent, jsxRuntime.jsx("label", { className: "form-check-label", htmlFor: id, children: label }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2939
+ return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('form-check', className) }, dataAttributes, { children: [inputComponent, jsxRuntime.jsx(DFormLabel, { className: "form-check-label", htmlFor: id, children: label }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2144
2940
  }
2145
2941
 
2146
2942
  function DInputPin({ id: idProp, label = '', placeholder, type = 'text', disabled = false, loading = false, secret = false, characters = 4, innerInputMode = 'text', hint, invalid = false, valid = false, className, style, dataAttributes, onChange, 'aria-label': ariaLabel = 'Pin character number', }) {
@@ -2216,14 +3012,14 @@ function DInputPin({ id: idProp, label = '', placeholder, type = 'text', disable
2216
3012
  const wheelInput = React.useCallback((event) => {
2217
3013
  event.currentTarget.blur();
2218
3014
  }, []);
2219
- return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('d-input-pin', className), style: style }, dataAttributes, { children: [label && (jsxRuntime.jsx("label", { htmlFor: "pinIndex0", children: label })), jsxRuntime.jsxs("div", { className: "d-input-pin-group", id: id, children: [Array.from({ length: characters }).map((_, index) => (jsxRuntime.jsx("input", Object.assign({ className: classNames({
3015
+ return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('d-input-pin', className), style: style }, dataAttributes, { children: [hasLabelContent(label) && (jsxRuntime.jsx(DFormLabel, { htmlFor: "pinIndex0", children: label })), jsxRuntime.jsxs("div", { className: "d-input-pin-group", id: id, children: [Array.from({ length: characters }).map((_, index) => (jsxRuntime.jsx("input", Object.assign({ className: classNames({
2220
3016
  'form-control': true,
2221
3017
  'is-invalid': invalid,
2222
3018
  'is-valid': valid,
2223
3019
  }), value: activeInput[index], type: secret ? 'password' : type, "aria-label": `${ariaLabel} ${index + 1} of ${characters}`, inputMode: innerInputMode, id: `pinIndex${index}`, name: `pin-${index}`, maxLength: 1, onInput: (event) => nextInput(event, index), onKeyDown: (event) => prevInput(event, index), onFocus: () => focusInput(index), onWheel: wheelInput, onClick: (event) => event.preventDefault(), onPaste: (event) => handlePaste(event), autoComplete: "off", placeholder: placeholder, disabled: disabled || loading, required: true }, type === 'number' && ({ min: 0, max: 9 })), index))), loading && (jsxRuntime.jsx("div", { className: "input-group-text", children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2224
3020
  }
2225
3021
 
2226
- function DInputSelect({ id: idProp, name, label = '', className, style, options = [], disabled = false, loading = false, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartAriaLabel, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndAriaLabel, hint, value, size, floatingLabel = false, invalid = false, valid = false, dataAttributes, valueExtractor, labelExtractor, onChange, onBlur, onIconStartClick, onIconEndClick, }) {
3022
+ function DInputSelect({ id: idProp, name, label = '', ariaLabel, className, style, options = [], disabled = false, loading = false, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartAriaLabel, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndAriaLabel, hint, value, size, floatingLabel = false, invalid = false, valid = false, dataAttributes, valueExtractor, labelExtractor, onChange, onBlur, onIconStartClick, onIconEndClick, }) {
2227
3023
  const innerId = React.useId();
2228
3024
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
2229
3025
  const internalValueExtractor = React.useCallback((option) => {
@@ -2273,8 +3069,9 @@ function DInputSelect({ id: idProp, name, label = '', className, style, options
2273
3069
  'floating-label': floatingLabel,
2274
3070
  'is-invalid': invalid,
2275
3071
  'is-valid': valid,
2276
- }), "aria-label": label, disabled: disabled || loading, onChange: changeHandler, onBlur: blurHandler }, ariaDescribedby && { 'aria-describedby': ariaDescribedby }, value && { value }, { children: options.map((option) => (jsxRuntime.jsx("option", { value: internalValueExtractor(option), children: internalLabelExtractor(option) }, internalValueExtractor(option)))) }))), [
3072
+ }), "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (isTextLabel(label) ? String(label) : undefined), disabled: disabled || loading, onChange: changeHandler, onBlur: blurHandler }, ariaDescribedby && { 'aria-describedby': ariaDescribedby }, value && { value }, { children: options.map((option) => (jsxRuntime.jsx("option", { value: internalValueExtractor(option), children: internalLabelExtractor(option) }, internalValueExtractor(option)))) }))), [
2277
3073
  ariaDescribedby,
3074
+ ariaLabel,
2278
3075
  blurHandler,
2279
3076
  changeHandler,
2280
3077
  disabled,
@@ -2291,7 +3088,7 @@ function DInputSelect({ id: idProp, name, label = '', className, style, options
2291
3088
  valid,
2292
3089
  size,
2293
3090
  ]);
2294
- const labelComponent = React.useMemo(() => (jsxRuntime.jsx("label", { htmlFor: id, children: label })), [
3091
+ const labelComponent = React.useMemo(() => (jsxRuntime.jsx(DFormLabel, { htmlFor: id, children: label })), [
2295
3092
  id,
2296
3093
  label,
2297
3094
  ]);
@@ -2301,15 +3098,26 @@ function DInputSelect({ id: idProp, name, label = '', className, style, options
2301
3098
  }
2302
3099
  return selectComponent;
2303
3100
  }, [floatingLabel, labelComponent, selectComponent]);
2304
- return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [label && !floatingLabel && (labelComponent), jsxRuntime.jsxs("div", { className: classNames({
3101
+ if (process.env.NODE_ENV !== 'production') {
3102
+ warnLabelUsage({
3103
+ component: 'DInputSelect',
3104
+ label,
3105
+ hasAccessibleName: !!ariaLabel,
3106
+ accessibleNameProp: 'ariaLabel',
3107
+ floatingLabel,
3108
+ });
3109
+ }
3110
+ return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [hasLabelContent(label) && !floatingLabel && (labelComponent), jsxRuntime.jsxs("div", { className: classNames({
2305
3111
  'input-group': true,
2306
3112
  }), children: [iconStart && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: iconStartClickHandler, disabled: disabled || loading, "aria-label": iconStartAriaLabel, children: iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix })) })), dynamicComponent, iconEnd && !loading && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: iconEndClickHandler, disabled: disabled || loading, "aria-label": iconEndAriaLabel, children: iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix })) })), loading && (jsxRuntime.jsx("div", { className: "input-group-text form-control-icon loading", children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2307
3113
  }
2308
3114
 
2309
- function DInputSwitch({ id: idProp, label, ariaLabel, name, checked, disabled, invalid = false, valid = false, hint, readonly, className, style, dataAttributes, inputClassName, onChange, }) {
3115
+ function DInputSwitch({ id: idProp, label, ariaLabel, name, checked, defaultChecked = false, disabled, invalid = false, valid = false, hint, readonly, className, style, dataAttributes, inputClassName, onChange, }) {
2310
3116
  const innerId = React.useId();
2311
3117
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
2312
- const [internalIsChecked, setInternalIsChecked] = React.useState(checked);
3118
+ // See `useControlledState` for why `onChange` takes part in this decision.
3119
+ const isControlled = checked !== undefined && onChange !== undefined;
3120
+ const [isChecked, setIsChecked] = useControlledState(checked, isControlled, defaultChecked);
2313
3121
  const ariaDescribedby = React.useMemo(() => ([
2314
3122
  !!hint && `${id}Hint`,
2315
3123
  ]
@@ -2318,18 +3126,23 @@ function DInputSwitch({ id: idProp, label, ariaLabel, name, checked, disabled, i
2318
3126
  id,
2319
3127
  hint,
2320
3128
  ]);
2321
- React.useEffect(() => {
2322
- setInternalIsChecked(checked);
2323
- }, [checked]);
2324
3129
  const changeHandler = React.useCallback((event) => {
2325
3130
  const value = event.currentTarget.checked;
2326
- setInternalIsChecked(value);
3131
+ setIsChecked(value);
2327
3132
  onChange === null || onChange === void 0 ? void 0 : onChange(value);
2328
- }, [onChange]);
3133
+ }, [setIsChecked, onChange]);
3134
+ if (process.env.NODE_ENV !== 'production') {
3135
+ warnLabelUsage({
3136
+ component: 'DInputSwitch',
3137
+ label,
3138
+ hasAccessibleName: !!ariaLabel,
3139
+ accessibleNameProp: 'ariaLabel',
3140
+ });
3141
+ }
2329
3142
  return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('form-check form-switch', className) }, dataAttributes, { children: [jsxRuntime.jsx("input", Object.assign({ id: id, name: name, onChange: readonly ? () => false : changeHandler, className: classNames('form-check-input', {
2330
3143
  'is-invalid': invalid,
2331
3144
  'is-valid': valid,
2332
- }, inputClassName), style: style, type: "checkbox", role: "switch", checked: internalIsChecked, disabled: disabled, "aria-label": ariaLabel }, ariaDescribedby && { 'aria-describedby': ariaDescribedby })), label && (jsxRuntime.jsx("label", { className: "form-check-label", htmlFor: id, children: label })), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
3145
+ }, inputClassName), style: style, type: "checkbox", role: "switch", checked: isChecked, disabled: disabled, "aria-label": ariaLabel }, ariaDescribedby && { 'aria-describedby': ariaDescribedby })), hasLabelContent(label) && (jsxRuntime.jsx(DFormLabel, { className: "form-check-label", htmlFor: id, children: label })), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2333
3146
  }
2334
3147
 
2335
3148
  function DInputRange(_a, ref) {
@@ -2361,15 +3174,61 @@ function DInputRange(_a, ref) {
2361
3174
  props,
2362
3175
  value,
2363
3176
  ]);
2364
- if (!label) {
3177
+ if (process.env.NODE_ENV !== 'production') {
3178
+ warnLabelUsage({
3179
+ component: 'DInputRange',
3180
+ label,
3181
+ // `{...props}` is spread after `aria-label={ariaLabel}`, so a native
3182
+ // `aria-label` wins — including when it is explicitly undefined.
3183
+ hasAccessibleName: !!('aria-label' in props ? props['aria-label'] : ariaLabel)
3184
+ || !!props['aria-labelledby'],
3185
+ accessibleNameProp: 'ariaLabel',
3186
+ });
3187
+ }
3188
+ if (!hasLabelContent(label)) {
2365
3189
  return inputComponent;
2366
3190
  }
2367
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("label", { className: "form-label", htmlFor: id, children: label }), inputComponent] }));
3191
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DFormLabel, { className: "form-label", htmlFor: id, children: label }), inputComponent] }));
2368
3192
  }
2369
3193
  const ForwardedDInputRange = React.forwardRef(DInputRange);
2370
3194
  ForwardedDInputRange.displayName = 'DInputRange';
2371
3195
 
2372
- function DListGroupItem({ as = 'li', action: actionProp, active, disabled, href, onClick, color, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartMaterialStyle, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndMaterialStyle, children, className, style, dataAttributes, }) {
3196
+ /**
3197
+ * Element the enclosing `DListGroup` renders, so each `DListGroupItem` can
3198
+ * check that its own element is valid content for it. `undefined` outside a
3199
+ * `DListGroup`.
3200
+ */
3201
+ const ListGroupContext = React.createContext(undefined);
3202
+
3203
+ /**
3204
+ * Combinations already reported, keyed by container and item element, so the
3205
+ * same mismatch only warns once per page load however many items share it.
3206
+ */
3207
+ const warnedCombinations = new Set();
3208
+ /**
3209
+ * Warns, once per combination and only outside production builds, that a
3210
+ * `DListGroupItem` renders an element its `DListGroup` container can't hold.
3211
+ *
3212
+ * Links and buttons inside a <ul>/<ol> are wrapped in an <li>, so the only
3213
+ * invalid pair left is a plain item (an <li>) inside `as="div"`: an <li>
3214
+ * outside of any list. It doesn't fail visibly, which is why it's reported.
3215
+ *
3216
+ * The `process.env.NODE_ENV` guard at the call site is what bundlers
3217
+ * constant-fold, so the whole call — and this module — drops out of a
3218
+ * consumer's production bundle.
3219
+ */
3220
+ function warnInvalidListMarkup(container, item) {
3221
+ const key = `${container}>${item}`;
3222
+ if (warnedCombinations.has(key))
3223
+ return;
3224
+ warnedCombinations.add(key);
3225
+ // eslint-disable-next-line no-console
3226
+ console.warn(`[Dynamic UI] DListGroupItem: a <${item}> inside a <${container}> is invalid markup. `
3227
+ + 'Keep the container as a list (the default `as="ul"`, or `numbered`), or render the item '
3228
+ + 'as a link or button. It never appears in production builds.');
3229
+ }
3230
+
3231
+ function DListGroupItem({ as = 'li', action: actionProp, active, ariaCurrent = 'true', disabled, href, onClick, color, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartMaterialStyle, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndMaterialStyle, children, className, style, dataAttributes, }) {
2373
3232
  const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
2374
3233
  const Tag = React.useMemo(() => {
2375
3234
  if (href) {
@@ -2380,29 +3239,45 @@ function DListGroupItem({ as = 'li', action: actionProp, active, disabled, href,
2380
3239
  }
2381
3240
  return as;
2382
3241
  }, [href, as, actionProp]);
2383
- const action = React.useMemo(() => {
2384
- if (Tag === 'a' || Tag === 'button') {
2385
- return true;
2386
- }
2387
- return actionProp;
2388
- }, [Tag, actionProp]);
3242
+ const container = React.useContext(ListGroupContext);
3243
+ const isInteractive = Tag === 'a' || Tag === 'button';
3244
+ // Inside a <ul>/<ol>, a link or button is wrapped in the <li> that carries
3245
+ // the item styles, so the group keeps list semantics (`list > listitem >
3246
+ // link|button`) without breaking Bootstrap's sibling selectors.
3247
+ const isWrapped = isInteractive && (container === 'ul' || container === 'ol');
3248
+ if (process.env.NODE_ENV !== 'production' && container === 'div' && Tag === 'li') {
3249
+ warnInvalidListMarkup(container, Tag);
3250
+ }
2389
3251
  const generateClasses = React.useMemo(() => ({
2390
3252
  'list-group-item': true,
2391
- 'list-group-item-action': action,
3253
+ 'list-group-item-action': isInteractive || actionProp,
3254
+ 'd-list-group-item-interactive': isWrapped,
2392
3255
  [`list-group-item-${color}`]: !!color,
2393
3256
  active,
2394
3257
  disabled,
2395
- }), [action, active, disabled, color]);
2396
- const ariaAttributes = React.useMemo(() => {
3258
+ }), [isInteractive, actionProp, isWrapped, active, disabled, color]);
3259
+ // A disabled link leaves the tab order and can't be activated: without
3260
+ // `href` and `onClick`, Enter does nothing. Dropping `href` also drops the
3261
+ // implicit link role, so it is set back explicitly. A button uses
3262
+ // `disabled`.
3263
+ const interactiveProps = React.useMemo(() => {
2397
3264
  if (Tag === 'button') {
2398
- return Object.assign(Object.assign({}, active && { 'aria-current': true }), disabled && { disabled: true });
3265
+ return Object.assign(Object.assign(Object.assign({ type: 'button' }, onClick && { onClick }), active && { 'aria-current': ariaCurrent }), disabled && { disabled: true });
3266
+ }
3267
+ if (Tag === 'a') {
3268
+ return disabled
3269
+ ? Object.assign({ role: 'link', 'aria-disabled': true, tabIndex: -1 }, active && { 'aria-current': ariaCurrent }) : Object.assign(Object.assign(Object.assign({}, href && { href }), onClick && { onClick }), active && { 'aria-current': ariaCurrent });
2399
3270
  }
2400
- return Object.assign(Object.assign({}, active && { 'aria-current': true }), disabled && { 'aria-disabled': true });
2401
- }, [Tag, active, disabled]);
2402
- return (jsxRuntime.jsxs(Tag, Object.assign({ className: classNames(generateClasses, className), style: style }, Tag === 'a' && href && { href }, onClick && { onClick }, ariaAttributes, dataAttributes, Tag === 'button' && { type: 'button' }, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle })), children, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle, className: "ms-auto" }))] })));
3271
+ return Object.assign(Object.assign(Object.assign({}, onClick && { onClick }), active && { 'aria-current': ariaCurrent }), disabled && { 'aria-disabled': true });
3272
+ }, [Tag, href, onClick, active, ariaCurrent, disabled]);
3273
+ const content = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle })), children, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle, className: "ms-auto" }))] }));
3274
+ if (isWrapped) {
3275
+ return (jsxRuntime.jsx("li", { className: classNames(generateClasses, className), style: style, children: jsxRuntime.jsx(Tag, Object.assign({ className: "d-list-group-item-link" }, interactiveProps, dataAttributes, { children: content })) }));
3276
+ }
3277
+ return (jsxRuntime.jsx(Tag, Object.assign({ className: classNames(generateClasses, className), style: style }, interactiveProps, dataAttributes, { children: content })));
2403
3278
  }
2404
3279
 
2405
- function DListGroup$1({ as = 'ul', numbered, flush, horizontal, children, className, style, dataAttributes, }) {
3280
+ function DListGroup$1({ as = 'ul', numbered, flush, horizontal, ariaLabel, ariaLabelledBy, children, className, style, dataAttributes, }) {
2406
3281
  const Tag = React.useMemo(() => {
2407
3282
  if (numbered) {
2408
3283
  return 'ol';
@@ -2420,7 +3295,14 @@ function DListGroup$1({ as = 'ul', numbered, flush, horizontal, children, classN
2420
3295
  [listGroupHorizontalClass]: !!horizontal,
2421
3296
  };
2422
3297
  }, [flush, horizontal, numbered]);
2423
- return (jsxRuntime.jsx(Tag, Object.assign({ className: classNames(generateClasses, className), style: style }, dataAttributes, { children: children })));
3298
+ const labelProps = React.useMemo(() => {
3299
+ if (!ariaLabelledBy && !ariaLabel)
3300
+ return {};
3301
+ return Object.assign(Object.assign({}, ariaLabelledBy
3302
+ ? { 'aria-labelledby': ariaLabelledBy }
3303
+ : { 'aria-label': ariaLabel }), Tag === 'div' && { role: 'group' });
3304
+ }, [ariaLabel, ariaLabelledBy, Tag]);
3305
+ return (jsxRuntime.jsx(ListGroupContext.Provider, { value: Tag, children: jsxRuntime.jsx(Tag, Object.assign({ className: classNames(generateClasses, className), style: style }, labelProps, dataAttributes, { children: children })) }));
2424
3306
  }
2425
3307
  var DListGroup = Object.assign(DListGroup$1, {
2426
3308
  Item: DListGroupItem,
@@ -2606,14 +3488,20 @@ function DPopover({ children, renderComponent, open, setOpen, adjustContentToRen
2606
3488
  }), ref: refs.setFloating, style: floatingStyles, "aria-labelledby": headingId }, getFloatingProps(), { children: children })) }))] })));
2607
3489
  }
2608
3490
 
2609
- function DProgress({ className, style, currentValue, minValue = 0, maxValue = 100, hideCurrentValue = false, enableStripedAnimation = false, height, dataAttributes, }) {
2610
- const percentage = React.useMemo(() => (Math.round((currentValue * 100) / maxValue)), [currentValue, maxValue]);
3491
+ function DProgress({ className, style, currentValue, minValue = 0, maxValue = 100, hideCurrentValue = false, enableStripedAnimation = false, height, ariaLabel, ariaLabelledBy, dataAttributes, }) {
3492
+ const percentage = React.useMemo(() => {
3493
+ const range = maxValue - minValue;
3494
+ if (range <= 0) {
3495
+ return 0;
3496
+ }
3497
+ return Math.round(((currentValue - minValue) * 100) / range);
3498
+ }, [currentValue, minValue, maxValue]);
2611
3499
  const formatProgress = React.useMemo(() => `${percentage}%`, [percentage]);
2612
3500
  const generateClasses = React.useMemo(() => ({
2613
3501
  'progress-bar': true,
2614
3502
  'progress-bar-striped progress-bar-animated': enableStripedAnimation,
2615
3503
  }), [enableStripedAnimation]);
2616
- return (jsxRuntime.jsx("div", Object.assign({ className: classNames('progress', className), style: Object.assign({ height }, style) }, dataAttributes, { children: jsxRuntime.jsx("div", { className: classNames(generateClasses), role: "progressbar", "aria-label": "Progress bar", style: { width: formatProgress }, "aria-valuenow": currentValue, "aria-valuemin": minValue, "aria-valuemax": maxValue, children: !hideCurrentValue && formatProgress }) })));
3504
+ return (jsxRuntime.jsx("div", Object.assign({ className: classNames('progress', className), style: Object.assign({ height }, style) }, dataAttributes, { children: jsxRuntime.jsx("div", { className: classNames(generateClasses), role: "progressbar", "aria-label": ariaLabelledBy ? undefined : (ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : 'Progress bar'), "aria-labelledby": ariaLabelledBy, style: { width: formatProgress }, "aria-valuenow": currentValue, "aria-valuemin": minValue, "aria-valuemax": maxValue, children: !hideCurrentValue && formatProgress }) })));
2617
3505
  }
2618
3506
 
2619
3507
  function DStepper$2({ options, currentStep, iconSuccess: iconSuccessProp, iconSuccessFamilyClass, iconSuccessFamilyPrefix, iconSuccessMaterialStyle, vertical = false, completed, alignStart = false, className, style, }) {
@@ -2717,14 +3605,15 @@ function DTooltip({ className, childrenClassName, style, offSet = ARROW_HEIGHT +
2717
3605
  function DTimeline({ className, style, dataAttributes, items, }) {
2718
3606
  return (jsxRuntime.jsx("div", Object.assign({ style: style, className: classNames('d-timeline', className) }, dataAttributes, { children: items.map((item, index) => (jsxRuntime.jsxs("div", { className: classNames('d-timeline-item', {
2719
3607
  [`d-timeline-item-${item.status}`]: item.status,
2720
- }), children: [jsxRuntime.jsx("div", { className: "d-timeline-item-connector" }), jsxRuntime.jsx("div", { className: "d-timeline-item-icon", children: jsxRuntime.jsx(DIcon, { icon: item.icon || 'check', size: "1rem" }) }), jsxRuntime.jsxs("div", { className: "d-timeline-item-content", children: [jsxRuntime.jsx("div", { className: "d-timeline-item-title", children: item.title }), item.description && jsxRuntime.jsx("div", { className: "d-timeline-item-description", children: item.description }), item.time && jsxRuntime.jsx("div", { className: "d-timeline-item-time", children: item.time }), item.children] })] }, index))) })));
3608
+ }), children: [jsxRuntime.jsx("div", { className: "d-timeline-item-connector" }), jsxRuntime.jsx("div", { className: "d-timeline-item-icon", children: jsxRuntime.jsx(DIcon, { icon: item.icon || 'Check', size: "1rem" }) }), jsxRuntime.jsxs("div", { className: "d-timeline-item-content", children: [jsxRuntime.jsx("div", { className: "d-timeline-item-title", children: item.title }), item.description && jsxRuntime.jsx("div", { className: "d-timeline-item-description", children: item.description }), item.time && jsxRuntime.jsx("div", { className: "d-timeline-item-time", children: item.time }), item.children] })] }, index))) })));
2721
3609
  }
2722
3610
 
2723
3611
  const TabContext = React.createContext(undefined);
3612
+ const TabsStateContext = React.createContext(undefined);
2724
3613
  function useTabContext() {
2725
3614
  const context = React.useContext(TabContext);
2726
3615
  if (context === undefined) {
2727
- throw new Error('useTabContext was used outside of MTab');
3616
+ throw new Error('useTabContext was used outside of DTabs');
2728
3617
  }
2729
3618
  return context;
2730
3619
  }
@@ -2734,33 +3623,44 @@ function DTabContent({ tab, children, className, style, }) {
2734
3623
  if (!isSelected(tab)) {
2735
3624
  return null;
2736
3625
  }
2737
- return (jsxRuntime.jsx("div", { className: classNames('tab-pane fade show active', className), id: `${tab}Pane`, role: "tabpanel", tabIndex: 0, "aria-labelledby": `${tab}Tab`, style: style, children: children }));
3626
+ return (jsxRuntime.jsx("div", { className: classNames('tab-pane fade show active', className), id: `${tab}Pane`, role: "tabpanel", tabIndex: 0, "aria-labelledby": `${tab}Tab`, style: style, children: jsxRuntime.jsx(TabsStateContext.Provider, { value: undefined, children: children }) }));
2738
3627
  }
2739
3628
 
2740
- function DTabs$1({ children, defaultSelected, onChange, options, className, classNameTab, style, vertical, variant = 'underline', dataAttributes, ariaLabel, ariaLabelledBy, }) {
3629
+ /**
3630
+ * Owns the selected tab so the tab bar (`DTabs`) and its panels (`DTabs.Tab`)
3631
+ * can live anywhere inside it, e.g. the bar in a header and the panels in
3632
+ * another column.
3633
+ */
3634
+ function DTabsProvider({ defaultSelected, children, }) {
2741
3635
  const [selected, setSelected] = React.useState(defaultSelected);
3636
+ React.useEffect(() => {
3637
+ setSelected(defaultSelected);
3638
+ }, [defaultSelected]);
3639
+ const isSelected = React.useCallback((tab) => (selected === tab), [selected]);
3640
+ const state = React.useMemo(() => ({ selected, setSelected }), [selected]);
3641
+ const value = React.useMemo(() => ({ isSelected }), [isSelected]);
3642
+ return (jsxRuntime.jsx(TabsStateContext.Provider, { value: state, children: jsxRuntime.jsx(TabContext.Provider, { value: value, children: children }) }));
3643
+ }
3644
+
3645
+ function DTabs$1({ children, defaultSelected, onChange, options, className, classNameTab, classNameContent, style, vertical, variant = 'underline', dataAttributes, ariaLabel, ariaLabelledBy, }) {
3646
+ var _a;
3647
+ const shared = React.useContext(TabsStateContext);
3648
+ const [ownSelected, setOwnSelected] = React.useState(defaultSelected);
3649
+ const selected = shared
3650
+ ? shared.selected
3651
+ : ownSelected !== null && ownSelected !== void 0 ? ownSelected : (_a = options.find((opt) => !opt.disabled)) === null || _a === void 0 ? void 0 : _a.tab;
3652
+ const setSelected = shared ? shared.setSelected : setOwnSelected;
2742
3653
  const onSelect = React.useCallback((option) => {
2743
3654
  if (option.tab) {
2744
3655
  setSelected(option.tab);
2745
3656
  }
2746
3657
  onChange === null || onChange === void 0 ? void 0 : onChange(option);
2747
- }, [onChange]);
3658
+ }, [onChange, setSelected]);
2748
3659
  React.useEffect(() => {
2749
- setSelected(defaultSelected);
3660
+ setOwnSelected(defaultSelected);
2750
3661
  }, [defaultSelected]);
2751
3662
  const generateClasses = React.useMemo(() => (Object.assign({ nav: true, 'd-tabs-nav-vertical': vertical && variant !== 'tabs', [`nav-${variant}`]: true }, className && { [className]: true })), [vertical, variant, className]);
2752
3663
  const tabRefs = React.useRef([]);
2753
- // Always holds the latest `options` without needing to be a dependency:
2754
- // `options` is commonly passed as an inline array literal (e.g.
2755
- // `options={[{ label: 'SMS', tab: 'sms' }, ...]}`), so it's a new array
2756
- // reference on every parent render even when its content hasn't changed.
2757
- // Reading it from this ref (updated synchronously on every render) lets
2758
- // the focus effect below react only to `selected` changing.
2759
- const optionsRef = React.useRef(options);
2760
- optionsRef.current = options;
2761
- React.useEffect(() => {
2762
- tabRefs.current = options.map((_, i) => tabRefs.current[i] || React.createRef());
2763
- }, [options]);
2764
3664
  // Ensure selected is never disabled
2765
3665
  React.useEffect(() => {
2766
3666
  if (options.length === 0)
@@ -2771,29 +3671,16 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2771
3671
  if (firstEnabled)
2772
3672
  setSelected(firstEnabled.tab);
2773
3673
  }
2774
- }, [options, selected]);
2775
- // Declarative focus management. Wrapped in `useCallback` with an empty
2776
- // dependency array since it only reads from the `tabRefs` ref, so its
2777
- // identity stays stable across renders and it can safely be used as an
2778
- // effect dependency below.
3674
+ }, [options, selected, setSelected]);
3675
+ // Focus only moves in response to the user: arrow keys and clicks call
3676
+ // this directly. Changes to `selected` that don't come from an interaction
3677
+ // (mount, a new `defaultSelected`, the disabled-tab fallback above) leave
3678
+ // focus where it is, so the page doesn't scroll to the tablist and no other
3679
+ // element (a search field, an OTP input) loses focus.
2779
3680
  const focusTab = React.useCallback((idx) => {
2780
3681
  var _a;
2781
- if ((_a = tabRefs.current[idx]) === null || _a === void 0 ? void 0 : _a.current) {
2782
- tabRefs.current[idx].current.focus();
2783
- }
3682
+ (_a = tabRefs.current[idx]) === null || _a === void 0 ? void 0 : _a.focus();
2784
3683
  }, []);
2785
- // Focus selected tab when selected changes.
2786
- // Reads `options` from `optionsRef` (see comment above) instead of
2787
- // depending on `options` directly, so a parent re-render that merely
2788
- // creates a new `options` reference with identical content doesn't call
2789
- // `focusTab` again and steal focus away from unrelated elements on the
2790
- // page (e.g. an OTP input).
2791
- React.useEffect(() => {
2792
- const idx = optionsRef.current.findIndex((opt) => opt.tab === selected && !opt.disabled);
2793
- if (idx !== -1) {
2794
- focusTab(idx);
2795
- }
2796
- }, [selected, focusTab]);
2797
3684
  const handleKeyDown = React.useCallback((idx, e) => {
2798
3685
  const count = options.length;
2799
3686
  if (count === 0)
@@ -2822,7 +3709,7 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2822
3709
  }
2823
3710
  }
2824
3711
  }
2825
- }, [options, vertical, focusTab]);
3712
+ }, [options, vertical, focusTab, setSelected]);
2826
3713
  let tablistProps = {};
2827
3714
  if (ariaLabelledBy) {
2828
3715
  tablistProps = { 'aria-labelledby': ariaLabelledBy };
@@ -2839,11 +3726,17 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2839
3726
  'd-tabs-column': !vertical || variant === 'tabs',
2840
3727
  }), style: style }, dataAttributes, { children: [jsxRuntime.jsx("ul", Object.assign({ className: classNames(generateClasses), role: "tablist", "aria-orientation": vertical ? 'vertical' : undefined }, tablistProps, { children: options.map((option, idx) => {
2841
3728
  const isTabSelected = !!option.tab && option.tab === selected;
2842
- return (jsxRuntime.jsx("li", { role: "presentation", className: "nav-item", children: jsxRuntime.jsx("button", { ref: tabRefs.current[idx], id: `${option.tab}Tab`, className: classNames('nav-link', { active: isTabSelected }, classNameTab), type: "button", role: "tab", "aria-controls": `${option.tab}Pane`, "aria-selected": isTabSelected, tabIndex: isTabSelected ? 0 : -1, disabled: option.disabled, onClick: () => onSelect(option), onKeyDown: (e) => handleKeyDown(idx, e), children: option.label }) }, option.tab));
2843
- }) })), jsxRuntime.jsx("div", { className: "d-tabs-content tab-content", children: children })] })) }));
3729
+ return (jsxRuntime.jsx("li", { role: "presentation", className: "nav-item", children: jsxRuntime.jsx("button", { ref: (element) => {
3730
+ tabRefs.current[idx] = element;
3731
+ }, id: `${option.tab}Tab`, className: classNames('nav-link', { active: isTabSelected }, classNameTab), type: "button", role: "tab", "aria-controls": `${option.tab}Pane`, "aria-selected": isTabSelected, tabIndex: isTabSelected ? 0 : -1, disabled: option.disabled, onClick: () => {
3732
+ focusTab(idx);
3733
+ onSelect(option);
3734
+ }, onKeyDown: (e) => handleKeyDown(idx, e), children: option.label }) }, option.tab));
3735
+ }) })), React.Children.toArray(children).length > 0 && (jsxRuntime.jsx("div", { className: classNames('d-tabs-content tab-content', classNameContent), children: children }))] })) }));
2844
3736
  }
2845
3737
  var DTabs = Object.assign(DTabs$1, {
2846
3738
  Tab: DTabContent,
3739
+ Provider: DTabsProvider,
2847
3740
  });
2848
3741
 
2849
3742
  function DToastHeader({ children, className, style }) {
@@ -2854,8 +3747,13 @@ function DToastBody({ children, className, style }) {
2854
3747
  return (jsxRuntime.jsx("div", { className: classNames('toast-body', className), style: style, children: children }));
2855
3748
  }
2856
3749
 
2857
- function DToast$1({ children, className, style, dataAttributes, }) {
2858
- return (jsxRuntime.jsx("div", Object.assign({ className: classNames('toast', className), role: "alert", "aria-live": "assertive", "aria-atomic": "true", style: style }, dataAttributes, { children: children })));
3750
+ const LIVE_REGION_ATTRIBUTES = {
3751
+ alert: { role: 'alert', 'aria-live': 'assertive', 'aria-atomic': 'true' },
3752
+ status: { role: 'status', 'aria-live': 'polite', 'aria-atomic': 'true' },
3753
+ none: {},
3754
+ };
3755
+ function DToast$1({ children, role = 'alert', className, style, dataAttributes, }) {
3756
+ return (jsxRuntime.jsx("div", Object.assign({ className: classNames('toast', className) }, LIVE_REGION_ATTRIBUTES[role], { style: style }, dataAttributes, { children: children })));
2859
3757
  }
2860
3758
  var DToast = Object.assign(DToast$1, {
2861
3759
  Header: DToastHeader,
@@ -2894,19 +3792,19 @@ function useDToast() {
2894
3792
  if (typeof data === 'function') {
2895
3793
  return reactHotToast.toast.custom(data, toastProps);
2896
3794
  }
2897
- const { title, description, icon, closeIcon, timestamp, color, } = data;
3795
+ const { title, description, icon, closeIcon, timestamp, color, role, closeAriaLabel = 'Close', } = data;
2898
3796
  return reactHotToast.toast.custom(({ id, visible }) => {
2899
3797
  if (!visible) {
2900
3798
  return null;
2901
3799
  }
2902
3800
  if (!description) {
2903
- return (jsxRuntime.jsx(DToast, { className: classNames({
3801
+ return (jsxRuntime.jsx(DToast, { role: role, className: classNames({
2904
3802
  [`toast-${color}`]: !!color,
2905
- }, 'show'), children: jsxRuntime.jsxs(DToast.Body, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }) }));
3803
+ }, 'show'), children: jsxRuntime.jsxs(DToast.Body, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": closeAriaLabel, onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }) }));
2906
3804
  }
2907
- return (jsxRuntime.jsxs(DToast, { className: classNames({
3805
+ return (jsxRuntime.jsxs(DToast, { role: role, className: classNames({
2908
3806
  [`toast-${color}`]: !!color,
2909
- }, 'show'), children: [jsxRuntime.jsxs(DToast.Header, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), timestamp && (jsxRuntime.jsx("small", { className: "toast-timestamp", children: timestamp })), jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }), jsxRuntime.jsx(DToast.Body, { children: jsxRuntime.jsx("span", { children: description }) })] }));
3807
+ }, 'show'), children: [jsxRuntime.jsxs(DToast.Header, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), timestamp && (jsxRuntime.jsx("small", { className: "toast-timestamp", children: timestamp })), jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": closeAriaLabel, onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }), jsxRuntime.jsx(DToast.Body, { children: jsxRuntime.jsx("span", { children: description }) })] }));
2910
3808
  }, toastProps);
2911
3809
  }, [xLg]);
2912
3810
  return {
@@ -3142,7 +4040,7 @@ function DInputPhone(_a, ref) {
3142
4040
  placeholder,
3143
4041
  valid,
3144
4042
  ]);
3145
- const labelComponent = React.useMemo(() => (jsxRuntime.jsx("label", { htmlFor: id, children: label })), [
4043
+ const labelComponent = React.useMemo(() => (jsxRuntime.jsx(DFormLabel, { htmlFor: id, children: label })), [
3146
4044
  id,
3147
4045
  label,
3148
4046
  ]);
@@ -3156,7 +4054,16 @@ function DInputPhone(_a, ref) {
3156
4054
  inputComponent,
3157
4055
  labelComponent,
3158
4056
  ]);
3159
- return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('d-input-phone', className), style: style }, dataAttributes, { children: [label && !floatingLabel && labelComponent, jsxRuntime.jsxs("div", { className: classNames({
4057
+ if (process.env.NODE_ENV !== 'production') {
4058
+ warnLabelUsage({
4059
+ component: 'DInputPhone',
4060
+ label,
4061
+ hasAccessibleName: !!inputProps['aria-label'] || !!inputProps['aria-labelledby'],
4062
+ accessibleNameProp: 'aria-label',
4063
+ floatingLabel,
4064
+ });
4065
+ }
4066
+ return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('d-input-phone', className), style: style }, dataAttributes, { children: [hasLabelContent(label) && !floatingLabel && labelComponent, jsxRuntime.jsxs("div", { className: classNames({
3160
4067
  [`input-group-${size}`]: !!size,
3161
4068
  'input-group': true,
3162
4069
  'has-validation': invalid || valid,
@@ -3623,11 +4530,11 @@ function DOtp({ className, action, isLoading, otpSize = 6, texts = TEXT_PROPS, s
3623
4530
  }, loading: isLoading || isSubmitting }), jsxRuntime.jsx("p", { className: "d-otp-contact", children: texts.contact })] })] })] }));
3624
4531
  }
3625
4532
 
3626
- function DefaultErrorBoundary({ resetErrorBoundary }) {
3627
- return (jsxRuntime.jsx(DAlert, { color: "danger", showClose: false, children: jsxRuntime.jsxs("div", { className: "d-error-boundary-content", children: [jsxRuntime.jsx("span", { children: "An unexpected error occurred." }), jsxRuntime.jsx(DButton, { color: "secondary", variant: "outline", size: "sm", onClick: resetErrorBoundary, children: "Retry" })] }) }));
4533
+ function DefaultErrorBoundary({ resetErrorBoundary, message = 'An unexpected error occurred.', retryMessage = 'Retry', }) {
4534
+ return (jsxRuntime.jsx(DAlert, { color: "danger", showClose: false, children: jsxRuntime.jsxs("div", { className: "d-error-boundary-content", children: [jsxRuntime.jsx("span", { children: message }), jsxRuntime.jsx(DButton, { color: "secondary", variant: "outline", size: "sm", onClick: resetErrorBoundary, children: retryMessage })] }) }));
3628
4535
  }
3629
4536
 
3630
- function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, children, }) {
4537
+ function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, messages, children, }) {
3631
4538
  const handleError = React.useCallback((error, info) => {
3632
4539
  // eslint-disable-next-line no-console
3633
4540
  console.error(`[DErrorBoundary${name ? `:${name}` : ''}]`, reactErrorBoundary.getErrorMessage(error), info);
@@ -3636,8 +4543,8 @@ function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, children,
3636
4543
  const FallbackRender = React.useCallback((props) => {
3637
4544
  if (fallback)
3638
4545
  return fallback(props);
3639
- return (jsxRuntime.jsx(DefaultErrorBoundary, { resetErrorBoundary: props.resetErrorBoundary }));
3640
- }, [fallback]);
4546
+ return (jsxRuntime.jsx(DefaultErrorBoundary, { resetErrorBoundary: props.resetErrorBoundary, message: messages === null || messages === void 0 ? void 0 : messages.error, retryMessage: messages === null || messages === void 0 ? void 0 : messages.retry }));
4547
+ }, [fallback, messages]);
3641
4548
  return (jsxRuntime.jsx(reactErrorBoundary.ErrorBoundary, { resetKeys: resetKeys, onReset: onReset, onError: handleError, fallbackRender: FallbackRender, children: children }));
3642
4549
  }
3643
4550
 
@@ -3658,6 +4565,11 @@ function render(renderable) {
3658
4565
  return null;
3659
4566
  return typeof renderable === 'function' ? renderable() : renderable;
3660
4567
  }
4568
+ function isEmpty(data) {
4569
+ if (Array.isArray(data))
4570
+ return data.length === 0;
4571
+ return data === null || data === undefined;
4572
+ }
3661
4573
  function DDataStateWrapper({ isLoading, isError, data, onRetry, messages, renderLoading, renderEmpty, renderError, children, }) {
3662
4574
  // 1. Loading
3663
4575
  if (isLoading) {
@@ -3671,13 +4583,15 @@ function DDataStateWrapper({ isLoading, isError, data, onRetry, messages, render
3671
4583
  return render(renderError);
3672
4584
  return (jsxRuntime.jsx(ErrorState, { onRetry: onRetry, message: messages === null || messages === void 0 ? void 0 : messages.error, retryMessage: messages === null || messages === void 0 ? void 0 : messages.retry }));
3673
4585
  }
3674
- // 3. Empty
3675
- if (!(data === null || data === void 0 ? void 0 : data.length)) {
4586
+ // 3. Empty: no items for a collection, null/undefined for a single resource
4587
+ if (isEmpty(data)) {
3676
4588
  if (renderEmpty)
3677
4589
  return render(renderEmpty);
3678
4590
  return (jsxRuntime.jsx(EmptyState, { message: messages === null || messages === void 0 ? void 0 : messages.empty }));
3679
4591
  }
3680
- // 4. Success
4592
+ // 4. Success: the render prop gets the same shape it was given
4593
+ // Both overloads pair `data` with its own `children` signature, so the
4594
+ // value is handed back exactly as it was received.
3681
4595
  return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(data) });
3682
4596
  }
3683
4597
 
@@ -3762,6 +4676,59 @@ function DConfirmModalContainer({ nodeId }) {
3762
4676
  return reactDom.createPortal(jsxRuntime.jsx(framerMotion.AnimatePresence, { children: entries.map((entry) => (jsxRuntime.jsxs(framerMotion.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0, transition: { delay: 0.3 } }, transition: { duration: 0.15, ease: 'linear' }, children: [jsxRuntime.jsx("div", { className: "backdrop backdrop-confirm-modal", onClick: entry.onCloseAction, role: "presentation" }), jsxRuntime.jsx(DConfirmModalUI, { entry: entry })] }, entry.id))) }), portalNode);
3763
4677
  }
3764
4678
 
4679
+ /**
4680
+ * The lucide-react icons Dynamic UI renders on its own, without the consumer
4681
+ * asking for them by name.
4682
+ *
4683
+ * Two sources feed this list:
4684
+ *
4685
+ * - The `iconMap` defaults of `DContextProvider` (`src/contexts/DContext.tsx`),
4686
+ * used by close buttons, chevrons, alert states, input adornments and the
4687
+ * stepper checkmark.
4688
+ * - Names hard-coded in component JSX or in a prop default, such as
4689
+ * `DDropdown`'s toggle or `DVoucher`'s action buttons.
4690
+ *
4691
+ * Any bundler-level optimisation that narrows lucide-react down to the icons a
4692
+ * widget actually uses must keep this set, or Dynamic UI components break for
4693
+ * reasons the widget author cannot see in their own source. That is why the
4694
+ * list ships in the package as `dist/icons-core.json` and is exported here.
4695
+ *
4696
+ * `src/icons/coreIcons.spec.ts` scans `src/components/**` and fails when an
4697
+ * internally used icon name is missing from this list or absent from
4698
+ * lucide-react, so the two cannot drift apart.
4699
+ */
4700
+ const CORE_LUCIDE_ICONS = [
4701
+ // iconMap defaults — src/contexts/DContext.tsx
4702
+ 'AlertCircle',
4703
+ 'AlertTriangle',
4704
+ 'Calendar',
4705
+ 'Check',
4706
+ 'CheckCircle',
4707
+ 'ChevronDown',
4708
+ 'ChevronLeft',
4709
+ 'ChevronRight',
4710
+ 'ChevronUp',
4711
+ 'Eye',
4712
+ 'EyeOff',
4713
+ 'Info',
4714
+ 'Minus',
4715
+ 'Plus',
4716
+ 'Search',
4717
+ 'Upload',
4718
+ 'X',
4719
+ // Hard-coded in component JSX or in a prop default
4720
+ 'Circle',
4721
+ 'CircleCheck',
4722
+ 'CircleCheckBig',
4723
+ 'Download',
4724
+ 'FileText',
4725
+ 'MoreVertical',
4726
+ 'Paperclip',
4727
+ 'RefreshCw',
4728
+ 'Share2',
4729
+ 'Trash',
4730
+ ];
4731
+
3765
4732
  Object.defineProperty(exports, "getErrorMessage", {
3766
4733
  enumerable: true,
3767
4734
  get: function () { return reactErrorBoundary.getErrorMessage; }
@@ -3770,6 +4737,7 @@ Object.defineProperty(exports, "useErrorBoundary", {
3770
4737
  enumerable: true,
3771
4738
  get: function () { return reactErrorBoundary.useErrorBoundary; }
3772
4739
  });
4740
+ exports.CORE_LUCIDE_ICONS = CORE_LUCIDE_ICONS;
3773
4741
  exports.DAlert = DAlert;
3774
4742
  exports.DAvatar = DAvatar;
3775
4743
  exports.DBadge = DBadge;
@@ -3831,6 +4799,7 @@ exports.DStepperDesktop = DStepper$2;
3831
4799
  exports.DStepperMobile = DStepper$1;
3832
4800
  exports.DTabContent = DTabContent;
3833
4801
  exports.DTabs = DTabs;
4802
+ exports.DTabsProvider = DTabsProvider;
3834
4803
  exports.DTimeline = DTimeline;
3835
4804
  exports.DToast = DToast;
3836
4805
  exports.DToastContainer = DToastContainer;