@customafk/lunas-ui 0.2.83 → 0.2.85
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/LoginPage-DGgThT3s.mjs +2 -0
- package/dist/LoginPage-DGgThT3s.mjs.map +1 -0
- package/dist/LoginPage-DPUdRD0r.cjs +2 -0
- package/dist/LoginPage-DPUdRD0r.cjs.map +1 -0
- package/dist/action-submit-B2qFdfi6.cjs +2 -0
- package/dist/{action-submit-B9vHpLoN.cjs.map → action-submit-B2qFdfi6.cjs.map} +1 -1
- package/dist/action-submit-DyNvI-F2.mjs +2 -0
- package/dist/{action-submit-BQdkz4mS.mjs.map → action-submit-DyNvI-F2.mjs.map} +1 -1
- package/dist/actions-form-CB0lZlE6.mjs +2 -0
- package/dist/{actions-form-D4n1W3-X.mjs.map → actions-form-CB0lZlE6.mjs.map} +1 -1
- package/dist/actions-form-DnlpVrgF.cjs +2 -0
- package/dist/{actions-form-DF2eYxGQ.cjs.map → actions-form-DnlpVrgF.cjs.map} +1 -1
- package/dist/checkbox-field-CFPHAcwN.cjs +2 -0
- package/dist/{checkbox-field-ldZRSCca.cjs.map → checkbox-field-CFPHAcwN.cjs.map} +1 -1
- package/dist/checkbox-field-DodKeFS_.mjs +2 -0
- package/dist/{checkbox-field-BRGTLOjq.mjs.map → checkbox-field-DodKeFS_.mjs.map} +1 -1
- package/dist/{cms-layout-BGsf1E4E.cjs → cms-layout-DYEmSqzx.cjs} +2 -2
- package/dist/{cms-layout-BGsf1E4E.cjs.map → cms-layout-DYEmSqzx.cjs.map} +1 -1
- package/dist/combobox-field-D7cZ8fhZ.cjs +2 -0
- package/dist/{combobox-field-ZYr7636s.cjs.map → combobox-field-D7cZ8fhZ.cjs.map} +1 -1
- package/dist/combobox-field-ebIvbhJn.mjs +2 -0
- package/dist/{combobox-field-CvkdZkQE.mjs.map → combobox-field-ebIvbhJn.mjs.map} +1 -1
- package/dist/data-display/country.cjs +1 -1
- package/dist/date-field-CNkxebta.mjs +2 -0
- package/dist/{date-field-DGMTG_hu.mjs.map → date-field-CNkxebta.mjs.map} +1 -1
- package/dist/date-field-Dm367ro9.cjs +2 -0
- package/dist/{date-field-1Hmcv8Zp.cjs.map → date-field-Dm367ro9.cjs.map} +1 -1
- package/dist/dialog-form-D9Bifsx6.cjs +2 -0
- package/dist/{dialog-form-C1qpltYx.cjs.map → dialog-form-D9Bifsx6.cjs.map} +1 -1
- package/dist/dialog-form-zK6Gk9Gk.mjs +2 -0
- package/dist/{dialog-form-BRSZpRTC.mjs.map → dialog-form-zK6Gk9Gk.mjs.map} +1 -1
- package/dist/{dist-DzA--RwW.cjs → dist-CIdWHwhj.cjs} +2 -2
- package/dist/{dist-DzA--RwW.cjs.map → dist-CIdWHwhj.cjs.map} +1 -1
- package/dist/email-field-BVl7u9pR.mjs +2 -0
- package/dist/{email-field-C2Lvl_rQ.mjs.map → email-field-BVl7u9pR.mjs.map} +1 -1
- package/dist/email-field-CqXXRG3w.cjs +2 -0
- package/dist/{email-field-D3nSkHj2.cjs.map → email-field-CqXXRG3w.cjs.map} +1 -1
- package/dist/features/tables/index.d.cts +1 -1
- package/dist/features/tables/index.d.mts +1 -1
- package/dist/features/tanstack-form/index.cjs +1 -1
- package/dist/features/tanstack-form/index.d.cts +1 -1
- package/dist/features/tanstack-form/index.d.mts +1 -1
- package/dist/features/tanstack-form/index.mjs +1 -1
- package/dist/features/text-editor/index.cjs +1 -1
- package/dist/features/text-editor/index.d.cts +1 -1
- package/dist/features/text-editor/index.d.mts +1 -1
- package/dist/features/text-editor/index.mjs +1 -1
- package/dist/{flex-C63_IqSY.d.cts → flex-D6RCsrv1.d.cts} +2 -2
- package/dist/{flex-C63_IqSY.d.mts → flex-D6RCsrv1.d.mts} +2 -2
- package/dist/group-field-8nINgAeO.mjs +2 -0
- package/dist/{group-field-gejvx_Td.mjs.map → group-field-8nINgAeO.mjs.map} +1 -1
- package/dist/group-field-DfGPDLyp.cjs +2 -0
- package/dist/{group-field-sVykO2JZ.cjs.map → group-field-DfGPDLyp.cjs.map} +1 -1
- package/dist/{index-DvC1inRy.d.cts → index-0HuXxgjQ.d.cts} +13 -10
- package/dist/index-6_zytlWu.d.cts +70 -0
- package/dist/index-6_zytlWu.d.mts +70 -0
- package/dist/{index-CrkOhm--.d.mts → index-BCBEYTyA.d.mts} +5 -5
- package/dist/{index--wNkNzCB.d.cts → index-CQPyC0Q2.d.cts} +5 -5
- package/dist/{index-DvC1inRy.d.mts → index-CZ_2KVir.d.mts} +13 -10
- package/dist/{index-DRAyYm8g.d.mts → index-CvFEJoon.d.cts} +12 -3
- package/dist/{index-DRAyYm8g.d.cts → index-CvFEJoon.d.mts} +12 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/layouts/cms-layout/index.cjs +1 -1
- package/dist/layouts/flex.d.cts +1 -1
- package/dist/layouts/flex.d.mts +1 -1
- package/dist/layouts/payment-layout/index.cjs +1 -1
- package/dist/number-field-BjGl_IiN.cjs +2 -0
- package/dist/{number-field-CWDYA3La.cjs.map → number-field-BjGl_IiN.cjs.map} +1 -1
- package/dist/number-field-CVemIwIg.mjs +2 -0
- package/dist/{number-field-CR5PLX6H.mjs.map → number-field-CVemIwIg.mjs.map} +1 -1
- package/dist/pages/LoginPage/index.cjs +1 -0
- package/dist/pages/LoginPage/index.d.cts +2 -0
- package/dist/pages/LoginPage/index.d.mts +2 -0
- package/dist/pages/LoginPage/index.mjs +1 -0
- package/dist/password-field-Be0Ltlxh.cjs +2 -0
- package/dist/{password-field-BHRhVyZ0.cjs.map → password-field-Be0Ltlxh.cjs.map} +1 -1
- package/dist/password-field-BxYaT8lO.mjs +2 -0
- package/dist/{password-field-B2_kzss6.mjs.map → password-field-BxYaT8lO.mjs.map} +1 -1
- package/dist/{payment-layout-BZtIcu-3.cjs → payment-layout-C38xIc2X.cjs} +2 -2
- package/dist/{payment-layout-BZtIcu-3.cjs.map → payment-layout-C38xIc2X.cjs.map} +1 -1
- package/dist/popover-form-Ba_Nw79b.cjs +2 -0
- package/dist/{popover-form-Ch_iMMSm.cjs.map → popover-form-Ba_Nw79b.cjs.map} +1 -1
- package/dist/popover-form-C9mNInr2.mjs +2 -0
- package/dist/{popover-form-CeRVpy1B.mjs.map → popover-form-C9mNInr2.mjs.map} +1 -1
- package/dist/radio-group-field-BCJWamjd.cjs +2 -0
- package/dist/{radio-group-field-BFhBfzoT.cjs.map → radio-group-field-BCJWamjd.cjs.map} +1 -1
- package/dist/radio-group-field-DYqU_6hW.mjs +2 -0
- package/dist/{radio-group-field-Iwo2Q2Ig.mjs.map → radio-group-field-DYqU_6hW.mjs.map} +1 -1
- package/dist/{resizable-CNqPu4Ck.d.cts → resizable-DTnfCua-.d.cts} +2 -2
- package/dist/{resizable-CNqPu4Ck.d.mts → resizable-DTnfCua-.d.mts} +2 -2
- package/dist/rolldown-runtime-DWNI8pZO.cjs +1 -0
- package/dist/select-field-D7Ny2Qne.mjs +2 -0
- package/dist/{select-field-NJgDDCO2.mjs.map → select-field-D7Ny2Qne.mjs.map} +1 -1
- package/dist/select-field-UWnBMAzL.cjs +2 -0
- package/dist/{select-field-CvDBZQTl.cjs.map → select-field-UWnBMAzL.cjs.map} +1 -1
- package/dist/simple-array-field-BSMxRilE.cjs +2 -0
- package/dist/{simple-array-field-aGXkDZXD.cjs.map → simple-array-field-BSMxRilE.cjs.map} +1 -1
- package/dist/simple-array-field-DJXa_vST.mjs +2 -0
- package/dist/{simple-array-field-BwNCeUnn.mjs.map → simple-array-field-DJXa_vST.mjs.map} +1 -1
- package/dist/simple-boolean-field-CVNT8NBk.mjs +2 -0
- package/dist/{simple-boolean-field-t_XDXqYs.mjs.map → simple-boolean-field-CVNT8NBk.mjs.map} +1 -1
- package/dist/simple-boolean-field-CxAyVQ1w.cjs +2 -0
- package/dist/{simple-boolean-field-BeVkn3c4.cjs.map → simple-boolean-field-CxAyVQ1w.cjs.map} +1 -1
- package/dist/simple-combobox-field-BP4IeDkA.cjs +2 -0
- package/dist/{simple-combobox-field-U0ZAl7VQ.cjs.map → simple-combobox-field-BP4IeDkA.cjs.map} +1 -1
- package/dist/simple-combobox-field-BnAUDfQU.mjs +2 -0
- package/dist/{simple-combobox-field-HCB4A33-.mjs.map → simple-combobox-field-BnAUDfQU.mjs.map} +1 -1
- package/dist/simple-date-field-DIa6UBF2.mjs +2 -0
- package/dist/{simple-date-field-Dj4bJoji.mjs.map → simple-date-field-DIa6UBF2.mjs.map} +1 -1
- package/dist/simple-date-field-NSawlSff.cjs +2 -0
- package/dist/{simple-date-field-DtxEpEZj.cjs.map → simple-date-field-NSawlSff.cjs.map} +1 -1
- package/dist/simple-email-field-2Abh3qZw.cjs +2 -0
- package/dist/{simple-email-field-DK9I_Tc_.cjs.map → simple-email-field-2Abh3qZw.cjs.map} +1 -1
- package/dist/simple-email-field-CKDQgzh5.mjs +2 -0
- package/dist/{simple-email-field-BDyhkmHV.mjs.map → simple-email-field-CKDQgzh5.mjs.map} +1 -1
- package/dist/simple-number-field-BjEYE54F.cjs +2 -0
- package/dist/{simple-number-field-BspqWU-3.cjs.map → simple-number-field-BjEYE54F.cjs.map} +1 -1
- package/dist/simple-number-field-SDbxL6L7.mjs +2 -0
- package/dist/{simple-number-field-hneXMhfJ.mjs.map → simple-number-field-SDbxL6L7.mjs.map} +1 -1
- package/dist/simple-password-field-Cjp6yzWd.cjs +2 -0
- package/dist/{simple-password-field-BEEcleom.cjs.map → simple-password-field-Cjp6yzWd.cjs.map} +1 -1
- package/dist/simple-password-field-Cnj-hf5g.mjs +2 -0
- package/dist/{simple-password-field-vsLRHDk4.mjs.map → simple-password-field-Cnj-hf5g.mjs.map} +1 -1
- package/dist/simple-radio-group-field-4-eWSXY7.mjs +2 -0
- package/dist/{simple-radio-group-field-BURm_BSA.mjs.map → simple-radio-group-field-4-eWSXY7.mjs.map} +1 -1
- package/dist/simple-radio-group-field-CuA0Q-XI.cjs +2 -0
- package/dist/{simple-radio-group-field-Wm8xB0fN.cjs.map → simple-radio-group-field-CuA0Q-XI.cjs.map} +1 -1
- package/dist/simple-select-field-C0ecQx68.mjs +2 -0
- package/dist/{simple-select-field-8cY51U4d.mjs.map → simple-select-field-C0ecQx68.mjs.map} +1 -1
- package/dist/simple-select-field-CXYJcRY2.cjs +2 -0
- package/dist/{simple-select-field-QQ65caBf.cjs.map → simple-select-field-CXYJcRY2.cjs.map} +1 -1
- package/dist/simple-text-field-DJl5SIGn.mjs +2 -0
- package/dist/{simple-text-field-DK72_OA_.mjs.map → simple-text-field-DJl5SIGn.mjs.map} +1 -1
- package/dist/simple-text-field-DPXqNCXh.cjs +2 -0
- package/dist/{simple-text-field-BHkCPiE5.cjs.map → simple-text-field-DPXqNCXh.cjs.map} +1 -1
- package/dist/simple-textarea-field-BcHdUMTK.mjs +2 -0
- package/dist/{simple-textarea-field-B72nNQHv.mjs.map → simple-textarea-field-BcHdUMTK.mjs.map} +1 -1
- package/dist/simple-textarea-field-ZpXPwYz-.cjs +2 -0
- package/dist/{simple-textarea-field-Bu5h7sfH.cjs.map → simple-textarea-field-ZpXPwYz-.cjs.map} +1 -1
- package/dist/switch-field-CCDuDdpf.cjs +2 -0
- package/dist/{switch-field-eLVlQZSI.cjs.map → switch-field-CCDuDdpf.cjs.map} +1 -1
- package/dist/switch-field-DBGFsmEZ.mjs +2 -0
- package/dist/{switch-field-DoPUWb9d.mjs.map → switch-field-DBGFsmEZ.mjs.map} +1 -1
- package/dist/tanstack-form-BhKAn9I0.mjs +2 -0
- package/dist/tanstack-form-BhKAn9I0.mjs.map +1 -0
- package/dist/tanstack-form-DCB2addt.cjs +2 -0
- package/dist/tanstack-form-DCB2addt.cjs.map +1 -0
- package/dist/text-editor-DWZf3yrW.cjs +2 -0
- package/dist/text-editor-DWZf3yrW.cjs.map +1 -0
- package/dist/text-editor-XBOAwJk-.mjs +2 -0
- package/dist/text-editor-XBOAwJk-.mjs.map +1 -0
- package/dist/text-field-Bx_vnc4O.mjs +2 -0
- package/dist/{text-field-Bm-k6Jr7.mjs.map → text-field-Bx_vnc4O.mjs.map} +1 -1
- package/dist/text-field-DMZZ6CND.cjs +2 -0
- package/dist/{text-field-DT50dOVI.cjs.map → text-field-DMZZ6CND.cjs.map} +1 -1
- package/dist/textarea-field-BmImMdg-.cjs +2 -0
- package/dist/{textarea-field-BhqX29CS.cjs.map → textarea-field-BmImMdg-.cjs.map} +1 -1
- package/dist/textarea-field-Cim3l0y8.mjs +2 -0
- package/dist/{textarea-field-DNLyrRH7.mjs.map → textarea-field-Cim3l0y8.mjs.map} +1 -1
- package/dist/title-field-2dABwspv.mjs +2 -0
- package/dist/{title-field-oAqHRw59.mjs.map → title-field-2dABwspv.mjs.map} +1 -1
- package/dist/title-field-CFnSgTGH.cjs +2 -0
- package/dist/{title-field-B2hM3S_S.cjs.map → title-field-CFnSgTGH.cjs.map} +1 -1
- package/dist/ui/breadcrumb.cjs +1 -1
- package/dist/ui/button-group.cjs +1 -1
- package/dist/ui/button.cjs +1 -1
- package/dist/ui/carousel.cjs +1 -1
- package/dist/ui/dropdown-menu.cjs +1 -1
- package/dist/ui/dropdown-menu.cjs.map +1 -1
- package/dist/ui/dropdown-menu.mjs +1 -1
- package/dist/ui/dropdown-menu.mjs.map +1 -1
- package/dist/ui/file-uploader.cjs +1 -1
- package/dist/ui/item.cjs +1 -1
- package/dist/ui/resizable.cjs +1 -1
- package/dist/ui/resizable.d.cts +1 -1
- package/dist/ui/resizable.d.mts +1 -1
- package/package.json +9 -4
- package/styles/text-editor.css +205 -38
- package/dist/LoginPage-DDljzV6A.d.cts +0 -43
- package/dist/LoginPage-DDljzV6A.d.mts +0 -43
- package/dist/action-submit-B9vHpLoN.cjs +0 -2
- package/dist/action-submit-BQdkz4mS.mjs +0 -2
- package/dist/actions-form-D4n1W3-X.mjs +0 -2
- package/dist/actions-form-DF2eYxGQ.cjs +0 -2
- package/dist/checkbox-field-BRGTLOjq.mjs +0 -2
- package/dist/checkbox-field-ldZRSCca.cjs +0 -2
- package/dist/combobox-field-CvkdZkQE.mjs +0 -2
- package/dist/combobox-field-ZYr7636s.cjs +0 -2
- package/dist/date-field-1Hmcv8Zp.cjs +0 -2
- package/dist/date-field-DGMTG_hu.mjs +0 -2
- package/dist/dialog-form-BRSZpRTC.mjs +0 -2
- package/dist/dialog-form-C1qpltYx.cjs +0 -2
- package/dist/email-field-C2Lvl_rQ.mjs +0 -2
- package/dist/email-field-D3nSkHj2.cjs +0 -2
- package/dist/field-BSLi-P0Q.cjs +0 -2
- package/dist/field-BSLi-P0Q.cjs.map +0 -1
- package/dist/field-V2DoD8qL.mjs +0 -2
- package/dist/field-V2DoD8qL.mjs.map +0 -1
- package/dist/group-field-gejvx_Td.mjs +0 -2
- package/dist/group-field-sVykO2JZ.cjs +0 -2
- package/dist/number-field-CR5PLX6H.mjs +0 -2
- package/dist/number-field-CWDYA3La.cjs +0 -2
- package/dist/pages/LoginPage.cjs +0 -2
- package/dist/pages/LoginPage.cjs.map +0 -1
- package/dist/pages/LoginPage.d.cts +0 -2
- package/dist/pages/LoginPage.d.mts +0 -2
- package/dist/pages/LoginPage.mjs +0 -2
- package/dist/pages/LoginPage.mjs.map +0 -1
- package/dist/password-field-B2_kzss6.mjs +0 -2
- package/dist/password-field-BHRhVyZ0.cjs +0 -2
- package/dist/popover-form-CeRVpy1B.mjs +0 -2
- package/dist/popover-form-Ch_iMMSm.cjs +0 -2
- package/dist/radio-group-field-BFhBfzoT.cjs +0 -2
- package/dist/radio-group-field-Iwo2Q2Ig.mjs +0 -2
- package/dist/rolldown-runtime-CMqjfN_6.cjs +0 -1
- package/dist/select-field-CvDBZQTl.cjs +0 -2
- package/dist/select-field-NJgDDCO2.mjs +0 -2
- package/dist/simple-array-field-BwNCeUnn.mjs +0 -2
- package/dist/simple-array-field-aGXkDZXD.cjs +0 -2
- package/dist/simple-boolean-field-BeVkn3c4.cjs +0 -2
- package/dist/simple-boolean-field-t_XDXqYs.mjs +0 -2
- package/dist/simple-combobox-field-HCB4A33-.mjs +0 -2
- package/dist/simple-combobox-field-U0ZAl7VQ.cjs +0 -2
- package/dist/simple-date-field-Dj4bJoji.mjs +0 -2
- package/dist/simple-date-field-DtxEpEZj.cjs +0 -2
- package/dist/simple-email-field-BDyhkmHV.mjs +0 -2
- package/dist/simple-email-field-DK9I_Tc_.cjs +0 -2
- package/dist/simple-number-field-BspqWU-3.cjs +0 -2
- package/dist/simple-number-field-hneXMhfJ.mjs +0 -2
- package/dist/simple-password-field-BEEcleom.cjs +0 -2
- package/dist/simple-password-field-vsLRHDk4.mjs +0 -2
- package/dist/simple-radio-group-field-BURm_BSA.mjs +0 -2
- package/dist/simple-radio-group-field-Wm8xB0fN.cjs +0 -2
- package/dist/simple-select-field-8cY51U4d.mjs +0 -2
- package/dist/simple-select-field-QQ65caBf.cjs +0 -2
- package/dist/simple-text-field-BHkCPiE5.cjs +0 -2
- package/dist/simple-text-field-DK72_OA_.mjs +0 -2
- package/dist/simple-textarea-field-B72nNQHv.mjs +0 -2
- package/dist/simple-textarea-field-Bu5h7sfH.cjs +0 -2
- package/dist/switch-field-DoPUWb9d.mjs +0 -2
- package/dist/switch-field-eLVlQZSI.cjs +0 -2
- package/dist/tanstack-form-C0ab8EEl.cjs +0 -2
- package/dist/tanstack-form-C0ab8EEl.cjs.map +0 -1
- package/dist/tanstack-form-fkaBh9RO.mjs +0 -2
- package/dist/tanstack-form-fkaBh9RO.mjs.map +0 -1
- package/dist/text-editor-BtfyZSgR.cjs +0 -2
- package/dist/text-editor-BtfyZSgR.cjs.map +0 -1
- package/dist/text-editor-CtR27_PR.mjs +0 -2
- package/dist/text-editor-CtR27_PR.mjs.map +0 -1
- package/dist/text-editor-field-B6HEsAHt.mjs +0 -2
- package/dist/text-editor-field-B6HEsAHt.mjs.map +0 -1
- package/dist/text-editor-field-DcEUC0iH.cjs +0 -2
- package/dist/text-editor-field-DcEUC0iH.cjs.map +0 -1
- package/dist/text-field-Bm-k6Jr7.mjs +0 -2
- package/dist/text-field-DT50dOVI.cjs +0 -2
- package/dist/textarea-field-BhqX29CS.cjs +0 -2
- package/dist/textarea-field-DNLyrRH7.mjs +0 -2
- package/dist/title-field-B2hM3S_S.cjs +0 -2
- package/dist/title-field-oAqHRw59.mjs +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-editor-XBOAwJk-.mjs","names":["TableIcon","ImageIcon","TableIcon","Image","Underline","Table","UnderlineIcon"],"sources":["../packages/components/features/text-editor/toolbar-primitives.tsx","../packages/components/features/text-editor/link-dialog.tsx","../packages/components/features/text-editor/image-node-view.tsx","../packages/components/features/text-editor/slash-command-menu.tsx","../packages/components/features/text-editor/slash-command.ts","../packages/components/features/text-editor/text-editor.variants.ts","../packages/components/features/text-editor/image-dialog.tsx","../packages/components/features/text-editor/table-insert-menu.tsx","../packages/components/features/text-editor/toolbar.tsx","../packages/components/features/text-editor/text-editor.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\ninterface ToolbarButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n isActive?: boolean;\n}\n\nfunction ToolbarButton({ isActive, className, children, ...props }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n data-slot=\"toolbar-button\"\n data-active={isActive || undefined}\n aria-pressed={isActive === undefined ? undefined : isActive}\n className={cn(\n 'inline-flex size-8 shrink-0 items-center justify-center rounded text-sm outline-none transition-colors min-w-8',\n 'hover:bg-muted-weak hover:text-text-positive-strong',\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n 'disabled:pointer-events-none disabled:opacity-40',\n isActive && 'bg-primary-muted text-primary hover:bg-primary-muted/80 hover:text-primary',\n className\n )}\n {...props}\n >\n {children}\n </button>\n );\n}\n\nfunction ToolbarDivider() {\n return <div data-slot=\"toolbar-divider\" role=\"separator\" aria-orientation=\"vertical\" className=\"mx-0.5 h-5 w-px shrink-0 bg-border\" />;\n}\n\n// biome-ignore lint/style/useComponentExportOnlyModules: type export needed by sibling feature files\nexport type { ToolbarButtonProps };\nexport { ToolbarButton, ToolbarDivider };\n","'use client';\n\nimport { useCallback, useState } from 'react';\n\nimport { Link2, Link2Off } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\n\nimport { type Editor, useEditorState } from '@tiptap/react';\nimport { ToolbarButton } from './toolbar-primitives';\n\nexport interface LinkDialogProps {\n editor: Editor;\n}\n\nfunction LinkDialog({ editor }: LinkDialogProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [url, setUrl] = useState('');\n\n const isLinkActive = useEditorState({\n editor,\n selector: ctx => ctx.editor.isActive('link'),\n });\n\n const handleOpenChange = useCallback(\n (open: boolean) => {\n if (open) {\n const existingHref = editor.getAttributes('link').href ?? '';\n setUrl(existingHref);\n }\n setIsOpen(open);\n },\n [editor]\n );\n\n const handleApply = useCallback(() => {\n const trimmed = url.trim();\n if (!trimmed) return;\n editor.chain().focus().setLink({ href: trimmed, target: '_blank' }).run();\n setIsOpen(false);\n }, [editor, url]);\n\n const handleRemove = useCallback(() => {\n editor.chain().focus().unsetLink().run();\n setIsOpen(false);\n }, [editor]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleApply();\n }\n if (e.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n return (\n <Popover open={isOpen} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <ToolbarButton isActive={isLinkActive} aria-label=\"Liên kết\" title=\"Link (Ctrl+K)\" data-slot=\"toolbar-link-button\">\n <Link2 className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent data-slot=\"link-dialog\" sideOffset={6} align=\"start\" className=\"w-72 p-3\">\n <p className=\"mb-2 font-medium text-text-positive-strong text-xs\">{isLinkActive ? 'Edit Link' : 'Insert Link'}</p>\n <div className=\"flex gap-1.5\">\n <input\n type=\"url\"\n placeholder=\"https://example.com\"\n value={url}\n onChange={e => setUrl(e.target.value)}\n onKeyDown={handleKeyDown}\n aria-label=\"Đường dẫn liên kết\"\n autoFocus\n className={cn(\n 'flex-1 rounded border border-border bg-transparent px-2.5 py-1.5 text-sm text-text-positive',\n 'outline-none placeholder:text-text-positive-muted',\n 'focus:border-primary focus:ring-1 focus:ring-primary/30'\n )}\n />\n <button\n type=\"button\"\n onClick={handleApply}\n disabled={!url.trim()}\n className={cn(\n 'rounded bg-primary px-2.5 py-1.5 font-medium text-text-negative text-xs',\n 'transition-colors hover:bg-primary-strong',\n 'disabled:pointer-events-none disabled:opacity-50'\n )}\n >\n Apply\n </button>\n </div>\n {isLinkActive && (\n <button type=\"button\" onClick={handleRemove} className=\"mt-2 flex items-center gap-1 text-danger-strong text-xs hover:underline\">\n <Link2Off className=\"h-3 w-3\" />\n Remove link\n </button>\n )}\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { LinkDialog };\n","'use client';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport type { ReactNodeViewProps } from '@tiptap/react';\nimport { NodeViewWrapper } from '@tiptap/react';\n\n/** Best-effort short label for the caption link — falls back to the raw src for relative/unparsable URLs. */\nfunction captionLabel(src: string): string {\n try {\n return new URL(src).hostname;\n } catch {\n return src;\n }\n}\n\nfunction ImageNodeView({ node, selected }: ReactNodeViewProps) {\n const src = node.attrs.src as string;\n const alt = node.attrs.alt as string | undefined;\n\n return (\n <NodeViewWrapper data-slot=\"text-editor-image\" className=\"my-5 flex flex-col items-center\">\n <img src={src} alt={alt ?? ''} className={cn('max-w-full rounded-lg border border-border', selected && 'outline-2 outline-primary outline-offset-2')} />\n <a\n href={src}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n contentEditable={false}\n className=\"mt-1.5 text-text-positive-weak text-xs hover:text-text-positive hover:underline\"\n >\n {captionLabel(src)}\n </a>\n </NodeViewWrapper>\n );\n}\n\nexport { ImageNodeView };\n","'use client';\n\nimport { forwardRef, useEffect, useImperativeHandle, useState } from 'react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport type { SlashCommandItem } from './slash-command';\n\nexport interface SlashCommandMenuProps {\n items: SlashCommandItem[];\n command: (item: SlashCommandItem) => void;\n}\n\nexport interface SlashCommandMenuHandle {\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n}\n\nconst SlashCommandMenu = forwardRef<SlashCommandMenuHandle, SlashCommandMenuProps>(({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n useEffect(() => {\n setSelectedIndex(0);\n }, []);\n\n useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }) => {\n if (items.length === 0) return false;\n if (event.key === 'ArrowDown') {\n setSelectedIndex(i => (i + 1) % items.length);\n return true;\n }\n if (event.key === 'ArrowUp') {\n setSelectedIndex(i => (i - 1 + items.length) % items.length);\n return true;\n }\n if (event.key === 'Enter') {\n const item = items[selectedIndex];\n if (item) command(item);\n return true;\n }\n return false;\n },\n }));\n\n if (items.length === 0) {\n return (\n <div data-slot=\"slash-command-menu\" className=\"w-56 rounded-md border border-border bg-popover p-2 text-text-positive-weak text-xs shadow-dropdown\">\n Không tìm thấy lệnh phù hợp\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"slash-command-menu\"\n role=\"listbox\"\n aria-label=\"Lệnh chèn nhanh\"\n className=\"max-h-72 w-56 overflow-y-auto rounded-md border border-border bg-popover p-1 shadow-dropdown\"\n >\n {items.map((item, index) => {\n const Icon = item.icon;\n return (\n <button\n key={item.label}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n onMouseEnter={() => setSelectedIndex(index)}\n className={cn(\n 'flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm transition-colors',\n index === selectedIndex ? 'bg-primary-muted text-primary' : 'text-text-positive hover:bg-muted-muted'\n )}\n >\n <Icon className=\"h-3.5 w-3.5 shrink-0\" />\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>\n );\n});\nSlashCommandMenu.displayName = 'SlashCommandMenu';\n\nexport { SlashCommandMenu };\n","import {\n Code2,\n Heading1,\n Heading2,\n Heading3,\n Image as ImageIcon,\n List,\n ListChecks,\n ListOrdered,\n type LucideIcon,\n Minus,\n Quote,\n Table as TableIcon,\n} from 'lucide-react';\n\nimport { type Editor, Extension, type Range, ReactRenderer } from '@tiptap/react';\nimport { Suggestion } from '@tiptap/suggestion';\nimport { SlashCommandMenu, type SlashCommandMenuHandle } from './slash-command-menu';\n\nexport interface SlashCommandItem {\n label: string;\n icon: LucideIcon;\n command: (editor: Editor, range: Range) => void;\n}\n\nexport interface SlashCommandExtensionOptions {\n enableTaskList?: boolean;\n enableTable?: boolean;\n enableImage?: boolean;\n onImageUpload?: (file: File) => Promise<string>;\n}\n\nfunction buildItems(options: SlashCommandExtensionOptions): SlashCommandItem[] {\n const items: SlashCommandItem[] = [\n { label: 'Heading 1', icon: Heading1, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 1 }).run() },\n { label: 'Heading 2', icon: Heading2, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 2 }).run() },\n { label: 'Heading 3', icon: Heading3, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 3 }).run() },\n { label: 'Bullet list', icon: List, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleBulletList().run() },\n { label: 'Ordered list', icon: ListOrdered, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleOrderedList().run() },\n ];\n\n if (options.enableTaskList) {\n items.push({\n label: 'Task list',\n icon: ListChecks,\n command: (editor, range) => editor.chain().focus().deleteRange(range).toggleTaskList().run(),\n });\n }\n\n items.push(\n { label: 'Blockquote', icon: Quote, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleBlockquote().run() },\n { label: 'Code block', icon: Code2, command: (editor, range) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run() },\n { label: 'Horizontal rule', icon: Minus, command: (editor, range) => editor.chain().focus().deleteRange(range).setHorizontalRule().run() }\n );\n\n if (options.enableTable) {\n items.push({\n label: 'Table',\n icon: TableIcon,\n command: (editor, range) => editor.chain().focus().deleteRange(range).insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(),\n });\n }\n\n // Slash-triggered image insertion only makes sense when there's an upload target —\n // there's no room in this transient popup to fall back to a \"paste a URL\" flow.\n if (options.enableImage && options.onImageUpload) {\n const upload = options.onImageUpload;\n items.push({\n label: 'Image',\n icon: ImageIcon,\n command: (editor, range) => {\n editor.chain().focus().deleteRange(range).run();\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = 'image/*';\n input.onchange = () => {\n const file = input.files?.[0];\n if (!file) return;\n const alt = file.name.replace(/\\.[^./]+$/, '');\n upload(file).then(src => editor.chain().focus().setImage({ src, alt }).run());\n };\n input.click();\n },\n });\n }\n\n return items;\n}\n\n/** Builds the `/` slash-command extension, scoped to whichever features are enabled on this editor instance. */\nfunction createSlashCommandExtension(options: SlashCommandExtensionOptions) {\n return Extension.create({\n name: 'slashCommand',\n\n addProseMirrorPlugins() {\n const editor = this.editor;\n\n return [\n Suggestion<SlashCommandItem>({\n editor,\n char: '/',\n startOfLine: false,\n command: ({ editor: e, range, props }) => props.command(e, range),\n items: ({ query }) => buildItems(options).filter(item => item.label.toLowerCase().includes(query.toLowerCase())),\n render: () => {\n let component: ReactRenderer<SlashCommandMenuHandle, SlashCommandMenuProps> | undefined;\n let unmount: (() => void) | undefined;\n\n return {\n onStart: props => {\n component = new ReactRenderer(SlashCommandMenu, {\n editor: props.editor,\n props: { items: props.items, command: (item: SlashCommandItem) => props.command(item) },\n });\n unmount = props.mount(component.element);\n },\n onUpdate: props => {\n component?.updateProps({ items: props.items, command: (item: SlashCommandItem) => props.command(item) });\n },\n onKeyDown: props => {\n if (props.event.key === 'Escape') {\n unmount?.();\n return true;\n }\n return component?.ref?.onKeyDown(props) ?? false;\n },\n onExit: () => {\n unmount?.();\n component?.destroy();\n },\n };\n },\n }),\n ];\n },\n });\n}\n\ninterface SlashCommandMenuProps {\n items: SlashCommandItem[];\n command: (item: SlashCommandItem) => void;\n}\n\nexport { createSlashCommandExtension };\n","import { cva, type VariantProps } from 'class-variance-authority';\n\nexport const textEditorVariants = cva(['flex flex-col overflow-hidden rounded-md'], {\n variants: {\n variant: {\n outline: 'border border-border bg-background shadow-xs',\n ghost: 'border-0',\n },\n size: {\n sm: 'text-xs [&_.ProseMirror]:min-h-24 [&_.ProseMirror]:px-2.5 [&_.ProseMirror]:py-1.5',\n md: 'text-sm',\n lg: 'text-base [&_.ProseMirror]:min-h-56 [&_.ProseMirror]:px-4 [&_.ProseMirror]:py-3',\n },\n },\n defaultVariants: {\n variant: 'outline',\n size: 'md',\n },\n});\n\nexport type TextEditorVariantProps = VariantProps<typeof textEditorVariants>;\n","'use client';\n\nimport { useCallback, useRef, useState } from 'react';\n\nimport { ImageIcon, Upload } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport { Spinner } from '@/components/ui/spinner';\n\nimport type { Editor } from '@tiptap/react';\nimport { ToolbarButton } from './toolbar-primitives';\n\nexport interface ImageDialogProps {\n editor: Editor;\n onImageUpload?: (file: File) => Promise<string>;\n}\n\nfunction ImageDialog({ editor, onImageUpload }: ImageDialogProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [url, setUrl] = useState('');\n const [alt, setAlt] = useState('');\n const [isUploading, setIsUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const handleOpenChange = useCallback((open: boolean) => {\n if (open) {\n setUrl('');\n setAlt('');\n setError(null);\n }\n setIsOpen(open);\n }, []);\n\n const insertImage = useCallback(\n (src: string, altText: string) => {\n editor\n .chain()\n .focus()\n .setImage({ src, alt: altText || undefined })\n .run();\n setIsOpen(false);\n },\n [editor]\n );\n\n const handleApplyUrl = useCallback(() => {\n const trimmed = url.trim();\n if (!trimmed) return;\n insertImage(trimmed, alt.trim());\n }, [url, alt, insertImage]);\n\n const handleFileChange = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file || !onImageUpload) return;\n\n setError(null);\n setIsUploading(true);\n try {\n const uploadedUrl = await onImageUpload(file);\n // Falls back to the file name (no extension) when the user didn't type an alt text.\n insertImage(uploadedUrl, alt.trim() || file.name.replace(/\\.[^./]+$/, ''));\n } catch {\n setError('Tải ảnh lên thất bại. Vui lòng thử lại.');\n } finally {\n setIsUploading(false);\n }\n },\n [onImageUpload, alt, insertImage]\n );\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleApplyUrl();\n }\n if (e.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n return (\n <Popover open={isOpen} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <ToolbarButton title=\"Chèn ảnh\" aria-label=\"Chèn ảnh\" data-slot=\"toolbar-image-button\">\n <ImageIcon className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent data-slot=\"image-dialog\" sideOffset={6} align=\"start\" className=\"w-72 p-3\">\n <p className=\"mb-2 font-medium text-text-positive-strong text-xs\">Chèn ảnh</p>\n\n {onImageUpload && (\n <>\n <input ref={fileInputRef} type=\"file\" accept=\"image/*\" className=\"hidden\" onChange={handleFileChange} />\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isUploading}\n className={cn(\n 'flex w-full items-center justify-center gap-1.5 rounded border border-border border-dashed py-2 text-text-positive-weak text-xs',\n 'transition-colors hover:border-primary hover:text-primary',\n 'disabled:pointer-events-none disabled:opacity-60'\n )}\n >\n {isUploading ? (\n <>\n <Spinner className=\"size-3.5\" /> Đang tải lên…\n </>\n ) : (\n <>\n <Upload className=\"h-3.5 w-3.5\" /> Tải ảnh từ máy\n </>\n )}\n </button>\n {error && <p className=\"mt-1.5 text-danger-strong text-xs\">{error}</p>}\n <div className=\"my-2 flex items-center gap-2\">\n <div className=\"h-px flex-1 bg-border\" />\n <span className=\"text-text-positive-muted text-xs\">hoặc</span>\n <div className=\"h-px flex-1 bg-border\" />\n </div>\n </>\n )}\n\n <div className=\"flex gap-1.5\">\n <input\n type=\"url\"\n placeholder=\"https://example.com/image.png\"\n value={url}\n onChange={e => setUrl(e.target.value)}\n onKeyDown={handleKeyDown}\n aria-label=\"Đường dẫn ảnh\"\n disabled={isUploading}\n className={cn(\n 'flex-1 rounded border border-border bg-transparent px-2.5 py-1.5 text-sm text-text-positive',\n 'outline-none placeholder:text-text-positive-muted',\n 'focus:border-primary focus:ring-1 focus:ring-primary/30',\n 'disabled:pointer-events-none disabled:opacity-60'\n )}\n />\n <button\n type=\"button\"\n onClick={handleApplyUrl}\n disabled={!url.trim() || isUploading}\n className={cn(\n 'rounded bg-primary px-2.5 py-1.5 font-medium text-text-negative text-xs',\n 'transition-colors hover:bg-primary-strong',\n 'disabled:pointer-events-none disabled:opacity-50'\n )}\n >\n Chèn\n </button>\n </div>\n <input\n type=\"text\"\n placeholder=\"Mô tả ảnh (alt text) — tùy chọn\"\n value={alt}\n onChange={e => setAlt(e.target.value)}\n aria-label=\"Mô tả ảnh\"\n disabled={isUploading}\n className={cn(\n 'mt-1.5 w-full rounded border border-border bg-transparent px-2.5 py-1.5 text-sm text-text-positive',\n 'outline-none placeholder:text-text-positive-muted',\n 'focus:border-primary focus:ring-1 focus:ring-primary/30',\n 'disabled:pointer-events-none disabled:opacity-60'\n )}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { ImageDialog };\n","'use client';\n\nimport { useCallback, useState } from 'react';\n\nimport { Table as TableIcon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\n\nimport type { Editor } from '@tiptap/react';\nimport { ToolbarButton } from './toolbar-primitives';\n\nexport interface TableInsertMenuProps {\n editor: Editor;\n}\n\nconst GRID_SIZE = 6;\n\nfunction TableInsertMenu({ editor }: TableInsertMenuProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [hovered, setHovered] = useState({ rows: 0, cols: 0 });\n\n const handleInsert = useCallback(\n (rows: number, cols: number) => {\n editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();\n setIsOpen(false);\n },\n [editor]\n );\n\n return (\n <Popover\n open={isOpen}\n onOpenChange={open => {\n setIsOpen(open);\n if (!open) setHovered({ rows: 0, cols: 0 });\n }}\n >\n <PopoverTrigger asChild>\n <ToolbarButton title=\"Chèn bảng\" aria-label=\"Chèn bảng\" data-slot=\"toolbar-table-button\">\n <TableIcon className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent data-slot=\"table-insert-menu\" sideOffset={6} align=\"start\" className=\"w-auto p-3\">\n <p className=\"mb-2 font-medium text-text-positive-strong text-xs\">\n {hovered.rows > 0 ? `Bảng ${hovered.rows} × ${hovered.cols}` : 'Chọn kích thước bảng'}\n </p>\n <div className=\"grid grid-cols-6 gap-1\" onMouseLeave={() => setHovered({ rows: 0, cols: 0 })}>\n {Array.from({ length: GRID_SIZE * GRID_SIZE }, (_, i) => {\n const row = Math.floor(i / GRID_SIZE) + 1;\n const col = (i % GRID_SIZE) + 1;\n const active = row <= hovered.rows && col <= hovered.cols;\n return (\n <button\n key={`${row}-${col}`}\n type=\"button\"\n aria-label={`Bảng ${row} hàng, ${col} cột`}\n onMouseEnter={() => setHovered({ rows: row, cols: col })}\n onClick={() => handleInsert(row, col)}\n className={cn('h-4 w-4 rounded-sm border border-border', active ? 'border-primary bg-primary-muted' : 'bg-muted-muted/40')}\n />\n );\n })}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { TableInsertMenu };\n","'use client';\n\nimport {\n AlignCenter,\n AlignJustify,\n AlignLeft,\n AlignRight,\n Bold,\n ChevronDown,\n Code,\n Code2,\n Columns3,\n Highlighter,\n Italic,\n List,\n ListChecks,\n ListOrdered,\n Minus,\n Palette,\n Quote,\n Redo,\n Rows3,\n Strikethrough,\n TableCellsMerge,\n Trash2,\n Underline,\n Undo,\n} from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\n\nimport { type Editor, useEditorState } from '@tiptap/react';\nimport { ImageDialog } from './image-dialog';\nimport { LinkDialog } from './link-dialog';\nimport { TableInsertMenu } from './table-insert-menu';\nimport { ToolbarButton, ToolbarDivider } from './toolbar-primitives';\n\nconst HIGHLIGHT_COLORS = [\n { label: 'Yellow', value: '#fde047' },\n { label: 'Green', value: '#86efac' },\n { label: 'Blue', value: '#93c5fd' },\n { label: 'Pink', value: '#f9a8d4' },\n { label: 'Orange', value: '#fdba74' },\n { label: 'Purple', value: '#c4b5fd' },\n { label: 'Red', value: '#fca5a5' },\n { label: 'Cyan', value: '#67e8f9' },\n { label: 'Lime', value: '#bef264' },\n { label: 'Gray', value: '#d1d5db' },\n];\n\nconst TEXT_COLORS = [\n { label: 'Default', value: '' },\n { label: 'Red', value: '#ef4444' },\n { label: 'Orange', value: '#f97316' },\n { label: 'Yellow', value: '#eab308' },\n { label: 'Green', value: '#22c55e' },\n { label: 'Blue', value: '#3b82f6' },\n { label: 'Purple', value: '#a855f7' },\n { label: 'Pink', value: '#ec4899' },\n { label: 'Gray', value: '#6b7280' },\n { label: 'Black', value: '#000000' },\n];\n\n/** Subset of lowlight's `common` grammar bundle relevant to a blog/docs editor. */\nconst CODE_LANGUAGES = [\n { label: 'Plain text', value: 'plaintext' },\n { label: 'JavaScript', value: 'javascript' },\n { label: 'TypeScript', value: 'typescript' },\n { label: 'Python', value: 'python' },\n { label: 'Java', value: 'java' },\n { label: 'C#', value: 'csharp' },\n { label: 'C++', value: 'cpp' },\n { label: 'Go', value: 'go' },\n { label: 'Rust', value: 'rust' },\n { label: 'PHP', value: 'php' },\n { label: 'Ruby', value: 'ruby' },\n { label: 'Swift', value: 'swift' },\n { label: 'Kotlin', value: 'kotlin' },\n { label: 'SQL', value: 'sql' },\n { label: 'Bash', value: 'bash' },\n { label: 'JSON', value: 'json' },\n { label: 'YAML', value: 'yaml' },\n { label: 'CSS', value: 'css' },\n { label: 'HTML/XML', value: 'xml' },\n { label: 'Markdown', value: 'markdown' },\n];\n\nexport interface TextEditorToolbarProps {\n editor: Editor;\n className?: string;\n enableLink?: boolean;\n enableTaskList?: boolean;\n enableHighlight?: boolean;\n enableColor?: boolean;\n enableImage?: boolean;\n enableTable?: boolean;\n onImageUpload?: (file: File) => Promise<string>;\n}\n\nfunction TextEditorToolbar({\n editor,\n className,\n enableLink,\n enableTaskList,\n enableHighlight,\n enableColor,\n enableImage,\n enableTable,\n onImageUpload,\n}: TextEditorToolbarProps) {\n // Grouped into a handful of object-returning selectors (instead of one hook per\n // boolean) to cut down `useEditorState` subscription overhead on every keystroke —\n // `useEditorState` deep-compares the returned value by default, so batching related\n // flags together is safe and doesn't cause extra re-renders.\n const heading = useEditorState({\n editor,\n selector: ctx => {\n if (ctx.editor.isActive('heading', { level: 1 })) return 1;\n if (ctx.editor.isActive('heading', { level: 2 })) return 2;\n if (ctx.editor.isActive('heading', { level: 3 })) return 3;\n return null;\n },\n });\n const marks = useEditorState({\n editor,\n selector: ctx => ({\n bold: ctx.editor.isActive('bold'),\n italic: ctx.editor.isActive('italic'),\n underline: ctx.editor.isActive('underline'),\n strike: ctx.editor.isActive('strike'),\n code: ctx.editor.isActive('code'),\n }),\n });\n const blocks = useEditorState({\n editor,\n selector: ctx => ({\n bulletList: ctx.editor.isActive('bulletList'),\n orderedList: ctx.editor.isActive('orderedList'),\n taskList: ctx.editor.isActive('taskList'),\n blockquote: ctx.editor.isActive('blockquote'),\n codeBlock: ctx.editor.isActive('codeBlock'),\n codeBlockLanguage: (ctx.editor.getAttributes('codeBlock').language as string | undefined) ?? 'plaintext',\n table: ctx.editor.isActive('table'),\n }),\n });\n const align = useEditorState({\n editor,\n selector: ctx => ({\n left: ctx.editor.isActive({ textAlign: 'left' }),\n center: ctx.editor.isActive({ textAlign: 'center' }),\n right: ctx.editor.isActive({ textAlign: 'right' }),\n justify: ctx.editor.isActive({ textAlign: 'justify' }),\n }),\n });\n\n const headingLabel = heading ? (`H${heading}` as const) : 'P';\n\n return (\n <div\n data-slot=\"text-editor-toolbar\"\n role=\"toolbar\"\n aria-label=\"Định dạng văn bản\"\n className={cn('sticky top-0 z-10 flex flex-wrap items-center gap-0.5 border-border border-b bg-muted-muted p-1.5', className)}\n >\n {/* History */}\n <ToolbarButton onClick={() => editor.chain().focus().undo().run()} disabled={!editor.can().undo()} aria-label=\"Hoàn tác\" title=\"Undo (Ctrl+Z)\">\n <Undo className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().redo().run()} disabled={!editor.can().redo()} aria-label=\"Làm lại\" title=\"Redo (Ctrl+Y)\">\n <Redo className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n\n <ToolbarDivider />\n\n {/* Heading dropdown */}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"toolbar-heading-trigger\"\n aria-label=\"Kiểu đoạn văn\"\n aria-haspopup=\"menu\"\n className={cn(\n 'inline-flex h-8 items-center gap-1 rounded px-1.5 font-medium text-xs transition-colors',\n 'hover:bg-muted-muted hover:text-text-positive-strong',\n 'disabled:pointer-events-none disabled:opacity-40',\n heading !== null && 'bg-primary-muted text-primary hover:bg-primary-muted/80 hover:text-primary'\n )}\n >\n <span className=\"w-5 text-center\">{headingLabel}</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"min-w-32\">\n <DropdownMenuItem className=\"px-2 py-1.5\" onClick={() => editor.chain().focus().setParagraph().run()}>\n <span className=\"text-sm\">Paragraph</span>\n </DropdownMenuItem>\n <DropdownMenuItem className=\"px-2 py-1.5\" onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}>\n <span className=\"font-extrabold text-sm\">Heading 1</span>\n </DropdownMenuItem>\n <DropdownMenuItem className=\"px-2 py-1.5\" onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}>\n <span className=\"font-semibold text-sm\">Heading 2</span>\n </DropdownMenuItem>\n <DropdownMenuItem className=\"px-2 py-1.5\" onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}>\n <span className=\"font-medium text-sm\">Heading 3</span>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n\n <ToolbarDivider />\n\n {/* Text formatting */}\n <ToolbarButton onClick={() => editor.chain().focus().toggleBold().run()} isActive={marks.bold} aria-label=\"In đậm\" title=\"Bold (Ctrl+B)\">\n <Bold className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().toggleItalic().run()} isActive={marks.italic} aria-label=\"In nghiêng\" title=\"Italic (Ctrl+I)\">\n <Italic className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n isActive={marks.underline}\n aria-label=\"Gạch chân\"\n title=\"Underline (Ctrl+U)\"\n >\n <Underline className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().toggleStrike().run()} isActive={marks.strike} aria-label=\"Gạch ngang\" title=\"Strikethrough\">\n <Strikethrough className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().toggleCode().run()} isActive={marks.code} aria-label=\"Mã inline\" title=\"Inline Code\">\n <Code className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n\n <ToolbarDivider />\n\n {/* Lists */}\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n isActive={blocks.bulletList}\n aria-label=\"Danh sách chấm\"\n title=\"Bullet List\"\n >\n <List className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n isActive={blocks.orderedList}\n aria-label=\"Danh sách số\"\n title=\"Ordered List\"\n >\n <ListOrdered className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {enableTaskList && (\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleTaskList().run()}\n isActive={blocks.taskList}\n aria-label=\"Danh sách công việc\"\n title=\"Task List\"\n >\n <ListChecks className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n )}\n\n <ToolbarDivider />\n\n {/* Blocks */}\n <ToolbarButton onClick={() => editor.chain().focus().toggleBlockquote().run()} isActive={blocks.blockquote} aria-label=\"Trích dẫn\" title=\"Blockquote\">\n <Quote className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().toggleCodeBlock().run()} isActive={blocks.codeBlock} aria-label=\"Khối mã\" title=\"Code Block\">\n <Code2 className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {blocks.codeBlock && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"toolbar-code-language-trigger\"\n aria-label=\"Ngôn ngữ lập trình\"\n aria-haspopup=\"menu\"\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded px-1.5 text-text-positive-weak text-xs transition-colors',\n 'hover:bg-muted-muted hover:text-text-positive-strong'\n )}\n >\n <span>{CODE_LANGUAGES.find(l => l.value === blocks.codeBlockLanguage)?.label ?? 'Plain text'}</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"max-h-72 min-w-40 overflow-y-auto\">\n {CODE_LANGUAGES.map(lang => (\n <DropdownMenuItem key={lang.value} onClick={() => editor.chain().focus().updateAttributes('codeBlock', { language: lang.value }).run()}>\n <span className=\"text-sm\">{lang.label}</span>\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <ToolbarButton onClick={() => editor.chain().focus().setHorizontalRule().run()} aria-label=\"Đường kẻ ngang\" title=\"Horizontal Rule\">\n <Minus className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n\n <ToolbarDivider />\n\n {/* Text alignment */}\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('left').run()} isActive={align.left} aria-label=\"Căn trái\" title=\"Align Left\">\n <AlignLeft className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('center').run()} isActive={align.center} aria-label=\"Căn giữa\" title=\"Align Center\">\n <AlignCenter className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('right').run()} isActive={align.right} aria-label=\"Căn phải\" title=\"Align Right\">\n <AlignRight className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('justify').run()} isActive={align.justify} aria-label=\"Căn đều\" title=\"Align Justify\">\n <AlignJustify className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n\n {/* Link */}\n {enableLink && (\n <>\n <ToolbarDivider />\n <LinkDialog editor={editor} />\n </>\n )}\n\n {/* Image */}\n {enableImage && (\n <>\n <ToolbarDivider />\n <ImageDialog editor={editor} onImageUpload={onImageUpload} />\n </>\n )}\n\n {/* Table */}\n {enableTable && (\n <>\n <ToolbarDivider />\n <TableInsertMenu editor={editor} />\n {blocks.table && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <ToolbarButton title=\"Sửa bảng\" aria-label=\"Sửa bảng\">\n <TableCellsMerge className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"min-w-44\">\n <DropdownMenuItem onClick={() => editor.chain().focus().addRowBefore().run()}>\n <Rows3 className=\"mr-2 h-3.5 w-3.5\" /> Thêm hàng phía trên\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => editor.chain().focus().addRowAfter().run()}>\n <Rows3 className=\"mr-2 h-3.5 w-3.5\" /> Thêm hàng phía dưới\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => editor.chain().focus().deleteRow().run()}>\n <Trash2 className=\"mr-2 h-3.5 w-3.5\" /> Xóa hàng\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem onClick={() => editor.chain().focus().addColumnBefore().run()}>\n <Columns3 className=\"mr-2 h-3.5 w-3.5\" /> Thêm cột bên trái\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => editor.chain().focus().addColumnAfter().run()}>\n <Columns3 className=\"mr-2 h-3.5 w-3.5\" /> Thêm cột bên phải\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => editor.chain().focus().deleteColumn().run()}>\n <Trash2 className=\"mr-2 h-3.5 w-3.5\" /> Xóa cột\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem onClick={() => editor.chain().focus().deleteTable().run()} className=\"text-danger-strong\">\n <Trash2 className=\"mr-2 h-3.5 w-3.5\" /> Xóa bảng\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </>\n )}\n\n {/* Color & Highlight */}\n {(enableColor || enableHighlight) && (\n <>\n <ToolbarDivider />\n {enableColor && (\n <Popover>\n <PopoverTrigger asChild>\n <ToolbarButton title=\"Text Color\" aria-label=\"Màu chữ\">\n <Palette className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-44 p-2\">\n <p className=\"mb-1.5 font-medium text-text-positive-weak text-xs\">Text Color</p>\n <div className=\"grid grid-cols-5 gap-1\">\n {TEXT_COLORS.map(color => (\n <button\n key={color.label}\n type=\"button\"\n title={color.label}\n onClick={() => {\n if (!color.value) {\n editor.chain().focus().unsetColor().run();\n } else {\n editor.chain().focus().setColor(color.value).run();\n }\n }}\n className={cn(\n 'h-6 w-6 rounded border border-border transition-transform hover:scale-110',\n !color.value && 'bg-background text-[10px] text-text-positive leading-none'\n )}\n style={color.value ? { backgroundColor: color.value } : undefined}\n >\n {!color.value && '∅'}\n </button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n )}\n {enableHighlight && (\n <Popover>\n <PopoverTrigger asChild>\n <ToolbarButton title=\"Highlight\" aria-label=\"Tô sáng\">\n <Highlighter className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-44 p-2\">\n <p className=\"mb-1.5 font-medium text-text-positive-weak text-xs\">Highlight</p>\n <div className=\"grid grid-cols-5 gap-1\">\n {HIGHLIGHT_COLORS.map(color => (\n <button\n key={color.label}\n type=\"button\"\n title={color.label}\n onClick={() => editor.chain().focus().toggleHighlight({ color: color.value }).run()}\n className=\"h-6 w-6 rounded border border-border transition-transform hover:scale-110\"\n style={{ backgroundColor: color.value }}\n />\n ))}\n </div>\n <button\n type=\"button\"\n onClick={() => editor.chain().focus().unsetHighlight().run()}\n className=\"mt-1.5 w-full rounded border border-border py-0.5 text-center text-text-positive-weak text-xs hover:bg-muted-muted\"\n >\n Clear highlight\n </button>\n </PopoverContent>\n </Popover>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport { TextEditorToolbar };\n","'use client';\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\nimport { Bold, Italic, Strikethrough, Underline as UnderlineIcon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { CharacterCount } from '@tiptap/extension-character-count';\nimport CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';\nimport { Highlight } from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport { Link } from '@tiptap/extension-link';\nimport { ListKeymap } from '@tiptap/extension-list-keymap';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport { Table, TableCell, TableHeader, TableRow } from '@tiptap/extension-table';\nimport { TaskItem } from '@tiptap/extension-task-item';\nimport { TaskList } from '@tiptap/extension-task-list';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { Color, TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport { type AnyExtension, EditorContent, ReactNodeViewRenderer, useEditor, useEditorState } from '@tiptap/react';\nimport { BubbleMenu } from '@tiptap/react/menus';\nimport StarterKit from '@tiptap/starter-kit';\nimport DOMPurify, { type Config as SanitizeConfig } from 'dompurify';\nimport { common, createLowlight } from 'lowlight';\nimport { ImageNodeView } from './image-node-view';\nimport { LinkDialog } from './link-dialog';\nimport { createSlashCommandExtension } from './slash-command';\nimport { type TextEditorVariantProps, textEditorVariants } from './text-editor.variants';\nimport { TextEditorToolbar } from './toolbar';\nimport { ToolbarButton, ToolbarDivider } from './toolbar-primitives';\n\n// Built once at module scope — constructing the grammar registry per render would be wasteful.\nconst lowlight = createLowlight(common);\n\n// Adds a centered wrapper + small \"source\" caption link below every image — purely a\n// live-rendering affordance (both editing and `readOnly`), not persisted: the caption is\n// derived from `src` on the fly, so `getHTML()` output is unaffected (still a plain `<img>`).\nconst ImageWithCaption = Image.extend({\n addNodeView() {\n return ReactNodeViewRenderer(ImageNodeView);\n },\n});\n\n// Node types every configuration can produce, regardless of feature flags.\nconst BASE_ALLOWED_TAGS = ['p', 'h1', 'h2', 'h3', 'strong', 'em', 's', 'u', 'code', 'pre', 'blockquote', 'ul', 'ol', 'li', 'hr', 'br'];\nconst BASE_ALLOWED_ATTR = ['class', 'style'];\n\ninterface SanitizeFlags {\n enableLink?: boolean;\n enableHighlight?: boolean;\n enableColor?: boolean;\n enableTaskList?: boolean;\n enableImage?: boolean;\n enableTable?: boolean;\n}\n\n/**\n * Builds a DOMPurify allowlist scoped to exactly the node/mark types the currently\n * enabled extensions can produce — not a generic \"allow all HTML\" config.\n */\nfunction buildSanitizeConfig(flags: SanitizeFlags): SanitizeConfig {\n const tags = new Set(BASE_ALLOWED_TAGS);\n const attr = new Set(BASE_ALLOWED_ATTR);\n\n if (flags.enableLink) {\n tags.add('a');\n attr.add('href').add('target').add('rel');\n }\n if (flags.enableHighlight) {\n tags.add('mark');\n attr.add('data-color');\n }\n if (flags.enableColor) {\n tags.add('span');\n }\n if (flags.enableTaskList) {\n for (const t of ['label', 'input', 'div', 'span']) tags.add(t);\n for (const a of ['data-type', 'data-checked', 'type', 'checked', 'disabled']) attr.add(a);\n }\n if (flags.enableImage) {\n tags.add('img');\n for (const a of ['src', 'alt', 'title']) attr.add(a);\n }\n if (flags.enableTable) {\n for (const t of ['table', 'tbody', 'thead', 'tr', 'td', 'th', 'colgroup', 'col']) tags.add(t);\n for (const a of ['colspan', 'rowspan', 'colwidth']) attr.add(a);\n }\n\n return { ALLOWED_TAGS: Array.from(tags), ALLOWED_ATTR: Array.from(attr) };\n}\n\n/** No-op on the server — DOMPurify needs a DOM, and SSR content gets sanitized again on hydration anyway. */\nfunction sanitizeHtml(html: string, config: SanitizeConfig): string {\n if (typeof window === 'undefined') return html;\n return DOMPurify.sanitize(html, config);\n}\n\n/** Best-effort alt text for drag/drop/paste image inserts, which have no dialog to ask for one. */\nfunction imageAltFromFile(file: File): string {\n return file.name.replace(/\\.[^./]+$/, '');\n}\n\nexport interface TextEditorProps extends TextEditorVariantProps {\n value?: string;\n defaultValue?: string;\n onChange?: (html: string) => void;\n placeholder?: string;\n readOnly?: boolean;\n showToolbar?: boolean;\n className?: string;\n toolbarClassName?: string;\n editorClassName?: string;\n // Character count\n maxLength?: number;\n showCharacterCount?: boolean;\n onCharacterCount?: (stats: { characters: number; words: number }) => void;\n // Feature flags\n enableLink?: boolean;\n enableBubbleMenu?: boolean;\n enableTaskList?: boolean;\n enableHighlight?: boolean;\n enableColor?: boolean;\n enableImage?: boolean;\n enableTable?: boolean;\n /** Enables the `/` slash-command menu for quickly inserting blocks (headings, lists, table, image, etc.). */\n enableSlashCommand?: boolean;\n /** Called when the user picks/drops/pastes an image file. Return the URL to embed. Without it, only pasting an image URL as text works. */\n onImageUpload?: (file: File) => Promise<string>;\n}\n\nfunction TextEditor({\n value,\n defaultValue,\n onChange,\n placeholder = 'Start writing...',\n readOnly = false,\n showToolbar = true,\n variant,\n size,\n className,\n toolbarClassName,\n editorClassName,\n maxLength,\n showCharacterCount,\n onCharacterCount,\n enableLink,\n enableBubbleMenu,\n enableTaskList,\n enableHighlight,\n enableColor,\n enableImage,\n enableTable,\n enableSlashCommand,\n onImageUpload,\n}: TextEditorProps) {\n const needsCharCount = maxLength !== undefined || showCharacterCount || onCharacterCount !== undefined;\n const needsTextStyle = enableColor || enableHighlight;\n\n const sanitizeConfig = useMemo(\n () => buildSanitizeConfig({ enableLink, enableHighlight, enableColor, enableTaskList, enableImage, enableTable }),\n [enableLink, enableHighlight, enableColor, enableTaskList, enableImage, enableTable]\n );\n\n const extensions = useMemo(() => {\n const base: AnyExtension[] = [\n // `link`/`underline`/`codeBlock`/`listKeymap` are disabled here because we register\n // our own configured instances below — leaving StarterKit's defaults on causes\n // duplicate extension name warnings and the later-registered config silently wins.\n StarterKit.configure({ link: false, underline: false, codeBlock: false, listKeymap: false }),\n Underline,\n Placeholder.configure({ placeholder }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n ListKeymap,\n CodeBlockLowlight.configure({ lowlight, defaultLanguage: 'plaintext' }),\n ];\n\n if (needsTextStyle) base.push(TextStyle);\n if (enableLink) {\n base.push(Link.configure({ openOnClick: false, linkOnPaste: true, defaultProtocol: 'https' }));\n }\n if (enableTaskList) {\n base.push(TaskList, TaskItem.configure({ nested: true }));\n }\n if (enableHighlight) {\n base.push(Highlight.configure({ multicolor: true }));\n }\n if (enableColor) {\n base.push(Color);\n }\n if (enableImage) {\n // `allowBase64: false` is deliberate — base64 images would bloat the stored HTML\n // (the API column has no size cap) instead of going through `onImageUpload`.\n base.push(ImageWithCaption.configure({ inline: false, allowBase64: false }));\n }\n if (enableTable) {\n base.push(Table.configure({ resizable: true }), TableRow, TableHeader, TableCell);\n }\n if (enableSlashCommand) {\n base.push(createSlashCommandExtension({ enableTaskList, enableTable, enableImage, onImageUpload }));\n }\n if (needsCharCount) {\n base.push(CharacterCount.configure({ limit: maxLength }));\n }\n\n return base;\n }, [\n needsCharCount,\n placeholder,\n maxLength,\n enableHighlight,\n needsTextStyle,\n enableTaskList,\n enableLink,\n enableColor,\n enableImage,\n enableTable,\n enableSlashCommand,\n onImageUpload,\n ]);\n\n // Computed once at mount via lazy initializer — `content` is only read the first time\n // `useEditor` builds the editor, so there's no dependency array to keep in sync.\n const [initialContent] = useState(() => {\n const html = value ?? defaultValue;\n return html ? sanitizeHtml(html, sanitizeConfig) : html;\n });\n\n // Debounce the outward-facing `onChange` — typing itself is always instant (ProseMirror\n // manages its own state independent of React), but serializing the whole doc to HTML,\n // sanitizing it, and pushing it into (often form) state on every keystroke is what causes\n // visible lag when typing fast, especially once the doc has tables/images/code blocks.\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const emitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Flushes any pending debounced emit immediately — called on blur and on unmount so a\n // \"type the last word then instantly submit/close\" sequence never drops the final edit\n // (the debounce alone would silently discard it if it fires within the 200ms window).\n const flushPendingChange = (html: string) => {\n if (!emitTimerRef.current) return;\n clearTimeout(emitTimerRef.current);\n emitTimerRef.current = null;\n onChangeRef.current?.(sanitizeHtml(html, sanitizeConfig));\n };\n const flushRef = useRef(flushPendingChange);\n flushRef.current = flushPendingChange;\n\n const editor = useEditor({\n extensions,\n content: initialContent,\n editable: !readOnly,\n // v3 default — kept explicit so a future `useEditor` option addition doesn't\n // accidentally reintroduce a re-render on every transaction/keystroke.\n shouldRerenderOnTransaction: false,\n editorProps: {\n handleDrop: (_view, event) => {\n if (!enableImage || !onImageUpload) return false;\n const file = event.dataTransfer?.files?.[0];\n if (!file?.type.startsWith('image/')) return false;\n event.preventDefault();\n onImageUpload(file).then(src =>\n editor\n ?.chain()\n .focus()\n .setImage({ src, alt: imageAltFromFile(file) })\n .run()\n );\n return true;\n },\n handlePaste: (_view, event) => {\n if (!enableImage || !onImageUpload) return false;\n const file = Array.from(event.clipboardData?.items ?? [])\n .find(item => item.type.startsWith('image/'))\n ?.getAsFile();\n if (!file) return false;\n event.preventDefault();\n onImageUpload(file).then(src =>\n editor\n ?.chain()\n .focus()\n .setImage({ src, alt: imageAltFromFile(file) })\n .run()\n );\n return true;\n },\n },\n onUpdate: ({ editor: e }) => {\n if (onCharacterCount) {\n const storage = e.storage.characterCount;\n onCharacterCount({ characters: storage.characters(), words: storage.words() });\n }\n if (!onChangeRef.current) return;\n if (emitTimerRef.current) clearTimeout(emitTimerRef.current);\n emitTimerRef.current = setTimeout(() => flushPendingChange(e.getHTML()), 200);\n },\n onBlur: ({ editor: e }) => flushPendingChange(e.getHTML()),\n });\n\n // Unmount-only flush. Reads `flushRef`/`editor` via refs (not the effect's own closure)\n // so it always uses the latest editor instance and sanitize config, not whatever they\n // were on first render.\n const editorRef = useRef(editor);\n editorRef.current = editor;\n useEffect(\n () => () => {\n if (editorRef.current) flushRef.current(editorRef.current.getHTML());\n },\n []\n );\n\n useEffect(() => {\n if (!editor || value === undefined) return;\n const sanitized = sanitizeHtml(value, sanitizeConfig);\n if (editor.getHTML() === sanitized) return;\n editor.commands.setContent(sanitized);\n }, [editor, value, sanitizeConfig]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(!readOnly);\n }, [editor, readOnly]);\n\n const charCount = useEditorState({\n editor,\n selector: ctx => {\n const storage = ctx.editor?.storage?.characterCount;\n return storage ? (storage.characters() as number) : 0;\n },\n });\n\n const wordCount = useEditorState({\n editor,\n selector: ctx => {\n const storage = ctx.editor?.storage?.characterCount;\n return storage ? (storage.words() as number) : 0;\n },\n });\n\n const showCharCount = showCharacterCount || maxLength !== undefined;\n const isNearLimit = maxLength !== undefined && charCount > maxLength * 0.9;\n\n // Grouped for the same reason as `toolbar.tsx`'s selectors: a single deep-compared\n // object subscription instead of reading `editor.isActive()` straight in JSX, which\n // would silently go stale now that `TextEditor` itself doesn't re-render per transaction.\n const bubbleMarks = useEditorState({\n editor,\n selector: ctx =>\n ctx.editor\n ? {\n bold: ctx.editor.isActive('bold'),\n italic: ctx.editor.isActive('italic'),\n underline: ctx.editor.isActive('underline'),\n strike: ctx.editor.isActive('strike'),\n }\n : { bold: false, italic: false, underline: false, strike: false },\n });\n\n return (\n <div data-slot=\"text-editor\" className={cn('lunas-text-editor', textEditorVariants({ variant, size }), className)}>\n {showToolbar && !readOnly && editor && (\n <TextEditorToolbar\n editor={editor}\n className={toolbarClassName}\n enableLink={enableLink}\n enableTaskList={enableTaskList}\n enableHighlight={enableHighlight}\n enableColor={enableColor}\n enableImage={enableImage}\n enableTable={enableTable}\n onImageUpload={onImageUpload}\n />\n )}\n\n {enableBubbleMenu && !readOnly && editor && (\n <BubbleMenu\n editor={editor}\n options={{ placement: 'top' }}\n className={cn('z-50 flex items-center gap-0.5 rounded-md border border-border bg-popover p-1 shadow-dropdown')}\n >\n <ToolbarButton onClick={() => editor.chain().focus().toggleBold().run()} isActive={bubbleMarks.bold} aria-label=\"In đậm\" title=\"Bold (Ctrl+B)\">\n <Bold className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleItalic().run()}\n isActive={bubbleMarks.italic}\n aria-label=\"In nghiêng\"\n title=\"Italic (Ctrl+I)\"\n >\n <Italic className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n isActive={bubbleMarks.underline}\n aria-label=\"Gạch chân\"\n title=\"Underline (Ctrl+U)\"\n >\n <UnderlineIcon className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor.chain().focus().toggleStrike().run()}\n isActive={bubbleMarks.strike}\n aria-label=\"Gạch ngang\"\n title=\"Strikethrough\"\n >\n <Strikethrough className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {enableLink && (\n <>\n <ToolbarDivider />\n <LinkDialog editor={editor} />\n </>\n )}\n </BubbleMenu>\n )}\n\n {/* No hardcoded font-size here — it inherits from `textEditorVariants`'s `size` class\n on the root (sm/md/lg), which is what lets the whole typography scale in\n `text-editor.css` (headings, paragraphs, lists use `em`) actually respond to `size`. */}\n <EditorContent editor={editor} data-slot=\"text-editor-content\" className={cn('flex-1 bg-white text-text-positive', editorClassName)} />\n\n {showCharCount && needsCharCount && (\n <div\n data-slot=\"text-editor-footer\"\n className=\"flex items-center justify-between border-border border-t bg-muted-muted/30 px-3 py-1 text-text-positive-weak text-xs\"\n >\n <span>{wordCount} words</span>\n <span data-warning={isNearLimit || undefined} className=\"data-warning:text-warning-strong\">\n {charCount}\n {maxLength ? ` / ${maxLength}` : ''} characters\n </span>\n </div>\n )}\n </div>\n );\n}\n\nexport { TextEditor };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{$ as e,G as t,J as n,K as r,X as i,Y as a,Z as o,et as s,nt as c,q as l,rt as u}from"./tanstack-form-BhKAn9I0.mjs";import{Input as d}from"./ui/input.mjs";import{BanIcon as f,Loader2Icon as p,XIcon as m}from"lucide-react";import{memo as h,useCallback as g,useId as _,useRef as v}from"react";import{cn as y}from"@customafk/react-toolkit/utils";import{jsx as b,jsxs as x}from"react/jsx-runtime";import{useStore as S}from"@tanstack/react-form";const C=e=>{e.key===`Enter`&&e.preventDefault()},w=h(({label:h,description:w,placeholder:T,tooltip:E,helperText:D,counter:O=!1,orientation:k=`responsive`,showClearButton:A=!1,showErrorMessage:j=!0,required:M=!1,disabled:N=!1,maxLength:P})=>{let F=_(),I=_(),L=v(null),{form:R,name:z,state:B,handleBlur:V,handleChange:H}=u(),U=S(R.store,({isSubmitting:e})=>e),W=N||U,G=B.meta.isDirty||B.meta.isTouched,K=G&&!B.meta.isValid,q=M&&B.value===null,J=B.meta.errors.length>0,Y=A&&!W&&!!B.value,X=O?B.value?.length??0:0,Z=O&&!!P&&X>=P*.8,Q=O&&!!P&&X>=P,$=O?P?`${X} / ${P} ký tự`:`${X} ký tự`:null,ee=O||G&&j&&J,te=g(({target:{value:e}})=>{O&&P&&e.length>P||H(e||null)},[O,P,H]),ne=g(()=>{H(null),L.current?.focus()},[H]);return x(i,{className:`gap-y-4 px-4`,children:[x(t,{orientation:k,"data-invalid":K,children:[x(r,{children:[x(o,{"aria-required":q,htmlFor:F,children:[h,E&&b(c,{tooltip:E})]}),w&&b(n,{children:w})]}),x(l,{children:[b(d,{ref:L,id:F,name:z,value:B.value??``,"aria-invalid":K,"aria-describedby":I,autoComplete:`off`,placeholder:T,autoCapitalize:`none`,autoCorrect:`off`,disabled:W,className:y(`pr-6`,W&&`pointer-events-none bg-muted-muted opacity-60`),onBlur:V,onChange:te,onKeyDown:C}),Y&&b(`button`,{type:`button`,"aria-label":`Xóa`,className:`absolute inset-e-1 top-2.5 flex size-4 cursor-pointer items-center justify-center rounded-md text-text-positive-weak outline-none transition-[color,transform] hover:text-text-positive focus-visible:text-primary-strong focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform`,onClick:ne,children:b(m,{"aria-hidden":`true`})}),U&&b(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak`,children:b(p,{size:14,className:`animate-spin text-primary-strong`})}),!Y&&G&&j&&J&&b(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong`,children:b(f,{"aria-hidden":`true`,size:14})}),ee&&x(`div`,{className:`my-1 flex w-full items-start justify-between gap-x-2`,children:[G&&j?b(a,{id:I,className:`flex-1`,errors:B.meta.errors}):b(`div`,{}),O&&b(`p`,{className:y(`h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors`,Q?`font-medium text-danger-strong`:Z?`text-warning-strong`:`text-text-positive-weak`),children:$})]}),b(e,{isShow:!!D,children:D})]})]}),b(s,{})]})});w.displayName=`TextField`;export{w as TextField};
|
|
2
|
+
//# sourceMappingURL=text-field-Bx_vnc4O.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-field-Bm-k6Jr7.mjs","names":[],"sources":["../packages/components/features/tanstack-form/components/fields/text-field.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useId, useRef } from 'react';\n\nimport { useStore } from '@tanstack/react-form';\n\nimport { BanIcon, Loader2Icon, XIcon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Input } from '@/components/ui/input';\nimport {\n Field,\n FieldContent,\n FieldContentMain,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldNote,\n FieldSeparator,\n FieldTooltip,\n} from '@/components/features/tanstack-form/components/ui/field';\nimport { useTanStackFieldContext } from '../form-context';\n\nimport type { TextFieldProps as Props } from '../../types';\n\n/** Fraction of maxLength at which the counter turns warning color (80%). */\nconst COUNTER_WARNING_THRESHOLD = 0.8;\n\n/** Pixel size for inline adornment icons (error ban, loading spinner). */\nconst ADORNMENT_ICON_SIZE = 14;\n\n// Static handler — no closure deps, shared across all TextField instances\nconst onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = e => {\n if (e.key === 'Enter') e.preventDefault();\n};\n\n/**\n * A TanStack Form-connected single-line text input with label, validation, and optional features.\n *\n * Must be rendered inside an `AppField` context from `useTanStackForm`.\n * The field value type is `string | null` — `null` represents an empty/unset state.\n *\n * ---\n *\n * **Basic usage**\n * @example\n * const { AppField } = useTanStackForm({ defaultValues: { name: null } });\n *\n * <AppField name=\"name\">\n * {field => <field.TextField label=\"Full name\" placeholder=\"Nguyễn Văn A\" />}\n * </AppField>\n *\n * ---\n *\n * **With validation**\n * @example\n * <AppField\n * name=\"username\"\n * validators={{ onChange: ({ value }) => !value ? 'Bắt buộc' : undefined }}\n * >\n * {field => <field.TextField label=\"Username\" required />}\n * </AppField>\n *\n * ---\n *\n * **With character counter and max length**\n * @example\n * <AppField name=\"bio\">\n * {field => (\n * <field.TextField\n * label=\"Bio\"\n * counter\n * maxLength={160}\n * placeholder=\"Giới thiệu bản thân…\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **With clear button and helper note**\n * @example\n * <AppField name=\"search\">\n * {field => (\n * <field.TextField\n * label=\"Tìm kiếm\"\n * showClearButton\n * helperText=\"Nhập ít nhất 3 ký tự để tìm kiếm.\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **Horizontal layout**\n * @example\n * <AppField name=\"code\">\n * {field => (\n * <field.TextField\n * label=\"Mã sản phẩm\"\n * orientation=\"horizontal\"\n * tooltip=\"Mã duy nhất định danh sản phẩm trong hệ thống.\"\n * />\n * )}\n * </AppField>\n */\nexport const TextField = memo<Props>(\n ({\n label,\n description,\n placeholder,\n\n tooltip,\n helperText,\n counter = false,\n orientation = 'responsive',\n showClearButton = false,\n showErrorMessage = true,\n\n required = false,\n disabled = false,\n maxLength,\n }) => {\n const id = useId();\n const errorId = useId();\n const inputRef = useRef<HTMLInputElement>(null);\n const { form, name, state, handleBlur, handleChange } = useTanStackFieldContext<string | null>();\n\n const isSubmitting = useStore(form.store, ({ isSubmitting }) => isSubmitting);\n const isDisabled = disabled || isSubmitting;\n\n const _touched = state.meta.isDirty || state.meta.isTouched;\n const _invalid = _touched && !state.meta.isValid;\n const _isEmpty = required && state.value === null;\n const _hasErrors = state.meta.errors.length > 0;\n const _showClearButton = showClearButton && !isDisabled && !!state.value;\n\n // Counter values are only computed when the feature is active\n const _count = counter ? (state.value?.length ?? 0) : 0;\n const _isNearLimit = counter && !!maxLength && _count >= maxLength * COUNTER_WARNING_THRESHOLD;\n const _isAtLimit = counter && !!maxLength && _count >= maxLength;\n const _countText = counter ? (maxLength ? `${_count} / ${maxLength} ký tự` : `${_count} ký tự`) : null;\n\n // Skip the bottom row entirely when neither error nor counter will render\n const _showBottomRow = counter || (_touched && showErrorMessage && _hasErrors);\n\n const onChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>(\n ({ target: { value } }) => {\n if (counter && maxLength && value.length > maxLength) return;\n handleChange(value || null);\n },\n [counter, maxLength, handleChange]\n );\n\n const onClear = useCallback(() => {\n handleChange(null);\n inputRef.current?.focus();\n }, [handleChange]);\n\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field orientation={orientation} data-invalid={_invalid}>\n <FieldContent>\n <FieldLabel aria-required={_isEmpty} htmlFor={id}>\n {label}\n {tooltip && <FieldTooltip tooltip={tooltip} />}\n </FieldLabel>\n {description && <FieldDescription>{description}</FieldDescription>}\n </FieldContent>\n <FieldContentMain>\n <Input\n ref={inputRef}\n id={id}\n name={name}\n value={state.value ?? ''}\n aria-invalid={_invalid}\n aria-describedby={errorId}\n autoComplete=\"off\"\n placeholder={placeholder}\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n disabled={isDisabled}\n className={cn('pr-6', isDisabled && 'pointer-events-none bg-muted-muted opacity-60')}\n onBlur={handleBlur}\n onChange={onChange}\n onKeyDown={onKeyDown}\n />\n {_showClearButton && (\n <button\n type=\"button\"\n aria-label=\"Xóa\"\n className=\"absolute inset-e-1 top-2.5 flex size-4 cursor-pointer items-center justify-center rounded-md text-text-positive-weak outline-none transition-[color,transform] hover:text-text-positive focus-visible:text-primary-strong focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform\"\n onClick={onClear}\n >\n <XIcon aria-hidden=\"true\" />\n </button>\n )}\n {isSubmitting && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak\">\n <Loader2Icon size={ADORNMENT_ICON_SIZE} className=\"animate-spin text-primary-strong\" />\n </div>\n )}\n {!_showClearButton && _touched && showErrorMessage && _hasErrors && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong\">\n <BanIcon aria-hidden=\"true\" size={ADORNMENT_ICON_SIZE} />\n </div>\n )}\n {_showBottomRow && (\n <div className=\"my-1 flex w-full items-start justify-between gap-x-2\">\n {_touched && showErrorMessage ? <FieldError id={errorId} className=\"flex-1\" errors={state.meta.errors} /> : <div />}\n {counter && (\n <p\n className={cn(\n 'h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors',\n _isAtLimit ? 'font-medium text-danger-strong' : _isNearLimit ? 'text-warning-strong' : 'text-text-positive-weak'\n )}\n >\n {_countText}\n </p>\n )}\n </div>\n )}\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </FieldContentMain>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n }\n);\n\nTextField.displayName = 'TextField';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"text-field-Bx_vnc4O.mjs","names":[],"sources":["../packages/components/features/tanstack-form/components/fields/text-field.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useId, useRef } from 'react';\n\nimport { useStore } from '@tanstack/react-form';\n\nimport { BanIcon, Loader2Icon, XIcon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Input } from '@/components/ui/input';\nimport {\n Field,\n FieldContent,\n FieldContentMain,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldNote,\n FieldSeparator,\n FieldTooltip,\n} from '@/components/features/tanstack-form/components/ui/field';\nimport { useTanStackFieldContext } from '../form-context';\n\nimport type { TextFieldProps as Props } from '../../types';\n\n/** Fraction of maxLength at which the counter turns warning color (80%). */\nconst COUNTER_WARNING_THRESHOLD = 0.8;\n\n/** Pixel size for inline adornment icons (error ban, loading spinner). */\nconst ADORNMENT_ICON_SIZE = 14;\n\n// Static handler — no closure deps, shared across all TextField instances\nconst onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = e => {\n if (e.key === 'Enter') e.preventDefault();\n};\n\n/**\n * A TanStack Form-connected single-line text input with label, validation, and optional features.\n *\n * Must be rendered inside an `AppField` context from `useTanStackForm`.\n * The field value type is `string | null` — `null` represents an empty/unset state.\n *\n * ---\n *\n * **Basic usage**\n * @example\n * const { AppField } = useTanStackForm({ defaultValues: { name: null } });\n *\n * <AppField name=\"name\">\n * {field => <field.TextField label=\"Full name\" placeholder=\"Nguyễn Văn A\" />}\n * </AppField>\n *\n * ---\n *\n * **With validation**\n * @example\n * <AppField\n * name=\"username\"\n * validators={{ onChange: ({ value }) => !value ? 'Bắt buộc' : undefined }}\n * >\n * {field => <field.TextField label=\"Username\" required />}\n * </AppField>\n *\n * ---\n *\n * **With character counter and max length**\n * @example\n * <AppField name=\"bio\">\n * {field => (\n * <field.TextField\n * label=\"Bio\"\n * counter\n * maxLength={160}\n * placeholder=\"Giới thiệu bản thân…\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **With clear button and helper note**\n * @example\n * <AppField name=\"search\">\n * {field => (\n * <field.TextField\n * label=\"Tìm kiếm\"\n * showClearButton\n * helperText=\"Nhập ít nhất 3 ký tự để tìm kiếm.\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **Horizontal layout**\n * @example\n * <AppField name=\"code\">\n * {field => (\n * <field.TextField\n * label=\"Mã sản phẩm\"\n * orientation=\"horizontal\"\n * tooltip=\"Mã duy nhất định danh sản phẩm trong hệ thống.\"\n * />\n * )}\n * </AppField>\n */\nexport const TextField = memo<Props>(\n ({\n label,\n description,\n placeholder,\n\n tooltip,\n helperText,\n counter = false,\n orientation = 'responsive',\n showClearButton = false,\n showErrorMessage = true,\n\n required = false,\n disabled = false,\n maxLength,\n }) => {\n const id = useId();\n const errorId = useId();\n const inputRef = useRef<HTMLInputElement>(null);\n const { form, name, state, handleBlur, handleChange } = useTanStackFieldContext<string | null>();\n\n const isSubmitting = useStore(form.store, ({ isSubmitting }) => isSubmitting);\n const isDisabled = disabled || isSubmitting;\n\n const _touched = state.meta.isDirty || state.meta.isTouched;\n const _invalid = _touched && !state.meta.isValid;\n const _isEmpty = required && state.value === null;\n const _hasErrors = state.meta.errors.length > 0;\n const _showClearButton = showClearButton && !isDisabled && !!state.value;\n\n // Counter values are only computed when the feature is active\n const _count = counter ? (state.value?.length ?? 0) : 0;\n const _isNearLimit = counter && !!maxLength && _count >= maxLength * COUNTER_WARNING_THRESHOLD;\n const _isAtLimit = counter && !!maxLength && _count >= maxLength;\n const _countText = counter ? (maxLength ? `${_count} / ${maxLength} ký tự` : `${_count} ký tự`) : null;\n\n // Skip the bottom row entirely when neither error nor counter will render\n const _showBottomRow = counter || (_touched && showErrorMessage && _hasErrors);\n\n const onChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>(\n ({ target: { value } }) => {\n if (counter && maxLength && value.length > maxLength) return;\n handleChange(value || null);\n },\n [counter, maxLength, handleChange]\n );\n\n const onClear = useCallback(() => {\n handleChange(null);\n inputRef.current?.focus();\n }, [handleChange]);\n\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field orientation={orientation} data-invalid={_invalid}>\n <FieldContent>\n <FieldLabel aria-required={_isEmpty} htmlFor={id}>\n {label}\n {tooltip && <FieldTooltip tooltip={tooltip} />}\n </FieldLabel>\n {description && <FieldDescription>{description}</FieldDescription>}\n </FieldContent>\n <FieldContentMain>\n <Input\n ref={inputRef}\n id={id}\n name={name}\n value={state.value ?? ''}\n aria-invalid={_invalid}\n aria-describedby={errorId}\n autoComplete=\"off\"\n placeholder={placeholder}\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n disabled={isDisabled}\n className={cn('pr-6', isDisabled && 'pointer-events-none bg-muted-muted opacity-60')}\n onBlur={handleBlur}\n onChange={onChange}\n onKeyDown={onKeyDown}\n />\n {_showClearButton && (\n <button\n type=\"button\"\n aria-label=\"Xóa\"\n className=\"absolute inset-e-1 top-2.5 flex size-4 cursor-pointer items-center justify-center rounded-md text-text-positive-weak outline-none transition-[color,transform] hover:text-text-positive focus-visible:text-primary-strong focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform\"\n onClick={onClear}\n >\n <XIcon aria-hidden=\"true\" />\n </button>\n )}\n {isSubmitting && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak\">\n <Loader2Icon size={ADORNMENT_ICON_SIZE} className=\"animate-spin text-primary-strong\" />\n </div>\n )}\n {!_showClearButton && _touched && showErrorMessage && _hasErrors && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong\">\n <BanIcon aria-hidden=\"true\" size={ADORNMENT_ICON_SIZE} />\n </div>\n )}\n {_showBottomRow && (\n <div className=\"my-1 flex w-full items-start justify-between gap-x-2\">\n {_touched && showErrorMessage ? <FieldError id={errorId} className=\"flex-1\" errors={state.meta.errors} /> : <div />}\n {counter && (\n <p\n className={cn(\n 'h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors',\n _isAtLimit ? 'font-medium text-danger-strong' : _isNearLimit ? 'text-warning-strong' : 'text-text-positive-weak'\n )}\n >\n {_countText}\n </p>\n )}\n </div>\n )}\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </FieldContentMain>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n }\n);\n\nTextField.displayName = 'TextField';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./ui/input.cjs"),t=require("./tanstack-form-DCB2addt.cjs");let n=require("lucide-react"),r=require("react"),i=require("@customafk/react-toolkit/utils"),a=require("react/jsx-runtime"),o=require("@tanstack/react-form");const s=e=>{e.key===`Enter`&&e.preventDefault()},c=(0,r.memo)(({label:c,description:l,placeholder:u,tooltip:d,helperText:f,counter:p=!1,orientation:m=`responsive`,showClearButton:h=!1,showErrorMessage:g=!0,required:_=!1,disabled:v=!1,maxLength:y})=>{let b=(0,r.useId)(),x=(0,r.useId)(),S=(0,r.useRef)(null),{form:C,name:w,state:T,handleBlur:E,handleChange:D}=t.rt(),O=(0,o.useStore)(C.store,({isSubmitting:e})=>e),k=v||O,A=T.meta.isDirty||T.meta.isTouched,j=A&&!T.meta.isValid,M=_&&T.value===null,N=T.meta.errors.length>0,P=h&&!k&&!!T.value,F=p?T.value?.length??0:0,I=p&&!!y&&F>=y*.8,L=p&&!!y&&F>=y,R=p?y?`${F} / ${y} ký tự`:`${F} ký tự`:null,z=p||A&&g&&N,B=(0,r.useCallback)(({target:{value:e}})=>{p&&y&&e.length>y||D(e||null)},[p,y,D]),V=(0,r.useCallback)(()=>{D(null),S.current?.focus()},[D]);return(0,a.jsxs)(t.X,{className:`gap-y-4 px-4`,children:[(0,a.jsxs)(t.G,{orientation:m,"data-invalid":j,children:[(0,a.jsxs)(t.K,{children:[(0,a.jsxs)(t.Z,{"aria-required":M,htmlFor:b,children:[c,d&&(0,a.jsx)(t.nt,{tooltip:d})]}),l&&(0,a.jsx)(t.J,{children:l})]}),(0,a.jsxs)(t.q,{children:[(0,a.jsx)(e.Input,{ref:S,id:b,name:w,value:T.value??``,"aria-invalid":j,"aria-describedby":x,autoComplete:`off`,placeholder:u,autoCapitalize:`none`,autoCorrect:`off`,disabled:k,className:(0,i.cn)(`pr-6`,k&&`pointer-events-none bg-muted-muted opacity-60`),onBlur:E,onChange:B,onKeyDown:s}),P&&(0,a.jsx)(`button`,{type:`button`,"aria-label":`Xóa`,className:`absolute inset-e-1 top-2.5 flex size-4 cursor-pointer items-center justify-center rounded-md text-text-positive-weak outline-none transition-[color,transform] hover:text-text-positive focus-visible:text-primary-strong focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform`,onClick:V,children:(0,a.jsx)(n.XIcon,{"aria-hidden":`true`})}),O&&(0,a.jsx)(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak`,children:(0,a.jsx)(n.Loader2Icon,{size:14,className:`animate-spin text-primary-strong`})}),!P&&A&&g&&N&&(0,a.jsx)(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong`,children:(0,a.jsx)(n.BanIcon,{"aria-hidden":`true`,size:14})}),z&&(0,a.jsxs)(`div`,{className:`my-1 flex w-full items-start justify-between gap-x-2`,children:[A&&g?(0,a.jsx)(t.Y,{id:x,className:`flex-1`,errors:T.meta.errors}):(0,a.jsx)(`div`,{}),p&&(0,a.jsx)(`p`,{className:(0,i.cn)(`h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors`,L?`font-medium text-danger-strong`:I?`text-warning-strong`:`text-text-positive-weak`),children:R})]}),(0,a.jsx)(t.$,{isShow:!!f,children:f})]})]}),(0,a.jsx)(t.et,{})]})});c.displayName=`TextField`,exports.TextField=c;
|
|
2
|
+
//# sourceMappingURL=text-field-DMZZ6CND.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-field-DT50dOVI.cjs","names":["useTanStackFieldContext","FieldGroup","Field","FieldContent","FieldLabel","FieldTooltip","FieldDescription","FieldContentMain","Input","XIcon","Loader2Icon","BanIcon","FieldError","FieldNote","FieldSeparator"],"sources":["../packages/components/features/tanstack-form/components/fields/text-field.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useId, useRef } from 'react';\n\nimport { useStore } from '@tanstack/react-form';\n\nimport { BanIcon, Loader2Icon, XIcon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Input } from '@/components/ui/input';\nimport {\n Field,\n FieldContent,\n FieldContentMain,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldNote,\n FieldSeparator,\n FieldTooltip,\n} from '@/components/features/tanstack-form/components/ui/field';\nimport { useTanStackFieldContext } from '../form-context';\n\nimport type { TextFieldProps as Props } from '../../types';\n\n/** Fraction of maxLength at which the counter turns warning color (80%). */\nconst COUNTER_WARNING_THRESHOLD = 0.8;\n\n/** Pixel size for inline adornment icons (error ban, loading spinner). */\nconst ADORNMENT_ICON_SIZE = 14;\n\n// Static handler — no closure deps, shared across all TextField instances\nconst onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = e => {\n if (e.key === 'Enter') e.preventDefault();\n};\n\n/**\n * A TanStack Form-connected single-line text input with label, validation, and optional features.\n *\n * Must be rendered inside an `AppField` context from `useTanStackForm`.\n * The field value type is `string | null` — `null` represents an empty/unset state.\n *\n * ---\n *\n * **Basic usage**\n * @example\n * const { AppField } = useTanStackForm({ defaultValues: { name: null } });\n *\n * <AppField name=\"name\">\n * {field => <field.TextField label=\"Full name\" placeholder=\"Nguyễn Văn A\" />}\n * </AppField>\n *\n * ---\n *\n * **With validation**\n * @example\n * <AppField\n * name=\"username\"\n * validators={{ onChange: ({ value }) => !value ? 'Bắt buộc' : undefined }}\n * >\n * {field => <field.TextField label=\"Username\" required />}\n * </AppField>\n *\n * ---\n *\n * **With character counter and max length**\n * @example\n * <AppField name=\"bio\">\n * {field => (\n * <field.TextField\n * label=\"Bio\"\n * counter\n * maxLength={160}\n * placeholder=\"Giới thiệu bản thân…\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **With clear button and helper note**\n * @example\n * <AppField name=\"search\">\n * {field => (\n * <field.TextField\n * label=\"Tìm kiếm\"\n * showClearButton\n * helperText=\"Nhập ít nhất 3 ký tự để tìm kiếm.\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **Horizontal layout**\n * @example\n * <AppField name=\"code\">\n * {field => (\n * <field.TextField\n * label=\"Mã sản phẩm\"\n * orientation=\"horizontal\"\n * tooltip=\"Mã duy nhất định danh sản phẩm trong hệ thống.\"\n * />\n * )}\n * </AppField>\n */\nexport const TextField = memo<Props>(\n ({\n label,\n description,\n placeholder,\n\n tooltip,\n helperText,\n counter = false,\n orientation = 'responsive',\n showClearButton = false,\n showErrorMessage = true,\n\n required = false,\n disabled = false,\n maxLength,\n }) => {\n const id = useId();\n const errorId = useId();\n const inputRef = useRef<HTMLInputElement>(null);\n const { form, name, state, handleBlur, handleChange } = useTanStackFieldContext<string | null>();\n\n const isSubmitting = useStore(form.store, ({ isSubmitting }) => isSubmitting);\n const isDisabled = disabled || isSubmitting;\n\n const _touched = state.meta.isDirty || state.meta.isTouched;\n const _invalid = _touched && !state.meta.isValid;\n const _isEmpty = required && state.value === null;\n const _hasErrors = state.meta.errors.length > 0;\n const _showClearButton = showClearButton && !isDisabled && !!state.value;\n\n // Counter values are only computed when the feature is active\n const _count = counter ? (state.value?.length ?? 0) : 0;\n const _isNearLimit = counter && !!maxLength && _count >= maxLength * COUNTER_WARNING_THRESHOLD;\n const _isAtLimit = counter && !!maxLength && _count >= maxLength;\n const _countText = counter ? (maxLength ? `${_count} / ${maxLength} ký tự` : `${_count} ký tự`) : null;\n\n // Skip the bottom row entirely when neither error nor counter will render\n const _showBottomRow = counter || (_touched && showErrorMessage && _hasErrors);\n\n const onChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>(\n ({ target: { value } }) => {\n if (counter && maxLength && value.length > maxLength) return;\n handleChange(value || null);\n },\n [counter, maxLength, handleChange]\n );\n\n const onClear = useCallback(() => {\n handleChange(null);\n inputRef.current?.focus();\n }, [handleChange]);\n\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field orientation={orientation} data-invalid={_invalid}>\n <FieldContent>\n <FieldLabel aria-required={_isEmpty} htmlFor={id}>\n {label}\n {tooltip && <FieldTooltip tooltip={tooltip} />}\n </FieldLabel>\n {description && <FieldDescription>{description}</FieldDescription>}\n </FieldContent>\n <FieldContentMain>\n <Input\n ref={inputRef}\n id={id}\n name={name}\n value={state.value ?? ''}\n aria-invalid={_invalid}\n aria-describedby={errorId}\n autoComplete=\"off\"\n placeholder={placeholder}\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n disabled={isDisabled}\n className={cn('pr-6', isDisabled && 'pointer-events-none bg-muted-muted opacity-60')}\n onBlur={handleBlur}\n onChange={onChange}\n onKeyDown={onKeyDown}\n />\n {_showClearButton && (\n <button\n type=\"button\"\n aria-label=\"Xóa\"\n className=\"absolute inset-e-1 top-2.5 flex size-4 cursor-pointer items-center justify-center rounded-md text-text-positive-weak outline-none transition-[color,transform] hover:text-text-positive focus-visible:text-primary-strong focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform\"\n onClick={onClear}\n >\n <XIcon aria-hidden=\"true\" />\n </button>\n )}\n {isSubmitting && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak\">\n <Loader2Icon size={ADORNMENT_ICON_SIZE} className=\"animate-spin text-primary-strong\" />\n </div>\n )}\n {!_showClearButton && _touched && showErrorMessage && _hasErrors && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong\">\n <BanIcon aria-hidden=\"true\" size={ADORNMENT_ICON_SIZE} />\n </div>\n )}\n {_showBottomRow && (\n <div className=\"my-1 flex w-full items-start justify-between gap-x-2\">\n {_touched && showErrorMessage ? <FieldError id={errorId} className=\"flex-1\" errors={state.meta.errors} /> : <div />}\n {counter && (\n <p\n className={cn(\n 'h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors',\n _isAtLimit ? 'font-medium text-danger-strong' : _isNearLimit ? 'text-warning-strong' : 'text-text-positive-weak'\n )}\n >\n {_countText}\n </p>\n )}\n </div>\n )}\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </FieldContentMain>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n }\n);\n\nTextField.displayName = 'TextField';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"text-field-DMZZ6CND.cjs","names":["useTanStackFieldContext","FieldGroup","Field","FieldContent","FieldLabel","FieldTooltip","FieldDescription","FieldContentMain","Input","XIcon","Loader2Icon","BanIcon","FieldError","FieldNote","FieldSeparator"],"sources":["../packages/components/features/tanstack-form/components/fields/text-field.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useId, useRef } from 'react';\n\nimport { useStore } from '@tanstack/react-form';\n\nimport { BanIcon, Loader2Icon, XIcon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Input } from '@/components/ui/input';\nimport {\n Field,\n FieldContent,\n FieldContentMain,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldNote,\n FieldSeparator,\n FieldTooltip,\n} from '@/components/features/tanstack-form/components/ui/field';\nimport { useTanStackFieldContext } from '../form-context';\n\nimport type { TextFieldProps as Props } from '../../types';\n\n/** Fraction of maxLength at which the counter turns warning color (80%). */\nconst COUNTER_WARNING_THRESHOLD = 0.8;\n\n/** Pixel size for inline adornment icons (error ban, loading spinner). */\nconst ADORNMENT_ICON_SIZE = 14;\n\n// Static handler — no closure deps, shared across all TextField instances\nconst onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = e => {\n if (e.key === 'Enter') e.preventDefault();\n};\n\n/**\n * A TanStack Form-connected single-line text input with label, validation, and optional features.\n *\n * Must be rendered inside an `AppField` context from `useTanStackForm`.\n * The field value type is `string | null` — `null` represents an empty/unset state.\n *\n * ---\n *\n * **Basic usage**\n * @example\n * const { AppField } = useTanStackForm({ defaultValues: { name: null } });\n *\n * <AppField name=\"name\">\n * {field => <field.TextField label=\"Full name\" placeholder=\"Nguyễn Văn A\" />}\n * </AppField>\n *\n * ---\n *\n * **With validation**\n * @example\n * <AppField\n * name=\"username\"\n * validators={{ onChange: ({ value }) => !value ? 'Bắt buộc' : undefined }}\n * >\n * {field => <field.TextField label=\"Username\" required />}\n * </AppField>\n *\n * ---\n *\n * **With character counter and max length**\n * @example\n * <AppField name=\"bio\">\n * {field => (\n * <field.TextField\n * label=\"Bio\"\n * counter\n * maxLength={160}\n * placeholder=\"Giới thiệu bản thân…\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **With clear button and helper note**\n * @example\n * <AppField name=\"search\">\n * {field => (\n * <field.TextField\n * label=\"Tìm kiếm\"\n * showClearButton\n * helperText=\"Nhập ít nhất 3 ký tự để tìm kiếm.\"\n * />\n * )}\n * </AppField>\n *\n * ---\n *\n * **Horizontal layout**\n * @example\n * <AppField name=\"code\">\n * {field => (\n * <field.TextField\n * label=\"Mã sản phẩm\"\n * orientation=\"horizontal\"\n * tooltip=\"Mã duy nhất định danh sản phẩm trong hệ thống.\"\n * />\n * )}\n * </AppField>\n */\nexport const TextField = memo<Props>(\n ({\n label,\n description,\n placeholder,\n\n tooltip,\n helperText,\n counter = false,\n orientation = 'responsive',\n showClearButton = false,\n showErrorMessage = true,\n\n required = false,\n disabled = false,\n maxLength,\n }) => {\n const id = useId();\n const errorId = useId();\n const inputRef = useRef<HTMLInputElement>(null);\n const { form, name, state, handleBlur, handleChange } = useTanStackFieldContext<string | null>();\n\n const isSubmitting = useStore(form.store, ({ isSubmitting }) => isSubmitting);\n const isDisabled = disabled || isSubmitting;\n\n const _touched = state.meta.isDirty || state.meta.isTouched;\n const _invalid = _touched && !state.meta.isValid;\n const _isEmpty = required && state.value === null;\n const _hasErrors = state.meta.errors.length > 0;\n const _showClearButton = showClearButton && !isDisabled && !!state.value;\n\n // Counter values are only computed when the feature is active\n const _count = counter ? (state.value?.length ?? 0) : 0;\n const _isNearLimit = counter && !!maxLength && _count >= maxLength * COUNTER_WARNING_THRESHOLD;\n const _isAtLimit = counter && !!maxLength && _count >= maxLength;\n const _countText = counter ? (maxLength ? `${_count} / ${maxLength} ký tự` : `${_count} ký tự`) : null;\n\n // Skip the bottom row entirely when neither error nor counter will render\n const _showBottomRow = counter || (_touched && showErrorMessage && _hasErrors);\n\n const onChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>(\n ({ target: { value } }) => {\n if (counter && maxLength && value.length > maxLength) return;\n handleChange(value || null);\n },\n [counter, maxLength, handleChange]\n );\n\n const onClear = useCallback(() => {\n handleChange(null);\n inputRef.current?.focus();\n }, [handleChange]);\n\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field orientation={orientation} data-invalid={_invalid}>\n <FieldContent>\n <FieldLabel aria-required={_isEmpty} htmlFor={id}>\n {label}\n {tooltip && <FieldTooltip tooltip={tooltip} />}\n </FieldLabel>\n {description && <FieldDescription>{description}</FieldDescription>}\n </FieldContent>\n <FieldContentMain>\n <Input\n ref={inputRef}\n id={id}\n name={name}\n value={state.value ?? ''}\n aria-invalid={_invalid}\n aria-describedby={errorId}\n autoComplete=\"off\"\n placeholder={placeholder}\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n disabled={isDisabled}\n className={cn('pr-6', isDisabled && 'pointer-events-none bg-muted-muted opacity-60')}\n onBlur={handleBlur}\n onChange={onChange}\n onKeyDown={onKeyDown}\n />\n {_showClearButton && (\n <button\n type=\"button\"\n aria-label=\"Xóa\"\n className=\"absolute inset-e-1 top-2.5 flex size-4 cursor-pointer items-center justify-center rounded-md text-text-positive-weak outline-none transition-[color,transform] hover:text-text-positive focus-visible:text-primary-strong focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform\"\n onClick={onClear}\n >\n <XIcon aria-hidden=\"true\" />\n </button>\n )}\n {isSubmitting && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak\">\n <Loader2Icon size={ADORNMENT_ICON_SIZE} className=\"animate-spin text-primary-strong\" />\n </div>\n )}\n {!_showClearButton && _touched && showErrorMessage && _hasErrors && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong\">\n <BanIcon aria-hidden=\"true\" size={ADORNMENT_ICON_SIZE} />\n </div>\n )}\n {_showBottomRow && (\n <div className=\"my-1 flex w-full items-start justify-between gap-x-2\">\n {_touched && showErrorMessage ? <FieldError id={errorId} className=\"flex-1\" errors={state.meta.errors} /> : <div />}\n {counter && (\n <p\n className={cn(\n 'h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors',\n _isAtLimit ? 'font-medium text-danger-strong' : _isNearLimit ? 'text-warning-strong' : 'text-text-positive-weak'\n )}\n >\n {_countText}\n </p>\n )}\n </div>\n )}\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </FieldContentMain>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n }\n);\n\nTextField.displayName = 'TextField';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./tanstack-form-DCB2addt.cjs"),t=require("./ui/textarea.cjs");let n=require("lucide-react"),r=require("react"),i=require("@customafk/react-toolkit/utils"),a=require("react/jsx-runtime"),o=require("@tanstack/react-form");const s=(0,r.memo)(({label:s,description:c,placeholder:l,tooltip:u,helperText:d,counter:f=!1,orientation:p=`responsive`,showErrorMessage:m=!0,required:h=!1,disabled:g=!1,maxLength:_})=>{let v=(0,r.useId)(),y=(0,r.useId)(),[b,x]=(0,r.useState)(!1),S=(0,r.useRef)(null),{form:C,state:w,name:T,handleBlur:E,handleChange:D}=e.rt(),O=(0,o.useStore)(C.store,({isSubmitting:e})=>e),k=g||O,A=w.value?.length??0,j=(0,r.useMemo)(()=>f?_?`${A} / ${_} ký tự`:`${A} ký tự`:``,[A,f,_]),M=!!_&&A>=_*.8,N=!!_&&A>=_,P=w.meta.isDirty||w.meta.isTouched,F=P&&!w.meta.isValid,I=h&&w.value===null,L=!!w.meta.errors.length,R=!!w.value&&!k,z=(0,r.useMemo)(()=>(0,i.cn)(`h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors`,N?`font-medium text-danger-strong`:M?`text-warning-strong`:`text-text-positive-weak`),[N,M]),B=(0,r.useCallback)(({target:{value:e}})=>{k||f&&_&&e.length>_||D(e||null)},[k,f,_,D]),V=(0,r.useCallback)(async()=>{w.value&&(await navigator.clipboard.writeText(w.value),S.current!==null&&window.clearTimeout(S.current),x(!0),S.current=window.setTimeout(()=>x(!1),1500))},[w.value]);return(0,r.useEffect)(()=>()=>{S.current!==null&&window.clearTimeout(S.current)},[]),(0,a.jsxs)(e.X,{className:`gap-y-4 px-4`,children:[(0,a.jsxs)(e.G,{orientation:p,"data-invalid":F,children:[(0,a.jsxs)(e.K,{children:[(0,a.jsxs)(e.Z,{"aria-required":I,htmlFor:v,children:[s,u&&(0,a.jsx)(e.nt,{tooltip:u})]}),(0,a.jsx)(e.J,{children:c})]}),(0,a.jsxs)(e.q,{children:[(0,a.jsx)(t.Textarea,{id:v,name:T,value:w.value??``,"aria-invalid":F,"aria-describedby":y,autoCapitalize:`none`,autoComplete:`off`,autoCorrect:`off`,placeholder:l,disabled:k,className:(0,i.cn)(k&&`pointer-events-none bg-muted-muted opacity-60`),onChange:B,onBlur:E}),R&&(0,a.jsx)(`button`,{type:`button`,"aria-label":`Sao chép`,className:`absolute inset-e-1 top-2 flex size-4 cursor-pointer items-center justify-center rounded-md outline-none transition-[color,transform] focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform`,onClick:V,children:b?(0,a.jsx)(n.CheckIcon,{"aria-hidden":`true`,className:`text-success-strong`}):(0,a.jsx)(n.CopyIcon,{"aria-hidden":`true`,className:`text-text-positive-weak transition-colors hover:text-text-positive focus-visible:text-primary-strong`})}),O&&(0,a.jsx)(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak`,children:(0,a.jsx)(n.Loader2Icon,{size:14,className:`animate-spin text-primary-strong`})}),!R&&P&&m&&L&&(0,a.jsx)(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong`,children:(0,a.jsx)(n.BanIcon,{size:14})}),(0,a.jsxs)(`div`,{className:`my-1 flex w-full items-start justify-between gap-x-2`,children:[P&&m?(0,a.jsx)(e.Y,{id:y,className:`flex-1`,errors:w.meta.errors}):(0,a.jsx)(`div`,{}),!!f&&(0,a.jsx)(`p`,{className:z,children:j})]}),(0,a.jsx)(e.$,{isShow:!!d,children:d})]})]}),(0,a.jsx)(e.et,{})]})});s.displayName=`TextareaField`,exports.TextareaField=s;
|
|
2
|
+
//# sourceMappingURL=textarea-field-BmImMdg-.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea-field-
|
|
1
|
+
{"version":3,"file":"textarea-field-BmImMdg-.cjs","names":["useTanStackFieldContext","FieldGroup","Field","FieldContent","FieldLabel","FieldTooltip","FieldDescription","FieldContentMain","Textarea","CheckIcon","CopyIcon","Loader2Icon","BanIcon","FieldError","FieldNote","FieldSeparator"],"sources":["../packages/components/features/tanstack-form/components/fields/textarea-field.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';\n\nimport { useStore } from '@tanstack/react-form';\n\nimport { BanIcon, CheckIcon, CopyIcon, Loader2Icon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Textarea } from '@/components/ui/textarea';\n\nimport { useTanStackFieldContext } from '../form-context';\nimport {\n Field,\n FieldContent,\n FieldContentMain,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldNote,\n FieldSeparator,\n FieldTooltip,\n} from '../ui/field';\n\nimport type { TextareaFieldProps as Props } from '../../types';\n\nexport const TextareaField = memo<Props>(\n ({\n label,\n description,\n placeholder,\n\n tooltip,\n helperText,\n counter = false,\n orientation = 'responsive',\n showErrorMessage = true,\n\n required = false,\n disabled = false,\n maxLength,\n }) => {\n const id = useId();\n const errorId = useId();\n const [copied, setCopied] = useState(false);\n const copyTimeoutRef = useRef<number | null>(null);\n\n const { form, state, name, handleBlur, handleChange } = useTanStackFieldContext<string | null>();\n\n const isSubmitting = useStore(form.store, ({ isSubmitting }) => isSubmitting);\n const isDisabled = disabled || isSubmitting;\n\n const _count = state.value?.length ?? 0;\n\n const _countText = useMemo(() => {\n if (!counter) return '';\n return maxLength ? `${_count} / ${maxLength} ký tự` : `${_count} ký tự`;\n }, [_count, counter, maxLength]);\n\n const _isNearLimit = !!maxLength && _count >= maxLength * 0.8;\n const _isAtLimit = !!maxLength && _count >= maxLength;\n\n const _touched = state.meta.isDirty || state.meta.isTouched;\n const _invalid = _touched && !state.meta.isValid;\n const _isEmpty = required && state.value === null;\n const _hasErrors = !!state.meta.errors.length;\n const _showCopy = !!state.value && !isDisabled;\n\n const _counterClass = useMemo(\n () =>\n cn(\n 'h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors',\n _isAtLimit ? 'font-medium text-danger-strong' : _isNearLimit ? 'text-warning-strong' : 'text-text-positive-weak'\n ),\n [_isAtLimit, _isNearLimit]\n );\n\n const onChange = useCallback<React.ChangeEventHandler<HTMLTextAreaElement>>(\n ({ target: { value } }) => {\n if (isDisabled) return;\n if (counter && maxLength && value.length > maxLength) return;\n handleChange(value || null);\n },\n [isDisabled, counter, maxLength, handleChange]\n );\n\n const onCopy = useCallback(async () => {\n if (!state.value) return;\n await navigator.clipboard.writeText(state.value);\n if (copyTimeoutRef.current !== null) window.clearTimeout(copyTimeoutRef.current);\n setCopied(true);\n copyTimeoutRef.current = window.setTimeout(() => setCopied(false), 1500);\n }, [state.value]);\n\n useEffect(() => {\n return () => {\n if (copyTimeoutRef.current !== null) window.clearTimeout(copyTimeoutRef.current);\n };\n }, []);\n\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field orientation={orientation} data-invalid={_invalid}>\n <FieldContent>\n <FieldLabel aria-required={_isEmpty} htmlFor={id}>\n {label}\n {tooltip && <FieldTooltip tooltip={tooltip} />}\n </FieldLabel>\n <FieldDescription>{description}</FieldDescription>\n </FieldContent>\n <FieldContentMain>\n <Textarea\n id={id}\n name={name}\n value={state.value ?? ''}\n aria-invalid={_invalid}\n aria-describedby={errorId}\n autoCapitalize=\"none\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n placeholder={placeholder}\n disabled={isDisabled}\n className={cn(isDisabled && 'pointer-events-none bg-muted-muted opacity-60')}\n onChange={onChange}\n onBlur={handleBlur}\n />\n {_showCopy && (\n <button\n type=\"button\"\n aria-label=\"Sao chép\"\n className=\"absolute inset-e-1 top-2 flex size-4 cursor-pointer items-center justify-center rounded-md outline-none transition-[color,transform] focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform\"\n onClick={onCopy}\n >\n {copied ? (\n <CheckIcon aria-hidden=\"true\" className=\"text-success-strong\" />\n ) : (\n <CopyIcon\n aria-hidden=\"true\"\n className=\"text-text-positive-weak transition-colors hover:text-text-positive focus-visible:text-primary-strong\"\n />\n )}\n </button>\n )}\n {isSubmitting && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak\">\n <Loader2Icon size={14} className=\"animate-spin text-primary-strong\" />\n </div>\n )}\n {!_showCopy && _touched && showErrorMessage && _hasErrors && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong\">\n <BanIcon size={14} />\n </div>\n )}\n <div className=\"my-1 flex w-full items-start justify-between gap-x-2\">\n {_touched && showErrorMessage ? <FieldError id={errorId} className=\"flex-1\" errors={state.meta.errors} /> : <div />}\n {!!counter && <p className={_counterClass}>{_countText}</p>}\n </div>\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </FieldContentMain>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n }\n);\n\nTextareaField.displayName = 'TextareaField';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{$ as e,G as t,J as n,K as r,X as i,Y as a,Z as o,et as s,nt as c,q as l,rt as u}from"./tanstack-form-BhKAn9I0.mjs";import{Textarea as d}from"./ui/textarea.mjs";import{BanIcon as f,CheckIcon as p,CopyIcon as ee,Loader2Icon as m}from"lucide-react";import{memo as h,useCallback as g,useEffect as _,useId as v,useMemo as y,useRef as b,useState as x}from"react";import{cn as S}from"@customafk/react-toolkit/utils";import{jsx as C,jsxs as w}from"react/jsx-runtime";import{useStore as T}from"@tanstack/react-form";const E=h(({label:h,description:E,placeholder:te,tooltip:D,helperText:O,counter:k=!1,orientation:A=`responsive`,showErrorMessage:j=!0,required:M=!1,disabled:N=!1,maxLength:P})=>{let F=v(),I=v(),[L,R]=x(!1),z=b(null),{form:B,state:V,name:H,handleBlur:U,handleChange:W}=u(),G=T(B.store,({isSubmitting:e})=>e),K=N||G,q=V.value?.length??0,J=y(()=>k?P?`${q} / ${P} ký tự`:`${q} ký tự`:``,[q,k,P]),Y=!!P&&q>=P*.8,X=!!P&&q>=P,Z=V.meta.isDirty||V.meta.isTouched,Q=Z&&!V.meta.isValid,ne=M&&V.value===null,re=!!V.meta.errors.length,$=!!V.value&&!K,ie=y(()=>S(`h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors`,X?`font-medium text-danger-strong`:Y?`text-warning-strong`:`text-text-positive-weak`),[X,Y]),ae=g(({target:{value:e}})=>{K||k&&P&&e.length>P||W(e||null)},[K,k,P,W]),oe=g(async()=>{V.value&&(await navigator.clipboard.writeText(V.value),z.current!==null&&window.clearTimeout(z.current),R(!0),z.current=window.setTimeout(()=>R(!1),1500))},[V.value]);return _(()=>()=>{z.current!==null&&window.clearTimeout(z.current)},[]),w(i,{className:`gap-y-4 px-4`,children:[w(t,{orientation:A,"data-invalid":Q,children:[w(r,{children:[w(o,{"aria-required":ne,htmlFor:F,children:[h,D&&C(c,{tooltip:D})]}),C(n,{children:E})]}),w(l,{children:[C(d,{id:F,name:H,value:V.value??``,"aria-invalid":Q,"aria-describedby":I,autoCapitalize:`none`,autoComplete:`off`,autoCorrect:`off`,placeholder:te,disabled:K,className:S(K&&`pointer-events-none bg-muted-muted opacity-60`),onChange:ae,onBlur:U}),$&&C(`button`,{type:`button`,"aria-label":`Sao chép`,className:`absolute inset-e-1 top-2 flex size-4 cursor-pointer items-center justify-center rounded-md outline-none transition-[color,transform] focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform`,onClick:oe,children:L?C(p,{"aria-hidden":`true`,className:`text-success-strong`}):C(ee,{"aria-hidden":`true`,className:`text-text-positive-weak transition-colors hover:text-text-positive focus-visible:text-primary-strong`})}),G&&C(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak`,children:C(m,{size:14,className:`animate-spin text-primary-strong`})}),!$&&Z&&j&&re&&C(`div`,{className:`absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong`,children:C(f,{size:14})}),w(`div`,{className:`my-1 flex w-full items-start justify-between gap-x-2`,children:[Z&&j?C(a,{id:I,className:`flex-1`,errors:V.meta.errors}):C(`div`,{}),!!k&&C(`p`,{className:ie,children:J})]}),C(e,{isShow:!!O,children:O})]})]}),C(s,{})]})});E.displayName=`TextareaField`;export{E as TextareaField};
|
|
2
|
+
//# sourceMappingURL=textarea-field-Cim3l0y8.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea-field-
|
|
1
|
+
{"version":3,"file":"textarea-field-Cim3l0y8.mjs","names":[],"sources":["../packages/components/features/tanstack-form/components/fields/textarea-field.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';\n\nimport { useStore } from '@tanstack/react-form';\n\nimport { BanIcon, CheckIcon, CopyIcon, Loader2Icon } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Textarea } from '@/components/ui/textarea';\n\nimport { useTanStackFieldContext } from '../form-context';\nimport {\n Field,\n FieldContent,\n FieldContentMain,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldNote,\n FieldSeparator,\n FieldTooltip,\n} from '../ui/field';\n\nimport type { TextareaFieldProps as Props } from '../../types';\n\nexport const TextareaField = memo<Props>(\n ({\n label,\n description,\n placeholder,\n\n tooltip,\n helperText,\n counter = false,\n orientation = 'responsive',\n showErrorMessage = true,\n\n required = false,\n disabled = false,\n maxLength,\n }) => {\n const id = useId();\n const errorId = useId();\n const [copied, setCopied] = useState(false);\n const copyTimeoutRef = useRef<number | null>(null);\n\n const { form, state, name, handleBlur, handleChange } = useTanStackFieldContext<string | null>();\n\n const isSubmitting = useStore(form.store, ({ isSubmitting }) => isSubmitting);\n const isDisabled = disabled || isSubmitting;\n\n const _count = state.value?.length ?? 0;\n\n const _countText = useMemo(() => {\n if (!counter) return '';\n return maxLength ? `${_count} / ${maxLength} ký tự` : `${_count} ký tự`;\n }, [_count, counter, maxLength]);\n\n const _isNearLimit = !!maxLength && _count >= maxLength * 0.8;\n const _isAtLimit = !!maxLength && _count >= maxLength;\n\n const _touched = state.meta.isDirty || state.meta.isTouched;\n const _invalid = _touched && !state.meta.isValid;\n const _isEmpty = required && state.value === null;\n const _hasErrors = !!state.meta.errors.length;\n const _showCopy = !!state.value && !isDisabled;\n\n const _counterClass = useMemo(\n () =>\n cn(\n 'h-4 flex-0 text-nowrap text-end text-xs tabular-nums transition-colors',\n _isAtLimit ? 'font-medium text-danger-strong' : _isNearLimit ? 'text-warning-strong' : 'text-text-positive-weak'\n ),\n [_isAtLimit, _isNearLimit]\n );\n\n const onChange = useCallback<React.ChangeEventHandler<HTMLTextAreaElement>>(\n ({ target: { value } }) => {\n if (isDisabled) return;\n if (counter && maxLength && value.length > maxLength) return;\n handleChange(value || null);\n },\n [isDisabled, counter, maxLength, handleChange]\n );\n\n const onCopy = useCallback(async () => {\n if (!state.value) return;\n await navigator.clipboard.writeText(state.value);\n if (copyTimeoutRef.current !== null) window.clearTimeout(copyTimeoutRef.current);\n setCopied(true);\n copyTimeoutRef.current = window.setTimeout(() => setCopied(false), 1500);\n }, [state.value]);\n\n useEffect(() => {\n return () => {\n if (copyTimeoutRef.current !== null) window.clearTimeout(copyTimeoutRef.current);\n };\n }, []);\n\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field orientation={orientation} data-invalid={_invalid}>\n <FieldContent>\n <FieldLabel aria-required={_isEmpty} htmlFor={id}>\n {label}\n {tooltip && <FieldTooltip tooltip={tooltip} />}\n </FieldLabel>\n <FieldDescription>{description}</FieldDescription>\n </FieldContent>\n <FieldContentMain>\n <Textarea\n id={id}\n name={name}\n value={state.value ?? ''}\n aria-invalid={_invalid}\n aria-describedby={errorId}\n autoCapitalize=\"none\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n placeholder={placeholder}\n disabled={isDisabled}\n className={cn(isDisabled && 'pointer-events-none bg-muted-muted opacity-60')}\n onChange={onChange}\n onBlur={handleBlur}\n />\n {_showCopy && (\n <button\n type=\"button\"\n aria-label=\"Sao chép\"\n className=\"absolute inset-e-1 top-2 flex size-4 cursor-pointer items-center justify-center rounded-md outline-none transition-[color,transform] focus-visible:[&>svg]:scale-125 [&>svg]:size-3.5 [&>svg]:transition-transform\"\n onClick={onCopy}\n >\n {copied ? (\n <CheckIcon aria-hidden=\"true\" className=\"text-success-strong\" />\n ) : (\n <CopyIcon\n aria-hidden=\"true\"\n className=\"text-text-positive-weak transition-colors hover:text-text-positive focus-visible:text-primary-strong\"\n />\n )}\n </button>\n )}\n {isSubmitting && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-muted-weak\">\n <Loader2Icon size={14} className=\"animate-spin text-primary-strong\" />\n </div>\n )}\n {!_showCopy && _touched && showErrorMessage && _hasErrors && (\n <div className=\"absolute inset-e-2 inset-y-0 top-2.5 text-danger-strong\">\n <BanIcon size={14} />\n </div>\n )}\n <div className=\"my-1 flex w-full items-start justify-between gap-x-2\">\n {_touched && showErrorMessage ? <FieldError id={errorId} className=\"flex-1\" errors={state.meta.errors} /> : <div />}\n {!!counter && <p className={_counterClass}>{_countText}</p>}\n </div>\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </FieldContentMain>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n }\n);\n\nTextareaField.displayName = 'TextareaField';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{$ as e,G as t,J as n,K as r,Q as i,X as a,et as o}from"./tanstack-form-BhKAn9I0.mjs";import{jsx as s,jsxs as c}from"react/jsx-runtime";const l=({title:l,description:u,helperText:d})=>c(a,{className:`gap-y-4 px-4`,children:[c(t,{className:`gap-0`,children:[c(r,{children:[s(i,{className:`mb-1`,children:l}),s(n,{children:u})]}),s(e,{isShow:!!d,children:d})]}),s(o,{})]});export{l as TanStackTitleField};
|
|
2
|
+
//# sourceMappingURL=title-field-2dABwspv.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"title-field-
|
|
1
|
+
{"version":3,"file":"title-field-2dABwspv.mjs","names":[],"sources":["../packages/components/features/tanstack-form/components/forms/title-field.tsx"],"sourcesContent":["import { Field, FieldContent, FieldDescription, FieldGroup, FieldLegend, FieldNote, FieldSeparator } from '../ui/field';\n\n/**\n * Renders a standalone section title with an optional description and helper note, followed by a separator.\n *\n * @example\n * import { TanStackTitleField } from '@customafk/lunas-ui/features/tanstack-form';\n *\n * <TanStackTitleField\n * title=\"Billing Address\"\n * description=\"Provide the address associated with your payment method.\"\n * helperText=\"Must match your bank records.\"\n * />\n */\nexport const TanStackTitleField: React.FC<{\n /** The heading text rendered as a legend element. */\n title: string;\n /** Optional supplementary text displayed beneath the title. */\n description?: string;\n /** Optional helper note shown below the description area. */\n helperText?: string;\n}> = ({ title, description, helperText }) => {\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field className=\"gap-0\">\n <FieldContent>\n <FieldLegend className=\"mb-1\">{title}</FieldLegend>\n <FieldDescription>{description}</FieldDescription>\n </FieldContent>\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n};\n"],"mappings":"8IAcA,MAAa,GAOP,CAAE,QAAO,cAAa,gBAExB,EAAC,EAAD,CAAY,UAAU,eAAtB,SAAA,CACE,EAAC,EAAD,CAAO,UAAU,QAAjB,SAAA,CACE,EAAC,EAAD,CAAA,SAAA,CACE,EAAC,EAAD,CAAa,UAAU,OAAQ,SAAA,CAAmB,CAAA,EAClD,EAAC,EAAD,CAAA,SAAmB,CAA8B,CAAA,CACrC,CAAA,CAAA,EACd,EAAC,EAAD,CAAW,OAAQ,CAAC,CAAC,EAAa,SAAA,CAAsB,CAAA,CACnD,CACP,CAAA,EAAA,EAAC,EAAD,CAAiB,CAAA,CACP"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./tanstack-form-DCB2addt.cjs");let t=require("react/jsx-runtime");const n=({title:n,description:r,helperText:i})=>(0,t.jsxs)(e.X,{className:`gap-y-4 px-4`,children:[(0,t.jsxs)(e.G,{className:`gap-0`,children:[(0,t.jsxs)(e.K,{children:[(0,t.jsx)(e.Q,{className:`mb-1`,children:n}),(0,t.jsx)(e.J,{children:r})]}),(0,t.jsx)(e.$,{isShow:!!i,children:i})]}),(0,t.jsx)(e.et,{})]});exports.TanStackTitleField=n;
|
|
2
|
+
//# sourceMappingURL=title-field-CFnSgTGH.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"title-field-
|
|
1
|
+
{"version":3,"file":"title-field-CFnSgTGH.cjs","names":["FieldGroup","Field","FieldContent","FieldLegend","FieldDescription","FieldNote","FieldSeparator"],"sources":["../packages/components/features/tanstack-form/components/forms/title-field.tsx"],"sourcesContent":["import { Field, FieldContent, FieldDescription, FieldGroup, FieldLegend, FieldNote, FieldSeparator } from '../ui/field';\n\n/**\n * Renders a standalone section title with an optional description and helper note, followed by a separator.\n *\n * @example\n * import { TanStackTitleField } from '@customafk/lunas-ui/features/tanstack-form';\n *\n * <TanStackTitleField\n * title=\"Billing Address\"\n * description=\"Provide the address associated with your payment method.\"\n * helperText=\"Must match your bank records.\"\n * />\n */\nexport const TanStackTitleField: React.FC<{\n /** The heading text rendered as a legend element. */\n title: string;\n /** Optional supplementary text displayed beneath the title. */\n description?: string;\n /** Optional helper note shown below the description area. */\n helperText?: string;\n}> = ({ title, description, helperText }) => {\n return (\n <FieldGroup className=\"gap-y-4 px-4\">\n <Field className=\"gap-0\">\n <FieldContent>\n <FieldLegend className=\"mb-1\">{title}</FieldLegend>\n <FieldDescription>{description}</FieldDescription>\n </FieldContent>\n <FieldNote isShow={!!helperText}>{helperText}</FieldNote>\n </Field>\n <FieldSeparator />\n </FieldGroup>\n );\n};\n"],"mappings":"mFAcA,MAAa,GAOP,CAAE,QAAO,cAAa,iBAExB,EAAA,EAAA,KAAA,CAACA,EAAAA,EAAD,CAAY,UAAU,eAAtB,SAAA,EACE,EAAA,EAAA,KAAA,CAACC,EAAAA,EAAD,CAAO,UAAU,QAAjB,SAAA,EACE,EAAA,EAAA,KAAA,CAACC,EAAAA,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAACC,EAAAA,EAAD,CAAa,UAAU,OAAQ,SAAA,CAAmB,CAAA,GAClD,EAAA,EAAA,IAAA,CAACC,EAAAA,EAAD,CAAA,SAAmB,CAA8B,CAAA,CACrC,CAAA,CAAA,GACd,EAAA,EAAA,IAAA,CAACC,EAAAA,EAAD,CAAW,OAAQ,CAAC,CAAC,EAAa,SAAA,CAAsB,CAAA,CACnD,CACP,CAAA,GAAA,EAAA,EAAA,IAAA,CAACC,EAAAA,GAAD,CAAiB,CAAA,CACP"}
|
package/dist/ui/breadcrumb.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../dist-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../dist-CIdWHwhj.cjs");let t=require("lucide-react"),n=require("@customafk/react-toolkit/utils"),r=require("react/jsx-runtime");function i({className:e,...t}){return(0,r.jsx)(`nav`,{"aria-label":`breadcrumb`,"data-slot":`breadcrumb`,className:(0,n.cn)(``,e),...t})}function a({className:e,...t}){return(0,r.jsx)(`ol`,{"data-slot":`breadcrumb-list`,className:(0,n.cn)(`flex flex-wrap items-center gap-1.5 sm:gap-2.5`,`text-sm break-words`,`text-text-positive-weak`,e),...t})}function o({className:e,...t}){return(0,r.jsx)(`li`,{"data-slot":`breadcrumb-item`,className:(0,n.cn)(`inline-flex items-center gap-1.5`,e),...t})}function s({asChild:t,className:i,...a}){return(0,r.jsx)(t?e.t:`a`,{"data-slot":`breadcrumb-link`,className:(0,n.cn)(`text-text-positive-weak`,`transition-colors duration-150`,`hover:text-text-positive focus-visible:text-text-positive`,`focus-visible:ring-ring focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1`,`rounded-sm`,i),...a})}function c({className:e,...t}){return(0,r.jsx)(`span`,{"data-slot":`breadcrumb-page`,"aria-current":`page`,className:(0,n.cn)(`text-text-positive-strong font-medium`,e),...t})}function l({children:e,className:i,...a}){return(0,r.jsx)(`li`,{"data-slot":`breadcrumb-separator`,role:`presentation`,"aria-hidden":`true`,className:(0,n.cn)(`text-text-positive-muted`,`[&>svg]:size-3.5`,i),...a,children:e??(0,r.jsx)(t.ChevronRight,{})})}function u({className:e,...i}){return(0,r.jsxs)(`button`,{"data-slot":`breadcrumb-ellipsis`,type:`button`,className:(0,n.cn)(`flex items-center justify-center`,`size-8 rounded-sm`,`text-text-positive-weak`,`hover:bg-secondary-muted hover:text-text-positive`,`focus-visible:ring-ring focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1`,`transition-colors duration-150`,e),...i,children:[(0,r.jsx)(t.MoreHorizontal,{className:`size-4`}),(0,r.jsx)(`span`,{className:`sr-only`,children:`Show more breadcrumbs`})]})}exports.Breadcrumb=i,exports.BreadcrumbEllipsis=u,exports.BreadcrumbItem=o,exports.BreadcrumbLink=s,exports.BreadcrumbList=a,exports.BreadcrumbPage=c,exports.BreadcrumbSeparator=l;
|
|
2
2
|
//# sourceMappingURL=breadcrumb.cjs.map
|
package/dist/ui/button-group.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../dist-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../dist-CIdWHwhj.cjs"),t=require("./separator.cjs");let n=require("@customafk/react-toolkit/utils"),r=require("class-variance-authority"),i=require("react/jsx-runtime");const a=(0,r.cva)(`flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1`,{variants:{orientation:{horizontal:`[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none`,vertical:`flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none`}},defaultVariants:{orientation:`horizontal`}});function o({className:e,orientation:t,...r}){return(0,i.jsx)(`div`,{role:`group`,"data-slot":`button-group`,"data-orientation":t,className:(0,n.cn)(a({orientation:t}),e),...r})}function s({className:t,asChild:r=!1,...a}){return(0,i.jsx)(r?e.t:`div`,{className:(0,n.cn)(`flex items-center gap-2 rounded-md border border-border bg-muted px-4 font-medium text-sm shadow-xs [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none`,t),...a})}function c({className:e,orientation:r=`vertical`,...a}){return(0,i.jsx)(t.Separator,{"data-slot":`button-group-separator`,orientation:r,className:(0,n.cn)(`relative m-0! self-stretch bg-muted-muted data-[orientation=vertical]:h-auto`,e),...a})}exports.ButtonGroup=o,exports.ButtonGroupSeparator=c,exports.ButtonGroupText=s,exports.buttonGroupVariants=a;
|
|
2
2
|
//# sourceMappingURL=button-group.cjs.map
|
package/dist/ui/button.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../dist-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../dist-CIdWHwhj.cjs"),t=require("../button.variants-DOglUhSE.cjs");let n=require("lucide-react"),r=require("@customafk/react-toolkit/utils"),i=require("react/jsx-runtime");function a({className:a,variant:o,size:s=`default`,color:c,asChild:l=!1,isLoading:u=!1,children:d,disabled:f,type:p=`button`,innerClassName:m,...h}){let g=l?e.t:`button`,_=f||u;return(0,i.jsxs)(g,{...l?void 0:{type:p,disabled:_},"data-slot":`button`,"data-state":u?`loading`:void 0,"aria-disabled":_?!0:void 0,"aria-busy":u?!0:void 0,className:(0,r.cn)(t.n({variant:o,size:s,color:c,className:a})),...h,children:[u&&(0,i.jsxs)(`div`,{className:t.t({variant:o,color:c}),children:[(0,i.jsx)(n.Loader2Icon,{size:16,className:`animate-spin`}),(0,i.jsx)(`span`,{className:`sr-only`,children:`Loading`})]}),l?(0,i.jsx)(e.n,{children:d}):(0,i.jsx)(`div`,{className:(0,r.cn)(`inline-flex items-center justify-center gap-x-1`,u&&`invisible pointer-events-none`,m),children:d})]})}exports.Button=a;
|
|
2
2
|
//# sourceMappingURL=button.cjs.map
|