@ryuzaki13/react-foundation-ui 1.0.23 → 1.1.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 (196) hide show
  1. package/README.md +86 -0
  2. package/dist/image/index.d.ts +2 -0
  3. package/dist/image/index.d.ts.map +1 -1
  4. package/dist/image/index.js +337 -121
  5. package/dist/image/index.js.map +1 -1
  6. package/dist/image/lib/buildImageViewerSlides.d.ts +8 -0
  7. package/dist/image/lib/buildImageViewerSlides.d.ts.map +1 -0
  8. package/dist/image/lib/resolveImageViewerLabels.d.ts +24 -0
  9. package/dist/image/lib/resolveImageViewerLabels.d.ts.map +1 -0
  10. package/dist/image/model/imageViewerTypes.d.ts +70 -0
  11. package/dist/image/model/imageViewerTypes.d.ts.map +1 -0
  12. package/dist/image/ui/ImageViewer.d.ts +12 -0
  13. package/dist/image/ui/ImageViewer.d.ts.map +1 -0
  14. package/dist/styles.css +1 -1
  15. package/package.json +6 -1
  16. package/src/styles/config/_index.scss +1 -0
  17. package/storybook-static/assets/{Badge-Dk1ppt6h.js → Badge-kXFWHX1o.js} +1 -1
  18. package/storybook-static/assets/{Badge.stories-DGed-OBQ.js → Badge.stories-fhovi8Mi.js} +1 -1
  19. package/storybook-static/assets/{Button-BrdeQlDt.js → Button-BzcIPScf.js} +1 -1
  20. package/storybook-static/assets/{Button-c4nDxtrg.js → Button-DuNV63ZS.js} +1 -1
  21. package/storybook-static/assets/{Button.stories-CYjvIvUW.js → Button.stories-DehjFhSM.js} +1 -1
  22. package/storybook-static/assets/{CheckBox-6Qr3jWMy.js → CheckBox-BmyFh4i7.js} +1 -1
  23. package/storybook-static/assets/{CheckBox-rqQjSYV-.js → CheckBox-D6mLxi2P.js} +1 -1
  24. package/storybook-static/assets/{CheckBox.stories-Q78_xCB-.js → CheckBox.stories-Cb7RC2GA.js} +1 -1
  25. package/storybook-static/assets/{Color-KMSTOMOX-BMBFTpCU.js → Color-KMSTOMOX-CX-Xl_mX.js} +1 -1
  26. package/storybook-static/assets/{ContextMenu-Dy6qjxlr.js → ContextMenu-Cef_K-t5.js} +1 -1
  27. package/storybook-static/assets/{ContextMenu.stories-ArVPg2Iz.js → ContextMenu.stories-AZHC-8P9.js} +1 -1
  28. package/storybook-static/assets/{DateInput-eu7DXgSU.js → DateInput-C55_ANq6.js} +1 -1
  29. package/storybook-static/assets/{DateInput-Dk1l_hxJ.js → DateInput-DSVMF1gB.js} +1 -1
  30. package/storybook-static/assets/{DateInput.stories-GCltuReT.js → DateInput.stories-CsC2qa2g.js} +1 -1
  31. package/storybook-static/assets/{DateRangePresetSelect-aiAJs_u-.js → DateRangePresetSelect-B8rYDJ5X.js} +1 -1
  32. package/storybook-static/assets/{DateRangePresetSelect-2dHNAnUX.js → DateRangePresetSelect-CIFpis74.js} +1 -1
  33. package/storybook-static/assets/{DateRangePresetSelect.stories-BXPIK_Co.js → DateRangePresetSelect.stories-BjtAEDa9.js} +1 -1
  34. package/storybook-static/assets/{Detail-C-0qKqgn.js → Detail-BgqY0SrV.js} +1 -1
  35. package/storybook-static/assets/{Detail.stories-BpD2YoZq.js → Detail.stories-Co8o1gqx.js} +1 -1
  36. package/storybook-static/assets/{Dialog-BHOqDS4r.js → Dialog-DoekeYCP.js} +1 -1
  37. package/storybook-static/assets/{Dialog.stories-DbY2IVcJ.js → Dialog.stories-v5TQS_1-.js} +1 -1
  38. package/storybook-static/assets/{Disclosure-BIS573Xf.js → Disclosure-E7Bm1F1a.js} +1 -1
  39. package/storybook-static/assets/{Disclosure.stories-BEXgpOqT.js → Disclosure.stories-CUYuBYh6.js} +1 -1
  40. package/storybook-static/assets/{DocsRenderer-JROSPFPF-DBLxlIku.js → DocsRenderer-JROSPFPF-9II2vBP-.js} +2 -2
  41. package/storybook-static/assets/{DropZone-DCOH76_B.js → DropZone-COhh7w0P.js} +1 -1
  42. package/storybook-static/assets/{DropZone.stories-D1SI6gcY.js → DropZone.stories-XU7u589t.js} +1 -1
  43. package/storybook-static/assets/{DropdownMenu-mb1Hg5Gl.js → DropdownMenu-3ney3Ng2.js} +1 -1
  44. package/storybook-static/assets/{ExpandableActionPanel.stories-CXJMi0D0.js → ExpandableActionPanel.stories-DJOZxMv_.js} +2 -2
  45. package/storybook-static/assets/{Flex-D1ZBJLmP.js → Flex-BtfZvPDu.js} +1 -1
  46. package/storybook-static/assets/{Flex-aMhUlmFA.js → Flex-CN6FIOe5.js} +1 -1
  47. package/storybook-static/assets/{Flex.stories-Cq9hTA2s.js → Flex.stories-BCZhfSVz.js} +1 -1
  48. package/storybook-static/assets/{Grid-DrwSe074.js → Grid-CdgOXXxt.js} +1 -1
  49. package/storybook-static/assets/{Grid-D4xblGva.js → Grid-D_d69WcK.js} +1 -1
  50. package/storybook-static/assets/{Grid.stories-CsQlxLGT.js → Grid.stories-DQoSH4q8.js} +1 -1
  51. package/storybook-static/assets/{ImageCropper.stories-Da0fJQ1n.js → ImageCropper.stories-BJiSLQAw.js} +1 -1
  52. package/storybook-static/assets/{ImageRenderer-dXWklz9J.js → ImageRenderer-D6M62W6a.js} +1 -1
  53. package/storybook-static/assets/{ImageRenderer.stories-C2l796Hd.js → ImageRenderer.stories-BZFA51MD.js} +1 -1
  54. package/storybook-static/assets/{ImageView.stories-Cee_V0zr.js → ImageView.stories-DFvdAL1U.js} +1 -1
  55. package/storybook-static/assets/ImageViewer-kKDkAY87.css +1 -0
  56. package/storybook-static/assets/ImageViewer.stories-BzUxxQAO.js +72 -0
  57. package/storybook-static/assets/{Input-DoTXagjB.js → Input-BrEoa-ul.js} +1 -1
  58. package/storybook-static/assets/Input-DD9fx2Sx.js +3 -0
  59. package/storybook-static/assets/{Input.stories-mPAKrIP1.js → Input.stories-BmsoCBN4.js} +1 -1
  60. package/storybook-static/assets/{InputDateTime-BgIJNCpv.js → InputDateTime-Cdmc7rX2.js} +1 -1
  61. package/storybook-static/assets/{InputDateTime.stories-Ck0MCRBJ.js → InputDateTime.stories-BhjQejJD.js} +1 -1
  62. package/storybook-static/assets/{InputFile-BZqKG2Zr.js → InputFile-BJfA5xNs.js} +1 -1
  63. package/storybook-static/assets/{InputFile.stories-QbTuArh1.js → InputFile.stories-BIJaIWlj.js} +1 -1
  64. package/storybook-static/assets/{InputFiles-BUoDhUef.js → InputFiles-DORMd5fm.js} +1 -1
  65. package/storybook-static/assets/{InputFiles-ChhwyB9P.js → InputFiles-ZK4wdii7.js} +1 -1
  66. package/storybook-static/assets/{InputFiles.stories-PD0pr8y6.js → InputFiles.stories-CVr0ENgC.js} +1 -1
  67. package/storybook-static/assets/{InputImage-YxBujSop.js → InputImage-Dk4j2KMa.js} +1 -1
  68. package/storybook-static/assets/{InputImage.stories-CHjyI709.js → InputImage.stories-Dtx-ynMr.js} +1 -1
  69. package/storybook-static/assets/{LayoutPicker-DJT0cL8d.js → LayoutPicker-CCvATGnT.js} +1 -1
  70. package/storybook-static/assets/{LayoutPicker.stories-DvTvYmfw.js → LayoutPicker.stories-MUwtf1U1.js} +1 -1
  71. package/storybook-static/assets/{Listbox-D7w7HJNg.js → Listbox-TfXOnGNL.js} +1 -1
  72. package/storybook-static/assets/{Listbox.stories-D4EMWR6l.js → Listbox.stories-Bxx8eTZ9.js} +1 -1
  73. package/storybook-static/assets/{Modal-BCn0aKBf.js → Modal-CeXVAufO.js} +1 -1
  74. package/storybook-static/assets/{Modal.stories-BgnMPuDy.js → Modal.stories-DmnMwG9h.js} +1 -1
  75. package/storybook-static/assets/{MultiSelect-CZW1VSbD.js → MultiSelect-Bq5Xj6i5.js} +1 -1
  76. package/storybook-static/assets/{MultiSelect-D7N8euwu.js → MultiSelect-CdEQ7LE0.js} +1 -1
  77. package/storybook-static/assets/{MultiSelect.stories-DV9nyDoE.js → MultiSelect.stories-BgjuPpG-.js} +1 -1
  78. package/storybook-static/assets/{NotificationsHost-DJILaEyi.js → NotificationsHost-B6xWf_Ae.js} +1 -1
  79. package/storybook-static/assets/{NotificationsHost.stories-DlO0kPbm.js → NotificationsHost.stories-BmnJ3G3S.js} +2 -2
  80. package/storybook-static/assets/{ODataMultiSelect-C-DKYFLx.js → ODataMultiSelect-BGFMSKC-.js} +1 -1
  81. package/storybook-static/assets/{ODataMultiSelect.stories-DypA5OPM.js → ODataMultiSelect.stories-Cw3mS3ds.js} +1 -1
  82. package/storybook-static/assets/{ODataSelect-ANgknc8h.js → ODataSelect-9gP8ejA7.js} +1 -1
  83. package/storybook-static/assets/{ODataSelect-CMrLfCwp.js → ODataSelect-BTpRnB-o.js} +1 -1
  84. package/storybook-static/assets/{ODataSelect.stories-icXU3Kk-.js → ODataSelect.stories-C8XnS6W-.js} +1 -1
  85. package/storybook-static/assets/{ODataTreeMultiSelect-C6yg6DFE.js → ODataTreeMultiSelect-TXpOQMB8.js} +1 -1
  86. package/storybook-static/assets/{ODataTreeMultiSelect.stories-CT_tBJph.js → ODataTreeMultiSelect.stories-ikxs50iA.js} +1 -1
  87. package/storybook-static/assets/{ODataTreeSelect-hiY9TK6F.js → ODataTreeSelect-CvgrWRYS.js} +1 -1
  88. package/storybook-static/assets/{ODataTreeSelect.stories-DPNG2-gB.js → ODataTreeSelect.stories-CWuZaxIF.js} +1 -1
  89. package/storybook-static/assets/{PeriodSelect-5CQZq007.js → PeriodSelect-CVT2XzOd.js} +1 -1
  90. package/storybook-static/assets/{PeriodSelect.stories-DuwM__eX.js → PeriodSelect.stories-B5jJRelX.js} +1 -1
  91. package/storybook-static/assets/{Popover-BuB6NaNL.js → Popover-D9YhHWsi.js} +1 -1
  92. package/storybook-static/assets/{Popover.stories-DNHTB9O2.js → Popover.stories-CtEUOX4O.js} +1 -1
  93. package/storybook-static/assets/{PresetRangeDateInput-C6ke1QJN.js → PresetRangeDateInput-D3r0iIRk.js} +1 -1
  94. package/storybook-static/assets/{PresetRangeDateInput.stories-BiFR6FF_.js → PresetRangeDateInput.stories-Dcc1dGV0.js} +1 -1
  95. package/storybook-static/assets/{RadioButton-bmKQ4B96.js → RadioButton-CWjkmWu9.js} +1 -1
  96. package/storybook-static/assets/{RadioButton.stories-CZatWIEO.js → RadioButton.stories-BXXJWoHz.js} +1 -1
  97. package/storybook-static/assets/{RadioGroup-Vnr3qZKk.js → RadioGroup-Bjc2XTVk.js} +1 -1
  98. package/storybook-static/assets/{RadioGroup-diPPm8OV.js → RadioGroup-DCZdrs0C.js} +1 -1
  99. package/storybook-static/assets/{RadioGroup.stories-BY_ORcvQ.js → RadioGroup.stories-CsfVUt9l.js} +1 -1
  100. package/storybook-static/assets/{Select-Cq66yRYB.js → Select-BBFQZObL.js} +1 -1
  101. package/storybook-static/assets/Select-BGu6rVaZ.js +12 -0
  102. package/storybook-static/assets/{Select.stories-CevvmTWz.js → Select.stories-wIbcvZYi.js} +1 -1
  103. package/storybook-static/assets/{SelectOptionContent-C1-duJ6s.js → SelectOptionContent-DkTRrtFu.js} +1 -1
  104. package/storybook-static/assets/{SerializableSelect-CnwfEnur.js → SerializableSelect-CJtNM-c6.js} +1 -1
  105. package/storybook-static/assets/{SerializableSelect-C6jj7wLf.js → SerializableSelect-CdnRBVHB.js} +1 -1
  106. package/storybook-static/assets/{SerializableSelect.stories-DOTdXeIP.js → SerializableSelect.stories-DVwxpiTF.js} +1 -1
  107. package/storybook-static/assets/{SingleDateTimeInput-BtSG1SZF.js → SingleDateTimeInput-BK2_uv0z.js} +1 -1
  108. package/storybook-static/assets/SingleDateTimeInput.stories-DPH4R-h9.js +78 -0
  109. package/storybook-static/assets/{Slider-pb7TgPmP.js → Slider-f4VatXYq.js} +1 -1
  110. package/storybook-static/assets/{Slider.stories-Cj9sU7k1.js → Slider.stories-CBnmt8h9.js} +1 -1
  111. package/storybook-static/assets/{SliderInput-Dp4r4RqZ.js → SliderInput-W2HVfY9Z.js} +1 -1
  112. package/storybook-static/assets/{SliderInput.stories-DKV9C_mb.js → SliderInput.stories-B7wzoUbu.js} +1 -1
  113. package/storybook-static/assets/{Sortable-BbVzA3mH.js → Sortable-CCL1UMb_.js} +1 -1
  114. package/storybook-static/assets/{Sortable.stories-nttQ0wQV.js → Sortable.stories-B8SgFjl7.js} +1 -1
  115. package/storybook-static/assets/{Splitter-DQip0vEb.js → Splitter-BsBb1sgN.js} +1 -1
  116. package/storybook-static/assets/{Splitter.stories-B7IdmITM.js → Splitter.stories-CuBHIA8-.js} +1 -1
  117. package/storybook-static/assets/{StateSelect-BZeQfOZk.js → StateSelect-BENsGD07.js} +1 -1
  118. package/storybook-static/assets/{StateSelect.stories-Cia0oYUX.js → StateSelect.stories-byEPoQ2E.js} +2 -2
  119. package/storybook-static/assets/{Switch-DzxseHUx.js → Switch-DKXE4SVs.js} +1 -1
  120. package/storybook-static/assets/Switch.stories-BWQdJoJT.js +26 -0
  121. package/storybook-static/assets/{Table-lzcaRxDg.js → Table-D9w3ncNi.js} +1 -1
  122. package/storybook-static/assets/{Table.stories-sckp-lho.js → Table.stories-znqy5cLP.js} +1 -1
  123. package/storybook-static/assets/{Tabs-DyBBFEsD.js → Tabs-D_OO95mK.js} +1 -1
  124. package/storybook-static/assets/{Tabs.stories-C6-sZs4H.js → Tabs.stories-B2A1HYeH.js} +1 -1
  125. package/storybook-static/assets/TagInput.stories-DaBnABh8.js +18 -0
  126. package/storybook-static/assets/{Textarea-D1fEct22.js → Textarea-Hwyvp6ll.js} +1 -1
  127. package/storybook-static/assets/{Textarea.stories-ByJH2uhT.js → Textarea.stories-DD3Icl3x.js} +1 -1
  128. package/storybook-static/assets/{TimePanel-B5c9MfmC.js → TimePanel-CdpxiwMQ.js} +1 -1
  129. package/storybook-static/assets/{TimePanel.stories-DBgXVhz1.js → TimePanel.stories-ytXuqm9R.js} +1 -1
  130. package/storybook-static/assets/{Toggle-CHx3ZZfu.js → Toggle-Sdruy8bN.js} +1 -1
  131. package/storybook-static/assets/{Toggle.stories-g3Eq8oVl.js → Toggle.stories-qgeQLvRn.js} +1 -1
  132. package/storybook-static/assets/{TreeMultiSelect-T05Sk9sh.js → TreeMultiSelect-CKfmI37X.js} +1 -1
  133. package/storybook-static/assets/{TreeMultiSelect-CrKusouJ.js → TreeMultiSelect-t2FNc6CY.js} +1 -1
  134. package/storybook-static/assets/{TreeMultiSelect.stories-C85O7gDN.js → TreeMultiSelect.stories-k1NBsKij.js} +1 -1
  135. package/storybook-static/assets/{TreePickerBase-VJSaTBqo.js → TreePickerBase-DXv--vBR.js} +2 -2
  136. package/storybook-static/assets/{TreeSelect-DQmAaKsJ.js → TreeSelect-DYHpUNGC.js} +1 -1
  137. package/storybook-static/assets/{TreeSelect-Dw-svVvg.js → TreeSelect-DgKArbMR.js} +1 -1
  138. package/storybook-static/assets/{TreeSelect.stories-Bqv2L08v.js → TreeSelect.stories-DTpkFKp7.js} +1 -1
  139. package/storybook-static/assets/{TreeTable-JJK6sE1o.js → TreeTable-BOy0Tit-.js} +1 -1
  140. package/storybook-static/assets/{TreeTable.stories-yOu-SM17.js → TreeTable.stories-By1BlhSx.js} +1 -1
  141. package/storybook-static/assets/{WithTooltip-7X4HQR7L-Cib98jGG.js → WithTooltip-7X4HQR7L-D0sHKirZ.js} +1 -1
  142. package/storybook-static/assets/{base-table-BbenC81q.js → base-table-CYO33S-X.js} +1 -1
  143. package/storybook-static/assets/{blocks-DsN3hykK.js → blocks-Be6sd6Mi.js} +6 -6
  144. package/storybook-static/assets/{button-C8nkqC6f.js → button-D2JNy4h8.js} +1 -1
  145. package/storybook-static/assets/{check-box-BRod99sY.js → check-box-B4tjJED4.js} +1 -1
  146. package/storybook-static/assets/{chunk-3LY4VQVK-DSSlp6en.js → chunk-3LY4VQVK-jaQG9QeP.js} +1 -1
  147. package/storybook-static/assets/{chunk-LITCR56V-CB95AMSh.js → chunk-LITCR56V-HuSFGdRZ.js} +1 -1
  148. package/storybook-static/assets/{contextMenu-iKy1R_pJ.js → contextMenu-DIpGZPH1.js} +1 -1
  149. package/storybook-static/assets/{error-ChmbU9Ma.js → error-DHDPaDqg.js} +1 -1
  150. package/storybook-static/assets/{flex-CMA-ZjxB.js → flex-BWOaXKdl.js} +1 -1
  151. package/storybook-static/assets/{floating-ui.react-BpBaLM_G.js → floating-ui.react-C83w-peW.js} +1 -1
  152. package/storybook-static/assets/formatter-L2YTDE3Q-CO9xKxzu.js +1 -0
  153. package/storybook-static/assets/{formatters-C0d3JoZj.js → formatters-BaQpdAeu.js} +1 -1
  154. package/storybook-static/assets/grid-BwTl00V9.js +1 -0
  155. package/storybook-static/assets/{hooks-BpEcTBvi.js → hooks-5ZdxaH2L.js} +1 -1
  156. package/storybook-static/assets/{iframe-D90hKkIJ.js → iframe-Dw3PG8YA.js} +4 -4
  157. package/storybook-static/assets/{input-D77vjPVL.js → input-Cv9ps4La.js} +1 -1
  158. package/storybook-static/assets/lucide-react-DMM2-ayk.js +1 -0
  159. package/storybook-static/assets/{mdx-react-shim-CdXPOrRW.js → mdx-react-shim-Be8o0WOk.js} +1 -1
  160. package/storybook-static/assets/{media-C4dafSHo.js → media-Bc0eGngh.js} +1 -1
  161. package/storybook-static/assets/misc-7hwZFEXA.js +5 -0
  162. package/storybook-static/assets/{notifications-Dt8NSgnT.js → notifications-DkxUPPfB.js} +1 -1
  163. package/storybook-static/assets/{odata-DQSm2H7O.js → odata-nO_yW-3w.js} +1 -1
  164. package/storybook-static/assets/odata-service-RLReWm0A.js +1 -0
  165. package/storybook-static/assets/{odataStoryFixtures-D0p0XtmS.js → odataStoryFixtures-J-OBpNg6.js} +1 -1
  166. package/storybook-static/assets/{picker-9SOfvJPJ.js → picker-BQr_wySR.js} +1 -1
  167. package/storybook-static/assets/{popover-Dph7wJ1_.js → popover-v9hxY66g.js} +1 -1
  168. package/storybook-static/assets/{preset-select-mubmWRO9.js → preset-select-d-pIvdGN.js} +1 -1
  169. package/storybook-static/assets/{react-18-CSg0VXTL.js → react-18-CXAAUXpx.js} +1 -1
  170. package/storybook-static/assets/{react-Cr3aZrbF.js → react-ByWJZTAG.js} +1 -1
  171. package/storybook-static/assets/{react-dom-Iu9X9_7I.js → react-dom-DXk3dBb-.js} +1 -1
  172. package/storybook-static/assets/select-BessDJfg.js +1 -0
  173. package/storybook-static/assets/{slider-C8Udy6I7.js → slider-tV7pXkKI.js} +1 -1
  174. package/storybook-static/assets/{sortable-xt2HCdf-.js → sortable-CHwTYQdY.js} +1 -1
  175. package/storybook-static/assets/{syntaxhighlighter-RTCFPKCB-4Ttp-ccw.js → syntaxhighlighter-RTCFPKCB-CsqLGhls.js} +1 -1
  176. package/storybook-static/assets/{text-CwWM1WCX.js → text-Bf_PqPja.js} +1 -1
  177. package/storybook-static/assets/{theming--foXdFDq.js → theming-D37tYvvG.js} +1 -1
  178. package/storybook-static/assets/ui-QO8tRKaR.js +1 -0
  179. package/storybook-static/assets/{useODataSelectBase-C7f1zxWc.js → useODataSelectBase-DSlg4q3l.js} +1 -1
  180. package/storybook-static/assets/{useODataTreeData-7hwK6Byp.js → useODataTreeData-d53TKU6r.js} +1 -1
  181. package/storybook-static/assets/{utils-Bg8QclQy.js → utils-CnV3Gszu.js} +1 -1
  182. package/storybook-static/iframe.html +1 -1
  183. package/storybook-static/index.json +1 -1
  184. package/storybook-static/project.json +1 -1
  185. package/storybook-static/assets/Input-B6UklXwb.js +0 -3
  186. package/storybook-static/assets/Select-BxK9yj6_.js +0 -12
  187. package/storybook-static/assets/SingleDateTimeInput.stories-CSFUpLfK.js +0 -78
  188. package/storybook-static/assets/Switch.stories-BVAIn4yd.js +0 -26
  189. package/storybook-static/assets/TagInput.stories-iI1hZ8CY.js +0 -18
  190. package/storybook-static/assets/formatter-L2YTDE3Q-tZc7M3Ad.js +0 -1
  191. package/storybook-static/assets/grid-Ck5-5Szv.js +0 -1
  192. package/storybook-static/assets/lucide-react-ClGevkpS.js +0 -1
  193. package/storybook-static/assets/misc-yRJvnOkX.js +0 -5
  194. package/storybook-static/assets/odata-service-CMTV9-cO.js +0 -1
  195. package/storybook-static/assets/select-DYXb71nn.js +0 -1
  196. package/storybook-static/assets/ui-BJnukgBU.js +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/image/lib/buildImageSrcSet.ts","../../src/image/lib/buildImageSources.ts","../../src/image/ui/ImageCropper.tsx","../../src/image/ui/ImagePlaceholder.tsx","../../src/image/ui/ImageRenderer.tsx","../../src/image/lib/getImageSelectionKey.ts","../../src/image/model/useImageSelectionState.ts","../../src/image/lib/getImageCropPreviewStyle.ts","../../src/image/ui/ImageSelectionFields.tsx","../../src/image/ui/ImageSelectionModal.tsx","../../src/image/ui/ImageSelectionPanel.tsx","../../src/image/ui/ImageView.tsx"],"sourcesContent":["import { type ImageCandidate } from \"../model/imageTypes\";\n\n/** Собирает валидный srcset из уже разрешённых вызывающим кодом URL вариантов. */\nexport function buildImageSrcSet(candidates: readonly ImageCandidate[]): string {\n\treturn candidates\n\t\t.map((candidate) => (\"width\" in candidate ? `${candidate.src} ${candidate.width}w` : `${candidate.src} ${candidate.density}x`))\n\t\t.join(\", \");\n}\n","import { type ImageSource, type ResponsiveImageSource } from \"../model/imageTypes\";\n\nimport { buildImageSrcSet } from \"./buildImageSrcSet\";\n\n/** Преобразует декларативные наборы кандидатов в готовые source-дескрипторы. */\nexport function buildImageSources(sources: readonly ResponsiveImageSource[] | undefined, defaultSizes?: string): ImageSource[] {\n\treturn (sources ?? [])\n\t\t.map((source) => ({\n\t\t\tsrcSet: buildImageSrcSet(source.candidates),\n\t\t\ttype: source.type,\n\t\t\tsizes: source.sizes ?? defaultSizes\n\t\t}))\n\t\t.filter((source) => source.srcSet.length > 0);\n}\n","import { useCallback, useState } from \"react\";\n\nimport Cropper from \"react-easy-crop\";\n\nimport { type ImageCropArea, type ImageCropperProps, type ImageCropPoint } from \"../model/imageCropTypes\";\n\nconst DEFAULT_CROP: ImageCropPoint = { x: 0, y: 0 };\n\n/**\n * Управляемая или автономная область обрезки изображения.\n * Числовой aspect задаётся вызывающим кодом и не связан с назначением изображения.\n */\nexport function ImageCropper({\n\timage,\n\taspect = 16 / 9,\n\twidth = \"100%\",\n\tclassName,\n\tstyle,\n\tcrop,\n\tdefaultCrop = DEFAULT_CROP,\n\tzoom,\n\tdefaultZoom = 1,\n\tminZoom = 1,\n\tmaxZoom = 3,\n\tzoomSpeed = 0.1,\n\tobjectFit = \"contain\",\n\tdisabled = false,\n\tonCropChange,\n\tonZoomChange,\n\tonCropComplete\n}: ImageCropperProps) {\n\tconst [uncontrolledCrop, setUncontrolledCrop] = useState(defaultCrop);\n\tconst [uncontrolledZoom, setUncontrolledZoom] = useState(defaultZoom);\n\tconst resolvedCrop = crop ?? uncontrolledCrop;\n\tconst resolvedZoom = zoom ?? uncontrolledZoom;\n\n\tconst handleCropChange = useCallback(\n\t\t(nextCrop: ImageCropPoint) => {\n\t\t\tif (disabled) return;\n\n\t\t\tif (crop === undefined) {\n\t\t\t\tsetUncontrolledCrop(nextCrop);\n\t\t\t}\n\n\t\t\tonCropChange?.(nextCrop);\n\t\t},\n\t\t[crop, disabled, onCropChange]\n\t);\n\n\tconst handleZoomChange = useCallback(\n\t\t(nextZoom: number) => {\n\t\t\tif (disabled) return;\n\n\t\t\tif (zoom === undefined) {\n\t\t\t\tsetUncontrolledZoom(nextZoom);\n\t\t\t}\n\n\t\t\tonZoomChange?.(nextZoom);\n\t\t},\n\t\t[disabled, onZoomChange, zoom]\n\t);\n\n\tconst handleCropComplete = useCallback(\n\t\t(croppedAreaPercentages: ImageCropArea, croppedAreaPixels: ImageCropArea) => {\n\t\t\tif (disabled) return;\n\n\t\t\tonCropComplete?.(croppedAreaPixels, croppedAreaPercentages);\n\t\t},\n\t\t[disabled, onCropComplete]\n\t);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={className}\n\t\t\tstyle={{ position: \"relative\", width, aspectRatio: aspect, pointerEvents: disabled ? \"none\" : undefined, ...style }}>\n\t\t\t<Cropper\n\t\t\t\timage={image}\n\t\t\t\tcrop={resolvedCrop}\n\t\t\t\tzoom={resolvedZoom}\n\t\t\t\taspect={aspect}\n\t\t\t\tminZoom={minZoom}\n\t\t\t\tmaxZoom={maxZoom}\n\t\t\t\tzoomSpeed={zoomSpeed}\n\t\t\t\tobjectFit={objectFit}\n\t\t\t\tonCropChange={handleCropChange}\n\t\t\t\tonZoomChange={handleZoomChange}\n\t\t\t\tonCropComplete={handleCropComplete}\n\t\t\t\tcropperProps={{ \"aria-disabled\": disabled || undefined }}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","export interface ImagePlaceholderProps {\n\twidth?: number | string;\n\theight?: number | string;\n\taspectRatio?: string | \"unset\";\n\tclassName?: string;\n}\n\nexport function ImagePlaceholder({ width = \"100%\", height = \"100%\", aspectRatio = \"16 / 9\", className }: ImagePlaceholderProps) {\n\treturn (\n\t\t<div style={{ width, height, aspectRatio }} className={className}>\n\t\t\t<svg\n\t\t\t\twidth={width}\n\t\t\t\theight={height}\n\t\t\t\tviewBox=\"0 0 200 200\"\n\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\trole=\"img\"\n\t\t\t\tfill=\"none\"\n\t\t\t\trx=\"10\"\n\t\t\t\try=\"10\"\n\t\t\t\tstyle={{ display: \"block\" }}\n\t\t\t\taria-label=\"Изображение недоступно\">\n\t\t\t\t{/* <rect width=\"200\" height=\"200\" fill=\"var(--surface-2)\" rx=\"10\" ry=\"10\" /> */}\n\t\t\t\t<g fill=\"var(--surface-0)\">\n\t\t\t\t\t<rect x=\"40\" y=\"50\" width=\"120\" height=\"100\" rx=\"10\" />\n\n\t\t\t\t\t<circle cx=\"60\" cy=\"70\" r=\"8\" fill=\"var(--surface-2)\" />\n\n\t\t\t\t\t<path d=\"M50 130 L75 100 L100 130 Z\" fill=\"var(--surface-2)\" />\n\t\t\t\t\t<path d=\"M90 130 L120 90 L150 130 Z\" fill=\"var(--surface-2)\" />\n\n\t\t\t\t\t<line x1=\"160\" y1=\"40\" x2=\"40\" y2=\"160\" stroke=\"var(--surface-0)\" strokeWidth=\"15\" strokeLinecap=\"round\" />\n\t\t\t\t\t<line x1=\"160\" y1=\"40\" x2=\"40\" y2=\"160\" stroke=\"var(--surface-2)\" strokeWidth=\"5\" strokeLinecap=\"round\" />\n\t\t\t\t</g>\n\t\t\t</svg>\n\t\t</div>\n\t);\n}\n","import { type CSSProperties, type ImgHTMLAttributes, type ReactNode, useEffect, useRef, useState } from \"react\";\n\nimport { type ImageLayout, type ImageSource } from \"../model/imageTypes\";\n\nimport { ImagePlaceholder } from \"./ImagePlaceholder\";\n\nexport interface ImageRendererProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\" | \"width\" | \"height\"> {\n\tsrc: string;\n\tsources?: readonly ImageSource[];\n\tlayout?: ImageLayout;\n\talt?: string;\n\trole?: string;\n\tariaLabel?: string;\n\tcaption?: string;\n\tfallback?: ReactNode;\n\tuseFigure?: boolean;\n\taspectRatio?: string | \"unset\";\n\twidth?: CSSProperties[\"width\"];\n\theight?: CSSProperties[\"height\"];\n\tintrinsicWidth?: number;\n\tintrinsicHeight?: number;\n\twrapperStyle?: CSSProperties;\n}\n\n/**\n * Базовый рендер изображения: lazy-появление, placeholder, обработка ошибки и figure/caption.\n * Компонент не знает, как устроено хранилище и какие форматы/размеры доступны.\n */\nexport function ImageRenderer({\n\tsrc,\n\tsources,\n\talt,\n\trole,\n\tariaLabel,\n\tcaption,\n\tfallback,\n\tuseFigure = false,\n\tlayout = \"cover\",\n\twidth = \"100%\",\n\theight = \"100%\",\n\taspectRatio,\n\tintrinsicWidth,\n\tintrinsicHeight,\n\tonLoad,\n\tonError,\n\tstyle,\n\twrapperStyle,\n\t...imgProps\n}: ImageRendererProps) {\n\tconst wrapperRef = useRef<HTMLDivElement | null>(null);\n\tconst [containerWidth, setContainerWidth] = useState(0);\n\tconst [isVisible, setIsVisible] = useState(false);\n\tconst [hasLoaded, setHasLoaded] = useState(false);\n\tconst [hasError, setHasError] = useState(false);\n\n\tuseEffect(() => {\n\t\tif (typeof IntersectionObserver === \"undefined\") {\n\t\t\t// Отложенный fallback сохраняет одинаковый первый render на сервере и в браузере без IntersectionObserver.\n\t\t\tconst timeoutId = window.setTimeout(() => setIsVisible(true), 0);\n\n\t\t\treturn () => window.clearTimeout(timeoutId);\n\t\t}\n\n\t\tconst observer = new IntersectionObserver(\n\t\t\t([entry]) => {\n\t\t\t\tif (entry.isIntersecting) {\n\t\t\t\t\tconst parentWidth = Math.round(entry.boundingClientRect.width);\n\n\t\t\t\t\tsetContainerWidth(parentWidth);\n\t\t\t\t\tsetIsVisible(true);\n\t\t\t\t\tobserver.disconnect();\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ rootMargin: \"100px\", threshold: 0.01 }\n\t\t);\n\n\t\tif (wrapperRef.current) {\n\t\t\tobserver.observe(wrapperRef.current);\n\t\t}\n\n\t\treturn () => observer.disconnect();\n\t}, []);\n\n\tconst isIntrinsic = layout === \"intrinsic\";\n\tconst resolvedHeight = isIntrinsic ? \"auto\" : height;\n\tconst intrinsicAspectRatio = intrinsicWidth && intrinsicHeight ? `${intrinsicWidth} / ${intrinsicHeight}` : undefined;\n\tconst resolvedAspectRatio = isIntrinsic ? (intrinsicAspectRatio ?? aspectRatio ?? \"auto\") : (aspectRatio ?? \"16 / 9\");\n\tconst placeholderHeight = isIntrinsic && resolvedAspectRatio !== \"auto\" && resolvedAspectRatio !== \"unset\" ? \"100%\" : resolvedHeight;\n\tconst sizesAttr = containerWidth ? `${containerWidth}px` : \"100vw\";\n\tconst defaultFallback = <ImagePlaceholder width={width} height={placeholderHeight} aspectRatio={resolvedAspectRatio} />;\n\tconst loadedImageStyle: CSSProperties = isIntrinsic\n\t\t? {\n\t\t\t\t...style,\n\t\t\t\tdisplay: \"block\",\n\t\t\t\twidth: \"100%\",\n\t\t\t\theight: \"auto\",\n\t\t\t\tobjectFit: undefined\n\t\t\t}\n\t\t: {\n\t\t\t\tdisplay: \"block\",\n\t\t\t\twidth: \"100%\",\n\t\t\t\theight: \"100%\",\n\t\t\t\tobjectFit: layout,\n\t\t\t\t...style\n\t\t\t};\n\n\tconst content = (\n\t\t<>\n\t\t\t{!hasLoaded && !hasError && (fallback || defaultFallback)}\n\t\t\t{isVisible && !hasError && (\n\t\t\t\t<picture>\n\t\t\t\t\t{sources?.map((source) => (\n\t\t\t\t\t\t<source\n\t\t\t\t\t\t\tkey={`${source.type ?? \"source\"}-${source.srcSet}`}\n\t\t\t\t\t\t\tsrcSet={source.srcSet}\n\t\t\t\t\t\t\ttype={source.type}\n\t\t\t\t\t\t\tsizes={source.sizes ?? sizesAttr}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={src}\n\t\t\t\t\t\talt={alt}\n\t\t\t\t\t\trole={role}\n\t\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\t\tonLoad={(event) => {\n\t\t\t\t\t\t\tsetHasLoaded(true);\n\t\t\t\t\t\t\tonLoad?.(event);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\t\tsetHasError(true);\n\t\t\t\t\t\t\tonError?.(event);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tstyle={!hasLoaded ? { display: \"none\" } : loadedImageStyle}\n\t\t\t\t\t\taria-busy={!hasLoaded}\n\t\t\t\t\t\twidth={intrinsicWidth}\n\t\t\t\t\t\theight={intrinsicHeight}\n\t\t\t\t\t\t{...imgProps}\n\t\t\t\t\t/>\n\t\t\t\t</picture>\n\t\t\t)}\n\t\t\t{hasError && defaultFallback}\n\t\t</>\n\t);\n\n\tconst wrapped = (\n\t\t<div ref={wrapperRef} style={{ width, height: resolvedHeight, aspectRatio: resolvedAspectRatio, ...wrapperStyle }}>\n\t\t\t{content}\n\t\t</div>\n\t);\n\n\treturn useFigure ? (\n\t\t<figure className=\"relative\">\n\t\t\t{wrapped}\n\t\t\t{caption && (\n\t\t\t\t<figcaption\n\t\t\t\t\tstyle={{ bottom: 2, right: 2, maxWidth: \"calc(100% - 4px)\" }}\n\t\t\t\t\tclassName=\"absolute paddingInlineMd textRight radiusSm fontSizeSm\">\n\t\t\t\t\t{caption}\n\t\t\t\t</figcaption>\n\t\t\t)}\n\t\t</figure>\n\t) : (\n\t\twrapped\n\t);\n}\n","import { type SelectedImageData } from \"../model/imageSelectionTypes\";\n\n/** Возвращает устойчивый ключ файла внутри текущего набора выбранных изображений. */\nexport function getImageSelectionKey(image: SelectedImageData, index: number): string {\n\treturn `${image.file.meta.name}-${image.file.meta.size}-${image.file.meta.lastModified}-${index}`;\n}\n","import { useCallback, useMemo, useState } from \"react\";\n\nimport { getImageSelectionKey } from \"../lib/getImageSelectionKey\";\n\nimport { type ImageCropArea, type ImageCropPoint } from \"./imageCropTypes\";\nimport {\n\ttype ImageCropperState,\n\ttype ImageSelectionPanelProps,\n\ttype ImageSelectionState,\n\ttype SelectedImageData,\n\ttype SelectedImageFile\n} from \"./imageSelectionTypes\";\n\nconst INITIAL_CROPPER_STATE: ImageCropperState = { crop: { x: 0, y: 0 }, zoom: 1 };\n\n/**\n * Управляет набором изображений в conventional controlled/uncontrolled-режиме.\n * В controlled-режиме каждое изменение сразу передаётся через onChange, а источник истины остаётся у вызывающего кода.\n */\nexport function useImageSelectionState({\n\tvalue,\n\tdefaultValue,\n\tdisabled,\n\tonChange,\n\tonApply\n}: Pick<ImageSelectionPanelProps, \"value\" | \"defaultValue\" | \"disabled\" | \"onChange\" | \"onApply\">): ImageSelectionState {\n\tconst [uncontrolledValue, setUncontrolledValue] = useState<SelectedImageData[]>(defaultValue ?? []);\n\tconst [requestedActiveIndex, setActiveIndex] = useState(0);\n\tconst [cropperStateByImage, setCropperStateByImage] = useState<Record<string, ImageCropperState>>({});\n\tconst resolvedValue = value ?? uncontrolledValue;\n\n\tconst updateValue = useCallback(\n\t\t(updater: (currentValue: SelectedImageData[]) => SelectedImageData[]) => {\n\t\t\tconst nextValue = updater(resolvedValue);\n\n\t\t\tif (value === undefined) {\n\t\t\t\tsetUncontrolledValue(nextValue);\n\t\t\t}\n\n\t\t\tonChange?.(nextValue);\n\t\t},\n\t\t[onChange, resolvedValue, value]\n\t);\n\n\tconst files = useMemo(() => resolvedValue.map((image) => image.file), [resolvedValue]);\n\tconst activeIndex = resolvedValue.length === 0 ? 0 : Math.min(requestedActiveIndex, resolvedValue.length - 1);\n\tconst activeImage = resolvedValue[activeIndex];\n\tconst activeImageKey = activeImage ? getImageSelectionKey(activeImage, activeIndex) : undefined;\n\tconst activeCropperState = activeImageKey ? (cropperStateByImage[activeImageKey] ?? INITIAL_CROPPER_STATE) : INITIAL_CROPPER_STATE;\n\tconst hasSelected = resolvedValue.length > 0;\n\tconst canApply = !disabled && hasSelected && resolvedValue.every((image) => image.alt.trim() && image.crop);\n\n\tconst setFiles = useCallback(\n\t\t(nextFiles: SelectedImageFile[]) => {\n\t\t\tupdateValue((currentValue) =>\n\t\t\t\tnextFiles.map((file) => {\n\t\t\t\t\t// Сохраняем описание и crop для файлов, которые остались в выборе.\n\t\t\t\t\tconst existingImage = currentValue.find((image) => image.file.file === file.file);\n\n\t\t\t\t\treturn existingImage ? { ...existingImage, file } : { file, alt: \"\" };\n\t\t\t\t})\n\t\t\t);\n\t\t},\n\t\t[updateValue]\n\t);\n\n\tconst setActiveAlt = useCallback(\n\t\t(alt: string) => {\n\t\t\tupdateValue((currentValue) => currentValue.map((image, index) => (index === activeIndex ? { ...image, alt } : image)));\n\t\t},\n\t\t[activeIndex, updateValue]\n\t);\n\n\tconst setActiveCrop = useCallback(\n\t\t(crop: ImageCropArea) => {\n\t\t\tupdateValue((currentValue) => currentValue.map((image, index) => (index === activeIndex ? { ...image, crop } : image)));\n\t\t},\n\t\t[activeIndex, updateValue]\n\t);\n\n\tconst setActiveCropperCrop = useCallback(\n\t\t(crop: ImageCropPoint) => {\n\t\t\tif (!activeImageKey) return;\n\n\t\t\tsetCropperStateByImage((currentState) => ({\n\t\t\t\t...currentState,\n\t\t\t\t[activeImageKey]: {\n\t\t\t\t\tcrop,\n\t\t\t\t\tzoom: currentState[activeImageKey]?.zoom ?? 1\n\t\t\t\t}\n\t\t\t}));\n\t\t},\n\t\t[activeImageKey]\n\t);\n\n\tconst setActiveCropperZoom = useCallback(\n\t\t(zoom: number) => {\n\t\t\tif (!activeImageKey) return;\n\n\t\t\tsetCropperStateByImage((currentState) => ({\n\t\t\t\t...currentState,\n\t\t\t\t[activeImageKey]: {\n\t\t\t\t\tcrop: currentState[activeImageKey]?.crop ?? { x: 0, y: 0 },\n\t\t\t\t\tzoom\n\t\t\t\t}\n\t\t\t}));\n\t\t},\n\t\t[activeImageKey]\n\t);\n\n\tconst apply = useCallback(() => {\n\t\tonApply?.(resolvedValue);\n\t}, [onApply, resolvedValue]);\n\n\treturn {\n\t\tvalue: resolvedValue,\n\t\tfiles,\n\t\tactiveIndex,\n\t\tactiveImage,\n\t\tactiveCropperState,\n\t\thasSelected,\n\t\tcanApply,\n\t\tapply,\n\t\tsetFiles,\n\t\tsetActiveIndex,\n\t\tsetActiveAlt,\n\t\tsetActiveCrop,\n\t\tsetActiveCropperCrop,\n\t\tsetActiveCropperZoom\n\t};\n}\n","import { type CSSProperties } from \"react\";\n\nimport { type SelectedImageData } from \"../model/imageSelectionTypes\";\n\nexport type ImageNaturalSize = {\n\twidth: number;\n\theight: number;\n};\n\n/** Масштабирует preview так, чтобы контейнер показывал только выбранную область crop. */\nexport function getImageCropPreviewStyle(image: SelectedImageData, size: ImageNaturalSize | undefined, aspect: number): CSSProperties {\n\tif (!image.crop || !size) {\n\t\treturn {\n\t\t\twidth: \"100%\",\n\t\t\taspectRatio: aspect,\n\t\t\tobjectFit: \"cover\",\n\t\t\tdisplay: \"block\"\n\t\t};\n\t}\n\n\tconst crop = image.crop;\n\n\treturn {\n\t\tposition: \"absolute\",\n\t\tleft: `${-(crop.x / crop.width) * 100}%`,\n\t\ttop: `${-(crop.y / crop.height) * 100}%`,\n\t\twidth: `${(size.width / crop.width) * 100}%`,\n\t\theight: `${(size.height / crop.height) * 100}%`,\n\t\tmaxWidth: \"none\"\n\t};\n}\n","import { useState } from \"react\";\n\nimport { GridContainer } from \"../../grid\";\nimport { InputText } from \"../../input\";\nimport { InputFiles } from \"../../input-files\";\nimport { getImageCropPreviewStyle, type ImageNaturalSize } from \"../lib/getImageCropPreviewStyle\";\nimport { getImageSelectionKey } from \"../lib/getImageSelectionKey\";\nimport { type ImageSelectionPanelProps, type ImageSelectionState } from \"../model/imageSelectionTypes\";\n\nimport { ImageCropper } from \"./ImageCropper\";\n\nconst DEFAULT_ALLOWED_IMAGE_MIME = [\"image/jpeg\", \"image/png\", \"image/webp\", \"image/avif\"] as const;\n\ninterface ImageSelectionFieldsProps extends Pick<\n\tImageSelectionPanelProps,\n\t\"aspect\" | \"disabled\" | \"allowedMime\" | \"maxBytes\" | \"minimumCropWidth\" | \"onReadError\"\n> {\n\tstate: ImageSelectionState;\n}\n\n/** Поля выбора файлов, crop и альтернативного текста для активного изображения. */\nexport function ImageSelectionFields({\n\taspect = 16 / 9,\n\tdisabled,\n\tallowedMime = DEFAULT_ALLOWED_IMAGE_MIME,\n\tmaxBytes,\n\tminimumCropWidth,\n\tonReadError,\n\tstate\n}: ImageSelectionFieldsProps) {\n\tconst area = state.activeImage?.crop;\n\tconst [imageSizes, setImageSizes] = useState<Record<string, ImageNaturalSize>>({});\n\n\treturn (\n\t\t<GridContainer gap=\"md\">\n\t\t\t<InputFiles\n\t\t\t\tlabel=\"Файл изображения\"\n\t\t\t\tallowedMime={allowedMime}\n\t\t\t\tvalue={state.files}\n\t\t\t\tonChange={state.setFiles}\n\t\t\t\tmaxBytes={maxBytes}\n\t\t\t\tdisabled={disabled}\n\t\t\t\tonReadError={onReadError}\n\t\t\t/>\n\n\t\t\t{state.hasSelected && state.activeImage && (\n\t\t\t\t<div>\n\t\t\t\t\t<GridContainer templateColumns=\"repeat(auto-fill, minmax(10em, 1fr))\" gap=\"xs\">\n\t\t\t\t\t\t{state.value.map((image, index) => {\n\t\t\t\t\t\t\tconst imageKey = getImageSelectionKey(image, index);\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={imageKey}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\t\t\t\tonClick={() => state.setActiveIndex(index)}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tpadding: 0,\n\t\t\t\t\t\t\t\t\t\tborder: index === state.activeIndex ? \"2px solid var(--border-accent)\" : \"2px solid transparent\",\n\t\t\t\t\t\t\t\t\t\tbackground: \"transparent\",\n\t\t\t\t\t\t\t\t\t\tcursor: disabled ? \"default\" : \"pointer\"\n\t\t\t\t\t\t\t\t\t}}>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\tposition: \"relative\",\n\t\t\t\t\t\t\t\t\t\t\tdisplay: \"block\",\n\t\t\t\t\t\t\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\t\t\t\t\t\t\taspectRatio: aspect,\n\t\t\t\t\t\t\t\t\t\t\toverflow: \"hidden\"\n\t\t\t\t\t\t\t\t\t\t}}>\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc={image.file.dataUrl}\n\t\t\t\t\t\t\t\t\t\t\talt={image.alt || `Предпросмотр изображения ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\tstyle={getImageCropPreviewStyle(image, imageSizes[imageKey], aspect)}\n\t\t\t\t\t\t\t\t\t\t\tonLoad={(event) => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst target = event.currentTarget;\n\n\t\t\t\t\t\t\t\t\t\t\t\tsetImageSizes((currentSizes) => ({\n\t\t\t\t\t\t\t\t\t\t\t\t\t...currentSizes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t[imageKey]: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: target.naturalWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: target.naturalHeight\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</GridContainer>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\tВыберите область изображения{\" \"}\n\t\t\t\t\t\t{area && (\n\t\t\t\t\t\t\t<code className={minimumCropWidth && area.width < minimumCropWidth ? \"statusError\" : \"statusSuccess\"}>\n\t\t\t\t\t\t\t\t{area.width}x{area.height}\n\t\t\t\t\t\t\t\t{minimumCropWidth && area.width < minimumCropWidth ? \" — недостаточная ширина\" : \"\"}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<ImageCropper\n\t\t\t\t\t\tkey={getImageSelectionKey(state.activeImage, state.activeIndex)}\n\t\t\t\t\t\timage={state.activeImage.file.dataUrl}\n\t\t\t\t\t\tcrop={state.activeCropperState.crop}\n\t\t\t\t\t\tzoom={state.activeCropperState.zoom}\n\t\t\t\t\t\taspect={aspect}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tonCropChange={state.setActiveCropperCrop}\n\t\t\t\t\t\tonZoomChange={state.setActiveCropperZoom}\n\t\t\t\t\t\tonCropComplete={state.setActiveCrop}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<InputText\n\t\t\t\trequired\n\t\t\t\tdisabled={!state.hasSelected || disabled}\n\t\t\t\tlabel=\"Краткое описание изображения\"\n\t\t\t\tplaceholder=\"\"\n\t\t\t\tvalue={state.activeImage?.alt ?? \"\"}\n\t\t\t\tonChange={state.setActiveAlt}\n\t\t\t/>\n\t\t</GridContainer>\n\t);\n}\n","import { Button } from \"../../button\";\nimport { Modal, ModalContent, ModalFooter } from \"../../modal\";\nimport { type ImageSelectionModalProps } from \"../model/imageSelectionTypes\";\nimport { useImageSelectionState } from \"../model/useImageSelectionState\";\n\nimport { ImageSelectionFields } from \"./ImageSelectionFields\";\n\n/** Модальная форма выбора изображений с необязательным этапом подтверждения. */\nexport function ImageSelectionModal({\n\tdisabled,\n\tisOpen = true,\n\tonClose = () => {},\n\ttitle = \"Загрузка нового изображения\",\n\t...props\n}: ImageSelectionModalProps) {\n\tconst state = useImageSelectionState({ ...props, disabled });\n\n\tif (!props.onApply) {\n\t\treturn (\n\t\t\t<Modal isOpen={isOpen} onClose={onClose} title={title} size=\"xl\">\n\t\t\t\t<ModalContent scrollable>\n\t\t\t\t\t<ImageSelectionFields {...props} disabled={disabled} state={state} />\n\t\t\t\t</ModalContent>\n\t\t\t</Modal>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Modal isOpen={isOpen} onClose={onClose} title={title} size=\"xl\">\n\t\t\t<ModalContent scrollable>\n\t\t\t\t<ImageSelectionFields {...props} disabled={disabled} state={state} />\n\t\t\t</ModalContent>\n\t\t\t<ModalFooter>\n\t\t\t\t<Button disabled={!state.canApply} tone=\"success\" onClick={state.apply}>\n\t\t\t\t\tПрименить\n\t\t\t\t</Button>\n\t\t\t</ModalFooter>\n\t\t</Modal>\n\t);\n}\n","import { Button } from \"../../button\";\nimport { type ImageSelectionActionsData, type ImageSelectionPanelProps } from \"../model/imageSelectionTypes\";\nimport { useImageSelectionState } from \"../model/useImageSelectionState\";\n\nimport { ImageSelectionFields } from \"./ImageSelectionFields\";\n\n/** Встраиваемая форма выбора и обрезки одного или нескольких изображений. */\nexport function ImageSelectionPanel({ disabled, renderActions, ...props }: ImageSelectionPanelProps) {\n\tconst state = useImageSelectionState({ ...props, disabled });\n\tconst actionsData: ImageSelectionActionsData = {\n\t\tvalue: state.value,\n\t\tactiveIndex: state.activeIndex,\n\t\thasSelected: state.hasSelected,\n\t\tcanApply: state.canApply,\n\t\tapply: state.apply\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<ImageSelectionFields {...props} disabled={disabled} state={state} />\n\t\t\t{renderActions\n\t\t\t\t? renderActions(actionsData)\n\t\t\t\t: props.onApply && (\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Button disabled={!state.canApply} tone=\"success\" onClick={state.apply}>\n\t\t\t\t\t\t\t\tПрименить\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t</>\n\t);\n}\n","import { type ImgHTMLAttributes } from \"react\";\n\nimport { buildImageSources } from \"../lib/buildImageSources\";\nimport { buildImageSrcSet } from \"../lib/buildImageSrcSet\";\nimport { type ResponsiveImageData } from \"../model/imageTypes\";\n\nimport { ImageRenderer, type ImageRendererProps } from \"./ImageRenderer\";\n\nexport interface ImageViewProps\n\textends\n\t\tOmit<ImageRendererProps, \"src\" | \"sources\" | \"alt\" | \"intrinsicWidth\" | \"intrinsicHeight\" | \"srcSet\" | \"sizes\">,\n\t\tPick<ImgHTMLAttributes<HTMLImageElement>, \"sizes\"> {\n\timage: ResponsiveImageData;\n\talt?: string;\n}\n\n/**\n * Собирает picture/srcset из готовых URL и делегирует отображение ImageRenderer.\n * Компонент не формирует имена файлов и не знает о форматах или размерах хранилища.\n */\nexport function ImageView({ image, alt, sizes, aspectRatio, ...props }: ImageViewProps) {\n\tconst sources = buildImageSources(image.sources, sizes);\n\tconst srcSet = image.candidates ? buildImageSrcSet(image.candidates) : undefined;\n\tconst resolvedAspectRatio =\n\t\taspectRatio ?? (image.intrinsicWidth && image.intrinsicHeight ? `${image.intrinsicWidth} / ${image.intrinsicHeight}` : undefined);\n\n\treturn (\n\t\t<ImageRenderer\n\t\t\t{...props}\n\t\t\tsrc={image.src}\n\t\t\tsrcSet={srcSet || undefined}\n\t\t\tsources={sources}\n\t\t\tsizes={sizes}\n\t\t\talt={alt ?? image.alt}\n\t\t\tintrinsicWidth={image.intrinsicWidth}\n\t\t\tintrinsicHeight={image.intrinsicHeight}\n\t\t\taspectRatio={resolvedAspectRatio}\n\t\t/>\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/image/lib/buildImageSrcSet.ts","../../src/image/lib/buildImageSources.ts","../../src/image/ui/ImageCropper.tsx","../../src/image/ui/ImagePlaceholder.tsx","../../src/image/ui/ImageRenderer.tsx","../../src/image/lib/getImageSelectionKey.ts","../../src/image/model/useImageSelectionState.ts","../../src/image/lib/getImageCropPreviewStyle.ts","../../src/image/ui/ImageSelectionFields.tsx","../../src/image/ui/ImageSelectionModal.tsx","../../src/image/ui/ImageSelectionPanel.tsx","../../src/image/ui/ImageView.tsx","../../src/image/lib/buildImageViewerSlides.ts","../../src/image/lib/resolveImageViewerLabels.ts","../../src/image/ui/ImageViewer.module.scss","../../src/image/ui/ImageViewer.tsx"],"sourcesContent":["import { type ImageCandidate } from \"../model/imageTypes\";\n\n/** Собирает валидный srcset из уже разрешённых вызывающим кодом URL вариантов. */\nexport function buildImageSrcSet(candidates: readonly ImageCandidate[]): string {\n\treturn candidates\n\t\t.map((candidate) => (\"width\" in candidate ? `${candidate.src} ${candidate.width}w` : `${candidate.src} ${candidate.density}x`))\n\t\t.join(\", \");\n}\n","import { type ImageSource, type ResponsiveImageSource } from \"../model/imageTypes\";\n\nimport { buildImageSrcSet } from \"./buildImageSrcSet\";\n\n/** Преобразует декларативные наборы кандидатов в готовые source-дескрипторы. */\nexport function buildImageSources(sources: readonly ResponsiveImageSource[] | undefined, defaultSizes?: string): ImageSource[] {\n\treturn (sources ?? [])\n\t\t.map((source) => ({\n\t\t\tsrcSet: buildImageSrcSet(source.candidates),\n\t\t\ttype: source.type,\n\t\t\tsizes: source.sizes ?? defaultSizes\n\t\t}))\n\t\t.filter((source) => source.srcSet.length > 0);\n}\n","import { useCallback, useState } from \"react\";\n\nimport Cropper from \"react-easy-crop\";\n\nimport { type ImageCropArea, type ImageCropperProps, type ImageCropPoint } from \"../model/imageCropTypes\";\n\nconst DEFAULT_CROP: ImageCropPoint = { x: 0, y: 0 };\n\n/**\n * Управляемая или автономная область обрезки изображения.\n * Числовой aspect задаётся вызывающим кодом и не связан с назначением изображения.\n */\nexport function ImageCropper({\n\timage,\n\taspect = 16 / 9,\n\twidth = \"100%\",\n\tclassName,\n\tstyle,\n\tcrop,\n\tdefaultCrop = DEFAULT_CROP,\n\tzoom,\n\tdefaultZoom = 1,\n\tminZoom = 1,\n\tmaxZoom = 3,\n\tzoomSpeed = 0.1,\n\tobjectFit = \"contain\",\n\tdisabled = false,\n\tonCropChange,\n\tonZoomChange,\n\tonCropComplete\n}: ImageCropperProps) {\n\tconst [uncontrolledCrop, setUncontrolledCrop] = useState(defaultCrop);\n\tconst [uncontrolledZoom, setUncontrolledZoom] = useState(defaultZoom);\n\tconst resolvedCrop = crop ?? uncontrolledCrop;\n\tconst resolvedZoom = zoom ?? uncontrolledZoom;\n\n\tconst handleCropChange = useCallback(\n\t\t(nextCrop: ImageCropPoint) => {\n\t\t\tif (disabled) return;\n\n\t\t\tif (crop === undefined) {\n\t\t\t\tsetUncontrolledCrop(nextCrop);\n\t\t\t}\n\n\t\t\tonCropChange?.(nextCrop);\n\t\t},\n\t\t[crop, disabled, onCropChange]\n\t);\n\n\tconst handleZoomChange = useCallback(\n\t\t(nextZoom: number) => {\n\t\t\tif (disabled) return;\n\n\t\t\tif (zoom === undefined) {\n\t\t\t\tsetUncontrolledZoom(nextZoom);\n\t\t\t}\n\n\t\t\tonZoomChange?.(nextZoom);\n\t\t},\n\t\t[disabled, onZoomChange, zoom]\n\t);\n\n\tconst handleCropComplete = useCallback(\n\t\t(croppedAreaPercentages: ImageCropArea, croppedAreaPixels: ImageCropArea) => {\n\t\t\tif (disabled) return;\n\n\t\t\tonCropComplete?.(croppedAreaPixels, croppedAreaPercentages);\n\t\t},\n\t\t[disabled, onCropComplete]\n\t);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={className}\n\t\t\tstyle={{ position: \"relative\", width, aspectRatio: aspect, pointerEvents: disabled ? \"none\" : undefined, ...style }}>\n\t\t\t<Cropper\n\t\t\t\timage={image}\n\t\t\t\tcrop={resolvedCrop}\n\t\t\t\tzoom={resolvedZoom}\n\t\t\t\taspect={aspect}\n\t\t\t\tminZoom={minZoom}\n\t\t\t\tmaxZoom={maxZoom}\n\t\t\t\tzoomSpeed={zoomSpeed}\n\t\t\t\tobjectFit={objectFit}\n\t\t\t\tonCropChange={handleCropChange}\n\t\t\t\tonZoomChange={handleZoomChange}\n\t\t\t\tonCropComplete={handleCropComplete}\n\t\t\t\tcropperProps={{ \"aria-disabled\": disabled || undefined }}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","export interface ImagePlaceholderProps {\n\twidth?: number | string;\n\theight?: number | string;\n\taspectRatio?: string | \"unset\";\n\tclassName?: string;\n}\n\nexport function ImagePlaceholder({ width = \"100%\", height = \"100%\", aspectRatio = \"16 / 9\", className }: ImagePlaceholderProps) {\n\treturn (\n\t\t<div style={{ width, height, aspectRatio }} className={className}>\n\t\t\t<svg\n\t\t\t\twidth={width}\n\t\t\t\theight={height}\n\t\t\t\tviewBox=\"0 0 200 200\"\n\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\trole=\"img\"\n\t\t\t\tfill=\"none\"\n\t\t\t\trx=\"10\"\n\t\t\t\try=\"10\"\n\t\t\t\tstyle={{ display: \"block\" }}\n\t\t\t\taria-label=\"Изображение недоступно\">\n\t\t\t\t{/* <rect width=\"200\" height=\"200\" fill=\"var(--surface-2)\" rx=\"10\" ry=\"10\" /> */}\n\t\t\t\t<g fill=\"var(--surface-0)\">\n\t\t\t\t\t<rect x=\"40\" y=\"50\" width=\"120\" height=\"100\" rx=\"10\" />\n\n\t\t\t\t\t<circle cx=\"60\" cy=\"70\" r=\"8\" fill=\"var(--surface-2)\" />\n\n\t\t\t\t\t<path d=\"M50 130 L75 100 L100 130 Z\" fill=\"var(--surface-2)\" />\n\t\t\t\t\t<path d=\"M90 130 L120 90 L150 130 Z\" fill=\"var(--surface-2)\" />\n\n\t\t\t\t\t<line x1=\"160\" y1=\"40\" x2=\"40\" y2=\"160\" stroke=\"var(--surface-0)\" strokeWidth=\"15\" strokeLinecap=\"round\" />\n\t\t\t\t\t<line x1=\"160\" y1=\"40\" x2=\"40\" y2=\"160\" stroke=\"var(--surface-2)\" strokeWidth=\"5\" strokeLinecap=\"round\" />\n\t\t\t\t</g>\n\t\t\t</svg>\n\t\t</div>\n\t);\n}\n","import { type CSSProperties, type ImgHTMLAttributes, type ReactNode, useEffect, useRef, useState } from \"react\";\n\nimport { type ImageLayout, type ImageSource } from \"../model/imageTypes\";\n\nimport { ImagePlaceholder } from \"./ImagePlaceholder\";\n\nexport interface ImageRendererProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\" | \"width\" | \"height\"> {\n\tsrc: string;\n\tsources?: readonly ImageSource[];\n\tlayout?: ImageLayout;\n\talt?: string;\n\trole?: string;\n\tariaLabel?: string;\n\tcaption?: string;\n\tfallback?: ReactNode;\n\tuseFigure?: boolean;\n\taspectRatio?: string | \"unset\";\n\twidth?: CSSProperties[\"width\"];\n\theight?: CSSProperties[\"height\"];\n\tintrinsicWidth?: number;\n\tintrinsicHeight?: number;\n\twrapperStyle?: CSSProperties;\n}\n\n/**\n * Базовый рендер изображения: lazy-появление, placeholder, обработка ошибки и figure/caption.\n * Компонент не знает, как устроено хранилище и какие форматы/размеры доступны.\n */\nexport function ImageRenderer({\n\tsrc,\n\tsources,\n\talt,\n\trole,\n\tariaLabel,\n\tcaption,\n\tfallback,\n\tuseFigure = false,\n\tlayout = \"cover\",\n\twidth = \"100%\",\n\theight = \"100%\",\n\taspectRatio,\n\tintrinsicWidth,\n\tintrinsicHeight,\n\tonLoad,\n\tonError,\n\tstyle,\n\twrapperStyle,\n\t...imgProps\n}: ImageRendererProps) {\n\tconst wrapperRef = useRef<HTMLDivElement | null>(null);\n\tconst [containerWidth, setContainerWidth] = useState(0);\n\tconst [isVisible, setIsVisible] = useState(false);\n\tconst [hasLoaded, setHasLoaded] = useState(false);\n\tconst [hasError, setHasError] = useState(false);\n\n\tuseEffect(() => {\n\t\tif (typeof IntersectionObserver === \"undefined\") {\n\t\t\t// Отложенный fallback сохраняет одинаковый первый render на сервере и в браузере без IntersectionObserver.\n\t\t\tconst timeoutId = window.setTimeout(() => setIsVisible(true), 0);\n\n\t\t\treturn () => window.clearTimeout(timeoutId);\n\t\t}\n\n\t\tconst observer = new IntersectionObserver(\n\t\t\t([entry]) => {\n\t\t\t\tif (entry.isIntersecting) {\n\t\t\t\t\tconst parentWidth = Math.round(entry.boundingClientRect.width);\n\n\t\t\t\t\tsetContainerWidth(parentWidth);\n\t\t\t\t\tsetIsVisible(true);\n\t\t\t\t\tobserver.disconnect();\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ rootMargin: \"100px\", threshold: 0.01 }\n\t\t);\n\n\t\tif (wrapperRef.current) {\n\t\t\tobserver.observe(wrapperRef.current);\n\t\t}\n\n\t\treturn () => observer.disconnect();\n\t}, []);\n\n\tconst isIntrinsic = layout === \"intrinsic\";\n\tconst resolvedHeight = isIntrinsic ? \"auto\" : height;\n\tconst intrinsicAspectRatio = intrinsicWidth && intrinsicHeight ? `${intrinsicWidth} / ${intrinsicHeight}` : undefined;\n\tconst resolvedAspectRatio = isIntrinsic ? (intrinsicAspectRatio ?? aspectRatio ?? \"auto\") : (aspectRatio ?? \"16 / 9\");\n\tconst placeholderHeight = isIntrinsic && resolvedAspectRatio !== \"auto\" && resolvedAspectRatio !== \"unset\" ? \"100%\" : resolvedHeight;\n\tconst sizesAttr = containerWidth ? `${containerWidth}px` : \"100vw\";\n\tconst defaultFallback = <ImagePlaceholder width={width} height={placeholderHeight} aspectRatio={resolvedAspectRatio} />;\n\tconst loadedImageStyle: CSSProperties = isIntrinsic\n\t\t? {\n\t\t\t\t...style,\n\t\t\t\tdisplay: \"block\",\n\t\t\t\twidth: \"100%\",\n\t\t\t\theight: \"auto\",\n\t\t\t\tobjectFit: undefined\n\t\t\t}\n\t\t: {\n\t\t\t\tdisplay: \"block\",\n\t\t\t\twidth: \"100%\",\n\t\t\t\theight: \"100%\",\n\t\t\t\tobjectFit: layout,\n\t\t\t\t...style\n\t\t\t};\n\n\tconst content = (\n\t\t<>\n\t\t\t{!hasLoaded && !hasError && (fallback || defaultFallback)}\n\t\t\t{isVisible && !hasError && (\n\t\t\t\t<picture>\n\t\t\t\t\t{sources?.map((source) => (\n\t\t\t\t\t\t<source\n\t\t\t\t\t\t\tkey={`${source.type ?? \"source\"}-${source.srcSet}`}\n\t\t\t\t\t\t\tsrcSet={source.srcSet}\n\t\t\t\t\t\t\ttype={source.type}\n\t\t\t\t\t\t\tsizes={source.sizes ?? sizesAttr}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={src}\n\t\t\t\t\t\talt={alt}\n\t\t\t\t\t\trole={role}\n\t\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\t\tonLoad={(event) => {\n\t\t\t\t\t\t\tsetHasLoaded(true);\n\t\t\t\t\t\t\tonLoad?.(event);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\t\tsetHasError(true);\n\t\t\t\t\t\t\tonError?.(event);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tstyle={!hasLoaded ? { display: \"none\" } : loadedImageStyle}\n\t\t\t\t\t\taria-busy={!hasLoaded}\n\t\t\t\t\t\twidth={intrinsicWidth}\n\t\t\t\t\t\theight={intrinsicHeight}\n\t\t\t\t\t\t{...imgProps}\n\t\t\t\t\t/>\n\t\t\t\t</picture>\n\t\t\t)}\n\t\t\t{hasError && defaultFallback}\n\t\t</>\n\t);\n\n\tconst wrapped = (\n\t\t<div ref={wrapperRef} style={{ width, height: resolvedHeight, aspectRatio: resolvedAspectRatio, ...wrapperStyle }}>\n\t\t\t{content}\n\t\t</div>\n\t);\n\n\treturn useFigure ? (\n\t\t<figure className=\"relative\">\n\t\t\t{wrapped}\n\t\t\t{caption && (\n\t\t\t\t<figcaption\n\t\t\t\t\tstyle={{ bottom: 2, right: 2, maxWidth: \"calc(100% - 4px)\" }}\n\t\t\t\t\tclassName=\"absolute paddingInlineMd textRight radiusSm fontSizeSm\">\n\t\t\t\t\t{caption}\n\t\t\t\t</figcaption>\n\t\t\t)}\n\t\t</figure>\n\t) : (\n\t\twrapped\n\t);\n}\n","import { type SelectedImageData } from \"../model/imageSelectionTypes\";\n\n/** Возвращает устойчивый ключ файла внутри текущего набора выбранных изображений. */\nexport function getImageSelectionKey(image: SelectedImageData, index: number): string {\n\treturn `${image.file.meta.name}-${image.file.meta.size}-${image.file.meta.lastModified}-${index}`;\n}\n","import { useCallback, useMemo, useState } from \"react\";\n\nimport { getImageSelectionKey } from \"../lib/getImageSelectionKey\";\n\nimport { type ImageCropArea, type ImageCropPoint } from \"./imageCropTypes\";\nimport {\n\ttype ImageCropperState,\n\ttype ImageSelectionPanelProps,\n\ttype ImageSelectionState,\n\ttype SelectedImageData,\n\ttype SelectedImageFile\n} from \"./imageSelectionTypes\";\n\nconst INITIAL_CROPPER_STATE: ImageCropperState = { crop: { x: 0, y: 0 }, zoom: 1 };\n\n/**\n * Управляет набором изображений в conventional controlled/uncontrolled-режиме.\n * В controlled-режиме каждое изменение сразу передаётся через onChange, а источник истины остаётся у вызывающего кода.\n */\nexport function useImageSelectionState({\n\tvalue,\n\tdefaultValue,\n\tdisabled,\n\tonChange,\n\tonApply\n}: Pick<ImageSelectionPanelProps, \"value\" | \"defaultValue\" | \"disabled\" | \"onChange\" | \"onApply\">): ImageSelectionState {\n\tconst [uncontrolledValue, setUncontrolledValue] = useState<SelectedImageData[]>(defaultValue ?? []);\n\tconst [requestedActiveIndex, setActiveIndex] = useState(0);\n\tconst [cropperStateByImage, setCropperStateByImage] = useState<Record<string, ImageCropperState>>({});\n\tconst resolvedValue = value ?? uncontrolledValue;\n\n\tconst updateValue = useCallback(\n\t\t(updater: (currentValue: SelectedImageData[]) => SelectedImageData[]) => {\n\t\t\tconst nextValue = updater(resolvedValue);\n\n\t\t\tif (value === undefined) {\n\t\t\t\tsetUncontrolledValue(nextValue);\n\t\t\t}\n\n\t\t\tonChange?.(nextValue);\n\t\t},\n\t\t[onChange, resolvedValue, value]\n\t);\n\n\tconst files = useMemo(() => resolvedValue.map((image) => image.file), [resolvedValue]);\n\tconst activeIndex = resolvedValue.length === 0 ? 0 : Math.min(requestedActiveIndex, resolvedValue.length - 1);\n\tconst activeImage = resolvedValue[activeIndex];\n\tconst activeImageKey = activeImage ? getImageSelectionKey(activeImage, activeIndex) : undefined;\n\tconst activeCropperState = activeImageKey ? (cropperStateByImage[activeImageKey] ?? INITIAL_CROPPER_STATE) : INITIAL_CROPPER_STATE;\n\tconst hasSelected = resolvedValue.length > 0;\n\tconst canApply = !disabled && hasSelected && resolvedValue.every((image) => image.alt.trim() && image.crop);\n\n\tconst setFiles = useCallback(\n\t\t(nextFiles: SelectedImageFile[]) => {\n\t\t\tupdateValue((currentValue) =>\n\t\t\t\tnextFiles.map((file) => {\n\t\t\t\t\t// Сохраняем описание и crop для файлов, которые остались в выборе.\n\t\t\t\t\tconst existingImage = currentValue.find((image) => image.file.file === file.file);\n\n\t\t\t\t\treturn existingImage ? { ...existingImage, file } : { file, alt: \"\" };\n\t\t\t\t})\n\t\t\t);\n\t\t},\n\t\t[updateValue]\n\t);\n\n\tconst setActiveAlt = useCallback(\n\t\t(alt: string) => {\n\t\t\tupdateValue((currentValue) => currentValue.map((image, index) => (index === activeIndex ? { ...image, alt } : image)));\n\t\t},\n\t\t[activeIndex, updateValue]\n\t);\n\n\tconst setActiveCrop = useCallback(\n\t\t(crop: ImageCropArea) => {\n\t\t\tupdateValue((currentValue) => currentValue.map((image, index) => (index === activeIndex ? { ...image, crop } : image)));\n\t\t},\n\t\t[activeIndex, updateValue]\n\t);\n\n\tconst setActiveCropperCrop = useCallback(\n\t\t(crop: ImageCropPoint) => {\n\t\t\tif (!activeImageKey) return;\n\n\t\t\tsetCropperStateByImage((currentState) => ({\n\t\t\t\t...currentState,\n\t\t\t\t[activeImageKey]: {\n\t\t\t\t\tcrop,\n\t\t\t\t\tzoom: currentState[activeImageKey]?.zoom ?? 1\n\t\t\t\t}\n\t\t\t}));\n\t\t},\n\t\t[activeImageKey]\n\t);\n\n\tconst setActiveCropperZoom = useCallback(\n\t\t(zoom: number) => {\n\t\t\tif (!activeImageKey) return;\n\n\t\t\tsetCropperStateByImage((currentState) => ({\n\t\t\t\t...currentState,\n\t\t\t\t[activeImageKey]: {\n\t\t\t\t\tcrop: currentState[activeImageKey]?.crop ?? { x: 0, y: 0 },\n\t\t\t\t\tzoom\n\t\t\t\t}\n\t\t\t}));\n\t\t},\n\t\t[activeImageKey]\n\t);\n\n\tconst apply = useCallback(() => {\n\t\tonApply?.(resolvedValue);\n\t}, [onApply, resolvedValue]);\n\n\treturn {\n\t\tvalue: resolvedValue,\n\t\tfiles,\n\t\tactiveIndex,\n\t\tactiveImage,\n\t\tactiveCropperState,\n\t\thasSelected,\n\t\tcanApply,\n\t\tapply,\n\t\tsetFiles,\n\t\tsetActiveIndex,\n\t\tsetActiveAlt,\n\t\tsetActiveCrop,\n\t\tsetActiveCropperCrop,\n\t\tsetActiveCropperZoom\n\t};\n}\n","import { type CSSProperties } from \"react\";\n\nimport { type SelectedImageData } from \"../model/imageSelectionTypes\";\n\nexport type ImageNaturalSize = {\n\twidth: number;\n\theight: number;\n};\n\n/** Масштабирует preview так, чтобы контейнер показывал только выбранную область crop. */\nexport function getImageCropPreviewStyle(image: SelectedImageData, size: ImageNaturalSize | undefined, aspect: number): CSSProperties {\n\tif (!image.crop || !size) {\n\t\treturn {\n\t\t\twidth: \"100%\",\n\t\t\taspectRatio: aspect,\n\t\t\tobjectFit: \"cover\",\n\t\t\tdisplay: \"block\"\n\t\t};\n\t}\n\n\tconst crop = image.crop;\n\n\treturn {\n\t\tposition: \"absolute\",\n\t\tleft: `${-(crop.x / crop.width) * 100}%`,\n\t\ttop: `${-(crop.y / crop.height) * 100}%`,\n\t\twidth: `${(size.width / crop.width) * 100}%`,\n\t\theight: `${(size.height / crop.height) * 100}%`,\n\t\tmaxWidth: \"none\"\n\t};\n}\n","import { useState } from \"react\";\n\nimport { GridContainer } from \"../../grid\";\nimport { InputText } from \"../../input\";\nimport { InputFiles } from \"../../input-files\";\nimport { getImageCropPreviewStyle, type ImageNaturalSize } from \"../lib/getImageCropPreviewStyle\";\nimport { getImageSelectionKey } from \"../lib/getImageSelectionKey\";\nimport { type ImageSelectionPanelProps, type ImageSelectionState } from \"../model/imageSelectionTypes\";\n\nimport { ImageCropper } from \"./ImageCropper\";\n\nconst DEFAULT_ALLOWED_IMAGE_MIME = [\"image/jpeg\", \"image/png\", \"image/webp\", \"image/avif\"] as const;\n\ninterface ImageSelectionFieldsProps extends Pick<\n\tImageSelectionPanelProps,\n\t\"aspect\" | \"disabled\" | \"allowedMime\" | \"maxBytes\" | \"minimumCropWidth\" | \"onReadError\"\n> {\n\tstate: ImageSelectionState;\n}\n\n/** Поля выбора файлов, crop и альтернативного текста для активного изображения. */\nexport function ImageSelectionFields({\n\taspect = 16 / 9,\n\tdisabled,\n\tallowedMime = DEFAULT_ALLOWED_IMAGE_MIME,\n\tmaxBytes,\n\tminimumCropWidth,\n\tonReadError,\n\tstate\n}: ImageSelectionFieldsProps) {\n\tconst area = state.activeImage?.crop;\n\tconst [imageSizes, setImageSizes] = useState<Record<string, ImageNaturalSize>>({});\n\n\treturn (\n\t\t<GridContainer gap=\"md\">\n\t\t\t<InputFiles\n\t\t\t\tlabel=\"Файл изображения\"\n\t\t\t\tallowedMime={allowedMime}\n\t\t\t\tvalue={state.files}\n\t\t\t\tonChange={state.setFiles}\n\t\t\t\tmaxBytes={maxBytes}\n\t\t\t\tdisabled={disabled}\n\t\t\t\tonReadError={onReadError}\n\t\t\t/>\n\n\t\t\t{state.hasSelected && state.activeImage && (\n\t\t\t\t<div>\n\t\t\t\t\t<GridContainer templateColumns=\"repeat(auto-fill, minmax(10em, 1fr))\" gap=\"xs\">\n\t\t\t\t\t\t{state.value.map((image, index) => {\n\t\t\t\t\t\t\tconst imageKey = getImageSelectionKey(image, index);\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={imageKey}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\t\t\t\tonClick={() => state.setActiveIndex(index)}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tpadding: 0,\n\t\t\t\t\t\t\t\t\t\tborder: index === state.activeIndex ? \"2px solid var(--border-accent)\" : \"2px solid transparent\",\n\t\t\t\t\t\t\t\t\t\tbackground: \"transparent\",\n\t\t\t\t\t\t\t\t\t\tcursor: disabled ? \"default\" : \"pointer\"\n\t\t\t\t\t\t\t\t\t}}>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\tposition: \"relative\",\n\t\t\t\t\t\t\t\t\t\t\tdisplay: \"block\",\n\t\t\t\t\t\t\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\t\t\t\t\t\t\taspectRatio: aspect,\n\t\t\t\t\t\t\t\t\t\t\toverflow: \"hidden\"\n\t\t\t\t\t\t\t\t\t\t}}>\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc={image.file.dataUrl}\n\t\t\t\t\t\t\t\t\t\t\talt={image.alt || `Предпросмотр изображения ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\tstyle={getImageCropPreviewStyle(image, imageSizes[imageKey], aspect)}\n\t\t\t\t\t\t\t\t\t\t\tonLoad={(event) => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst target = event.currentTarget;\n\n\t\t\t\t\t\t\t\t\t\t\t\tsetImageSizes((currentSizes) => ({\n\t\t\t\t\t\t\t\t\t\t\t\t\t...currentSizes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t[imageKey]: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: target.naturalWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: target.naturalHeight\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</GridContainer>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\tВыберите область изображения{\" \"}\n\t\t\t\t\t\t{area && (\n\t\t\t\t\t\t\t<code className={minimumCropWidth && area.width < minimumCropWidth ? \"statusError\" : \"statusSuccess\"}>\n\t\t\t\t\t\t\t\t{area.width}x{area.height}\n\t\t\t\t\t\t\t\t{minimumCropWidth && area.width < minimumCropWidth ? \" — недостаточная ширина\" : \"\"}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<ImageCropper\n\t\t\t\t\t\tkey={getImageSelectionKey(state.activeImage, state.activeIndex)}\n\t\t\t\t\t\timage={state.activeImage.file.dataUrl}\n\t\t\t\t\t\tcrop={state.activeCropperState.crop}\n\t\t\t\t\t\tzoom={state.activeCropperState.zoom}\n\t\t\t\t\t\taspect={aspect}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tonCropChange={state.setActiveCropperCrop}\n\t\t\t\t\t\tonZoomChange={state.setActiveCropperZoom}\n\t\t\t\t\t\tonCropComplete={state.setActiveCrop}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<InputText\n\t\t\t\trequired\n\t\t\t\tdisabled={!state.hasSelected || disabled}\n\t\t\t\tlabel=\"Краткое описание изображения\"\n\t\t\t\tplaceholder=\"\"\n\t\t\t\tvalue={state.activeImage?.alt ?? \"\"}\n\t\t\t\tonChange={state.setActiveAlt}\n\t\t\t/>\n\t\t</GridContainer>\n\t);\n}\n","import { Button } from \"../../button\";\nimport { Modal, ModalContent, ModalFooter } from \"../../modal\";\nimport { type ImageSelectionModalProps } from \"../model/imageSelectionTypes\";\nimport { useImageSelectionState } from \"../model/useImageSelectionState\";\n\nimport { ImageSelectionFields } from \"./ImageSelectionFields\";\n\n/** Модальная форма выбора изображений с необязательным этапом подтверждения. */\nexport function ImageSelectionModal({\n\tdisabled,\n\tisOpen = true,\n\tonClose = () => {},\n\ttitle = \"Загрузка нового изображения\",\n\t...props\n}: ImageSelectionModalProps) {\n\tconst state = useImageSelectionState({ ...props, disabled });\n\n\tif (!props.onApply) {\n\t\treturn (\n\t\t\t<Modal isOpen={isOpen} onClose={onClose} title={title} size=\"xl\">\n\t\t\t\t<ModalContent scrollable>\n\t\t\t\t\t<ImageSelectionFields {...props} disabled={disabled} state={state} />\n\t\t\t\t</ModalContent>\n\t\t\t</Modal>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Modal isOpen={isOpen} onClose={onClose} title={title} size=\"xl\">\n\t\t\t<ModalContent scrollable>\n\t\t\t\t<ImageSelectionFields {...props} disabled={disabled} state={state} />\n\t\t\t</ModalContent>\n\t\t\t<ModalFooter>\n\t\t\t\t<Button disabled={!state.canApply} tone=\"success\" onClick={state.apply}>\n\t\t\t\t\tПрименить\n\t\t\t\t</Button>\n\t\t\t</ModalFooter>\n\t\t</Modal>\n\t);\n}\n","import { Button } from \"../../button\";\nimport { type ImageSelectionActionsData, type ImageSelectionPanelProps } from \"../model/imageSelectionTypes\";\nimport { useImageSelectionState } from \"../model/useImageSelectionState\";\n\nimport { ImageSelectionFields } from \"./ImageSelectionFields\";\n\n/** Встраиваемая форма выбора и обрезки одного или нескольких изображений. */\nexport function ImageSelectionPanel({ disabled, renderActions, ...props }: ImageSelectionPanelProps) {\n\tconst state = useImageSelectionState({ ...props, disabled });\n\tconst actionsData: ImageSelectionActionsData = {\n\t\tvalue: state.value,\n\t\tactiveIndex: state.activeIndex,\n\t\thasSelected: state.hasSelected,\n\t\tcanApply: state.canApply,\n\t\tapply: state.apply\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<ImageSelectionFields {...props} disabled={disabled} state={state} />\n\t\t\t{renderActions\n\t\t\t\t? renderActions(actionsData)\n\t\t\t\t: props.onApply && (\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Button disabled={!state.canApply} tone=\"success\" onClick={state.apply}>\n\t\t\t\t\t\t\t\tПрименить\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t</>\n\t);\n}\n","import { type ImgHTMLAttributes } from \"react\";\n\nimport { buildImageSources } from \"../lib/buildImageSources\";\nimport { buildImageSrcSet } from \"../lib/buildImageSrcSet\";\nimport { type ResponsiveImageData } from \"../model/imageTypes\";\n\nimport { ImageRenderer, type ImageRendererProps } from \"./ImageRenderer\";\n\nexport interface ImageViewProps\n\textends\n\t\tOmit<ImageRendererProps, \"src\" | \"sources\" | \"alt\" | \"intrinsicWidth\" | \"intrinsicHeight\" | \"srcSet\" | \"sizes\">,\n\t\tPick<ImgHTMLAttributes<HTMLImageElement>, \"sizes\"> {\n\timage: ResponsiveImageData;\n\talt?: string;\n}\n\n/**\n * Собирает picture/srcset из готовых URL и делегирует отображение ImageRenderer.\n * Компонент не формирует имена файлов и не знает о форматах или размерах хранилища.\n */\nexport function ImageView({ image, alt, sizes, aspectRatio, ...props }: ImageViewProps) {\n\tconst sources = buildImageSources(image.sources, sizes);\n\tconst srcSet = image.candidates ? buildImageSrcSet(image.candidates) : undefined;\n\tconst resolvedAspectRatio =\n\t\taspectRatio ?? (image.intrinsicWidth && image.intrinsicHeight ? `${image.intrinsicWidth} / ${image.intrinsicHeight}` : undefined);\n\n\treturn (\n\t\t<ImageRenderer\n\t\t\t{...props}\n\t\t\tsrc={image.src}\n\t\t\tsrcSet={srcSet || undefined}\n\t\t\tsources={sources}\n\t\t\tsizes={sizes}\n\t\t\talt={alt ?? image.alt}\n\t\t\tintrinsicWidth={image.intrinsicWidth}\n\t\t\tintrinsicHeight={image.intrinsicHeight}\n\t\t\taspectRatio={resolvedAspectRatio}\n\t\t/>\n\t);\n}\n","import { type Slide } from \"yet-another-react-lightbox\";\n\nimport { type ImageCandidate } from \"../model/imageTypes\";\nimport { type ImageViewerImage } from \"../model/imageViewerTypes\";\n\n/** Проверяет размер до передачи в srcset внешней lightbox-библиотеки. */\nfunction isPositiveFinite(value: number): boolean {\n\treturn Number.isFinite(value) && value > 0;\n}\n\n/**\n * Преобразует width- и density-кандидат в физическую ширину.\n * Density имеет смысл только вместе с известной intrinsic-шириной базового изображения.\n */\nfunction resolveCandidateWidth(candidate: ImageCandidate, intrinsicWidth: number): number | null {\n\tconst width =\n\t\tcandidate.width !== undefined ? candidate.width : candidate.density !== undefined ? intrinsicWidth * candidate.density : 0;\n\n\treturn isPositiveFinite(width) ? Math.round(width) : null;\n}\n\n/**\n * Строит srcSet YARL, которому для каждого варианта нужны одновременно width и height.\n * Альтернативные picture sources намеренно остаются контрактом ImageView: YARL 3.x не поддерживает их\n * в стандартном image slide, а custom slide лишает viewer встроенного responsive preload и Zoom.\n */\nfunction buildLightboxSrcSet(image: ImageViewerImage): Slide[\"srcSet\"] {\n\tconst { intrinsicWidth, intrinsicHeight } = image;\n\n\tif (\n\t\tintrinsicWidth === undefined ||\n\t\tintrinsicHeight === undefined ||\n\t\t!isPositiveFinite(intrinsicWidth) ||\n\t\t!isPositiveFinite(intrinsicHeight)\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst candidatesByWidth = new Map<number, { src: string; width: number; height: number }>();\n\n\tfor (const candidate of image.candidates ?? []) {\n\t\tconst candidateWidth = resolveCandidateWidth(candidate, intrinsicWidth);\n\n\t\tif (candidateWidth === null || candidatesByWidth.has(candidateWidth)) continue;\n\n\t\tcandidatesByWidth.set(candidateWidth, {\n\t\t\tsrc: candidate.src,\n\t\t\twidth: candidateWidth,\n\t\t\theight: Math.max(1, Math.round((candidateWidth * intrinsicHeight) / intrinsicWidth))\n\t\t});\n\t}\n\n\tconst candidates = [...candidatesByWidth.values()].sort((left, right) => left.width - right.width);\n\treturn candidates.length > 0 ? candidates : undefined;\n}\n\n/**\n * Адаптирует storage-agnostic ResponsiveImageData к image slides Yet Another React Lightbox.\n * Функция остаётся приватной для `/image`, чтобы vendor contract не становился public API пакета.\n */\nexport function buildImageViewerSlides(images: readonly ImageViewerImage[]): Slide[] {\n\treturn images.map((image) => {\n\t\tconst hasIntrinsicSize = isPositiveFinite(image.intrinsicWidth ?? 0) && isPositiveFinite(image.intrinsicHeight ?? 0);\n\n\t\treturn {\n\t\t\tsrc: image.src,\n\t\t\talt: image.alt,\n\t\t\twidth: hasIntrinsicSize ? image.intrinsicWidth : undefined,\n\t\t\theight: hasIntrinsicSize ? image.intrinsicHeight : undefined,\n\t\t\tsrcSet: buildLightboxSrcSet(image),\n\t\t\ttitle: image.title === undefined ? image.alt : image.title,\n\t\t\tdescription: image.description,\n\t\t\tthumbnail: image.thumbnail,\n\t\t\tdownload: image.download\n\t\t};\n\t});\n}\n","import { type ImageViewerLabels } from \"../model/imageViewerTypes\";\n\nconst DEFAULT_IMAGE_VIEWER_LABELS: ImageViewerLabels = {\n\tprevious: \"Предыдущее изображение\",\n\tnext: \"Следующее изображение\",\n\tclose: \"Закрыть просмотр\",\n\tslide: \"Изображение\",\n\tcarousel: \"Карусель изображений\",\n\tlightbox: \"Просмотр изображений\",\n\tphotoGallery: \"Галерея изображений\",\n\tslideCounter: \"{index} из {total}\",\n\tzoomIn: \"Увеличить\",\n\tzoomOut: \"Уменьшить\",\n\tenterFullscreen: \"Перейти в полноэкранный режим\",\n\texitFullscreen: \"Выйти из полноэкранного режима\",\n\tthumbnails: \"Миниатюры\",\n\tshowThumbnails: \"Показать миниатюры\",\n\thideThumbnails: \"Скрыть миниатюры\",\n\tcaption: \"Подпись изображения\",\n\tshowCaptions: \"Показать подписи\",\n\thideCaptions: \"Скрыть подписи\",\n\tdownload: \"Скачать изображение\"\n};\n\n/** Преобразует стабильные semantic labels foundation в ключи локализации YARL. */\nexport function resolveImageViewerLabels(overrides: Partial<ImageViewerLabels> | undefined) {\n\tconst labels = { ...DEFAULT_IMAGE_VIEWER_LABELS, ...overrides };\n\n\treturn {\n\t\tPrevious: labels.previous,\n\t\tNext: labels.next,\n\t\tClose: labels.close,\n\t\tSlide: labels.slide,\n\t\tCarousel: labels.carousel,\n\t\tLightbox: labels.lightbox,\n\t\t\"Photo gallery\": labels.photoGallery,\n\t\t\"{index} of {total}\": labels.slideCounter,\n\t\t\"Zoom in\": labels.zoomIn,\n\t\t\"Zoom out\": labels.zoomOut,\n\t\t\"Enter Fullscreen\": labels.enterFullscreen,\n\t\t\"Exit Fullscreen\": labels.exitFullscreen,\n\t\tThumbnails: labels.thumbnails,\n\t\t\"Show thumbnails\": labels.showThumbnails,\n\t\t\"Hide thumbnails\": labels.hideThumbnails,\n\t\tCaption: labels.caption,\n\t\t\"Show captions\": labels.showCaptions,\n\t\t\"Hide captions\": labels.hideCaptions,\n\t\tDownload: labels.download\n\t};\n}\n",".viewer {\n\t// Переменные YARL остаются внутренней деталью; потребители настраивают только стабильное пространство image-viewer.\n\t--yarl__portal_zindex: var(--image-viewer-z-index, var(--z-image-viewer, 300));\n\t--yarl__color_backdrop: var(--image-viewer-backdrop, rgb(0 0 0 / 92%));\n\t--yarl__container_background_color: var(--image-viewer-backdrop, rgb(0 0 0 / 92%));\n\t--yarl__color_button: var(--image-viewer-control-color, rgb(255 255 255 / 82%));\n\t--yarl__color_button_active: var(--image-viewer-control-active-color, var(--white, #fff));\n\t--yarl__color_button_disabled: var(--image-viewer-control-disabled-color, rgb(255 255 255 / 38%));\n\t--yarl__button_background_color: var(--image-viewer-control-background, transparent);\n\t--yarl__button_border: var(--image-viewer-control-border, 0);\n\t--yarl__button_filter: var(--image-viewer-control-filter, drop-shadow(0 0.125rem 0.25rem rgb(0 0 0 / 70%)));\n\t--yarl__icon_size: var(--image-viewer-icon-size, 1.75rem);\n\t--yarl__toolbar_padding: var(--image-viewer-toolbar-padding, var(--space-md, 0.625rem));\n\t--yarl__slide_icon_loading_color: var(--image-viewer-loading-color, var(--image-viewer-control-color, rgb(255 255 255 / 82%)));\n\t--yarl__slide_icon_error_color: var(--image-viewer-error-color, var(--status-error-fill, #d32f2f));\n\t--yarl__slide_captions_container_background: var(--image-viewer-caption-background, rgb(0 0 0 / 62%));\n\t--yarl__slide_title_color: var(--image-viewer-caption-color, var(--white, #fff));\n\t--yarl__slide_description_color: var(--image-viewer-caption-color, var(--white, #fff));\n\t--yarl__counter_color: var(--image-viewer-counter-color, var(--image-viewer-control-color, rgb(255 255 255 / 82%)));\n\t--yarl__thumbnails_container_background_color: var(--image-viewer-backdrop, rgb(0 0 0 / 92%));\n\t--yarl__thumbnails_thumbnail_background: var(--image-viewer-thumbnail-background, rgb(255 255 255 / 8%));\n\t--yarl__thumbnails_thumbnail_border_color: var(\n\t\t--image-viewer-thumbnail-border-color,\n\t\tvar(--image-viewer-control-color, rgb(255 255 255 / 82%))\n\t);\n\t--yarl__thumbnails_thumbnail_active_border_color: var(\n\t\t--image-viewer-thumbnail-active-border-color,\n\t\tvar(--image-viewer-control-active-color, var(--white, #fff))\n\t);\n\n\tfont-family: var(--font-family-base, sans-serif);\n}\n\n.viewer :global(.yarl__button) {\n\tborder-radius: var(--image-viewer-control-radius, var(--radius-sm, 0.3125rem));\n\ttransition:\n\t\tcolor var(--transition-fast, 0.2s) ease,\n\t\tbackground-color var(--transition-fast, 0.2s) ease;\n}\n\n.viewer :global(.yarl__button):focus-visible,\n.viewer :global(.yarl__thumbnails_thumbnail):focus-visible {\n\toutline: var(--image-viewer-focus, var(--focus, 0.125rem solid currentColor));\n\toutline-offset: var(--image-viewer-focus-offset, var(--focus-offset, 0.125rem));\n}\n\n.viewer :global(.yarl__thumbnails_thumbnail):focus-visible {\n\tbox-shadow: none;\n}\n\n.viewer :global(.yarl__slide_image) {\n\tborder-radius: var(--image-viewer-image-radius, var(--radius-sm, 0.3125rem));\n}\n\n.icon {\n\twidth: var(--image-viewer-icon-size, 1.75rem);\n\theight: var(--image-viewer-icon-size, 1.75rem);\n\tstroke-width: var(--image-viewer-icon-stroke-width, 2);\n}\n\n.loadingIcon {\n\tanimation: imageViewerSpin 1s linear infinite;\n}\n\n@keyframes imageViewerSpin {\n\tto {\n\t\ttransform: rotate(1turn);\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.loadingIcon {\n\t\tanimation: none;\n\t}\n}\n","import { useMemo } from \"react\";\n\nimport { cn } from \"@ryuzaki13/react-foundation-lib/utils\";\nimport {\n\tCaptionsIcon,\n\tCaptionsOffIcon,\n\tChevronLeftIcon,\n\tChevronRightIcon,\n\tDownloadIcon,\n\tGalleryThumbnailsIcon,\n\tImageOffIcon,\n\tLoaderCircleIcon,\n\tMaximize2Icon,\n\tMinimize2Icon,\n\tXIcon,\n\tZoomInIcon,\n\tZoomOutIcon\n} from \"lucide-react\";\nimport Lightbox, { isImageSlide, type LightboxExternalProps, type Plugin } from \"yet-another-react-lightbox\";\nimport Captions from \"yet-another-react-lightbox/plugins/captions\";\nimport Counter from \"yet-another-react-lightbox/plugins/counter\";\nimport Download from \"yet-another-react-lightbox/plugins/download\";\nimport Fullscreen from \"yet-another-react-lightbox/plugins/fullscreen\";\nimport Thumbnails from \"yet-another-react-lightbox/plugins/thumbnails\";\nimport Zoom from \"yet-another-react-lightbox/plugins/zoom\";\n\nimport { buildImageViewerSlides } from \"../lib/buildImageViewerSlides\";\nimport { resolveImageViewerLabels } from \"../lib/resolveImageViewerLabels\";\nimport { type ImageViewerProps } from \"../model/imageViewerTypes\";\n\nimport \"yet-another-react-lightbox/plugins/captions.css\";\nimport \"yet-another-react-lightbox/plugins/counter.css\";\nimport \"yet-another-react-lightbox/plugins/thumbnails.css\";\nimport \"yet-another-react-lightbox/styles.css\";\n\nimport styles from \"./ImageViewer.module.scss\";\n\n/**\n * Управляемый полноэкранный viewer поверх Yet Another React Lightbox.\n * Компонент изолирует внешний API, сохраняет безопасный для SSR первый render и принимает тот же\n * независимый от хранилища responsive-контракт, что ImageView. Визуальные настройки публикуются только как `--image-viewer-*`.\n */\nexport function ImageViewer({\n\topen,\n\timages,\n\tindex = 0,\n\tonClose,\n\tonIndexChange,\n\tfeatures,\n\tloop = false,\n\tcloseOnBackdropClick = true,\n\tcloseOnPullDown = true,\n\tlabels,\n\tclassName,\n\tstyle\n}: ImageViewerProps) {\n\t// Стабильная ссылка предотвращает внешний reset текущего slide при несвязанном rerender родителя.\n\tconst slides = useMemo(() => buildImageViewerSlides(images), [images]);\n\tconst requestedIndex = Number.isFinite(index) ? Math.trunc(index) : 0;\n\tconst resolvedIndex = slides.length > 0 ? Math.min(Math.max(requestedIndex, 0), slides.length - 1) : 0;\n\tconst hasCaptions = images.some((image) => {\n\t\tconst title = image.title === undefined ? image.alt : image.title;\n\t\treturn (title !== undefined && title !== null) || (image.description !== undefined && image.description !== null);\n\t});\n\tconst captionsEnabled = features?.captions ?? hasCaptions;\n\tconst counterEnabled = features?.counter ?? slides.length > 1;\n\tconst downloadEnabled = features?.download ?? false;\n\tconst fullscreenEnabled = features?.fullscreen ?? true;\n\tconst thumbnailsEnabled = features?.thumbnails ?? slides.length > 1;\n\tconst zoomEnabled = features?.zoom ?? true;\n\tconst plugins = useMemo<Plugin[]>(\n\t\t() => [\n\t\t\t...(captionsEnabled ? [Captions] : []),\n\t\t\t...(counterEnabled ? [Counter] : []),\n\t\t\t...(downloadEnabled ? [Download] : []),\n\t\t\t...(fullscreenEnabled ? [Fullscreen] : []),\n\t\t\t...(thumbnailsEnabled ? [Thumbnails] : []),\n\t\t\t...(zoomEnabled ? [Zoom] : [])\n\t\t],\n\t\t[captionsEnabled, counterEnabled, downloadEnabled, fullscreenEnabled, thumbnailsEnabled, zoomEnabled]\n\t);\n\tconst resolvedLabels = useMemo(() => resolveImageViewerLabels(labels), [labels]);\n\tconst lightboxProps: LightboxExternalProps = {\n\t\topen: open && slides.length > 0,\n\t\tclose: onClose,\n\t\tindex: resolvedIndex,\n\t\tslides,\n\t\tplugins,\n\t\tclassName: cn(styles.viewer, className),\n\t\tportal: { container: { style } },\n\t\tlabels: resolvedLabels,\n\t\tcarousel: { finite: !loop },\n\t\tcontroller: { closeOnBackdropClick, closeOnPullDown },\n\t\tcaptions: { showToggle: true },\n\t\tcounter: { separator: \" / \" },\n\t\tthumbnails: { showToggle: true },\n\t\tdownload: {\n\t\t\tdownload: ({ slide, saveAs }) => {\n\t\t\t\tif (!isImageSlide(slide) || slide.download === false) return;\n\n\t\t\t\tif (typeof slide.download === \"string\") {\n\t\t\t\t\tsaveAs(slide.download);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (typeof slide.download === \"object\") {\n\t\t\t\t\tsaveAs(slide.download.url, slide.download.filename);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsaveAs(slide.src);\n\t\t\t}\n\t\t},\n\t\ton: {\n\t\t\tview: ({ index: nextIndex }) => {\n\t\t\t\tif (nextIndex !== resolvedIndex) onIndexChange?.(nextIndex);\n\t\t\t}\n\t\t},\n\t\trender: {\n\t\t\ticonPrev: () => <ChevronLeftIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonNext: () => <ChevronRightIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonClose: () => <XIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonLoading: () => <LoaderCircleIcon aria-hidden=\"true\" className={cn(styles.icon, styles.loadingIcon)} />,\n\t\t\ticonError: () => <ImageOffIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonZoomIn: () => <ZoomInIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonZoomOut: () => <ZoomOutIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonEnterFullscreen: () => <Maximize2Icon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonExitFullscreen: () => <Minimize2Icon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonThumbnailsVisible: () => <GalleryThumbnailsIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonThumbnailsHidden: () => <GalleryThumbnailsIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonCaptionsVisible: () => <CaptionsIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonCaptionsHidden: () => <CaptionsOffIcon aria-hidden=\"true\" className={styles.icon} />,\n\t\t\ticonDownload: () => <DownloadIcon aria-hidden=\"true\" className={styles.icon} />\n\t\t}\n\t};\n\n\treturn <Lightbox {...lightboxProps} />;\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type Slide } from "yet-another-react-lightbox";
2
+ import { type ImageViewerImage } from "../model/imageViewerTypes";
3
+ /**
4
+ * Адаптирует storage-agnostic ResponsiveImageData к image slides Yet Another React Lightbox.
5
+ * Функция остаётся приватной для `/image`, чтобы vendor contract не становился public API пакета.
6
+ */
7
+ export declare function buildImageViewerSlides(images: readonly ImageViewerImage[]): Slide[];
8
+ //# sourceMappingURL=buildImageViewerSlides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildImageViewerSlides.d.ts","sourceRoot":"","sources":["../../../src/image/lib/buildImageViewerSlides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAGxD,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAqDlE;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,KAAK,EAAE,CAgBnF"}
@@ -0,0 +1,24 @@
1
+ import { type ImageViewerLabels } from "../model/imageViewerTypes";
2
+ /** Преобразует стабильные semantic labels foundation в ключи локализации YARL. */
3
+ export declare function resolveImageViewerLabels(overrides: Partial<ImageViewerLabels> | undefined): {
4
+ Previous: string;
5
+ Next: string;
6
+ Close: string;
7
+ Slide: string;
8
+ Carousel: string;
9
+ Lightbox: string;
10
+ "Photo gallery": string;
11
+ "{index} of {total}": string;
12
+ "Zoom in": string;
13
+ "Zoom out": string;
14
+ "Enter Fullscreen": string;
15
+ "Exit Fullscreen": string;
16
+ Thumbnails: string;
17
+ "Show thumbnails": string;
18
+ "Hide thumbnails": string;
19
+ Caption: string;
20
+ "Show captions": string;
21
+ "Hide captions": string;
22
+ Download: string;
23
+ };
24
+ //# sourceMappingURL=resolveImageViewerLabels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveImageViewerLabels.d.ts","sourceRoot":"","sources":["../../../src/image/lib/resolveImageViewerLabels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAwBnE,kFAAkF;AAClF,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;EAwBzF"}
@@ -0,0 +1,70 @@
1
+ import { type CSSProperties, type ReactNode } from "react";
2
+ import { type ResponsiveImageData } from "./imageTypes";
3
+ type ImageViewerDownload = false | string | {
4
+ readonly url: string;
5
+ readonly filename: string;
6
+ };
7
+ /**
8
+ * Изображение lightbox поверх базового responsive-контракта.
9
+ * Дополнительные поля описывают только представление в viewer и не связывают primitive со схемой хранения.
10
+ */
11
+ export type ImageViewerImage = ResponsiveImageData & {
12
+ readonly title?: ReactNode;
13
+ readonly description?: ReactNode;
14
+ readonly thumbnail?: string;
15
+ readonly download?: ImageViewerDownload;
16
+ };
17
+ /** Набор независимо включаемых возможностей ImageViewer. */
18
+ export type ImageViewerFeatures = {
19
+ readonly captions?: boolean;
20
+ readonly counter?: boolean;
21
+ readonly download?: boolean;
22
+ readonly fullscreen?: boolean;
23
+ readonly thumbnails?: boolean;
24
+ readonly zoom?: boolean;
25
+ };
26
+ /**
27
+ * Семантические подписи viewer без раскрытия ключей конкретной lightbox-библиотеки.
28
+ * Шаблон slideCounter поддерживает маркеры `{index}` и `{total}`.
29
+ */
30
+ export type ImageViewerLabels = {
31
+ readonly previous: string;
32
+ readonly next: string;
33
+ readonly close: string;
34
+ readonly slide: string;
35
+ readonly carousel: string;
36
+ readonly lightbox: string;
37
+ readonly photoGallery: string;
38
+ readonly slideCounter: string;
39
+ readonly zoomIn: string;
40
+ readonly zoomOut: string;
41
+ readonly enterFullscreen: string;
42
+ readonly exitFullscreen: string;
43
+ readonly thumbnails: string;
44
+ readonly showThumbnails: string;
45
+ readonly hideThumbnails: string;
46
+ readonly caption: string;
47
+ readonly showCaptions: string;
48
+ readonly hideCaptions: string;
49
+ readonly download: string;
50
+ };
51
+ type ImageViewerCssVariable = "--image-viewer-z-index" | "--image-viewer-backdrop" | "--image-viewer-control-color" | "--image-viewer-control-active-color" | "--image-viewer-control-disabled-color" | "--image-viewer-control-background" | "--image-viewer-control-border" | "--image-viewer-control-radius" | "--image-viewer-control-filter" | "--image-viewer-icon-size" | "--image-viewer-icon-stroke-width" | "--image-viewer-toolbar-padding" | "--image-viewer-focus" | "--image-viewer-focus-offset" | "--image-viewer-image-radius" | "--image-viewer-caption-background" | "--image-viewer-caption-color" | "--image-viewer-counter-color" | "--image-viewer-thumbnail-background" | "--image-viewer-thumbnail-border-color" | "--image-viewer-thumbnail-active-border-color" | "--image-viewer-loading-color" | "--image-viewer-error-color";
52
+ /** Inline-стили viewer с типизированным публичным набором CSS-переменных. */
53
+ export type ImageViewerStyle = CSSProperties & Partial<Record<ImageViewerCssVariable, string | number>>;
54
+ /** Публичный controlled-контракт полноэкранного просмотра изображений. */
55
+ export type ImageViewerProps = {
56
+ readonly open: boolean;
57
+ readonly images: readonly ImageViewerImage[];
58
+ readonly index?: number;
59
+ readonly onClose: () => void;
60
+ readonly onIndexChange?: (index: number) => void;
61
+ readonly features?: ImageViewerFeatures;
62
+ readonly loop?: boolean;
63
+ readonly closeOnBackdropClick?: boolean;
64
+ readonly closeOnPullDown?: boolean;
65
+ readonly labels?: Partial<ImageViewerLabels>;
66
+ readonly className?: string;
67
+ readonly style?: ImageViewerStyle;
68
+ };
69
+ export {};
70
+ //# sourceMappingURL=imageViewerTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageViewerTypes.d.ts","sourceRoot":"","sources":["../../../src/image/model/imageViewerTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type ImageViewerProps } from "../model/imageViewerTypes";
2
+ import "yet-another-react-lightbox/plugins/captions.css";
3
+ import "yet-another-react-lightbox/plugins/counter.css";
4
+ import "yet-another-react-lightbox/plugins/thumbnails.css";
5
+ import "yet-another-react-lightbox/styles.css";
6
+ /**
7
+ * Управляемый полноэкранный viewer поверх Yet Another React Lightbox.
8
+ * Компонент изолирует внешний API, сохраняет безопасный для SSR первый render и принимает тот же
9
+ * независимый от хранилища responsive-контракт, что ImageView. Визуальные настройки публикуются только как `--image-viewer-*`.
10
+ */
11
+ export declare function ImageViewer({ open, images, index, onClose, onIndexChange, features, loop, closeOnBackdropClick, closeOnPullDown, labels, className, style }: ImageViewerProps): import("react").JSX.Element;
12
+ //# sourceMappingURL=ImageViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageViewer.d.ts","sourceRoot":"","sources":["../../../src/image/ui/ImageViewer.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,iDAAiD,CAAC;AACzD,OAAO,gDAAgD,CAAC;AACxD,OAAO,mDAAmD,CAAC;AAC3D,OAAO,uCAAuC,CAAC;AAI/C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC3B,IAAI,EACJ,MAAM,EACN,KAAS,EACT,OAAO,EACP,aAAa,EACb,QAAQ,EACR,IAAY,EACZ,oBAA2B,EAC3B,eAAsB,EACtB,MAAM,EACN,SAAS,EACT,KAAK,EACL,EAAE,gBAAgB,+BAkFlB"}