@ryuzaki13/react-foundation-ui 1.8.6 → 1.8.7
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.
- package/dist/chunks/date-input-g6l1iDmf.js.map +1 -1
- package/package.json +4 -4
- package/storybook-static/assets/{Badge-CV8uR5lW.js → Badge-C4U4sxtF.js} +1 -1
- package/storybook-static/assets/{Badge.stories-DqTFDEB6.js → Badge.stories-DHmJTpdf.js} +1 -1
- package/storybook-static/assets/{BadgeList-CVWvhSS7.js → BadgeList-BOs5ycWn.js} +1 -1
- package/storybook-static/assets/{Button-C5Z6nJ9s.js → Button-BTtYeCS_.js} +1 -1
- package/storybook-static/assets/{Button-DtySOeyD.js → Button-Bkb3wQw6.js} +1 -1
- package/storybook-static/assets/{Button.stories--Wr67DaY.js → Button.stories-DgFLAdcg.js} +1 -1
- package/storybook-static/assets/{CalendarView-ZzqQ8UIe.js → CalendarView-BIFHbOTr.js} +1 -1
- package/storybook-static/assets/{CheckBox-BfNP6vO6.js → CheckBox-76M-N56J.js} +1 -1
- package/storybook-static/assets/{CheckBox-BBNTe4u8.js → CheckBox-D4u-58g8.js} +1 -1
- package/storybook-static/assets/{CheckBox.module-CoYygdZx.js → CheckBox.module-BOAVN-Ny.js} +1 -1
- package/storybook-static/assets/{CheckBox.stories-B4Sv2TXp.js → CheckBox.stories-Caw6-hSQ.js} +1 -1
- package/storybook-static/assets/{Color-6AOWCN3U-Ce5w8Y2l.js → Color-6AOWCN3U-CPYGaU3y.js} +1 -1
- package/storybook-static/assets/{ContextMenu-D-lV53Zc.js → ContextMenu-CUWt-eg1.js} +1 -1
- package/storybook-static/assets/{ContextMenu-Boi0R7-s.js → ContextMenu-gfYYiVdR.js} +1 -1
- package/storybook-static/assets/{ContextMenu.stories-D_rxDbLk.js → ContextMenu.stories-BuQS57wW.js} +1 -1
- package/storybook-static/assets/{CustomOptionButton-VYfjUfhW.js → CustomOptionButton-Be7sd0Lb.js} +1 -1
- package/storybook-static/assets/{DateInput-f-RcBdOb.js → DateInput-B4oviF7J.js} +1 -1
- package/storybook-static/assets/{DateInput-q2hPsJcH.js → DateInput-BQGUMYsW.js} +1 -1
- package/storybook-static/assets/{DateInput.stories-zQzjIsfx.js → DateInput.stories-D6q7J1C4.js} +1 -1
- package/storybook-static/assets/{DateRangePresetSelect-DQBOF8f5.js → DateRangePresetSelect-BAxaG0HP.js} +1 -1
- package/storybook-static/assets/{DateRangePresetSelect-Ciikik1_.js → DateRangePresetSelect-Cq6pOZPu.js} +1 -1
- package/storybook-static/assets/{DateRangePresetSelect.stories-BF8IP5Ep.js → DateRangePresetSelect.stories-d4HFbao6.js} +1 -1
- package/storybook-static/assets/{Detail-CEl8URNQ.js → Detail-DlETcWVp.js} +1 -1
- package/storybook-static/assets/{Detail.stories-Mr3ypLAA.js → Detail.stories-BEwFXEZH.js} +1 -1
- package/storybook-static/assets/{Dialog-Dj2y3EO1.js → Dialog-Bk8aoiOf.js} +1 -1
- package/storybook-static/assets/{Dialog-BydPFV17.js → Dialog-D63TJr1M.js} +1 -1
- package/storybook-static/assets/{Dialog.stories-C1WlZdyp.js → Dialog.stories-BR7NqeZj.js} +1 -1
- package/storybook-static/assets/{Disclosure-DH77E5hY.js → Disclosure-zQK54WKU.js} +1 -1
- package/storybook-static/assets/{Disclosure.stories-B9ZVczUX.js → Disclosure.stories-CNLpB5YJ.js} +1 -1
- package/storybook-static/assets/{DisclosureGroup-CIoFeosY.js → DisclosureGroup-C_nZSLZw.js} +1 -1
- package/storybook-static/assets/{DocsRenderer-JROSPFPF-CbJN3X6f.js → DocsRenderer-JROSPFPF-CoU8mWAw.js} +2 -2
- package/storybook-static/assets/{DropZone-CR7xRn_b.js → DropZone-CUz1Q4bA.js} +1 -1
- package/storybook-static/assets/{DropZone-DTBV-X-J.js → DropZone-Y5hvED_x.js} +1 -1
- package/storybook-static/assets/{DropZone.stories-Cw_og1Cs.js → DropZone.stories-BH4FSM4P.js} +1 -1
- package/storybook-static/assets/{DropdownMenu-CVIeHT8N.js → DropdownMenu-Bi6-5Gqf.js} +1 -1
- package/storybook-static/assets/{ExpandableActionPanel-D8U6v8X7.js → ExpandableActionPanel-BfWj887r.js} +1 -1
- package/storybook-static/assets/{ExpandableActionPanel.stories-D42CZyOc.js → ExpandableActionPanel.stories-CGxcwRMU.js} +1 -1
- package/storybook-static/assets/{Flex-Bcsl0U5O.js → Flex-Dm-Qfvzk.js} +1 -1
- package/storybook-static/assets/{Flex.stories-C0vSa3mh.js → Flex.stories-B3zMz03s.js} +1 -1
- package/storybook-static/assets/{FlexContainer-LmZIdhq2.js → FlexContainer-BWyPlTMB.js} +1 -1
- package/storybook-static/assets/{FlexItem-vkDYa5tA.js → FlexItem-pu3E11Sf.js} +1 -1
- package/storybook-static/assets/{FloatingPopover-DvU3oc_q.js → FloatingPopover-NMRTnqfo.js} +1 -1
- package/storybook-static/assets/FormattedTableCell-BISjHrBJ.js +15 -0
- package/storybook-static/assets/{Grid-BCzygAqy.js → Grid-CzO0Ae1r.js} +1 -1
- package/storybook-static/assets/{Grid-Wl0Y0YAh.js → Grid-vZyA-712.js} +1 -1
- package/storybook-static/assets/{Grid.stories-G6gMmKCP.js → Grid.stories-BQqK0zom.js} +1 -1
- package/storybook-static/assets/{GridContainer-qDguE1HS.js → GridContainer-fKYgDsLl.js} +1 -1
- package/storybook-static/assets/{ImageCropper.stories-u6rOvDbd.js → ImageCropper.stories-Ca4MZWOW.js} +3 -3
- package/storybook-static/assets/{ImageRenderer-BYIRHN5G.js → ImageRenderer-BWIk_76e.js} +1 -1
- package/storybook-static/assets/{ImageRenderer.stories-BMEHEIVH.js → ImageRenderer.stories-C1FfOcFD.js} +1 -1
- package/storybook-static/assets/{ImageView.stories-DiEA93Yz.js → ImageView.stories-DyuR7kdq.js} +1 -1
- package/storybook-static/assets/{ImageViewer.stories-DyxctGxM.js → ImageViewer.stories-PcvOFYqC.js} +2 -2
- package/storybook-static/assets/{ImageViewerRuntime--h_3C2s0.js → ImageViewerRuntime-BiI7Xukq.js} +2 -2
- package/storybook-static/assets/{Input-CViRQ_rj.js → Input-CsD-x1BQ.js} +1 -1
- package/storybook-static/assets/{Input-B2kOwWOU.js → Input-D_4GoCLl.js} +1 -1
- package/storybook-static/assets/{Input.stories-D9eo9MWc.js → Input.stories-D-1urO1N.js} +1 -1
- package/storybook-static/assets/{InputDateTime-T9_iyG4G.js → InputDateTime-CFQanHxB.js} +1 -1
- package/storybook-static/assets/{InputDateTime.stories-DzegljaN.js → InputDateTime.stories-Q4Ao5Tat.js} +1 -1
- package/storybook-static/assets/{InputFile-CA3YpXnm.js → InputFile-Bj8PXGY3.js} +1 -1
- package/storybook-static/assets/{InputFile-Cvdj6RfC.js → InputFile-DM6fBXUx.js} +1 -1
- package/storybook-static/assets/{InputFile.stories-C18UaXPS.js → InputFile.stories-DC27BYoQ.js} +1 -1
- package/storybook-static/assets/{InputFiles-nS_qsXY7.js → InputFiles-D59MGS0Z.js} +1 -1
- package/storybook-static/assets/{InputFiles-C4OkTG9M.js → InputFiles-teFj6f6X.js} +1 -1
- package/storybook-static/assets/{InputFiles.stories-BYd0Pe0R.js → InputFiles.stories-DX5UEvYR.js} +1 -1
- package/storybook-static/assets/{InputFilesList-CXPf7Ui_.js → InputFilesList-BD4Y0gN-.js} +1 -1
- package/storybook-static/assets/{InputImage-kd50L1Gf.js → InputImage-DmsFGNLJ.js} +1 -1
- package/storybook-static/assets/{InputImage-jDVIxP7S.js → InputImage-Du6jGDmY.js} +1 -1
- package/storybook-static/assets/{InputImage.stories-D-xmS4Qj.js → InputImage.stories-u0hrr-Da.js} +1 -1
- package/storybook-static/assets/{InteractiveComponents-D13nzSK7.js → InteractiveComponents-BLQiSO3p.js} +1 -1
- package/storybook-static/assets/{InteractiveComponents.stories-yITzDh-Y.js → InteractiveComponents.stories-BxIVvRz5.js} +1 -1
- package/storybook-static/assets/{LayoutPicker-BfYXGIKb.js → LayoutPicker-TecE4x6w.js} +1 -1
- package/storybook-static/assets/{LayoutPicker-9FVo0bF3.js → LayoutPicker-xUooYcbS.js} +1 -1
- package/storybook-static/assets/{LayoutPicker.stories-B-UnuGFM.js → LayoutPicker.stories-YDMnmhJ4.js} +1 -1
- package/storybook-static/assets/{Listbox-9LVlsDzw.js → Listbox-D2k8QfTs.js} +1 -1
- package/storybook-static/assets/{Listbox-C53-i-L6.js → Listbox-DplAn6ND.js} +1 -1
- package/storybook-static/assets/{Listbox.stories-CCN72ABE.js → Listbox.stories-Bg3_UQD4.js} +1 -1
- package/storybook-static/assets/{Modal-CoXpuByh.js → Modal-4OdkCwCv.js} +1 -1
- package/storybook-static/assets/{Modal.stories-DAj_oPSt.js → Modal.stories-BwawDSGR.js} +1 -1
- package/storybook-static/assets/{ModalManagerProvider-C7pGT0B3.js → ModalManagerProvider-CYR_xbpx.js} +1 -1
- package/storybook-static/assets/{MultiSelect-DqeGNK15.js → MultiSelect-CXn1CY9G.js} +1 -1
- package/storybook-static/assets/{MultiSelect-DZ2krz-D.js → MultiSelect-iwbCRD9H.js} +1 -1
- package/storybook-static/assets/{MultiSelect.stories-BoVHXMbn.js → MultiSelect.stories-BSjAL2ua.js} +1 -1
- package/storybook-static/assets/{NoData-N6Pbu0LD.js → NoData-6Scjk2OD.js} +1 -1
- package/storybook-static/assets/{NotificationsHost--uPz1G3j.js → NotificationsHost-DK3A-iq7.js} +1 -1
- package/storybook-static/assets/{NotificationsHost.stories-B-X-Ljxs.js → NotificationsHost.stories-BRmPf9Q7.js} +1 -1
- package/storybook-static/assets/{ODataMultiSelect-7U6nJdqB.js → ODataMultiSelect-CuNGV1QU.js} +1 -1
- package/storybook-static/assets/{ODataMultiSelect.stories-B-t6MsOr.js → ODataMultiSelect.stories-CyAonfZC.js} +1 -1
- package/storybook-static/assets/{ODataSelect-Dy-fSgE6.js → ODataSelect-vhh4NJS_.js} +1 -1
- package/storybook-static/assets/{ODataSelect.stories-DhqHtcmD.js → ODataSelect.stories-aJBFF45H.js} +1 -1
- package/storybook-static/assets/{ODataTreeMultiSelect-Ol0nuytU.js → ODataTreeMultiSelect-vPtlTloF.js} +1 -1
- package/storybook-static/assets/{ODataTreeMultiSelect.stories-BupVzhbY.js → ODataTreeMultiSelect.stories-DTBAF628.js} +1 -1
- package/storybook-static/assets/{ODataTreeSelect-EkpDmDyk.js → ODataTreeSelect-BCVVLB9M.js} +1 -1
- package/storybook-static/assets/{ODataTreeSelect.stories-CFdbwyEg.js → ODataTreeSelect.stories-CptC0p6O.js} +1 -1
- package/storybook-static/assets/{OneStepScroller-rmyPl9f5.js → OneStepScroller-DerjCiL3.js} +1 -1
- package/storybook-static/assets/{Option-72PrSDkj.js → Option-DkAw9S3l.js} +1 -1
- package/storybook-static/assets/{OptionButton-Br8QoJL7.js → OptionButton-CGurT23Z.js} +1 -1
- package/storybook-static/assets/{PeriodSelect-C5Xk-g4Y.js → PeriodSelect-DOO139ke.js} +1 -1
- package/storybook-static/assets/{PeriodSelect-DEZVGWGR.js → PeriodSelect-TrGTMIid.js} +1 -1
- package/storybook-static/assets/{PeriodSelect.stories-BVBP97HW.js → PeriodSelect.stories-B3GEgBZ1.js} +1 -1
- package/storybook-static/assets/{PickerTriggerActions-DSmRHX1I.js → PickerTriggerActions-CKVPuKEC.js} +1 -1
- package/storybook-static/assets/{Popover-BpbeIq6-.js → Popover-BNItmUQ9.js} +1 -1
- package/storybook-static/assets/{Popover-1KQwBxEJ.js → Popover-DgXqO0PN.js} +1 -1
- package/storybook-static/assets/{Popover.stories-6RDZ0TND.js → Popover.stories-bX5S41pH.js} +1 -1
- package/storybook-static/assets/{PresetRangeDateInput-BdkKzY69.js → PresetRangeDateInput-B4FYPy8g.js} +1 -1
- package/storybook-static/assets/{PresetRangeDateInput-CDhgmyFs.js → PresetRangeDateInput-GSnnpZzy.js} +1 -1
- package/storybook-static/assets/{PresetRangeDateInput.stories-BoE41IUK.js → PresetRangeDateInput.stories-Ca0mIo33.js} +1 -1
- package/storybook-static/assets/{PresetSelect-CA5AyOOy.js → PresetSelect-B_KRl4SQ.js} +1 -1
- package/storybook-static/assets/README-1RIcD7RV.js +694 -0
- package/storybook-static/assets/README-B1XUsXZj.js +334 -0
- package/storybook-static/assets/README-BH78sIZ_.js +288 -0
- package/storybook-static/assets/README-BgeYwR6t.js +347 -0
- package/storybook-static/assets/README-BhsUMTSk.js +860 -0
- package/storybook-static/assets/README-Bj88Ecb7.js +876 -0
- package/storybook-static/assets/README-C9saKIyr.js +417 -0
- package/storybook-static/assets/README-CP1S4LYF.js +1309 -0
- package/storybook-static/assets/README-Cc-guFb0.js +1487 -0
- package/storybook-static/assets/README-CheO0E1A.js +282 -0
- package/storybook-static/assets/README-CiuVVUgE.js +420 -0
- package/storybook-static/assets/README-CvRg9lqh.js +273 -0
- package/storybook-static/assets/README-DCdV2RQL.js +1093 -0
- package/storybook-static/assets/README-DyNQqotW.js +338 -0
- package/storybook-static/assets/README-W1rhe8Sk.js +648 -0
- package/storybook-static/assets/{RadioButton-IDzSzftl.js → RadioButton-CA9DN5bY.js} +1 -1
- package/storybook-static/assets/{RadioButton-DCoo55ld.js → RadioButton-DcXbcFW1.js} +1 -1
- package/storybook-static/assets/{RadioButton.stories-BEk1uc2x.js → RadioButton.stories-1_kzxnXY.js} +1 -1
- package/storybook-static/assets/{RadioGroup-CbBmuFtC.js → RadioGroup-bEwTHCyS.js} +1 -1
- package/storybook-static/assets/{RadioGroup-VSiqFUI1.js → RadioGroup-mfYgdfXM.js} +1 -1
- package/storybook-static/assets/{RadioGroup.stories-DR7Vskyy.js → RadioGroup.stories-CecdqPms.js} +1 -1
- package/storybook-static/assets/{Scrollable-CDZ18AWy.js → Scrollable-1fdTDJHK.js} +1 -1
- package/storybook-static/assets/{Select-IWQpo0LQ.js → Select-CvQZGS1G.js} +1 -1
- package/storybook-static/assets/{Select-YjugVUKX.js → Select-Df5Md2Cm.js} +1 -1
- package/storybook-static/assets/{Select.stories-4JpnXfaB.js → Select.stories-M4M80KTq.js} +1 -1
- package/storybook-static/assets/{SerializableSelect-oxnbV26a.js → SerializableSelect-CnsGo9Ep.js} +1 -1
- package/storybook-static/assets/{SerializableSelect.stories-CjVcuU4t.js → SerializableSelect.stories-BJ6MlNDx.js} +1 -1
- package/storybook-static/assets/{SingleDateTimeInput-DFPke7bv.js → SingleDateTimeInput-BCV0gfB9.js} +1 -1
- package/storybook-static/assets/{SingleDateTimeInput-CQ1_ABKA.js → SingleDateTimeInput-DJH7zSIz.js} +1 -1
- package/storybook-static/assets/{SingleDateTimeInput.stories--Nky_yBh.js → SingleDateTimeInput.stories-DgEwAFe8.js} +1 -1
- package/storybook-static/assets/{Slider-DGVl0dbV.js → Slider-D504FPH3.js} +1 -1
- package/storybook-static/assets/{Slider-L7pqW7xG.js → Slider-cLRAHPLi.js} +1 -1
- package/storybook-static/assets/{Slider.stories-DkqUavTk.js → Slider.stories-EI314Iff.js} +1 -1
- package/storybook-static/assets/{SliderInput-B7U8M2UN.js → SliderInput-Bt0H8rgN.js} +1 -1
- package/storybook-static/assets/{SliderInput-Dn5-Dt4w.js → SliderInput-E5p3n3VY.js} +1 -1
- package/storybook-static/assets/{SliderInput.stories-B9WBQOiA.js → SliderInput.stories-CDYPq7fP.js} +1 -1
- package/storybook-static/assets/{Sortable-DxdXjG1a.js → Sortable-Bzd7JK4E.js} +1 -1
- package/storybook-static/assets/{Sortable.stories-D1rp2l9M.js → Sortable.stories-DNGbmn2a.js} +1 -1
- package/storybook-static/assets/{Splitter-yZpkev96.js → Splitter-DSf48AOW.js} +1 -1
- package/storybook-static/assets/{Splitter.stories-C3TCKNhu.js → Splitter.stories-DUKsOqNN.js} +1 -1
- package/storybook-static/assets/{StateSelect-DZjPblfU.js → StateSelect-BxuKIsLa.js} +1 -1
- package/storybook-static/assets/{StateSelect-CaAFL9Jr.js → StateSelect-HKOT6kq9.js} +1 -1
- package/storybook-static/assets/{StateSelect.stories-D7EwT0sO.js → StateSelect.stories-CbLvq7ou.js} +1 -1
- package/storybook-static/assets/{StringToggle.stories-DuaWy2LK.js → StringToggle.stories-DOKL-aWy.js} +1 -1
- package/storybook-static/assets/{Switch-C0GQ2oXw.js → Switch-CLAfhk73.js} +1 -1
- package/storybook-static/assets/{Switch-DCNQT47U.js → Switch-CVTjSEq1.js} +1 -1
- package/storybook-static/assets/{Switch.stories-CAIEfQb6.js → Switch.stories-CL5Xe_HQ.js} +1 -1
- package/storybook-static/assets/{Table-Be1uta4G.js → Table-DKc19RME.js} +1 -1
- package/storybook-static/assets/Table.stories-CJ9DR1S-.js +207 -0
- package/storybook-static/assets/{Tabs-Co7Z92UK.js → Tabs-De_4F0H3.js} +1 -1
- package/storybook-static/assets/{Tabs.stories-D84U8aJM.js → Tabs.stories-DPAvvkh9.js} +2 -2
- package/storybook-static/assets/{TabsBox-CUpOXGdh.js → TabsBox-Cy5CICcb.js} +1 -1
- package/storybook-static/assets/{TagInput-BbLK6uxZ.js → TagInput-7n98ftvD.js} +1 -1
- package/storybook-static/assets/{TagInput.stories-D34JCfyM.js → TagInput.stories-B6JAb3XA.js} +1 -1
- package/storybook-static/assets/{Text-DtJw3wMU.js → Text-CjSG8mvD.js} +1 -1
- package/storybook-static/assets/{Textarea-Cz6RWJdh.js → Textarea-CQqwaJ0Q.js} +1 -1
- package/storybook-static/assets/{Textarea-C9MWeqLC.js → Textarea-Dea29_yQ.js} +1 -1
- package/storybook-static/assets/{Textarea.stories-DqK16XGS.js → Textarea.stories-BZf3Cu4S.js} +1 -1
- package/storybook-static/assets/{TimePanel-DcZ-UUgO.js → TimePanel-CPZ4jOw2.js} +1 -1
- package/storybook-static/assets/{TimePanel.stories-BafOmX3I.js → TimePanel.stories-y5Lp9sMG.js} +1 -1
- package/storybook-static/assets/{Toggle-CgU6o7qM.js → Toggle-CJHzo9mB.js} +1 -1
- package/storybook-static/assets/{Toggle-oYTkCbEJ.js → Toggle-s7SzQ11W.js} +1 -1
- package/storybook-static/assets/{Toggle.stories-CbmOBWIu.js → Toggle.stories-Dk3C5Le5.js} +1 -1
- package/storybook-static/assets/{TreeMultiSelect-B4WfXc4Z.js → TreeMultiSelect-CxXF7pQE.js} +1 -1
- package/storybook-static/assets/{TreeMultiSelect-DN_CYg-F.js → TreeMultiSelect-DfeggvLr.js} +1 -1
- package/storybook-static/assets/{TreeMultiSelect.stories-DYVj6Rm6.js → TreeMultiSelect.stories-DKiHT_6L.js} +1 -1
- package/storybook-static/assets/{TreePickerBase-8gA_hIXn.js → TreePickerBase-DKJJDVC_.js} +1 -1
- package/storybook-static/assets/{TreeSelect-CTtmHF-B.js → TreeSelect-2E1KhJMi.js} +1 -1
- package/storybook-static/assets/{TreeSelect-2bjYl5ox.js → TreeSelect-h23orAoA.js} +1 -1
- package/storybook-static/assets/{TreeSelect.stories-BEAY61pJ.js → TreeSelect.stories-DO06yaS0.js} +1 -1
- package/storybook-static/assets/{TreeTable-B4BaHybr.js → TreeTable-ByV2_E0S.js} +1 -1
- package/storybook-static/assets/{TreeTable.stories-lLwH2Gzl.js → TreeTable.stories-D4gbQx59.js} +2 -2
- package/storybook-static/assets/{WithTooltip-7X4HQR7L-Cakt_JaX.js → WithTooltip-7X4HQR7L-DQ4Qthn2.js} +1 -1
- package/storybook-static/assets/{array-D-_yMMUK-CIRmlF93.js → array-D-_yMMUK-BQ0uu1hZ.js} +1 -1
- package/storybook-static/assets/{async-DFALSYBD.js → async-BI7SlYp0.js} +1 -1
- package/storybook-static/assets/axe-_9eNhXQJ.js +20 -0
- package/storybook-static/assets/{blocks-Dr191OF5.js → blocks-B8F5vBhs.js} +6 -6
- package/storybook-static/assets/check-mlY_aVEI.js +1 -0
- package/storybook-static/assets/chevron-down-C7c-TcEc.js +1 -0
- package/storybook-static/assets/chevron-left-2LrGBhDY.js +1 -0
- package/storybook-static/assets/chevron-right-D6NN1_UQ.js +1 -0
- package/storybook-static/assets/{chunk-242VQQM5-BMWWiybC.js → chunk-242VQQM5-bHL6qIJq.js} +1 -1
- package/storybook-static/assets/{chunk-DRQKCCAG-CqE-Zrl9.js → chunk-DRQKCCAG-DvsmU7ha.js} +1 -1
- package/storybook-static/assets/{chunk-IMSF75WX-yvVUwIQZ.js → chunk-IMSF75WX-CGYlPTyG.js} +1 -1
- package/storybook-static/assets/{chunk-KJHJLCBK-D-yQfkOi.js → chunk-KJHJLCBK-yRlRMfNs.js} +1 -1
- package/storybook-static/assets/{chunk-QQBDHPVZ-B98qboss.js → chunk-QQBDHPVZ-DC5n6Pix.js} +1 -1
- package/storybook-static/assets/{chunk-W22LQPXL-BR_vFDlt.js → chunk-W22LQPXL-5sD3hVeE.js} +1 -1
- package/storybook-static/assets/{circle-alert-Is-jAV5H.js → circle-alert-H_7GSXFp.js} +1 -1
- package/storybook-static/assets/circle-check-B0voRIBT.js +1 -0
- package/storybook-static/assets/{components-DqFIc_sf.js → components-Bw-2TeQP.js} +12 -12
- package/storybook-static/assets/{contextMenu-CWKJO78E.js → contextMenu-B8JusItT.js} +1 -1
- package/storybook-static/assets/{createControlledStoryRender-DHsyXc1B.js → createControlledStoryRender-BouhvX-R.js} +1 -1
- package/storybook-static/assets/{date-segments-D3HouZ37.js → date-segments-DeoMkKv9.js} +1 -1
- package/storybook-static/assets/{defaultRenderers-V5HI39JH.js → defaultRenderers-BHXboKuy.js} +1 -1
- package/storybook-static/assets/{dom-B3G_GtJw.js → dom-DBd_DWF8.js} +1 -1
- package/storybook-static/assets/{download-B61X2hm8.js → download-CSY7aIz0.js} +1 -1
- package/storybook-static/assets/{error-BZrNlEUO.js → error-De9R4E_F.js} +1 -1
- package/storybook-static/assets/{file-vPGnhPr-.js → file-CwRoWxeB.js} +1 -1
- package/storybook-static/assets/floating-ui.react-D7RwPYeO.js +1 -0
- package/storybook-static/assets/formatter-L2YTDE3Q-CRGuSxAF.js +1 -0
- package/storybook-static/assets/{formatters-CBgrBeSA-DY6OIAc8.js → formatters-CBgrBeSA-CqB1pZxS.js} +1 -1
- package/storybook-static/assets/{foundationLib-10USAmEi.js → foundationLib-BoYGVeXe.js} +1 -1
- package/storybook-static/assets/{hooks-C8GJ9Cvf.js → hooks-DQXvhAPr.js} +2 -2
- package/storybook-static/assets/iframe-B8PAuhnL.js +1 -0
- package/storybook-static/assets/{iframe-Bu7ux79g.js → iframe-CViB2sFm.js} +23 -23
- package/storybook-static/assets/{info-CnuHFdaB.js → info-MrQcS8jn.js} +1 -1
- package/storybook-static/assets/{matchers-JIUQ3HVY-DvR2XErZ.js → matchers-JIUQ3HVY-C789_MAK.js} +1 -1
- package/storybook-static/assets/{media-BycWpjAl.js → media-D9cE2tzi.js} +1 -1
- package/storybook-static/assets/odata-CBGaUmRT.js +1 -0
- package/storybook-static/assets/{odata-service-B6TxBSLm-BW9QLSrS.js → odata-service-B6TxBSLm-Bq8Ya67E.js} +1 -1
- package/storybook-static/assets/{odataFetchFn-CAzdQ6ar.js → odataFetchFn-CPO8XIYX.js} +1 -1
- package/storybook-static/assets/{odataStoryFixtures-CX6euJaD.js → odataStoryFixtures--SWu2yC_.js} +2 -2
- package/storybook-static/assets/{persisted-BKBboERB.js → persisted-B2dfNRCz.js} +1 -1
- package/storybook-static/assets/{preload-helper-BHmFeTtP.js → preload-helper-wdlQj8DP.js} +1 -1
- package/storybook-static/assets/react-18-D4o7zN8K.js +9 -0
- package/storybook-static/assets/{react-C21x__mS.js → react-BZJXY1be.js} +1 -1
- package/storybook-static/assets/{react-qN2cStNd.js → react-Bl2r1tuC.js} +1 -1
- package/storybook-static/assets/{react-CvkUhHUq.js → react-GkniIHEK.js} +1 -1
- package/storybook-static/assets/{react-dom-2o2phaJG.js → react-dom-BzEl8usk.js} +1 -1
- package/storybook-static/assets/react-iQQwtFPL.js +9 -0
- package/storybook-static/assets/{refresh-cw-ByNiehPJ.js → refresh-cw-_wTFfhR2.js} +1 -1
- package/storybook-static/assets/{rolldown-runtime-C0FnF6B9.js → rolldown-runtime-DkW27tQK.js} +1 -1
- package/storybook-static/assets/{settings-2-BGG8WBgX.js → settings-2-BqduNJjJ.js} +1 -1
- package/storybook-static/assets/{shim-BUMlC1GG.js → shim-DlYORV19.js} +1 -1
- package/storybook-static/assets/{sortable-6C6S-hq-.js → sortable-D-m6SEp7.js} +1 -1
- package/storybook-static/assets/syntaxhighlighter-QEQV7K2Y-CEo9I1qZ.js +6 -0
- package/storybook-static/assets/{theming-mTWhffkp.js → theming-Cvtrnj7n.js} +1 -1
- package/storybook-static/assets/{trash-2-EYPGQg68.js → trash-2-65ZF92zp.js} +1 -1
- package/storybook-static/assets/{treeStoryFixtures-Ci7STpiz.js → treeStoryFixtures-DXHh9GL3.js} +1 -1
- package/storybook-static/assets/{triangle-alert-qcSLqRkW.js → triangle-alert-k_CRNi0k.js} +1 -1
- package/storybook-static/assets/{ui.module-D4CuGr8r.js → ui.module-Cat84Nza.js} +1 -1
- package/storybook-static/assets/{uiClasses-CVGbL980.js → uiClasses-B9c6dZuu.js} +1 -1
- package/storybook-static/assets/{useFlexClasses-Gn9spXEB.js → useFlexClasses-SgVG37Bb.js} +1 -1
- package/storybook-static/assets/{useODataSelectBase-LQU24QnI.js → useODataSelectBase-Ck_hduYv.js} +1 -1
- package/storybook-static/assets/{useODataTreeData-0I485gNf.js → useODataTreeData-DTrxPY_k.js} +1 -1
- package/storybook-static/assets/usePickerDefaultFilter-5Q1pAV4d.js +1 -0
- package/storybook-static/assets/{usePickerQuery-B0a5nSeM.js → usePickerQuery-DdeWtJWc.js} +1 -1
- package/storybook-static/assets/{usePickerTriggerController-DMRm063p.js → usePickerTriggerController-D8hxWmGo.js} +1 -1
- package/storybook-static/assets/{utils-DUM89eIj.js → utils-tg3ibbzi.js} +1 -1
- package/storybook-static/assets/x-C5XG0bD4.js +1 -0
- package/storybook-static/iframe.html +7 -7
- package/storybook-static/index.json +1 -1
- package/storybook-static/project.json +1 -1
- package/storybook-static/sb-addons/chromatic-com-storybook-1/manager-bundle.js +35 -33
- package/storybook-static/sb-manager/globals-runtime.js +7 -7
- package/storybook-static/sb-manager/globals.js +1 -1
- package/storybook-static/sb-manager/runtime.js +88 -89
- package/storybook-static/vite-inject-mocker-entry.js +2 -2
- package/storybook-static/assets/FormattedTableCell-CGREP0wz.js +0 -15
- package/storybook-static/assets/Table.stories-ByUJbaZl.js +0 -207
- package/storybook-static/assets/axe-Bq-2xuso.js +0 -20
- package/storybook-static/assets/browserAndReact-BDUP5jYY.js +0 -70
- package/storybook-static/assets/check-D56EYchd.js +0 -1
- package/storybook-static/assets/chevron-down-CzEzTRkp.js +0 -1
- package/storybook-static/assets/chevron-left-Dl69q13G.js +0 -1
- package/storybook-static/assets/chevron-right-CJNjsAmt.js +0 -1
- package/storybook-static/assets/circle-check-6tyIZ3Kl.js +0 -1
- package/storybook-static/assets/filesAndExcel-Bs3kqlsI.js +0 -92
- package/storybook-static/assets/floating-ui.react-bhmHzD7p.js +0 -1
- package/storybook-static/assets/formatter-L2YTDE3Q-x0raBH34.js +0 -1
- package/storybook-static/assets/formatters-NGPd1d-h.js +0 -202
- package/storybook-static/assets/formatters-runtime-CCfGWt-f.js +0 -329
- package/storybook-static/assets/iframe-BEvUkaBB.js +0 -1
- package/storybook-static/assets/odata-BslPtJet.js +0 -1
- package/storybook-static/assets/odataAndTables-Cyl3UJOz.js +0 -77
- package/storybook-static/assets/pureUtilities-Bj6GraNW.js +0 -30
- package/storybook-static/assets/react-18-CgkwBzTo.js +0 -9
- package/storybook-static/assets/react-CjYyitxm.js +0 -9
- package/storybook-static/assets/runtimeAndState-mq-BaPyx.js +0 -93
- package/storybook-static/assets/syntaxhighlighter-T5IQBZDB-B5rzfkHU.js +0 -6
- package/storybook-static/assets/usePickerDefaultFilter-Be420WNF.js +0 -1
- package/storybook-static/assets/x-FZLq51ZK.js +0 -1
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
import{n as e}from"./rolldown-runtime-DkW27tQK.js";import{n as t}from"./iframe-CViB2sFm.js";import{i as n,r}from"./react-Bl2r1tuC.js";import{l as i,m as a}from"./blocks-B8F5vBhs.js";function o(e){let t={a:`a`,code:`code`,h1:`h1`,h2:`h2`,h3:`h3`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,table:`table`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,...n(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(i,{title:`Foundation Lib/API/Copy`}),`
|
|
2
|
+
`,(0,c.jsxs)(t.h1,{id:`копирование-текста-через-ryuzaki13react-foundation-libcopy`,children:[`Копирование текста через `,(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/copy`})]}),`
|
|
3
|
+
`,(0,c.jsxs)(t.p,{children:[`Модуль `,(0,c.jsx)(t.code,{children:`copy`}),` содержит React hooks для копирования текста в системный буфер обмена, чтения текста из DOM-элемента и показа короткого состояния «скопировано».`]}),`
|
|
4
|
+
`,(0,c.jsx)(t.p,{children:`Основные сценарии:`}),`
|
|
5
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
6
|
+
`,(0,c.jsx)(t.li,{children:`скопировать готовую строку;`}),`
|
|
7
|
+
`,(0,c.jsx)(t.li,{children:`скопировать текст, который сейчас находится внутри элемента;`}),`
|
|
8
|
+
`,(0,c.jsx)(t.li,{children:`на две секунды заменить подпись или иконку кнопки после успешного копирования.`}),`
|
|
9
|
+
`]}),`
|
|
10
|
+
`,(0,c.jsx)(t.p,{children:`Модуль ориентирован на браузер. Он скрывает технический выбор между современным Clipboard API и legacy fallback, но не отменяет браузерные разрешения и ограничения безопасности.`}),`
|
|
11
|
+
`,(0,c.jsx)(t.p,{children:`README рассчитан на разработчика, который использует опубликованный пакет без доступа к исходному коду. Здесь объясняются базовые понятия React и DOM, точное поведение каждого hook, обработка ошибок, доступность, тестирование и известные ограничения.`}),`
|
|
12
|
+
`,(0,c.jsx)(t.h2,{id:`содержание`,children:`Содержание`}),`
|
|
13
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
14
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BD%D0%B0%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B8-%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%8B-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8F`,children:`Назначение и границы модуля`})}),`
|
|
15
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8-%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82`,children:`Установка и импорт`})}),`
|
|
16
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BF%D0%BE%D0%BD%D1%8F%D1%82%D0%B8%D1%8F`,children:`Минимальные понятия`})}),`
|
|
17
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BA%D0%B0%D0%BA-%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D1%82%D1%8C-api`,children:`Как выбрать API`})}),`
|
|
18
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B1%D1%8B%D1%81%D1%82%D1%80%D1%8B%D0%B9-%D1%81%D1%82%D0%B0%D1%80%D1%82`,children:`Быстрый старт`})}),`
|
|
19
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#usecopytext`,children:(0,c.jsx)(t.code,{children:`useCopyText`})})}),`
|
|
20
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#useelementtext`,children:(0,c.jsx)(t.code,{children:`useElementText`})})}),`
|
|
21
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#usecopyelementtext`,children:(0,c.jsx)(t.code,{children:`useCopyElementText`})})}),`
|
|
22
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#usecopyfeedback`,children:(0,c.jsx)(t.code,{children:`useCopyFeedback`})})}),`
|
|
23
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#usecopyelementtextwithfeedback`,children:(0,c.jsx)(t.code,{children:`useCopyElementTextWithFeedback`})})}),`
|
|
24
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BA%D0%B0%D0%BA-%D0%B2%D1%8B%D0%B1%D0%B8%D1%80%D0%B0%D0%B5%D1%82%D1%81%D1%8F-%D1%81%D0%BF%D0%BE%D1%81%D0%BE%D0%B1-%D0%BA%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F`,children:`Как выбирается способ копирования`})}),`
|
|
25
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D1%8B%D0%B5-%D1%80%D0%B5%D1%86%D0%B5%D0%BF%D1%82%D1%8B`,children:`Готовые рецепты`})}),`
|
|
26
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D0%B8-ux`,children:`Доступность и UX`})}),`
|
|
27
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#ssr-%D0%B8-browser-boundary`,children:`SSR и browser boundary`})}),`
|
|
28
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5`,children:`Тестирование`})}),`
|
|
29
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B8-%D1%87%D0%B0%D1%81%D1%82%D1%8B%D0%B5-%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8`,children:`Ограничения и частые ошибки`})}),`
|
|
30
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BA%D1%80%D0%B0%D1%82%D0%BA%D0%B8%D0%B9-%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%BE%D1%87%D0%BD%D0%B8%D0%BA-api`,children:`Краткий справочник API`})}),`
|
|
31
|
+
`,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D1%8B-%D0%B8-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D1%8B`,children:`Вопросы и ответы`})}),`
|
|
32
|
+
`]}),`
|
|
33
|
+
`,(0,c.jsx)(t.h2,{id:`назначение-и-границы-модуля`,children:`Назначение и границы модуля`}),`
|
|
34
|
+
`,(0,c.jsxs)(t.h3,{id:`что-делает-copy`,children:[`Что делает `,(0,c.jsx)(t.code,{children:`copy`})]}),`
|
|
35
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
36
|
+
`,(0,c.jsxs)(t.li,{children:[`копирует непустую строку через `,(0,c.jsx)(t.code,{children:`navigator.clipboard.writeText`}),`, когда это возможно;`]}),`
|
|
37
|
+
`,(0,c.jsxs)(t.li,{children:[`использует fallback через временный DOM-элемент и `,(0,c.jsx)(t.code,{children:`document.execCommand("copy")`}),`, если современный Clipboard API недоступен или страница не является secure context;`]}),`
|
|
38
|
+
`,(0,c.jsxs)(t.li,{children:[`возвращает `,(0,c.jsx)(t.code,{children:`Promise<boolean>`}),`, чтобы вызывающий код узнал об успехе или неудаче;`]}),`
|
|
39
|
+
`,(0,c.jsxs)(t.li,{children:[`читает актуальный `,(0,c.jsx)(t.code,{children:`textContent`}),` DOM-элемента через React ref;`]}),`
|
|
40
|
+
`,(0,c.jsx)(t.li,{children:`объединяет чтение элемента и копирование;`}),`
|
|
41
|
+
`,(0,c.jsxs)(t.li,{children:[`предоставляет двухсекундное состояние `,(0,c.jsx)(t.code,{children:`isCopied`}),` для UI-feedback.`]}),`
|
|
42
|
+
`]}),`
|
|
43
|
+
`,(0,c.jsx)(t.h3,{id:`чего-модуль-не-делает`,children:`Чего модуль не делает`}),`
|
|
44
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
45
|
+
`,(0,c.jsx)(t.li,{children:`не читает системный буфер обмена;`}),`
|
|
46
|
+
`,(0,c.jsx)(t.li,{children:`не копирует изображения, файлы, HTML или произвольные MIME-данные;`}),`
|
|
47
|
+
`,(0,c.jsx)(t.li,{children:`не запрашивает разрешение отдельным диалогом и не обходит политику браузера;`}),`
|
|
48
|
+
`,(0,c.jsx)(t.li,{children:`не гарантирует работу вне пользовательского действия;`}),`
|
|
49
|
+
`,(0,c.jsx)(t.li,{children:`не показывает toast, tooltip или иконку самостоятельно;`}),`
|
|
50
|
+
`,(0,c.jsx)(t.li,{children:`не предоставляет React-компонент кнопки;`}),`
|
|
51
|
+
`,(0,c.jsxs)(t.li,{children:[`не копирует `,(0,c.jsx)(t.code,{children:`value`}),` из `,(0,c.jsx)(t.code,{children:`<input>`}),` или `,(0,c.jsx)(t.code,{children:`<textarea>`}),` через element hooks;`]}),`
|
|
52
|
+
`,(0,c.jsx)(t.li,{children:`не сообщает причину ошибки отдельным объектом;`}),`
|
|
53
|
+
`,(0,c.jsx)(t.li,{children:`не повторяет операцию автоматически;`}),`
|
|
54
|
+
`,(0,c.jsx)(t.li,{children:`не синхронизирует feedback между несколькими кнопками.`}),`
|
|
55
|
+
`]}),`
|
|
56
|
+
`,(0,c.jsx)(t.h3,{id:`соседние-задачи`,children:`Соседние задачи`}),`
|
|
57
|
+
`,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Задача`}),(0,c.jsx)(t.th,{children:`Что использовать`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Скопировать текст`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/copy`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[`Скачать готовый `,(0,c.jsx)(t.code,{children:`Blob`}),` или JSON-файл`]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/dom`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Прочитать пользовательский файл`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/file`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[`Декодировать base64 в `,(0,c.jsx)(t.code,{children:`Blob`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/binary`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Реализовать визуальную кнопку/tooltip`}),(0,c.jsx)(t.td,{children:`UI-компонент приложения или foundation UI-пакет`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Скопировать изображение или rich HTML`}),(0,c.jsx)(t.td,{children:`Отдельная реализация Clipboard API для нужного формата`})]})]})]}),`
|
|
58
|
+
`,(0,c.jsx)(t.h2,{id:`установка-и-импорт`,children:`Установка и импорт`}),`
|
|
59
|
+
`,(0,c.jsx)(t.p,{children:`Установите пакет:`}),`
|
|
60
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-bash`,children:`npm install @ryuzaki13/react-foundation-lib
|
|
61
|
+
`})}),`
|
|
62
|
+
`,(0,c.jsx)(t.p,{children:`Модуль использует React hooks. Приложение должно предоставить совместимые peer-зависимости React:`}),`
|
|
63
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-bash`,children:`npm install react react-dom
|
|
64
|
+
`})}),`
|
|
65
|
+
`,(0,c.jsxs)(t.p,{children:[`Текущий контракт пакета требует React `,(0,c.jsx)(t.code,{children:`>=19.2.0 <20.0.0`}),`.`]}),`
|
|
66
|
+
`,(0,c.jsxs)(t.p,{children:[`Импортируйте API только из опубликованного subpath `,(0,c.jsx)(t.code,{children:`/copy`}),`:`]}),`
|
|
67
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
|
|
68
|
+
useCopyElementText,
|
|
69
|
+
useCopyElementTextWithFeedback,
|
|
70
|
+
useCopyFeedback,
|
|
71
|
+
useCopyText,
|
|
72
|
+
useElementText
|
|
73
|
+
} from "@ryuzaki13/react-foundation-lib/copy";
|
|
74
|
+
`})}),`
|
|
75
|
+
`,(0,c.jsx)(t.p,{children:`Корневой импорт пакета не поддерживается:`}),`
|
|
76
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`// Неправильно.
|
|
77
|
+
import { useCopyText } from "@ryuzaki13/react-foundation-lib";
|
|
78
|
+
|
|
79
|
+
// Правильно.
|
|
80
|
+
import { useCopyText } from "@ryuzaki13/react-foundation-lib/copy";
|
|
81
|
+
`})}),`
|
|
82
|
+
`,(0,c.jsx)(t.p,{children:`Модуль поставляется как ESM и использует browser APIs:`}),`
|
|
83
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
84
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`navigator.clipboard`}),`;`]}),`
|
|
85
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`window.isSecureContext`}),`;`]}),`
|
|
86
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`window.getSelection`}),`;`]}),`
|
|
87
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`document.createElement`}),`;`]}),`
|
|
88
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`document.createRange`}),`;`]}),`
|
|
89
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`document.execCommand`}),`.`]}),`
|
|
90
|
+
`]}),`
|
|
91
|
+
`,(0,c.jsx)(t.h2,{id:`минимальные-понятия`,children:`Минимальные понятия`}),`
|
|
92
|
+
`,(0,c.jsx)(t.h3,{id:`буфер-обмена`,children:`Буфер обмена`}),`
|
|
93
|
+
`,(0,c.jsx)(t.p,{children:`Буфер обмена — системное временное хранилище, которое используется командами Copy и Paste. После успешного вызова пользователь сможет вставить скопированную строку в другое поле или приложение.`}),`
|
|
94
|
+
`,(0,c.jsx)(t.h3,{id:`react-hook`,children:`React hook`}),`
|
|
95
|
+
`,(0,c.jsxs)(t.p,{children:[`Hook — функция React, имя которой начинается с `,(0,c.jsx)(t.code,{children:`use`}),`. Hook вызывают только:`]}),`
|
|
96
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
97
|
+
`,(0,c.jsx)(t.li,{children:`на верхнем уровне React-компонента;`}),`
|
|
98
|
+
`,(0,c.jsx)(t.li,{children:`внутри другого custom hook.`}),`
|
|
99
|
+
`]}),`
|
|
100
|
+
`,(0,c.jsxs)(t.p,{children:[`Нельзя вызывать hook внутри `,(0,c.jsx)(t.code,{children:`if`}),`, цикла, обработчика клика или обычной utility-функции:`]}),`
|
|
101
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`function CopyButton() {
|
|
102
|
+
// Правильно: hook вызывается при рендере компонента.
|
|
103
|
+
const { copyToClipboard } = useCopyText();
|
|
104
|
+
|
|
105
|
+
const handleClick = async () => {
|
|
106
|
+
// Правильно: обработчик вызывает функцию, полученную из hook.
|
|
107
|
+
await copyToClipboard("Текст");
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<button type="button" onClick={() => void handleClick()}>
|
|
112
|
+
Копировать
|
|
113
|
+
</button>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
`})}),`
|
|
117
|
+
`,(0,c.jsxs)(t.h3,{id:`асинхронный-результат-и-promiseboolean`,children:[`Асинхронный результат и `,(0,c.jsx)(t.code,{children:`Promise<boolean>`})]}),`
|
|
118
|
+
`,(0,c.jsxs)(t.p,{children:[`Копирование может завершиться не сразу. Поэтому функции возвращают `,(0,c.jsx)(t.code,{children:`Promise<boolean>`}),`:`]}),`
|
|
119
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const success = await copyToClipboard("Текст");
|
|
120
|
+
`})}),`
|
|
121
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
122
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`true`}),` — операция сообщила об успехе;`]}),`
|
|
123
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`false`}),` — строка была пустой либо браузерный способ копирования не сработал.`]}),`
|
|
124
|
+
`]}),`
|
|
125
|
+
`,(0,c.jsxs)(t.p,{children:[`Не проверяйте сам `,(0,c.jsx)(t.code,{children:`Promise`}),` как boolean:`]}),`
|
|
126
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`// Неправильно: result является Promise, а не итоговым boolean.
|
|
127
|
+
const result = copyToClipboard("Текст");
|
|
128
|
+
if (result) {
|
|
129
|
+
// Эта ветка не означает успешное копирование.
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Правильно.
|
|
133
|
+
const success = await copyToClipboard("Текст");
|
|
134
|
+
if (success) {
|
|
135
|
+
// Копирование завершилось успешно.
|
|
136
|
+
}
|
|
137
|
+
`})}),`
|
|
138
|
+
`,(0,c.jsx)(t.h3,{id:`react-ref`,children:`React ref`}),`
|
|
139
|
+
`,(0,c.jsx)(t.p,{children:`Ref хранит ссылку на реальный DOM-элемент:`}),`
|
|
140
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`const ref = useRef<HTMLSpanElement>(null);
|
|
141
|
+
|
|
142
|
+
return <span ref={ref}>Текст</span>;
|
|
143
|
+
`})}),`
|
|
144
|
+
`,(0,c.jsxs)(t.p,{children:[`До монтирования элемента `,(0,c.jsx)(t.code,{children:`ref.current`}),` равен `,(0,c.jsx)(t.code,{children:`null`}),`. После монтирования React помещает туда `,(0,c.jsx)(t.code,{children:`HTMLSpanElement`}),`.`]}),`
|
|
145
|
+
`,(0,c.jsx)(t.h3,{id:`textcontent`,children:(0,c.jsx)(t.code,{children:`textContent`})}),`
|
|
146
|
+
`,(0,c.jsxs)(t.p,{children:[`Element hooks читают свойство `,(0,c.jsx)(t.code,{children:`textContent`}),`. Оно объединяет текст самого элемента и всех его потомков:`]}),`
|
|
147
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-html`,children:`<span>Заказ <strong>№ 42</strong></span>
|
|
148
|
+
`})}),`
|
|
149
|
+
`,(0,c.jsx)(t.p,{children:`Результат:`}),`
|
|
150
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`Заказ № 42
|
|
151
|
+
`})}),`
|
|
152
|
+
`,(0,c.jsx)(t.p,{children:`Это не то же самое, что визуально отрисованный текст:`}),`
|
|
153
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
154
|
+
`,(0,c.jsx)(t.li,{children:`CSS не участвует в формировании результата;`}),`
|
|
155
|
+
`,(0,c.jsx)(t.li,{children:`текст скрытого потомка тоже может попасть в результат;`}),`
|
|
156
|
+
`,(0,c.jsx)(t.li,{children:`подписи вложенных кнопок тоже попадают в результат;`}),`
|
|
157
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`value`}),` у `,(0,c.jsx)(t.code,{children:`<input>`}),` и `,(0,c.jsx)(t.code,{children:`<textarea>`}),` не является их `,(0,c.jsx)(t.code,{children:`textContent`}),`;`]}),`
|
|
158
|
+
`,(0,c.jsx)(t.li,{children:`переносы, созданные только layout/CSS, не добавляются автоматически.`}),`
|
|
159
|
+
`]}),`
|
|
160
|
+
`,(0,c.jsx)(t.h3,{id:`secure-context`,children:`Secure context`}),`
|
|
161
|
+
`,(0,c.jsx)(t.p,{children:`Современный Clipboard API доступен только в безопасном контексте. Обычно это:`}),`
|
|
162
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
163
|
+
`,(0,c.jsx)(t.li,{children:`страница по HTTPS;`}),`
|
|
164
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`localhost`}),` в режиме разработки;`]}),`
|
|
165
|
+
`,(0,c.jsx)(t.li,{children:`среда, которую браузер отдельно признаёт доверенной.`}),`
|
|
166
|
+
`]}),`
|
|
167
|
+
`,(0,c.jsx)(t.p,{children:`Обычная production-страница по HTTP обычно не является secure context.`}),`
|
|
168
|
+
`,(0,c.jsx)(t.h3,{id:`пользовательское-действие`,children:`Пользовательское действие`}),`
|
|
169
|
+
`,(0,c.jsx)(t.p,{children:`Браузер может разрешать запись в буфер только во время явного действия пользователя: клика, нажатия клавиши или команды меню. Поэтому вызывайте копирование непосредственно из обработчика такого действия.`}),`
|
|
170
|
+
`,(0,c.jsx)(t.h2,{id:`как-выбрать-api`,children:`Как выбрать API`}),`
|
|
171
|
+
`,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Задача`}),(0,c.jsx)(t.th,{children:`API`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Скопировать уже имеющуюся строку`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyText`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[`Прочитать `,(0,c.jsx)(t.code,{children:`textContent`}),` из собственного ref`]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useElementText`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Получить готовый ref и скопировать текст элемента`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyElementText`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Только показать состояние «готово» на две секунды`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyFeedback`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[`Скопировать текст элемента и показать `,(0,c.jsx)(t.code,{children:`isCopied`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyElementTextWithFeedback`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[`Скопировать текущее значение `,(0,c.jsx)(t.code,{children:`<input>`}),` или `,(0,c.jsx)(t.code,{children:`<textarea>`})]}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`useCopyText`}),` с переменной `,(0,c.jsx)(t.code,{children:`value`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Скопировать строку, которой ещё нет в DOM`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyText`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Копировать HTML, изображение или файл`}),(0,c.jsx)(t.td,{children:`Этот модуль не подходит`})]})]})]}),`
|
|
172
|
+
`,(0,c.jsx)(t.h2,{id:`быстрый-старт`,children:`Быстрый старт`}),`
|
|
173
|
+
`,(0,c.jsx)(t.h3,{id:`копирование-готовой-строки`,children:`Копирование готовой строки`}),`
|
|
174
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { useState } from "react";
|
|
175
|
+
import { useCopyText } from "@ryuzaki13/react-foundation-lib/copy";
|
|
176
|
+
|
|
177
|
+
export function CopyOrderNumber() {
|
|
178
|
+
const { copyToClipboard } = useCopyText();
|
|
179
|
+
const [message, setMessage] = useState("");
|
|
180
|
+
|
|
181
|
+
const handleCopy = async () => {
|
|
182
|
+
const success = await copyToClipboard("ORDER-42");
|
|
183
|
+
setMessage(success ? "Номер скопирован" : "Не удалось скопировать");
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<div>
|
|
188
|
+
<button type="button" onClick={() => void handleCopy()}>
|
|
189
|
+
Копировать номер
|
|
190
|
+
</button>
|
|
191
|
+
<span aria-live="polite">{message}</span>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
`})}),`
|
|
196
|
+
`,(0,c.jsx)(t.h3,{id:`копирование-текста-элемента-с-feedback`,children:`Копирование текста элемента с feedback`}),`
|
|
197
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { useCopyElementTextWithFeedback } from "@ryuzaki13/react-foundation-lib/copy";
|
|
198
|
+
|
|
199
|
+
export function CopyAddress() {
|
|
200
|
+
const { containerRef, copyElementText, isCopied } = useCopyElementTextWithFeedback<HTMLSpanElement>();
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div>
|
|
204
|
+
<span ref={containerRef}>г. Екатеринбург, ул. Ленина, 1</span>
|
|
205
|
+
<button type="button" onClick={() => void copyElementText()}>
|
|
206
|
+
{isCopied ? "Скопировано" : "Копировать адрес"}
|
|
207
|
+
</button>
|
|
208
|
+
<span className="visually-hidden" aria-live="polite">
|
|
209
|
+
{isCopied ? "Адрес скопирован" : ""}
|
|
210
|
+
</span>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
`})}),`
|
|
215
|
+
`,(0,c.jsxs)(t.p,{children:[`Ref установлен только на `,(0,c.jsx)(t.code,{children:`<span>`}),` с адресом. Если поставить его на общий `,(0,c.jsx)(t.code,{children:`<div>`}),`, в копируемую строку может попасть текст кнопки и live region.`]}),`
|
|
216
|
+
`,(0,c.jsx)(t.h2,{id:`usecopytext`,children:(0,c.jsx)(t.code,{children:`useCopyText`})}),`
|
|
217
|
+
`,(0,c.jsx)(t.p,{children:`Копирует переданную строку и возвращает результат операции.`}),`
|
|
218
|
+
`,(0,c.jsx)(t.h3,{id:`сигнатура`,children:`Сигнатура`}),`
|
|
219
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`declare const useCopyText: () => {
|
|
220
|
+
copyToClipboard: (text: string) => Promise<boolean>;
|
|
221
|
+
};
|
|
222
|
+
`})}),`
|
|
223
|
+
`,(0,c.jsxs)(t.h3,{id:`параметр-text`,children:[`Параметр `,(0,c.jsx)(t.code,{children:`text`})]}),`
|
|
224
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`text`}),` — строка, которую нужно поместить в буфер обмена.`]}),`
|
|
225
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`await copyToClipboard("INV-2026-0001");
|
|
226
|
+
`})}),`
|
|
227
|
+
`,(0,c.jsx)(t.h3,{id:`возвращаемое-значение`,children:`Возвращаемое значение`}),`
|
|
228
|
+
`,(0,c.jsxs)(t.p,{children:[`Hook возвращает объект с функцией `,(0,c.jsx)(t.code,{children:`copyToClipboard`}),`.`]}),`
|
|
229
|
+
`,(0,c.jsx)(t.p,{children:`Сама функция возвращает:`}),`
|
|
230
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
231
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`true`}),`, если `,(0,c.jsx)(t.code,{children:`navigator.clipboard.writeText`}),` завершился без ошибки;`]}),`
|
|
232
|
+
`,(0,c.jsxs)(t.li,{children:[`результат `,(0,c.jsx)(t.code,{children:`document.execCommand("copy")`}),`, если был выбран fallback;`]}),`
|
|
233
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`false`}),`, если строка пустая, состоит только из whitespace или произошла ошибка.`]}),`
|
|
234
|
+
`]}),`
|
|
235
|
+
`,(0,c.jsxs)(t.p,{children:[`Функция стабильна между рендерами компонента: она создаётся через `,(0,c.jsx)(t.code,{children:`useCallback`}),` без зависимостей.`]}),`
|
|
236
|
+
`,(0,c.jsx)(t.h3,{id:`проверка-пустой-строки`,children:`Проверка пустой строки`}),`
|
|
237
|
+
`,(0,c.jsxs)(t.p,{children:[`Перед копированием выполняется проверка `,(0,c.jsx)(t.code,{children:`text.trim()`}),`:`]}),`
|
|
238
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`await copyToClipboard(""); // false
|
|
239
|
+
await copyToClipboard(" "); // false
|
|
240
|
+
await copyToClipboard("\\n\\t"); // false
|
|
241
|
+
`})}),`
|
|
242
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`trim()`}),` используется только для проверки. Непустой исходный текст копируется без обрезки:`]}),`
|
|
243
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`await copyToClipboard(" ABC \\n");
|
|
244
|
+
`})}),`
|
|
245
|
+
`,(0,c.jsx)(t.p,{children:`В буфер попадёт строка с двумя пробелами в начале, двумя пробелами и переводом строки в конце.`}),`
|
|
246
|
+
`,(0,c.jsx)(t.h3,{id:`обработка-ошибок`,children:`Обработка ошибок`}),`
|
|
247
|
+
`,(0,c.jsxs)(t.p,{children:[`Hook не выбрасывает ошибку копирования вызывающему коду. Он возвращает `,(0,c.jsx)(t.code,{children:`false`}),`.`]}),`
|
|
248
|
+
`,(0,c.jsxs)(t.p,{children:[`Если современный `,(0,c.jsx)(t.code,{children:`navigator.clipboard.writeText`}),` выбросил ошибку, она дополнительно записывается в консоль:`]}),`
|
|
249
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`Failed to copy text: <исходная ошибка>
|
|
250
|
+
`})}),`
|
|
251
|
+
`,(0,c.jsx)(t.p,{children:`Ошибки legacy fallback поглощаются без записи в консоль.`}),`
|
|
252
|
+
`,(0,c.jsx)(t.h3,{id:`важное-правило-fallback`,children:`Важное правило fallback`}),`
|
|
253
|
+
`,(0,c.jsxs)(t.p,{children:[`Fallback выбирается только заранее, если Clipboard API отсутствует или `,(0,c.jsx)(t.code,{children:`window.isSecureContext`}),` равен `,(0,c.jsx)(t.code,{children:`false`}),`.`]}),`
|
|
254
|
+
`,(0,c.jsxs)(t.p,{children:[`Если современный `,(0,c.jsx)(t.code,{children:`writeText`}),` существует, но отклоняет операцию из-за разрешения, iframe policy или отсутствия user activation, повторной попытки через `,(0,c.jsx)(t.code,{children:`execCommand`}),` не будет. Итогом станет `,(0,c.jsx)(t.code,{children:`false`}),`.`]}),`
|
|
255
|
+
`,(0,c.jsx)(t.h3,{id:`пример-с-полем-ввода`,children:`Пример с полем ввода`}),`
|
|
256
|
+
`,(0,c.jsxs)(t.p,{children:[`Для `,(0,c.jsx)(t.code,{children:`<input>`}),` передавайте его `,(0,c.jsx)(t.code,{children:`value`}),` напрямую:`]}),`
|
|
257
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { useState } from "react";
|
|
258
|
+
import { useCopyText } from "@ryuzaki13/react-foundation-lib/copy";
|
|
259
|
+
|
|
260
|
+
export function CopyInputValue() {
|
|
261
|
+
const [value, setValue] = useState("Пользовательский текст");
|
|
262
|
+
const { copyToClipboard } = useCopyText();
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<label>
|
|
266
|
+
Текст
|
|
267
|
+
<input value={value} onChange={(event) => setValue(event.target.value)} />
|
|
268
|
+
<button type="button" onClick={() => void copyToClipboard(value)}>
|
|
269
|
+
Копировать
|
|
270
|
+
</button>
|
|
271
|
+
</label>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
`})}),`
|
|
275
|
+
`,(0,c.jsxs)(t.p,{children:[`Не используйте для этого `,(0,c.jsx)(t.code,{children:`useCopyElementText<HTMLInputElement>`}),`: `,(0,c.jsx)(t.code,{children:`textContent`}),` input-элемента не содержит введённое `,(0,c.jsx)(t.code,{children:`value`}),`.`]}),`
|
|
276
|
+
`,(0,c.jsx)(t.h2,{id:`useelementtext`,children:(0,c.jsx)(t.code,{children:`useElementText`})}),`
|
|
277
|
+
`,(0,c.jsxs)(t.p,{children:[`Возвращает функцию, которая читает актуальный `,(0,c.jsx)(t.code,{children:`textContent`}),` переданного DOM ref.`]}),`
|
|
278
|
+
`,(0,c.jsx)(t.h3,{id:`сигнатура-1`,children:`Сигнатура`}),`
|
|
279
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`declare const useElementText: <T extends HTMLElement = HTMLElement>(
|
|
280
|
+
ref: RefObject<T | null>
|
|
281
|
+
) => {
|
|
282
|
+
getElementText: () => string;
|
|
283
|
+
};
|
|
284
|
+
`})}),`
|
|
285
|
+
`,(0,c.jsx)(t.h3,{id:`параметр`,children:`Параметр`}),`
|
|
286
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ref`}),` — React ref на `,(0,c.jsx)(t.code,{children:`HTMLElement`}),` или его конкретный тип.`]}),`
|
|
287
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`const paragraphRef = useRef<HTMLParagraphElement>(null);
|
|
288
|
+
const { getElementText } = useElementText(paragraphRef);
|
|
289
|
+
`})}),`
|
|
290
|
+
`,(0,c.jsxs)(t.p,{children:[`Generic `,(0,c.jsx)(t.code,{children:`T`}),` помогает TypeScript проверить, к какому элементу прикрепляется ref. Если конкретный тип не важен, используется `,(0,c.jsx)(t.code,{children:`HTMLElement`}),`.`]}),`
|
|
291
|
+
`,(0,c.jsx)(t.h3,{id:`точное-поведение`,children:`Точное поведение`}),`
|
|
292
|
+
`,(0,c.jsxs)(t.p,{children:[`При каждом вызове `,(0,c.jsx)(t.code,{children:`getElementText()`}),` hook:`]}),`
|
|
293
|
+
`,(0,c.jsxs)(t.ol,{children:[`
|
|
294
|
+
`,(0,c.jsxs)(t.li,{children:[`читает текущее `,(0,c.jsx)(t.code,{children:`ref.current`}),`;`]}),`
|
|
295
|
+
`,(0,c.jsxs)(t.li,{children:[`читает `,(0,c.jsx)(t.code,{children:`ref.current.textContent`}),`;`]}),`
|
|
296
|
+
`,(0,c.jsxs)(t.li,{children:[`обрезает whitespace по краям через `,(0,c.jsx)(t.code,{children:`trim()`}),`;`]}),`
|
|
297
|
+
`,(0,c.jsx)(t.li,{children:`возвращает пустую строку, если ref ещё не установлен или текста нет.`}),`
|
|
298
|
+
`]}),`
|
|
299
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`const text = getElementText();
|
|
300
|
+
`})}),`
|
|
301
|
+
`,(0,c.jsx)(t.p,{children:`Текст не сохраняется во время рендера. Он читается именно в момент вызова, поэтому изменения DOM до клика будут учтены.`}),`
|
|
302
|
+
`,(0,c.jsx)(t.h3,{id:`пример-без-копирования`,children:`Пример без копирования`}),`
|
|
303
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { useRef, useState } from "react";
|
|
304
|
+
import { useElementText } from "@ryuzaki13/react-foundation-lib/copy";
|
|
305
|
+
|
|
306
|
+
export function InspectText() {
|
|
307
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
308
|
+
const { getElementText } = useElementText(ref);
|
|
309
|
+
const [result, setResult] = useState("");
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<div>
|
|
313
|
+
<div ref={ref}>
|
|
314
|
+
Заказ <strong>№ 42</strong>
|
|
315
|
+
</div>
|
|
316
|
+
<button type="button" onClick={() => setResult(getElementText())}>
|
|
317
|
+
Прочитать DOM-текст
|
|
318
|
+
</button>
|
|
319
|
+
<output>{result}</output>
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
`})}),`
|
|
324
|
+
`,(0,c.jsxs)(t.h3,{id:`textcontent-и-скрытый-текст`,children:[(0,c.jsx)(t.code,{children:`textContent`}),` и скрытый текст`]}),`
|
|
325
|
+
`,(0,c.jsx)(t.p,{children:`Если внутри контейнера есть скрытая подпись, она всё равно может быть прочитана:`}),`
|
|
326
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`<span ref={ref}>
|
|
327
|
+
ABC
|
|
328
|
+
<span className="visually-hidden">Служебное пояснение</span>
|
|
329
|
+
</span>
|
|
330
|
+
`})}),`
|
|
331
|
+
`,(0,c.jsxs)(t.p,{children:[`Результат будет содержать и `,(0,c.jsx)(t.code,{children:`ABC`}),`, и «Служебное пояснение». Если копировать нужно строго определённое значение, безопаснее передать эту строку в `,(0,c.jsx)(t.code,{children:`useCopyText`}),`.`]}),`
|
|
332
|
+
`,(0,c.jsx)(t.h2,{id:`usecopyelementtext`,children:(0,c.jsx)(t.code,{children:`useCopyElementText`})}),`
|
|
333
|
+
`,(0,c.jsxs)(t.p,{children:[`Создаёт ref, читает `,(0,c.jsx)(t.code,{children:`textContent`}),` соответствующего элемента и копирует его.`]}),`
|
|
334
|
+
`,(0,c.jsx)(t.h3,{id:`сигнатура-2`,children:`Сигнатура`}),`
|
|
335
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`declare const useCopyElementText: <T extends HTMLElement = HTMLElement>() => {
|
|
336
|
+
containerRef: RefObject<T | null>;
|
|
337
|
+
copyElementText: () => Promise<boolean>;
|
|
338
|
+
getElementText: () => string;
|
|
339
|
+
};
|
|
340
|
+
`})}),`
|
|
341
|
+
`,(0,c.jsx)(t.h3,{id:`возвращаемые-поля`,children:`Возвращаемые поля`}),`
|
|
342
|
+
`,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Поле`}),(0,c.jsx)(t.th,{children:`Тип`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`containerRef`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`RefObject<T | null>`})}),(0,c.jsx)(t.td,{children:`Установить на элемент с копируемым текстом.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`copyElementText`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`() => Promise<boolean>`})}),(0,c.jsx)(t.td,{children:`Прочитать элемент и скопировать его текст.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getElementText`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`() => string`})}),(0,c.jsx)(t.td,{children:`Только получить текущий текст без копирования.`})]})]})]}),`
|
|
343
|
+
`,(0,c.jsx)(t.h3,{id:`алгоритм`,children:`Алгоритм`}),`
|
|
344
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`copyElementText()`}),`:`]}),`
|
|
345
|
+
`,(0,c.jsxs)(t.ol,{children:[`
|
|
346
|
+
`,(0,c.jsxs)(t.li,{children:[`вызывает `,(0,c.jsx)(t.code,{children:`getElementText()`}),`;`]}),`
|
|
347
|
+
`,(0,c.jsxs)(t.li,{children:[`получает trimmed `,(0,c.jsx)(t.code,{children:`textContent`}),`;`]}),`
|
|
348
|
+
`,(0,c.jsxs)(t.li,{children:[`возвращает `,(0,c.jsx)(t.code,{children:`false`}),`, если результат пустой;`]}),`
|
|
349
|
+
`,(0,c.jsxs)(t.li,{children:[`иначе передаёт строку в `,(0,c.jsx)(t.code,{children:`copyToClipboard`}),` из `,(0,c.jsx)(t.code,{children:`useCopyText`}),`;`]}),`
|
|
350
|
+
`,(0,c.jsx)(t.li,{children:`возвращает итоговый boolean.`}),`
|
|
351
|
+
`]}),`
|
|
352
|
+
`,(0,c.jsxs)(t.p,{children:[`Поскольку `,(0,c.jsx)(t.code,{children:`useElementText`}),` уже применил `,(0,c.jsx)(t.code,{children:`trim()`}),`, при копировании из элемента внешний whitespace удаляется. Это отличается от прямого `,(0,c.jsx)(t.code,{children:`useCopyText`}),`, который сохраняет внешний whitespace непустой строки.`]}),`
|
|
353
|
+
`,(0,c.jsx)(t.h3,{id:`пример-с-проверкой-результата`,children:`Пример с проверкой результата`}),`
|
|
354
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { useState } from "react";
|
|
355
|
+
import { useCopyElementText } from "@ryuzaki13/react-foundation-lib/copy";
|
|
356
|
+
|
|
357
|
+
export function CopyCustomerName() {
|
|
358
|
+
const { containerRef, copyElementText } = useCopyElementText<HTMLSpanElement>();
|
|
359
|
+
const [status, setStatus] = useState("");
|
|
360
|
+
|
|
361
|
+
const handleCopy = async () => {
|
|
362
|
+
const success = await copyElementText();
|
|
363
|
+
setStatus(success ? "Имя скопировано" : "Копирование недоступно");
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<div>
|
|
368
|
+
<span ref={containerRef}>Анна Иванова</span>
|
|
369
|
+
<button type="button" onClick={() => void handleCopy()}>
|
|
370
|
+
Копировать имя
|
|
371
|
+
</button>
|
|
372
|
+
<span aria-live="polite">{status}</span>
|
|
373
|
+
</div>
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
`})}),`
|
|
377
|
+
`,(0,c.jsx)(t.h3,{id:`где-ставить-ref`,children:`Где ставить ref`}),`
|
|
378
|
+
`,(0,c.jsxs)(t.p,{children:[`Ставьте `,(0,c.jsx)(t.code,{children:`containerRef`}),` на самый узкий элемент, содержащий только нужный текст:`]}),`
|
|
379
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`// Правильно: кнопка находится вне копируемого элемента.
|
|
380
|
+
<div>
|
|
381
|
+
<span ref={containerRef}>{code}</span>
|
|
382
|
+
<button type="button" onClick={() => void copyElementText()}>
|
|
383
|
+
Копировать
|
|
384
|
+
</button>
|
|
385
|
+
</div>
|
|
386
|
+
`})}),`
|
|
387
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`// Рискованно: подпись кнопки тоже является textContent общего div.
|
|
388
|
+
<div ref={containerRef}>
|
|
389
|
+
<span>{code}</span>
|
|
390
|
+
<button type="button" onClick={() => void copyElementText()}>
|
|
391
|
+
Копировать
|
|
392
|
+
</button>
|
|
393
|
+
</div>
|
|
394
|
+
`})}),`
|
|
395
|
+
`,(0,c.jsx)(t.h2,{id:`usecopyfeedback`,children:(0,c.jsx)(t.code,{children:`useCopyFeedback`})}),`
|
|
396
|
+
`,(0,c.jsxs)(t.p,{children:[`Предоставляет boolean-состояние, которое становится `,(0,c.jsx)(t.code,{children:`true`}),` и автоматически переключается обратно в `,(0,c.jsx)(t.code,{children:`false`}),` через 2000 миллисекунд.`]}),`
|
|
397
|
+
`,(0,c.jsx)(t.h3,{id:`сигнатура-3`,children:`Сигнатура`}),`
|
|
398
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`declare const useCopyFeedback: () => {
|
|
399
|
+
isCopied: boolean;
|
|
400
|
+
showFeedback: () => () => void;
|
|
401
|
+
};
|
|
402
|
+
`})}),`
|
|
403
|
+
`,(0,c.jsx)(t.h3,{id:`точное-поведение-1`,children:`Точное поведение`}),`
|
|
404
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
405
|
+
`,(0,c.jsxs)(t.li,{children:[`начальное значение `,(0,c.jsx)(t.code,{children:`isCopied`}),` — `,(0,c.jsx)(t.code,{children:`false`}),`;`]}),`
|
|
406
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`showFeedback()`}),` сразу запрашивает установку `,(0,c.jsx)(t.code,{children:`isCopied = true`}),`;`]}),`
|
|
407
|
+
`,(0,c.jsx)(t.li,{children:`затем создаёт timer на 2000 мс;`}),`
|
|
408
|
+
`,(0,c.jsxs)(t.li,{children:[`timer устанавливает `,(0,c.jsx)(t.code,{children:`isCopied = false`}),`;`]}),`
|
|
409
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`showFeedback()`}),` возвращает cleanup-функцию, отменяющую именно созданный timer.`]}),`
|
|
410
|
+
`]}),`
|
|
411
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`const { isCopied, showFeedback } = useCopyFeedback();
|
|
412
|
+
|
|
413
|
+
const handleAction = () => {
|
|
414
|
+
showFeedback();
|
|
415
|
+
};
|
|
416
|
+
`})}),`
|
|
417
|
+
`,(0,c.jsxs)(t.p,{children:[`Hook ничего не копирует. Название `,(0,c.jsx)(t.code,{children:`isCopied`}),` описывает типовой UI-сценарий, но вызывающий код сам решает, когда вызывать `,(0,c.jsx)(t.code,{children:`showFeedback`}),`.`]}),`
|
|
418
|
+
`,(0,c.jsx)(t.h3,{id:`cleanup-функция`,children:`Cleanup-функция`}),`
|
|
419
|
+
`,(0,c.jsxs)(t.p,{children:[`Результат `,(0,c.jsx)(t.code,{children:`showFeedback()`}),` имеет тип `,(0,c.jsx)(t.code,{children:`() => void`}),`:`]}),`
|
|
420
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const cancelTimer = showFeedback();
|
|
421
|
+
cancelTimer();
|
|
422
|
+
`})}),`
|
|
423
|
+
`,(0,c.jsxs)(t.p,{children:[`Cleanup только отменяет переход в `,(0,c.jsx)(t.code,{children:`false`}),`. Он не сбрасывает `,(0,c.jsx)(t.code,{children:`isCopied`}),` сразу. Если вызвать cleanup, пока компонент продолжает жить, состояние может остаться `,(0,c.jsx)(t.code,{children:`true`}),` до другого обновления. Поэтому не используйте cleanup как команду «скрыть сообщение».`]}),`
|
|
424
|
+
`,(0,c.jsx)(t.p,{children:`Текущая реализация hook не сохраняет cleanup автоматически и не отменяет timer при unmount. Это важно для компонентов с очень коротким жизненным циклом.`}),`
|
|
425
|
+
`,(0,c.jsx)(t.h3,{id:`повторные-вызовы`,children:`Повторные вызовы`}),`
|
|
426
|
+
`,(0,c.jsxs)(t.p,{children:[`Каждый вызов `,(0,c.jsx)(t.code,{children:`showFeedback()`}),` создаёт отдельный timer. Timers не заменяют друг друга.`]}),`
|
|
427
|
+
`,(0,c.jsxs)(t.p,{children:[`Если вызвать функцию второй раз через одну секунду, timer первого вызова всё равно сможет установить `,(0,c.jsx)(t.code,{children:`false`}),` ещё через одну секунду. Поэтому feedback не гарантирует «ровно две секунды после последнего вызова».`]}),`
|
|
428
|
+
`,(0,c.jsx)(t.p,{children:`Для редких пользовательских кликов это обычно незаметно. Для частых программных вызовов нужен отдельный feedback hook с last-write-wins timer policy.`}),`
|
|
429
|
+
`,(0,c.jsx)(t.h2,{id:`usecopyelementtextwithfeedback`,children:(0,c.jsx)(t.code,{children:`useCopyElementTextWithFeedback`})}),`
|
|
430
|
+
`,(0,c.jsxs)(t.p,{children:[`Объединяет `,(0,c.jsx)(t.code,{children:`useCopyElementText`}),` и `,(0,c.jsx)(t.code,{children:`useCopyFeedback`}),`.`]}),`
|
|
431
|
+
`,(0,c.jsx)(t.h3,{id:`сигнатура-4`,children:`Сигнатура`}),`
|
|
432
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`declare const useCopyElementTextWithFeedback: <T extends HTMLElement = HTMLElement>() => {
|
|
433
|
+
containerRef: RefObject<T | null>;
|
|
434
|
+
copyElementText: () => Promise<boolean>;
|
|
435
|
+
getElementText: () => string;
|
|
436
|
+
isCopied: boolean;
|
|
437
|
+
};
|
|
438
|
+
`})}),`
|
|
439
|
+
`,(0,c.jsx)(t.h3,{id:`точное-поведение-2`,children:`Точное поведение`}),`
|
|
440
|
+
`,(0,c.jsxs)(t.p,{children:[`При вызове `,(0,c.jsx)(t.code,{children:`copyElementText()`}),`:`]}),`
|
|
441
|
+
`,(0,c.jsxs)(t.ol,{children:[`
|
|
442
|
+
`,(0,c.jsxs)(t.li,{children:[`читается trimmed `,(0,c.jsx)(t.code,{children:`textContent`}),` элемента;`]}),`
|
|
443
|
+
`,(0,c.jsx)(t.li,{children:`выполняется попытка копирования;`}),`
|
|
444
|
+
`,(0,c.jsxs)(t.li,{children:[`если получен `,(0,c.jsx)(t.code,{children:`true`}),`, вызывается `,(0,c.jsx)(t.code,{children:`showFeedback()`}),`;`]}),`
|
|
445
|
+
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`isCopied`}),` становится `,(0,c.jsx)(t.code,{children:`true`}),` примерно на две секунды;`]}),`
|
|
446
|
+
`,(0,c.jsx)(t.li,{children:`возвращается тот же boolean-результат копирования.`}),`
|
|
447
|
+
`]}),`
|
|
448
|
+
`,(0,c.jsxs)(t.p,{children:[`При `,(0,c.jsx)(t.code,{children:`false`}),` feedback не показывается.`]}),`
|
|
449
|
+
`,(0,c.jsx)(t.h3,{id:`пример-с-иконкой-и-доступной-подписью`,children:`Пример с иконкой и доступной подписью`}),`
|
|
450
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { useCopyElementTextWithFeedback } from "@ryuzaki13/react-foundation-lib/copy";
|
|
451
|
+
|
|
452
|
+
export function CopyReference({ value }: { value: string }) {
|
|
453
|
+
const { containerRef, copyElementText, isCopied } = useCopyElementTextWithFeedback<HTMLCodeElement>();
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<div>
|
|
457
|
+
<code ref={containerRef}>{value}</code>
|
|
458
|
+
<button type="button" onClick={() => void copyElementText()} aria-label={isCopied ? "Скопировано" : "Копировать значение"}>
|
|
459
|
+
{isCopied ? "✓" : "⧉"}
|
|
460
|
+
</button>
|
|
461
|
+
<span className="visually-hidden" aria-live="polite">
|
|
462
|
+
{isCopied ? "Значение скопировано" : ""}
|
|
463
|
+
</span>
|
|
464
|
+
</div>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
`})}),`
|
|
468
|
+
`,(0,c.jsxs)(t.p,{children:[`Hook наследует ограничения `,(0,c.jsx)(t.code,{children:`useCopyFeedback`}),`: timers не объединяются, cleanup не вызывается автоматически при unmount.`]}),`
|
|
469
|
+
`,(0,c.jsx)(t.h2,{id:`как-выбирается-способ-копирования`,children:`Как выбирается способ копирования`}),`
|
|
470
|
+
`,(0,c.jsxs)(t.p,{children:[`Упрощённый алгоритм `,(0,c.jsx)(t.code,{children:`useCopyText`}),`:`]}),`
|
|
471
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`text.trim() пустой?
|
|
472
|
+
├─ да → false
|
|
473
|
+
└─ нет
|
|
474
|
+
├─ navigator.clipboard существует И window.isSecureContext === true
|
|
475
|
+
│ ├─ writeText завершился → true
|
|
476
|
+
│ └─ writeText выбросил ошибку → console.error + false
|
|
477
|
+
└─ иначе
|
|
478
|
+
├─ execCommand("copy") вернул true → true
|
|
479
|
+
└─ вернул false или произошла ошибка → false
|
|
480
|
+
`})}),`
|
|
481
|
+
`,(0,c.jsx)(t.h3,{id:`современный-путь`,children:`Современный путь`}),`
|
|
482
|
+
`,(0,c.jsx)(t.p,{children:`Используется:`}),`
|
|
483
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`await navigator.clipboard.writeText(text);
|
|
484
|
+
`})}),`
|
|
485
|
+
`,(0,c.jsx)(t.p,{children:`На результат могут влиять:`}),`
|
|
486
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
487
|
+
`,(0,c.jsx)(t.li,{children:`HTTPS/secure context;`}),`
|
|
488
|
+
`,(0,c.jsx)(t.li,{children:`user activation;`}),`
|
|
489
|
+
`,(0,c.jsx)(t.li,{children:`разрешение браузера;`}),`
|
|
490
|
+
`,(0,c.jsx)(t.li,{children:`Permissions Policy родительской страницы;`}),`
|
|
491
|
+
`,(0,c.jsx)(t.li,{children:`настройки iframe;`}),`
|
|
492
|
+
`,(0,c.jsx)(t.li,{children:`настройки пользователя или корпоративного браузера.`}),`
|
|
493
|
+
`]}),`
|
|
494
|
+
`,(0,c.jsx)(t.h3,{id:`legacy-fallback`,children:`Legacy fallback`}),`
|
|
495
|
+
`,(0,c.jsx)(t.p,{children:`Fallback:`}),`
|
|
496
|
+
`,(0,c.jsxs)(t.ol,{children:[`
|
|
497
|
+
`,(0,c.jsxs)(t.li,{children:[`создаёт невидимый `,(0,c.jsx)(t.code,{children:`contentEditable`}),` `,(0,c.jsx)(t.code,{children:`<div>`}),`;`]}),`
|
|
498
|
+
`,(0,c.jsxs)(t.li,{children:[`помещает его в `,(0,c.jsx)(t.code,{children:`document.body`}),`;`]}),`
|
|
499
|
+
`,(0,c.jsxs)(t.li,{children:[`записывает строку в `,(0,c.jsx)(t.code,{children:`textContent`}),`;`]}),`
|
|
500
|
+
`,(0,c.jsxs)(t.li,{children:[`выделяет содержимое через `,(0,c.jsx)(t.code,{children:`Range`}),` и `,(0,c.jsx)(t.code,{children:`Selection`}),`;`]}),`
|
|
501
|
+
`,(0,c.jsxs)(t.li,{children:[`вызывает `,(0,c.jsx)(t.code,{children:`document.execCommand("copy")`}),`;`]}),`
|
|
502
|
+
`,(0,c.jsx)(t.li,{children:`при обычном завершении снимает выделение и удаляет временный элемент.`}),`
|
|
503
|
+
`]}),`
|
|
504
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`document.execCommand`}),` является устаревшим и browser-dependent API. Наличие fallback не означает поддержку любого старого браузера.`]}),`
|
|
505
|
+
`,(0,c.jsxs)(t.p,{children:[`Если внутри fallback произойдёт исключение после добавления временного элемента, текущая реализация не использует `,(0,c.jsx)(t.code,{children:`finally`}),`. В таком редком случае она вернёт `,(0,c.jsx)(t.code,{children:`false`}),`, но не гарантирует очистку временного элемента и selection.`]}),`
|
|
506
|
+
`,(0,c.jsx)(t.h2,{id:`готовые-рецепты`,children:`Готовые рецепты`}),`
|
|
507
|
+
`,(0,c.jsx)(t.h3,{id:`копирование-вычисленного-значения`,children:`Копирование вычисленного значения`}),`
|
|
508
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`function CopyCoordinates({ latitude, longitude }: { latitude: number; longitude: number }) {
|
|
509
|
+
const { copyToClipboard } = useCopyText();
|
|
510
|
+
const value = \`\${latitude}, \${longitude}\`;
|
|
511
|
+
|
|
512
|
+
return (
|
|
513
|
+
<button type="button" onClick={() => void copyToClipboard(value)}>
|
|
514
|
+
Копировать координаты
|
|
515
|
+
</button>
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
`})}),`
|
|
519
|
+
`,(0,c.jsx)(t.h3,{id:`отдельная-обработка-ошибки`,children:`Отдельная обработка ошибки`}),`
|
|
520
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`function CopyToken({ token }: { token: string }) {
|
|
521
|
+
const { copyToClipboard } = useCopyText();
|
|
522
|
+
const [error, setError] = useState("");
|
|
523
|
+
|
|
524
|
+
const handleCopy = async () => {
|
|
525
|
+
const success = await copyToClipboard(token);
|
|
526
|
+
setError(success ? "" : "Разрешите доступ к буферу обмена или скопируйте значение вручную");
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
return (
|
|
530
|
+
<div>
|
|
531
|
+
<button type="button" onClick={() => void handleCopy()}>
|
|
532
|
+
Копировать
|
|
533
|
+
</button>
|
|
534
|
+
{error ? <p role="alert">{error}</p> : null}
|
|
535
|
+
</div>
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
`})}),`
|
|
539
|
+
`,(0,c.jsx)(t.h3,{id:`клавиатурная-команда`,children:`Клавиатурная команда`}),`
|
|
540
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`function CopyOnShortcut({ value }: { value: string }) {
|
|
541
|
+
const { copyToClipboard } = useCopyText();
|
|
542
|
+
|
|
543
|
+
const handleKeyDown = (event: React.KeyboardEvent) => {
|
|
544
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "c") {
|
|
545
|
+
event.preventDefault();
|
|
546
|
+
void copyToClipboard(value);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
return (
|
|
551
|
+
<div tabIndex={0} onKeyDown={handleKeyDown}>
|
|
552
|
+
Нажмите Ctrl/Cmd+C, чтобы скопировать значение
|
|
553
|
+
</div>
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
`})}),`
|
|
557
|
+
`,(0,c.jsx)(t.p,{children:`Не перехватывайте стандартный Ctrl/Cmd+C глобально без явной необходимости: пользователь может копировать выделенный им текст.`}),`
|
|
558
|
+
`,(0,c.jsx)(t.h3,{id:`копирование-текста-который-меняется-в-dom`,children:`Копирование текста, который меняется в DOM`}),`
|
|
559
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`function CopyLiveCounter({ count }: { count: number }) {
|
|
560
|
+
const { containerRef, copyElementText } = useCopyElementText<HTMLOutputElement>();
|
|
561
|
+
|
|
562
|
+
return (
|
|
563
|
+
<div>
|
|
564
|
+
<output ref={containerRef}>Обработано: {count}</output>
|
|
565
|
+
<button type="button" onClick={() => void copyElementText()}>
|
|
566
|
+
Копировать текущий результат
|
|
567
|
+
</button>
|
|
568
|
+
</div>
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
`})}),`
|
|
572
|
+
`,(0,c.jsxs)(t.p,{children:[`При клике будет прочитан текст с актуальным `,(0,c.jsx)(t.code,{children:`count`}),`, а не значение первого рендера.`]}),`
|
|
573
|
+
`,(0,c.jsx)(t.h2,{id:`доступность-и-ux`,children:`Доступность и UX`}),`
|
|
574
|
+
`,(0,c.jsx)(t.h3,{id:`используйте-настоящую-кнопку`,children:`Используйте настоящую кнопку`}),`
|
|
575
|
+
`,(0,c.jsxs)(t.p,{children:[`Для команды копирования предпочтителен `,(0,c.jsx)(t.code,{children:`<button type="button">`}),`, а не кликабельный `,(0,c.jsx)(t.code,{children:`<div>`}),`. Кнопка уже поддерживает клавиатуру, фокус и семантику действия.`]}),`
|
|
576
|
+
`,(0,c.jsx)(t.h3,{id:`не-передавайте-успех-только-цветом-или-иконкой`,children:`Не передавайте успех только цветом или иконкой`}),`
|
|
577
|
+
`,(0,c.jsx)(t.p,{children:`Текст «Скопировано» понятнее, чем одна смена цвета. Для screen reader добавьте live region:`}),`
|
|
578
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`<span aria-live="polite">{isCopied ? "Скопировано" : ""}</span>
|
|
579
|
+
`})}),`
|
|
580
|
+
`,(0,c.jsxs)(t.h3,{id:`не-показывайте-успех-до-завершения-await`,children:[`Не показывайте успех до завершения `,(0,c.jsx)(t.code,{children:`await`})]}),`
|
|
581
|
+
`,(0,c.jsxs)(t.p,{children:[`Операция может быть отклонена. Показывайте успешный feedback только после `,(0,c.jsx)(t.code,{children:`true`}),`. `,(0,c.jsx)(t.code,{children:`useCopyElementTextWithFeedback`}),` делает это автоматически.`]}),`
|
|
582
|
+
`,(0,c.jsx)(t.h3,{id:`оставьте-ручной-способ-копирования`,children:`Оставьте ручной способ копирования`}),`
|
|
583
|
+
`,(0,c.jsx)(t.p,{children:`Если значение важно, оставьте его видимым и выделяемым. Тогда пользователь сможет скопировать его вручную, даже если browser API заблокирован.`}),`
|
|
584
|
+
`,(0,c.jsx)(t.h3,{id:`осторожно-с-чувствительными-данными`,children:`Осторожно с чувствительными данными`}),`
|
|
585
|
+
`,(0,c.jsx)(t.p,{children:`Не копируйте пароль, token или персональные данные без явного действия пользователя. Буфер обмена доступен другим приложениям и может сохраняться менеджером истории clipboard.`}),`
|
|
586
|
+
`,(0,c.jsx)(t.h2,{id:`ssr-и-browser-boundary`,children:`SSR и browser boundary`}),`
|
|
587
|
+
`,(0,c.jsx)(t.p,{children:`Hooks можно импортировать в React-код, но фактическое копирование требует browser globals.`}),`
|
|
588
|
+
`,(0,c.jsx)(t.p,{children:`Следуйте правилам:`}),`
|
|
589
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
590
|
+
`,(0,c.jsxs)(t.li,{children:[`вызывайте `,(0,c.jsx)(t.code,{children:`copyToClipboard`}),` и `,(0,c.jsx)(t.code,{children:`copyElementText`}),` только на клиенте;`]}),`
|
|
591
|
+
`,(0,c.jsx)(t.li,{children:`связывайте вызов с пользовательским событием после монтирования;`}),`
|
|
592
|
+
`,(0,c.jsx)(t.li,{children:`не вызывайте копирование во время render;`}),`
|
|
593
|
+
`,(0,c.jsx)(t.li,{children:`не вызывайте его в server loader, server action или Node-only utility;`}),`
|
|
594
|
+
`,(0,c.jsx)(t.li,{children:`в тестовой среде предоставляйте DOM и mock Clipboard API.`}),`
|
|
595
|
+
`]}),`
|
|
596
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`getElementText()`}),` безопасно вернёт пустую строку, пока ref равен `,(0,c.jsx)(t.code,{children:`null`}),`. Но это не означает, что сам сценарий копирования является server API.`]}),`
|
|
597
|
+
`,(0,c.jsx)(t.h2,{id:`тестирование`,children:`Тестирование`}),`
|
|
598
|
+
`,(0,c.jsx)(t.h3,{id:`что-стоит-проверять-в-consumer-компоненте`,children:`Что стоит проверять в consumer-компоненте`}),`
|
|
599
|
+
`,(0,c.jsxs)(t.ul,{children:[`
|
|
600
|
+
`,(0,c.jsxs)(t.li,{children:[`правильная строка передаётся в `,(0,c.jsx)(t.code,{children:`navigator.clipboard.writeText`}),`;`]}),`
|
|
601
|
+
`,(0,c.jsxs)(t.li,{children:[`UI показывает успех только после результата `,(0,c.jsx)(t.code,{children:`true`}),`;`]}),`
|
|
602
|
+
`,(0,c.jsxs)(t.li,{children:[`при `,(0,c.jsx)(t.code,{children:`false`}),` отображается сообщение об ошибке;`]}),`
|
|
603
|
+
`,(0,c.jsx)(t.li,{children:`ref установлен на нужный элемент;`}),`
|
|
604
|
+
`,(0,c.jsx)(t.li,{children:`hidden/button text не попадает в копируемое значение;`}),`
|
|
605
|
+
`,(0,c.jsx)(t.li,{children:`feedback исчезает после 2000 мс;`}),`
|
|
606
|
+
`,(0,c.jsx)(t.li,{children:`повторные клики соответствуют ожидаемой UX-policy.`}),`
|
|
607
|
+
`]}),`
|
|
608
|
+
`,(0,c.jsx)(t.h3,{id:`mock-современного-clipboard-api`,children:`Mock современного Clipboard API`}),`
|
|
609
|
+
`,(0,c.jsx)(t.p,{children:`Пример для Vitest и DOM test environment:`}),`
|
|
610
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`import { vi } from "vitest";
|
|
611
|
+
|
|
612
|
+
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
613
|
+
|
|
614
|
+
Object.defineProperty(navigator, "clipboard", {
|
|
615
|
+
configurable: true,
|
|
616
|
+
value: { writeText }
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
Object.defineProperty(window, "isSecureContext", {
|
|
620
|
+
configurable: true,
|
|
621
|
+
value: true
|
|
622
|
+
});
|
|
623
|
+
`})}),`
|
|
624
|
+
`,(0,c.jsx)(t.p,{children:`После пользовательского клика:`}),`
|
|
625
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`expect(writeText).toHaveBeenCalledWith("ORDER-42");
|
|
626
|
+
`})}),`
|
|
627
|
+
`,(0,c.jsx)(t.h3,{id:`проверка-ошибки`,children:`Проверка ошибки`}),`
|
|
628
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`writeText.mockRejectedValueOnce(new DOMException("Not allowed", "NotAllowedError"));
|
|
629
|
+
`})}),`
|
|
630
|
+
`,(0,c.jsxs)(t.p,{children:[`Поскольку hook пишет ошибку в `,(0,c.jsx)(t.code,{children:`console.error`}),`, в тесте можно временно установить spy, а после теста обязательно восстановить его.`]}),`
|
|
631
|
+
`,(0,c.jsx)(t.h3,{id:`проверка-feedback-через-fake-timers`,children:`Проверка feedback через fake timers`}),`
|
|
632
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`vi.useFakeTimers();
|
|
633
|
+
|
|
634
|
+
// После showFeedback() или успешного клика ожидается isCopied === true.
|
|
635
|
+
vi.advanceTimersByTime(2000);
|
|
636
|
+
// После React update ожидается isCopied === false.
|
|
637
|
+
|
|
638
|
+
vi.useRealTimers();
|
|
639
|
+
`})}),`
|
|
640
|
+
`,(0,c.jsxs)(t.p,{children:[`При работе с React Testing Library изменение времени нужно выполнять внутри `,(0,c.jsx)(t.code,{children:`act`}),`.`]}),`
|
|
641
|
+
`,(0,c.jsx)(t.h2,{id:`ограничения-и-частые-ошибки`,children:`Ограничения и частые ошибки`}),`
|
|
642
|
+
`,(0,c.jsx)(t.h3,{id:`вызов-вне-пользовательского-события`,children:`Вызов вне пользовательского события`}),`
|
|
643
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`useEffect(() => {
|
|
644
|
+
void copyToClipboard(value);
|
|
645
|
+
}, [copyToClipboard, value]);
|
|
646
|
+
`})}),`
|
|
647
|
+
`,(0,c.jsx)(t.p,{children:`Браузер может заблокировать такой вызов. Кроме того, неожиданное изменение clipboard при открытии страницы является плохим UX.`}),`
|
|
648
|
+
`,(0,c.jsxs)(t.h3,{id:`игнорирование-promise`,children:[`Игнорирование `,(0,c.jsx)(t.code,{children:`Promise`})]}),`
|
|
649
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`// Ошибка: UI сразу говорит об успехе.
|
|
650
|
+
copyToClipboard(value);
|
|
651
|
+
setMessage("Скопировано");
|
|
652
|
+
`})}),`
|
|
653
|
+
`,(0,c.jsx)(t.p,{children:`Правильно дождаться результата:`}),`
|
|
654
|
+
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`const success = await copyToClipboard(value);
|
|
655
|
+
setMessage(success ? "Скопировано" : "Не удалось скопировать");
|
|
656
|
+
`})}),`
|
|
657
|
+
`,(0,c.jsx)(t.h3,{id:`ref-охватывает-лишний-ui`,children:`Ref охватывает лишний UI`}),`
|
|
658
|
+
`,(0,c.jsxs)(t.p,{children:[`В `,(0,c.jsx)(t.code,{children:`textContent`}),` попадут подписи вложенных элементов. Установите ref только на значение либо используйте `,(0,c.jsx)(t.code,{children:`useCopyText(value)`}),`.`]}),`
|
|
659
|
+
`,(0,c.jsxs)(t.h3,{id:`попытка-прочитать-inputvalue-через-textcontent`,children:[`Попытка прочитать `,(0,c.jsx)(t.code,{children:`input.value`}),` через `,(0,c.jsx)(t.code,{children:`textContent`})]}),`
|
|
660
|
+
`,(0,c.jsxs)(t.p,{children:[`Element hooks не читают значение form control. Передавайте controlled state или `,(0,c.jsx)(t.code,{children:`inputRef.current?.value`}),` в `,(0,c.jsx)(t.code,{children:`useCopyText`}),`.`]}),`
|
|
661
|
+
`,(0,c.jsx)(t.h3,{id:`ожидание-rich-clipboard`,children:`Ожидание rich clipboard`}),`
|
|
662
|
+
`,(0,c.jsx)(t.p,{children:`Модуль пишет plain text. Форматирование HTML, изображение и файлы не сохраняются.`}),`
|
|
663
|
+
`,(0,c.jsx)(t.h3,{id:`ожидание-fallback-после-отказа-clipboard-api`,children:`Ожидание fallback после отказа Clipboard API`}),`
|
|
664
|
+
`,(0,c.jsxs)(t.p,{children:[`Если `,(0,c.jsx)(t.code,{children:`navigator.clipboard.writeText`}),` был выбран и выбросил ошибку, legacy fallback не запускается.`]}),`
|
|
665
|
+
`,(0,c.jsx)(t.h3,{id:`несколько-быстрых-feedback-вызовов`,children:`Несколько быстрых feedback-вызовов`}),`
|
|
666
|
+
`,(0,c.jsx)(t.p,{children:`Timers независимы. Более ранний timer может скрыть сообщение раньше, чем через две секунды после последнего клика.`}),`
|
|
667
|
+
`,(0,c.jsxs)(t.h3,{id:`слепое-доверие-true`,children:[`Слепое доверие `,(0,c.jsx)(t.code,{children:`true`})]}),`
|
|
668
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`true`}),` означает, что выбранный browser API сообщил об успехе. Модуль не читает clipboard обратно и не сравнивает содержимое.`]}),`
|
|
669
|
+
`,(0,c.jsx)(t.h2,{id:`краткий-справочник-api`,children:`Краткий справочник API`}),`
|
|
670
|
+
`,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`API`}),(0,c.jsx)(t.th,{children:`Вход`}),(0,c.jsx)(t.th,{children:`Результат`}),(0,c.jsx)(t.th,{children:`Главное ограничение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyText`})}),(0,c.jsxs)(t.td,{children:[`Строка в `,(0,c.jsx)(t.code,{children:`copyToClipboard`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Promise<boolean>`})}),(0,c.jsx)(t.td,{children:`Только plain text; зависит от browser policy.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useElementText`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`RefObject<T | null>`})}),(0,c.jsxs)(t.td,{children:[`Trimmed `,(0,c.jsx)(t.code,{children:`textContent`}),` или `,(0,c.jsx)(t.code,{children:`""`})]}),(0,c.jsxs)(t.td,{children:[`Не читает `,(0,c.jsx)(t.code,{children:`input.value`}),`; учитывает hidden descendants.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyElementText`})}),(0,c.jsx)(t.td,{children:`Generic типа элемента`}),(0,c.jsx)(t.td,{children:`Ref, getter и async copy`}),(0,c.jsx)(t.td,{children:`Внешний whitespace элемента обрезается.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyFeedback`})}),(0,c.jsxs)(t.td,{children:[`Вызов `,(0,c.jsx)(t.code,{children:`showFeedback()`})]}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`isCopied = true`}),`, затем `,(0,c.jsx)(t.code,{children:`false`}),` через 2000 мс`]}),(0,c.jsx)(t.td,{children:`Timers не объединяются и не очищаются автоматически.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useCopyElementTextWithFeedback`})}),(0,c.jsx)(t.td,{children:`Generic типа элемента`}),(0,c.jsxs)(t.td,{children:[`Ref, getter, async copy и `,(0,c.jsx)(t.code,{children:`isCopied`})]}),(0,c.jsx)(t.td,{children:`Наследует ограничения element text и feedback timer.`})]})]})]}),`
|
|
671
|
+
`,(0,c.jsx)(t.p,{children:`Все hooks должны вызываться по правилам React hooks.`}),`
|
|
672
|
+
`,(0,c.jsx)(t.h2,{id:`вопросы-и-ответы`,children:`Вопросы и ответы`}),`
|
|
673
|
+
`,(0,c.jsxs)(t.h3,{id:`почему-функция-возвращает-false-на-строке-из-пробелов`,children:[`Почему функция возвращает `,(0,c.jsx)(t.code,{children:`false`}),` на строке из пробелов?`]}),`
|
|
674
|
+
`,(0,c.jsxs)(t.p,{children:[`Потому что перед копированием проверяется `,(0,c.jsx)(t.code,{children:`text.trim()`}),`. Whitespace-only строка считается отсутствующим полезным текстом.`]}),`
|
|
675
|
+
`,(0,c.jsxs)(t.h3,{id:`обрезает-ли-usecopytext-пробелы-у-нормальной-строки`,children:[`Обрезает ли `,(0,c.jsx)(t.code,{children:`useCopyText`}),` пробелы у нормальной строки?`]}),`
|
|
676
|
+
`,(0,c.jsxs)(t.p,{children:[`Нет. `,(0,c.jsx)(t.code,{children:`trim()`}),` используется только для проверки пустоты. Исходная непустая строка копируется буквально.`]}),`
|
|
677
|
+
`,(0,c.jsxs)(t.h3,{id:`обрезает-ли-usecopyelementtext-пробелы`,children:[`Обрезает ли `,(0,c.jsx)(t.code,{children:`useCopyElementText`}),` пробелы?`]}),`
|
|
678
|
+
`,(0,c.jsxs)(t.p,{children:[`Да. `,(0,c.jsx)(t.code,{children:`useElementText`}),` возвращает `,(0,c.jsx)(t.code,{children:`textContent.trim()`}),`, и именно этот результат передаётся на копирование.`]}),`
|
|
679
|
+
`,(0,c.jsx)(t.h3,{id:`почему-на-localhost-работает-а-на-http-стенде-нет`,children:`Почему на localhost работает, а на HTTP-стенде нет?`}),`
|
|
680
|
+
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`localhost`}),` обычно считается доверенным контекстом, а обычный HTTP-хост — нет. На стенде может использоваться legacy fallback, который браузер также вправе заблокировать.`]}),`
|
|
681
|
+
`,(0,c.jsx)(t.h3,{id:`почему-копирование-не-работает-внутри-iframe`,children:`Почему копирование не работает внутри iframe?`}),`
|
|
682
|
+
`,(0,c.jsx)(t.p,{children:`Родительская страница или Permissions Policy может запрещать clipboard-write. Проверьте настройки iframe и вызывайте операцию из пользовательского события.`}),`
|
|
683
|
+
`,(0,c.jsx)(t.h3,{id:`можно-ли-копировать-значение-input`,children:`Можно ли копировать значение input?`}),`
|
|
684
|
+
`,(0,c.jsxs)(t.p,{children:[`Да, но через `,(0,c.jsx)(t.code,{children:`useCopyText(inputValue)`}),`, а не через `,(0,c.jsx)(t.code,{children:`useCopyElementText`}),`.`]}),`
|
|
685
|
+
`,(0,c.jsx)(t.h3,{id:`можно-ли-использовать-hook-без-feedback`,children:`Можно ли использовать hook без feedback?`}),`
|
|
686
|
+
`,(0,c.jsxs)(t.p,{children:[`Да. Используйте `,(0,c.jsx)(t.code,{children:`useCopyText`}),` или `,(0,c.jsx)(t.code,{children:`useCopyElementText`}),`.`]}),`
|
|
687
|
+
`,(0,c.jsx)(t.h3,{id:`можно-ли-использовать-feedback-для-другой-успешной-команды`,children:`Можно ли использовать feedback для другой успешной команды?`}),`
|
|
688
|
+
`,(0,c.jsxs)(t.p,{children:[`Технически да: `,(0,c.jsx)(t.code,{children:`useCopyFeedback`}),` только переключает boolean. Учитывайте фиксированные 2000 мс и независимые timers.`]}),`
|
|
689
|
+
`,(0,c.jsx)(t.h3,{id:`можно-ли-изменить-длительность-двух-секунд`,children:`Можно ли изменить длительность двух секунд?`}),`
|
|
690
|
+
`,(0,c.jsx)(t.p,{children:`Нет. В текущем публичном API длительность не настраивается.`}),`
|
|
691
|
+
`,(0,c.jsxs)(t.h3,{id:`почему-showfeedback-возвращает-функцию`,children:[`Почему `,(0,c.jsx)(t.code,{children:`showFeedback`}),` возвращает функцию?`]}),`
|
|
692
|
+
`,(0,c.jsxs)(t.p,{children:[`Она отменяет созданный timer. Hook не вызывает её автоматически. Отмена timer не устанавливает `,(0,c.jsx)(t.code,{children:`isCopied`}),` обратно в `,(0,c.jsx)(t.code,{children:`false`}),`.`]}),`
|
|
693
|
+
`,(0,c.jsxs)(t.h3,{id:`что-выбрать-usecopyelementtext-или-usecopytext`,children:[`Что выбрать: `,(0,c.jsx)(t.code,{children:`useCopyElementText`}),` или `,(0,c.jsx)(t.code,{children:`useCopyText`}),`?`]}),`
|
|
694
|
+
`,(0,c.jsxs)(t.p,{children:[`Если исходная строка уже есть в state, props или переменной, выбирайте `,(0,c.jsx)(t.code,{children:`useCopyText`}),`: результат будет предсказуемее. Element hook удобен, когда источником действительно является собранный DOM-текст.`]})]})}function s(e={}){let{wrapper:t}={...n(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(o,{...e})}):o(e)}var c;function l(){return(l=e((()=>{c=t(),r(),a()})))()}l();export{s as default};
|