@customafk/lunas-ui 0.0.26 → 0.0.27
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/{alert-dialog-D-oR0YW3.cjs → alert-dialog-26JyobJb.cjs} +1 -1
- package/dist/{alert-dialog-D-oR0YW3.cjs.map → alert-dialog-26JyobJb.cjs.map} +1 -1
- package/dist/{alert-dialog-CNHyP_A9.js → alert-dialog-Dv0cgymS.js} +1 -1
- package/dist/{alert-dialog-CNHyP_A9.js.map → alert-dialog-Dv0cgymS.js.map} +1 -1
- package/dist/{button-BmBtMriU.d.ts → button-CPBLm_02.d.ts} +5 -5
- package/dist/{button-tCnJHt8O.d.cts → button-ghXW1WTr.d.cts} +5 -5
- package/dist/{calendar-CbuOOMpK.js → calendar-B66UT_fu.js} +1 -1
- package/dist/{calendar-CbuOOMpK.js.map → calendar-B66UT_fu.js.map} +1 -1
- package/dist/{calendar-B8IKzrdN.cjs → calendar-DXegnGRJ.cjs} +1 -1
- package/dist/{calendar-B8IKzrdN.cjs.map → calendar-DXegnGRJ.cjs.map} +1 -1
- package/dist/{command-Dr2XmKV6.d.cts → command-CE_Qcgo7.d.ts} +12 -12
- package/dist/{command-Dvq-HZwH.d.ts → command-CiEjoEnP.d.cts} +12 -12
- package/dist/{command-TFZwJmux.js → command-Dbkg10QA.js} +2 -2
- package/dist/{command-TFZwJmux.js.map → command-Dbkg10QA.js.map} +1 -1
- package/dist/{command-CoB5R5FI.cjs → command-IL3ogESu.cjs} +2 -2
- package/dist/{command-CoB5R5FI.cjs.map → command-IL3ogESu.cjs.map} +1 -1
- package/dist/data-display/empty.d.cts +2 -2
- package/dist/data-display/statistic.d.cts +2 -2
- package/dist/data-display/statistic.d.ts +2 -2
- package/dist/{dialog-CSDDiHKO.js → dialog-BEdMA5Lp.js} +1 -1
- package/dist/{dialog-CSDDiHKO.js.map → dialog-BEdMA5Lp.js.map} +1 -1
- package/dist/{dialog-C-DYoapJ.d.ts → dialog-Bw_T5aco.d.cts} +12 -12
- package/dist/{dialog-_Cgij9NP.cjs → dialog-C4oHnJNN.cjs} +1 -1
- package/dist/{dialog-_Cgij9NP.cjs.map → dialog-C4oHnJNN.cjs.map} +1 -1
- package/dist/{dialog-CA0E_tNX.d.cts → dialog-CoH1pTBH.d.ts} +12 -12
- package/dist/dialogs/confirm-dialog.cjs +1 -1
- package/dist/dialogs/confirm-dialog.js +1 -1
- package/dist/dialogs/detail-dialog/component/sidebar.d.cts +4 -4
- package/dist/dialogs/detail-dialog/component/sidebar.d.ts +28 -28
- package/dist/dialogs/detail-dialog/index.cjs +1 -1
- package/dist/dialogs/detail-dialog/index.js +1 -1
- package/dist/dialogs/error-dialog.cjs +1 -1
- package/dist/dialogs/error-dialog.js +1 -1
- package/dist/dialogs/form-dialog.cjs +1 -1
- package/dist/dialogs/form-dialog.d.cts +2 -2
- package/dist/dialogs/form-dialog.d.ts +2 -2
- package/dist/dialogs/form-dialog.js +1 -1
- package/dist/dialogs/loading-dialog.cjs +1 -1
- package/dist/dialogs/loading-dialog.js +1 -1
- package/dist/{error-dialog-D5R5le-W.cjs → error-dialog-C0PDn-pj.cjs} +2 -2
- package/dist/{error-dialog-D5R5le-W.cjs.map → error-dialog-C0PDn-pj.cjs.map} +1 -1
- package/dist/{error-dialog-Q99thrSD.js → error-dialog-CCdQ8zbQ.js} +2 -2
- package/dist/{error-dialog-Q99thrSD.js.map → error-dialog-CCdQ8zbQ.js.map} +1 -1
- package/dist/{form-wrapper-CY-stRaQ.cjs → form-wrapper-Cd8c5MIL.cjs} +2 -2
- package/dist/{form-wrapper-CY-stRaQ.cjs.map → form-wrapper-Cd8c5MIL.cjs.map} +1 -1
- package/dist/{form-wrapper-C2xlvWx0.js → form-wrapper-DntbFqN1.js} +2 -2
- package/dist/{form-wrapper-C2xlvWx0.js.map → form-wrapper-DntbFqN1.js.map} +1 -1
- package/dist/forms/combobox-field.cjs +1 -1
- package/dist/forms/combobox-field.d.cts +2 -2
- package/dist/forms/combobox-field.d.ts +2 -2
- package/dist/forms/combobox-field.js +1 -1
- package/dist/forms/date-field.cjs +1 -1
- package/dist/forms/date-field.d.cts +2 -2
- package/dist/forms/date-field.d.ts +2 -2
- package/dist/forms/date-field.js +1 -1
- package/dist/forms/form-wrapper.cjs +1 -1
- package/dist/forms/form-wrapper.d.cts +2 -2
- package/dist/forms/form-wrapper.d.ts +2 -2
- package/dist/forms/form-wrapper.js +1 -1
- package/dist/forms/multi-select-field.cjs +1 -1
- package/dist/forms/multi-select-field.cjs.map +1 -1
- package/dist/forms/multi-select-field.d.cts +2 -2
- package/dist/forms/multi-select-field.d.ts +2 -2
- package/dist/forms/multi-select-field.js +1 -1
- package/dist/forms/number-field.d.cts +2 -2
- package/dist/forms/number-field.d.ts +2 -2
- package/dist/forms/password-field.d.cts +2 -2
- package/dist/forms/password-field.d.ts +2 -2
- package/dist/forms/select-field.d.cts +2 -2
- package/dist/forms/select-field.d.ts +2 -2
- package/dist/forms/switch-field.d.cts +2 -2
- package/dist/forms/switch-field.d.ts +2 -2
- package/dist/forms/text-field.d.cts +2 -2
- package/dist/forms/text-field.d.ts +2 -2
- package/dist/forms/textarea-field.d.cts +2 -2
- package/dist/forms/textarea-field.d.ts +2 -2
- package/dist/{input-CjNcnHb_.d.ts → input-CUDclgoU.d.ts} +3 -3
- package/dist/{input-BsXm0_Iq.d.cts → input-y_n-XGys.d.cts} +3 -3
- package/dist/layouts/app-layout/index.d.cts +5 -5
- package/dist/layouts/app-layout/index.d.ts +3 -3
- package/dist/layouts/flex.d.cts +4 -4
- package/dist/layouts/flex.d.ts +4 -4
- package/dist/layouts/main/index.d.cts +4 -4
- package/dist/layouts/main/index.d.ts +4 -4
- package/dist/{multi-select-Cjvm4tK5.cjs → multi-select-3yxD9gbY.cjs} +2 -2
- package/dist/{multi-select-Cjvm4tK5.cjs.map → multi-select-3yxD9gbY.cjs.map} +1 -1
- package/dist/{multi-select-D0bZVOnm.js → multi-select-Cb07NlA7.js} +2 -2
- package/dist/{multi-select-D0bZVOnm.js.map → multi-select-Cb07NlA7.js.map} +1 -1
- package/dist/{popover-CTFp7ZXi.js → popover-CqAD7HNo.js} +1 -1
- package/dist/{popover-CTFp7ZXi.js.map → popover-CqAD7HNo.js.map} +1 -1
- package/dist/{popover-DCcPvdz2.cjs → popover-Dd0acCEk.cjs} +1 -1
- package/dist/{popover-DCcPvdz2.cjs.map → popover-Dd0acCEk.cjs.map} +1 -1
- package/dist/{separator-5FdzRGaJ.d.ts → separator-BLYi41aC.d.ts} +3 -3
- package/dist/{separator-DpxrpK_H.d.cts → separator-ClGBPiYE.d.cts} +3 -3
- package/dist/table/index.d.cts +2 -2
- package/dist/table/index.d.ts +2 -2
- package/dist/{toggle-Cx9H5u3p.d.cts → toggle-BWQWsKMA.d.ts} +4 -4
- package/dist/{toggle-DH9IssyE.d.ts → toggle-DnGvFJh8.d.cts} +4 -4
- package/dist/{tooltip-Djh_dcy5.d.ts → tooltip-Bk8VwHdM.d.ts} +6 -6
- package/dist/{tooltip-BAtWdJyX.d.cts → tooltip-cisSpCHG.d.cts} +6 -6
- package/dist/typography/paragraph.d.cts +2 -2
- package/dist/typography/paragraph.d.ts +2 -2
- package/dist/typography/title.d.cts +2 -2
- package/dist/typography/title.d.ts +2 -2
- package/dist/ui/alert-dialog.cjs +1 -1
- package/dist/ui/alert-dialog.d.cts +12 -12
- package/dist/ui/alert-dialog.d.ts +12 -12
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/alert.d.cts +6 -6
- package/dist/ui/alert.d.ts +6 -6
- package/dist/ui/aspect-ratio.d.cts +2 -2
- package/dist/ui/aspect-ratio.d.ts +2 -2
- package/dist/ui/avatar.d.cts +4 -4
- package/dist/ui/avatar.d.ts +4 -4
- package/dist/ui/badge.d.cts +2 -2
- package/dist/ui/badge.d.ts +4 -4
- package/dist/ui/breadcrumb.d.cts +8 -8
- package/dist/ui/breadcrumb.d.ts +8 -8
- package/dist/ui/button.d.cts +1 -1
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/calendar.cjs +1 -1
- package/dist/ui/calendar.d.cts +4 -4
- package/dist/ui/calendar.d.ts +4 -4
- package/dist/ui/calendar.js +1 -1
- package/dist/ui/card.d.cts +8 -8
- package/dist/ui/card.d.ts +8 -8
- package/dist/ui/carousel.d.cts +7 -7
- package/dist/ui/carousel.d.ts +7 -7
- package/dist/ui/collapsible.d.cts +4 -4
- package/dist/ui/collapsible.d.ts +4 -4
- package/dist/ui/command.cjs +1 -1
- package/dist/ui/command.d.cts +2 -2
- package/dist/ui/command.d.ts +2 -2
- package/dist/ui/command.js +1 -1
- package/dist/ui/context-menu.d.cts +16 -16
- package/dist/ui/context-menu.d.ts +16 -16
- package/dist/ui/dialog.cjs +1 -1
- package/dist/ui/dialog.d.cts +1 -1
- package/dist/ui/dialog.d.ts +1 -1
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dropdown-menu.d.cts +16 -16
- package/dist/ui/dropdown-menu.d.ts +16 -16
- package/dist/ui/file-uploader.d.cts +2 -2
- package/dist/ui/file-uploader.d.ts +2 -2
- package/dist/ui/form.d.cts +7 -7
- package/dist/ui/form.d.ts +7 -7
- package/dist/ui/hover-card.d.cts +4 -4
- package/dist/ui/hover-card.d.ts +4 -4
- package/dist/ui/input-otp.d.cts +5 -5
- package/dist/ui/input-otp.d.ts +5 -5
- package/dist/ui/input.d.cts +1 -1
- package/dist/ui/input.d.ts +1 -1
- package/dist/ui/inputs/search-input.d.cts +1 -1
- package/dist/ui/inputs/search-input.d.ts +3 -3
- package/dist/ui/label.d.cts +2 -2
- package/dist/ui/label.d.ts +2 -2
- package/dist/ui/menubar.d.cts +17 -17
- package/dist/ui/menubar.d.ts +17 -17
- package/dist/ui/multi-select.cjs +1 -1
- package/dist/ui/multi-select.d.cts +2 -2
- package/dist/ui/multi-select.d.ts +2 -2
- package/dist/ui/multi-select.js +1 -1
- package/dist/ui/navigation-menu.d.cts +11 -11
- package/dist/ui/navigation-menu.d.ts +9 -9
- package/dist/ui/pagination.d.cts +9 -9
- package/dist/ui/pagination.d.ts +9 -9
- package/dist/ui/popover.cjs +1 -1
- package/dist/ui/popover.d.cts +5 -5
- package/dist/ui/popover.d.ts +5 -5
- package/dist/ui/popover.js +1 -1
- package/dist/ui/progress.d.cts +2 -2
- package/dist/ui/progress.d.ts +2 -2
- package/dist/ui/radio-group.d.cts +3 -3
- package/dist/ui/radio-group.d.ts +3 -3
- package/dist/ui/resizable.d.cts +4 -4
- package/dist/ui/resizable.d.ts +4 -4
- package/dist/ui/scroll-area.d.ts +3 -3
- package/dist/ui/select.d.cts +11 -11
- package/dist/ui/select.d.ts +11 -11
- package/dist/ui/separator.d.cts +1 -1
- package/dist/ui/separator.d.ts +1 -1
- package/dist/ui/sheet.d.cts +9 -9
- package/dist/ui/sheet.d.ts +9 -9
- package/dist/ui/sidebar.d.cts +30 -30
- package/dist/ui/sidebar.d.ts +30 -30
- package/dist/ui/skeleton.d.cts +2 -2
- package/dist/ui/skeleton.d.ts +2 -2
- package/dist/ui/slider.d.cts +2 -2
- package/dist/ui/slider.d.ts +2 -2
- package/dist/ui/sonner.d.cts +2 -2
- package/dist/ui/sonner.d.ts +2 -2
- package/dist/ui/switch.d.cts +2 -2
- package/dist/ui/switch.d.ts +2 -2
- package/dist/ui/table.d.cts +9 -9
- package/dist/ui/table.d.ts +9 -9
- package/dist/ui/tabs.d.cts +5 -5
- package/dist/ui/tabs.d.ts +5 -5
- package/dist/ui/textarea.d.cts +2 -2
- package/dist/ui/textarea.d.ts +2 -2
- package/dist/ui/toggle-group.d.cts +4 -4
- package/dist/ui/toggle-group.d.ts +4 -4
- package/dist/ui/toggle.d.cts +1 -1
- package/dist/ui/toggle.d.ts +1 -1
- package/dist/ui/tooltip.d.cts +1 -1
- package/dist/ui/tooltip.d.ts +1 -1
- package/package.json +2 -2
- package/styles/base.css +29 -0
- package/styles/loader.css +113 -0
- package/styles/theme.css +104 -0
- package/styles/typography.css +6 -0
- package/packages/components/cards/simple-card.tsx +0 -23
- package/packages/components/data-display/country.tsx +0 -53
- package/packages/components/data-display/data-list.tsx +0 -29
- package/packages/components/data-display/date-tooltip.tsx +0 -23
- package/packages/components/data-display/date.tsx +0 -157
- package/packages/components/data-display/empty.tsx +0 -13
- package/packages/components/data-display/name.tsx +0 -23
- package/packages/components/data-display/phone-number.tsx +0 -14
- package/packages/components/data-display/role-badge.tsx +0 -29
- package/packages/components/data-display/statistic.tsx +0 -139
- package/packages/components/dialogs/confirm-dialog.tsx +0 -44
- package/packages/components/dialogs/detail-dialog/component/header.tsx +0 -10
- package/packages/components/dialogs/detail-dialog/component/main.tsx +0 -35
- package/packages/components/dialogs/detail-dialog/component/sidebar.tsx +0 -618
- package/packages/components/dialogs/detail-dialog/component/wrapper.tsx +0 -15
- package/packages/components/dialogs/detail-dialog/index.tsx +0 -91
- package/packages/components/dialogs/error-dialog.tsx +0 -34
- package/packages/components/dialogs/form-dialog.tsx +0 -74
- package/packages/components/dialogs/loading-dialog.tsx +0 -20
- package/packages/components/forms/combobox-field.tsx +0 -104
- package/packages/components/forms/date-field.tsx +0 -62
- package/packages/components/forms/form-wrapper.tsx +0 -120
- package/packages/components/forms/multi-select-field.tsx +0 -56
- package/packages/components/forms/number-field.tsx +0 -54
- package/packages/components/forms/password-field.tsx +0 -38
- package/packages/components/forms/select-field.tsx +0 -58
- package/packages/components/forms/switch-field.tsx +0 -40
- package/packages/components/forms/text-field.tsx +0 -73
- package/packages/components/forms/textarea-field.tsx +0 -45
- package/packages/components/index.tsx +0 -0
- package/packages/components/layouts/app-layout/index.tsx +0 -74
- package/packages/components/layouts/app-layout/sidebar.tsx +0 -663
- package/packages/components/layouts/flex.tsx +0 -99
- package/packages/components/layouts/main/content.tsx +0 -11
- package/packages/components/layouts/main/header.tsx +0 -41
- package/packages/components/layouts/main/index.tsx +0 -3
- package/packages/components/layouts/main/wrapper.tsx +0 -5
- package/packages/components/table/data-table-pagination.tsx +0 -70
- package/packages/components/table/data-table.tsx +0 -241
- package/packages/components/table/index.tsx +0 -107
- package/packages/components/typography/paragraph.tsx +0 -29
- package/packages/components/typography/title.tsx +0 -31
- package/packages/components/ui/alert-dialog.tsx +0 -99
- package/packages/components/ui/alert.tsx +0 -44
- package/packages/components/ui/aspect-ratio.tsx +0 -11
- package/packages/components/ui/avatar.tsx +0 -25
- package/packages/components/ui/badge.tsx +0 -57
- package/packages/components/ui/breadcrumb.tsx +0 -74
- package/packages/components/ui/button.tsx +0 -205
- package/packages/components/ui/buttons/add-new.tsx +0 -19
- package/packages/components/ui/buttons/edit.tsx +0 -14
- package/packages/components/ui/buttons/refresh.tsx +0 -19
- package/packages/components/ui/buttons/trash.tsx +0 -14
- package/packages/components/ui/buttons/upload-image.tsx +0 -25
- package/packages/components/ui/calendar.tsx +0 -141
- package/packages/components/ui/card.tsx +0 -44
- package/packages/components/ui/carousel.tsx +0 -187
- package/packages/components/ui/checkbox.tsx +0 -34
- package/packages/components/ui/collapsible.tsx +0 -17
- package/packages/components/ui/command.tsx +0 -138
- package/packages/components/ui/context-menu.tsx +0 -196
- package/packages/components/ui/dialog.tsx +0 -119
- package/packages/components/ui/dropdown-menu.tsx +0 -249
- package/packages/components/ui/file-uploader.tsx +0 -314
- package/packages/components/ui/form.tsx +0 -104
- package/packages/components/ui/hover-card.tsx +0 -32
- package/packages/components/ui/input-otp.tsx +0 -67
- package/packages/components/ui/input.tsx +0 -59
- package/packages/components/ui/inputs/number-input.tsx +0 -317
- package/packages/components/ui/inputs/password-input.tsx +0 -29
- package/packages/components/ui/inputs/search-input.tsx +0 -24
- package/packages/components/ui/label.tsx +0 -26
- package/packages/components/ui/menubar.tsx +0 -212
- package/packages/components/ui/multi-select.tsx +0 -626
- package/packages/components/ui/navigation-menu.tsx +0 -124
- package/packages/components/ui/pagination.tsx +0 -70
- package/packages/components/ui/popover.tsx +0 -49
- package/packages/components/ui/progress.tsx +0 -19
- package/packages/components/ui/radio-group.tsx +0 -36
- package/packages/components/ui/resizable.tsx +0 -47
- package/packages/components/ui/scroll-area.tsx +0 -35
- package/packages/components/ui/select.tsx +0 -199
- package/packages/components/ui/separator.tsx +0 -25
- package/packages/components/ui/sheet.tsx +0 -119
- package/packages/components/ui/sidebar.tsx +0 -675
- package/packages/components/ui/skeleton.tsx +0 -8
- package/packages/components/ui/slider.tsx +0 -45
- package/packages/components/ui/sonner.tsx +0 -40
- package/packages/components/ui/switch.tsx +0 -46
- package/packages/components/ui/table.tsx +0 -66
- package/packages/components/ui/tabs.tsx +0 -53
- package/packages/components/ui/textarea.tsx +0 -46
- package/packages/components/ui/toggle-group.tsx +0 -63
- package/packages/components/ui/toggle.tsx +0 -34
- package/packages/components/ui/tooltip.tsx +0 -53
- package/packages/constants.tsx +0 -45
- package/packages/hooks/use-callback-ref.ts +0 -22
- package/packages/hooks/use-controllable-state.ts +0 -58
- package/packages/hooks/use-mobile.ts +0 -19
- package/packages/index.css +0 -133
- package/packages/lib/utils.ts +0 -24
- package/packages/stories/1.Guideline.mdx +0 -3
- package/packages/stories/2.Colors.mdx +0 -145
- package/packages/stories/3.Pypography.mdx +0 -51
- package/packages/stories/cards/simple-card.stories.tsx +0 -24
- package/packages/stories/data-display/data-list.stories.tsx +0 -22
- package/packages/stories/data-display/empty.stories.tsx +0 -13
- package/packages/stories/data-display/form-dialog.stories.tsx +0 -27
- package/packages/stories/data-display/name.stories.tsx +0 -25
- package/packages/stories/data-display/phone-number.stories.tsx +0 -17
- package/packages/stories/data-display/statistic.stories.tsx +0 -41
- package/packages/stories/dialogs/confirm-dialog.stories.tsx +0 -26
- package/packages/stories/dialogs/detail-dialog.stories.tsx +0 -34
- package/packages/stories/dialogs/error-dialog.stories.tsx +0 -27
- package/packages/stories/dialogs/loading-dialog.stories.tsx +0 -16
- package/packages/stories/forms/combobox-field.stories.tsx +0 -38
- package/packages/stories/forms/form-wrapper.stories.tsx +0 -59
- package/packages/stories/forms/multi-select-field.stories.tsx +0 -42
- package/packages/stories/forms/number-field.stories.tsx +0 -35
- package/packages/stories/forms/select-field.stories.tsx +0 -39
- package/packages/stories/forms/switch-field.stories.tsx +0 -36
- package/packages/stories/forms/text-field.stories.tsx +0 -41
- package/packages/stories/forms/textarea-field.stories.tsx +0 -34
- package/packages/stories/layouts/app-layout.stories.tsx +0 -49
- package/packages/stories/typography/paragraph.stories.tsx +0 -45
- package/packages/stories/typography/title.stories.tsx +0 -39
- package/packages/stories/ui/alert-dialog.stories.tsx +0 -49
- package/packages/stories/ui/alert.stories.tsx +0 -63
- package/packages/stories/ui/aspect-ratio.stories.tsx +0 -22
- package/packages/stories/ui/badge.stories.tsx +0 -170
- package/packages/stories/ui/breadcrumb.stories.tsx +0 -172
- package/packages/stories/ui/button.stories.tsx +0 -181
- package/packages/stories/ui/card.stories.tsx +0 -58
- package/packages/stories/ui/checkbox.stories.tsx +0 -48
- package/packages/stories/ui/command.stories.tsx +0 -58
- package/packages/stories/ui/dialog.stories.tsx +0 -55
- package/packages/stories/ui/dropdown-menu.stories.tsx +0 -89
- package/packages/stories/ui/file-uploader.stories.tsx +0 -18
- package/packages/stories/ui/input.stories.tsx +0 -38
- package/packages/stories/ui/label.stories.tsx +0 -17
- package/packages/stories/ui/multi-select.stories.tsx +0 -29
- package/packages/stories/ui/popover.stories.tsx +0 -52
- package/packages/stories/ui/progress.stories.tsx +0 -17
- package/packages/stories/ui/radio-group.stories.tsx +0 -31
- package/packages/stories/ui/select.stories.tsx +0 -31
- package/packages/stories/ui/separator.stories.tsx +0 -31
- package/packages/stories/ui/sheet.stories.tsx +0 -50
- package/packages/stories/ui/skeleton.stories.tsx +0 -27
- package/packages/stories/ui/switch.stories.tsx +0 -21
- package/packages/stories/ui/table.stories.tsx +0 -82
- package/packages/stories/ui/tabs.stories.tsx +0 -70
- package/packages/stories/ui/textarea.stories.tsx +0 -15
- package/packages/stories/ui/toaster.stories.tsx +0 -87
- package/packages/types.ts +0 -50
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { FileText, Image, Trash2, Upload } from 'lucide-react'
|
|
4
|
-
import Dropzone, { type DropzoneProps, type FileRejection } from 'react-dropzone'
|
|
5
|
-
import { toast } from 'sonner'
|
|
6
|
-
|
|
7
|
-
import { Button } from '@/components/ui/button'
|
|
8
|
-
import { Progress } from '@/components/ui/progress'
|
|
9
|
-
import { useControllableState } from '@/hooks/use-controllable-state'
|
|
10
|
-
import { cn, formatBytes } from '@/lib/utils'
|
|
11
|
-
|
|
12
|
-
interface FileUploaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
13
|
-
/**
|
|
14
|
-
* Value of the uploader.
|
|
15
|
-
* @type File[]
|
|
16
|
-
* @default undefined
|
|
17
|
-
* @example value={files}
|
|
18
|
-
*/
|
|
19
|
-
value?: File[]
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Function to be called when the value changes.
|
|
23
|
-
* @type (files: File[]) => void
|
|
24
|
-
* @default undefined
|
|
25
|
-
* @example onValueChange={(files) => setFiles(files)}
|
|
26
|
-
*/
|
|
27
|
-
onValueChange?: (files: File[]) => void
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Function to be called when files are uploaded.
|
|
31
|
-
* @type (files: File[]) => Promise<void>
|
|
32
|
-
* @default undefined
|
|
33
|
-
* @example onUpload={(files) => uploadFiles(files)}
|
|
34
|
-
*/
|
|
35
|
-
onUpload?: (files: File[]) => Promise<void>
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Progress of the uploaded files.
|
|
39
|
-
* @type Record<string, number> | undefined
|
|
40
|
-
* @default undefined
|
|
41
|
-
* @example progresses={{ "file1.png": 50 }}
|
|
42
|
-
*/
|
|
43
|
-
progresses?: Record<string, number>
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Accepted file types for the uploader.
|
|
47
|
-
* @type { [key: string]: string[]}
|
|
48
|
-
* @default
|
|
49
|
-
* ```ts
|
|
50
|
-
* { "image/*": [] }
|
|
51
|
-
* ```
|
|
52
|
-
* @example accept={["image/png", "image/jpeg"]}
|
|
53
|
-
*/
|
|
54
|
-
accept?: DropzoneProps['accept']
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Maximum file size for the uploader.
|
|
58
|
-
* @type number | undefined
|
|
59
|
-
* @default 1024 * 1024 * 2 // 2MB
|
|
60
|
-
* @example maxSize={1024 * 1024 * 2} // 2MB
|
|
61
|
-
*/
|
|
62
|
-
maxSize?: DropzoneProps['maxSize']
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Maximum number of files for the uploader.
|
|
66
|
-
* @type number | undefined
|
|
67
|
-
* @default 1
|
|
68
|
-
* @example maxFileCount={4}
|
|
69
|
-
*/
|
|
70
|
-
maxFileCount?: DropzoneProps['maxFiles']
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Whether the uploader should accept multiple files.
|
|
74
|
-
* @type boolean
|
|
75
|
-
* @default false
|
|
76
|
-
* @example multiple
|
|
77
|
-
*/
|
|
78
|
-
multiple?: boolean
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Whether the uploader is disabled.
|
|
82
|
-
* @type boolean
|
|
83
|
-
* @default false
|
|
84
|
-
* @example disabled
|
|
85
|
-
*/
|
|
86
|
-
disabled?: boolean
|
|
87
|
-
|
|
88
|
-
showText?: boolean
|
|
89
|
-
iconSize?: number
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function FileUploader(props: FileUploaderProps) {
|
|
93
|
-
const {
|
|
94
|
-
value: valueProp,
|
|
95
|
-
onValueChange,
|
|
96
|
-
onUpload,
|
|
97
|
-
progresses,
|
|
98
|
-
accept = {
|
|
99
|
-
'image/*': [],
|
|
100
|
-
},
|
|
101
|
-
maxSize = 1024 * 1024 * 2,
|
|
102
|
-
maxFileCount = 1,
|
|
103
|
-
multiple = false,
|
|
104
|
-
disabled = false,
|
|
105
|
-
showText = true,
|
|
106
|
-
iconSize,
|
|
107
|
-
className,
|
|
108
|
-
...dropzoneProps
|
|
109
|
-
} = props
|
|
110
|
-
|
|
111
|
-
const [files, setFiles] = useControllableState({
|
|
112
|
-
prop: valueProp,
|
|
113
|
-
onChange: onValueChange,
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
const onDrop = React.useCallback(
|
|
117
|
-
(acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
|
|
118
|
-
if (!multiple && maxFileCount === 1 && acceptedFiles.length > 1) {
|
|
119
|
-
toast.error('Cannot upload more than 1 file at a time')
|
|
120
|
-
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if ((files?.length ?? 0) + acceptedFiles.length > maxFileCount) {
|
|
125
|
-
toast.error(`Cannot upload more than ${maxFileCount} files`)
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const newFiles = acceptedFiles.map((file) =>
|
|
130
|
-
Object.assign(file, {
|
|
131
|
-
preview: URL.createObjectURL(file),
|
|
132
|
-
}),
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
const updatedFiles = files ? [...files, ...newFiles] : newFiles
|
|
136
|
-
|
|
137
|
-
setFiles(updatedFiles)
|
|
138
|
-
|
|
139
|
-
if (rejectedFiles.length > 0) {
|
|
140
|
-
rejectedFiles.forEach(({ file }) => {
|
|
141
|
-
toast.error(`File ${file.name} was rejected`)
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (onUpload && updatedFiles.length > 0 && updatedFiles.length <= maxFileCount) {
|
|
146
|
-
const target = updatedFiles.length > 0 ? `${updatedFiles.length} files` : `file`
|
|
147
|
-
|
|
148
|
-
onUpload(updatedFiles).then(() => {
|
|
149
|
-
setFiles([])
|
|
150
|
-
toast(`${target} uploaded`)
|
|
151
|
-
})
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
|
|
155
|
-
[files, maxFileCount, multiple, onUpload, setFiles],
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
function onRemove(index: number) {
|
|
159
|
-
if (!files) return
|
|
160
|
-
|
|
161
|
-
if (isFileWithPreview(files[index])) {
|
|
162
|
-
URL.revokeObjectURL(files[index].preview)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const newFiles = files.filter((_, i) => i !== index)
|
|
166
|
-
setFiles(newFiles)
|
|
167
|
-
onValueChange?.(newFiles)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Revoke preview url when component unmounts
|
|
171
|
-
React.useEffect(() => {
|
|
172
|
-
return () => {
|
|
173
|
-
if (!files) return
|
|
174
|
-
files.forEach((file) => {
|
|
175
|
-
if (isFileWithPreview(file)) {
|
|
176
|
-
URL.revokeObjectURL(file.preview)
|
|
177
|
-
}
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
181
|
-
}, [])
|
|
182
|
-
|
|
183
|
-
const isDisabled = disabled || (files?.length ?? 0) >= maxFileCount
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<div className="relative flex w-full flex-col gap-6">
|
|
187
|
-
<Dropzone onDrop={onDrop} accept={accept} maxSize={maxSize} maxFiles={maxFileCount} multiple={maxFileCount > 1 || multiple} disabled={isDisabled}>
|
|
188
|
-
{({ getRootProps, getInputProps, isDragActive }) => (
|
|
189
|
-
<div
|
|
190
|
-
{...getRootProps()}
|
|
191
|
-
className={cn(
|
|
192
|
-
'group',
|
|
193
|
-
'border-border-weak',
|
|
194
|
-
'hover:bg-border-weak/20',
|
|
195
|
-
'relative grid h-52 w-full cursor-pointer place-items-center rounded-lg border-2 border-dashed px-5 py-2.5 text-center transition',
|
|
196
|
-
'ring-offset-background',
|
|
197
|
-
'focus-visible:ring-primary-weak',
|
|
198
|
-
'focus-visible:ring-2',
|
|
199
|
-
'focus-visible:ring-offset-2',
|
|
200
|
-
'focus-visible:outline-hidden',
|
|
201
|
-
isDragActive && 'border-border-primary-strong bg-border-weak/40',
|
|
202
|
-
isDisabled && 'pointer-events-none opacity-60',
|
|
203
|
-
className,
|
|
204
|
-
)}
|
|
205
|
-
{...dropzoneProps}
|
|
206
|
-
>
|
|
207
|
-
<input {...getInputProps()} />
|
|
208
|
-
{isDragActive ? (
|
|
209
|
-
<div className="flex flex-col items-center justify-center gap-4 sm:px-5">
|
|
210
|
-
<div className="rounded-full border border-dashed p-3">
|
|
211
|
-
<Upload className="text-text-positive-muted size-7" aria-hidden="true" />
|
|
212
|
-
</div>
|
|
213
|
-
<p className="text-text-positive-muted font-medium">Drop the files here</p>
|
|
214
|
-
</div>
|
|
215
|
-
) : (
|
|
216
|
-
<div className="flex flex-col items-center justify-center gap-4 sm:px-5">
|
|
217
|
-
<div className="rounded-full border border-dashed p-3">
|
|
218
|
-
<Upload size={iconSize || 24} className="text-muted-foreground" aria-hidden="true" />
|
|
219
|
-
</div>
|
|
220
|
-
{showText && (
|
|
221
|
-
<div className="flex flex-col gap-px">
|
|
222
|
-
<p className="text-text-positive-weak text-xs font-medium">Drag {`'n'`} drop files here, or click to select files</p>
|
|
223
|
-
<p className="text-text-positive-muted text-xs">
|
|
224
|
-
You can upload
|
|
225
|
-
{maxFileCount > 1
|
|
226
|
-
? ` ${maxFileCount === Infinity ? 'multiple' : maxFileCount}
|
|
227
|
-
files (up to ${formatBytes(maxSize)} each)`
|
|
228
|
-
: ` a file with ${formatBytes(maxSize)}`}
|
|
229
|
-
</p>
|
|
230
|
-
</div>
|
|
231
|
-
)}
|
|
232
|
-
</div>
|
|
233
|
-
)}
|
|
234
|
-
</div>
|
|
235
|
-
)}
|
|
236
|
-
</Dropzone>
|
|
237
|
-
{files?.length ? (
|
|
238
|
-
<div className="h-fit w-full overflow-y-auto">
|
|
239
|
-
<div className="flex items-center justify-between px-3 py-2.5">
|
|
240
|
-
<div className="text-text-positive-weak flex items-center gap-1">
|
|
241
|
-
<Image size={16} aria-hidden="true" />
|
|
242
|
-
<p className="text-sm">Ảnh đã tải lên ({files.length})</p>
|
|
243
|
-
</div>
|
|
244
|
-
<button
|
|
245
|
-
className="hover:text-text-positive text-text-positive-muted flex cursor-pointer items-center gap-1 transition-colors duration-200"
|
|
246
|
-
onClick={() => {
|
|
247
|
-
if (!files) return
|
|
248
|
-
files.forEach((file) => {
|
|
249
|
-
if (!isFileWithPreview(file)) return
|
|
250
|
-
URL.revokeObjectURL(file.preview)
|
|
251
|
-
})
|
|
252
|
-
setFiles([])
|
|
253
|
-
onValueChange?.([])
|
|
254
|
-
}}
|
|
255
|
-
>
|
|
256
|
-
<Trash2 size={16} aria-hidden="true" />
|
|
257
|
-
<p className="text-sm">Xóa tất cả</p>
|
|
258
|
-
</button>
|
|
259
|
-
</div>
|
|
260
|
-
<div className="flex flex-col gap-2.5">
|
|
261
|
-
{files?.map((file, index) => (
|
|
262
|
-
<FileCard key={index} file={file} onRemove={() => onRemove(index)} progress={progresses?.[file.name]} />
|
|
263
|
-
))}
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
) : null}
|
|
267
|
-
</div>
|
|
268
|
-
)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
interface FileCardProps {
|
|
272
|
-
file: File
|
|
273
|
-
onRemove: () => void
|
|
274
|
-
progress?: number
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function FileCard({ file, progress, onRemove }: FileCardProps) {
|
|
278
|
-
return (
|
|
279
|
-
<div className="border-border-weak hover:border-border relative flex items-center gap-4 rounded-md border py-2 pr-3 pl-2 shadow-xs transition-colors">
|
|
280
|
-
<div className="flex flex-1 gap-2.5">
|
|
281
|
-
{isFileWithPreview(file) ? <FilePreview file={file} /> : null}
|
|
282
|
-
<div className="flex w-full flex-col gap-2">
|
|
283
|
-
<div className="flex flex-col gap-y-0.5">
|
|
284
|
-
<p className="text-text-positive line-clamp-1 text-sm">{file.name}</p>
|
|
285
|
-
<p className="text-text-positive-muted text-xs">{formatBytes(file.size)}</p>
|
|
286
|
-
</div>
|
|
287
|
-
{progress ? <Progress value={progress} /> : null}
|
|
288
|
-
</div>
|
|
289
|
-
</div>
|
|
290
|
-
<div className="flex items-center gap-2">
|
|
291
|
-
<Button type="button" variant="ghost" color="secondary" size="icon" className="size-7" onClick={onRemove}>
|
|
292
|
-
<Trash2 size={16} className="text-text-positive-muted" aria-hidden="true" />
|
|
293
|
-
<span className="sr-only">Remove file</span>
|
|
294
|
-
</Button>
|
|
295
|
-
</div>
|
|
296
|
-
</div>
|
|
297
|
-
)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function isFileWithPreview(file: File): file is File & { preview: string } {
|
|
301
|
-
return 'preview' in file && typeof file.preview === 'string'
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
interface FilePreviewProps {
|
|
305
|
-
file: File & { preview: string }
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function FilePreview({ file }: FilePreviewProps) {
|
|
309
|
-
if (file.type.startsWith('image/')) {
|
|
310
|
-
return <img src={file.preview} className="border-border aspect-square size-10 shrink-0 rounded border object-cover object-top shadow-sm" />
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return <FileText className="text-text-positive-muted size-10 border" aria-hidden="true" />
|
|
314
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { Controller, type ControllerProps, type FieldPath, type FieldValues, FormProvider, useFormContext, useFormState } from 'react-hook-form'
|
|
4
|
-
import { Label as LabelPrimitive, Slot as SlotPrimitive } from 'radix-ui'
|
|
5
|
-
|
|
6
|
-
import { Label } from '@/components/ui/label'
|
|
7
|
-
import { cn } from '@/lib/utils'
|
|
8
|
-
|
|
9
|
-
const Form = FormProvider
|
|
10
|
-
|
|
11
|
-
type FormFieldContextValue<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = {
|
|
12
|
-
name: TName
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue)
|
|
16
|
-
|
|
17
|
-
const FormField = <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({
|
|
18
|
-
...props
|
|
19
|
-
}: ControllerProps<TFieldValues, TName>) => {
|
|
20
|
-
return (
|
|
21
|
-
<FormFieldContext.Provider value={{ name: props.name }}>
|
|
22
|
-
<Controller {...props} />
|
|
23
|
-
</FormFieldContext.Provider>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const useFormField = () => {
|
|
28
|
-
const fieldContext = React.useContext(FormFieldContext)
|
|
29
|
-
const itemContext = React.useContext(FormItemContext)
|
|
30
|
-
const { getFieldState, control, resetField } = useFormContext()
|
|
31
|
-
const formState = useFormState({ name: fieldContext.name })
|
|
32
|
-
const fieldState = getFieldState(fieldContext.name, formState)
|
|
33
|
-
|
|
34
|
-
if (!fieldContext) {
|
|
35
|
-
throw new Error('useFormField should be used within <FormField>')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const { id } = itemContext
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
id,
|
|
42
|
-
control,
|
|
43
|
-
resetField,
|
|
44
|
-
name: fieldContext.name,
|
|
45
|
-
formItemId: `${id}-form-item`,
|
|
46
|
-
formDescriptionId: `${id}-form-item-description`,
|
|
47
|
-
formMessageId: `${id}-form-item-message`,
|
|
48
|
-
...fieldState,
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
type FormItemContextValue = {
|
|
53
|
-
id: string
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue)
|
|
57
|
-
|
|
58
|
-
function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
|
|
59
|
-
const id = React.useId()
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<FormItemContext.Provider value={{ id }}>
|
|
63
|
-
<div data-slot="form-item" className={cn('grid gap-1', className)} {...props} />
|
|
64
|
-
</FormItemContext.Provider>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function FormLabel({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
69
|
-
const { formItemId } = useFormField()
|
|
70
|
-
return <Label data-slot="form-label" className={cn('text-xs', className)} htmlFor={formItemId} {...props} />
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function FormControl({ ...props }: React.ComponentProps<typeof SlotPrimitive.Slot>) {
|
|
74
|
-
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<SlotPrimitive.Slot
|
|
78
|
-
data-slot="form-control"
|
|
79
|
-
id={formItemId}
|
|
80
|
-
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
|
|
81
|
-
aria-invalid={!!error}
|
|
82
|
-
{...props}
|
|
83
|
-
/>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
|
88
|
-
const { formDescriptionId } = useFormField()
|
|
89
|
-
|
|
90
|
-
return <p data-slot="form-description" id={formDescriptionId} className={cn('text-text-positive-muted text-sm', className)} {...props} />
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function FormMessage({ className, children, ...props }: React.ComponentProps<'p'>) {
|
|
94
|
-
const { error, formMessageId } = useFormField()
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<span data-slot="form-message" id={formMessageId} className={cn('text-danger text-xs', className)} {...props}>
|
|
98
|
-
{error ? String(error?.message ?? '') : children}
|
|
99
|
-
</span>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// eslint-disable-next-line react-refresh/only-export-components
|
|
104
|
-
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField }
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { HoverCard as HoverCardPrimitive } from 'radix-ui'
|
|
4
|
-
|
|
5
|
-
import { cn } from '@/lib/utils'
|
|
6
|
-
|
|
7
|
-
function HoverCard({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
|
8
|
-
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function HoverCardTrigger({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
|
12
|
-
return <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function HoverCardContent({ className, align = 'center', sideOffset = 4, ...props }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
|
16
|
-
return (
|
|
17
|
-
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
|
18
|
-
<HoverCardPrimitive.Content
|
|
19
|
-
data-slot="hover-card-content"
|
|
20
|
-
align={align}
|
|
21
|
-
sideOffset={sideOffset}
|
|
22
|
-
className={cn(
|
|
23
|
-
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden',
|
|
24
|
-
className,
|
|
25
|
-
)}
|
|
26
|
-
{...props}
|
|
27
|
-
/>
|
|
28
|
-
</HoverCardPrimitive.Portal>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export { HoverCard, HoverCardContent, HoverCardTrigger }
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { OTPInput, OTPInputContext } from 'input-otp'
|
|
4
|
-
import { MinusIcon } from 'lucide-react'
|
|
5
|
-
|
|
6
|
-
import { cn } from '@/lib/utils'
|
|
7
|
-
|
|
8
|
-
function InputOTP({
|
|
9
|
-
className,
|
|
10
|
-
containerClassName,
|
|
11
|
-
...props
|
|
12
|
-
}: React.ComponentProps<typeof OTPInput> & {
|
|
13
|
-
containerClassName?: string
|
|
14
|
-
}) {
|
|
15
|
-
return (
|
|
16
|
-
<OTPInput
|
|
17
|
-
data-slot="input-otp"
|
|
18
|
-
containerClassName={cn('flex items-center gap-2 has-disabled:opacity-50', containerClassName)}
|
|
19
|
-
className={cn('disabled:cursor-not-allowed', className)}
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
26
|
-
return <div data-slot="input-otp-group" className={cn('flex items-center', className)} {...props} />
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function InputOTPSlot({
|
|
30
|
-
index,
|
|
31
|
-
className,
|
|
32
|
-
...props
|
|
33
|
-
}: React.ComponentProps<'div'> & {
|
|
34
|
-
index: number
|
|
35
|
-
}) {
|
|
36
|
-
const inputOTPContext = React.useContext(OTPInputContext)
|
|
37
|
-
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div
|
|
41
|
-
data-slot="input-otp-slot"
|
|
42
|
-
data-active={isActive}
|
|
43
|
-
className={cn(
|
|
44
|
-
'data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]',
|
|
45
|
-
className,
|
|
46
|
-
)}
|
|
47
|
-
{...props}
|
|
48
|
-
>
|
|
49
|
-
{char}
|
|
50
|
-
{hasFakeCaret && (
|
|
51
|
-
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
52
|
-
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
|
|
53
|
-
</div>
|
|
54
|
-
)}
|
|
55
|
-
</div>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
|
|
60
|
-
return (
|
|
61
|
-
<div data-slot="input-otp-separator" role="separator" {...props}>
|
|
62
|
-
<MinusIcon />
|
|
63
|
-
</div>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot }
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import { cn } from '@/lib/utils'
|
|
5
|
-
|
|
6
|
-
function Input({
|
|
7
|
-
className,
|
|
8
|
-
type,
|
|
9
|
-
onChange,
|
|
10
|
-
onValueChange,
|
|
11
|
-
...props
|
|
12
|
-
}: React.ComponentProps<'input'> & {
|
|
13
|
-
onValueChange?: (value: string) => void
|
|
14
|
-
}) {
|
|
15
|
-
const handleChange = React.useCallback(
|
|
16
|
-
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
17
|
-
onChange?.(e)
|
|
18
|
-
onValueChange?.(e.target.value)
|
|
19
|
-
},
|
|
20
|
-
[onChange, onValueChange],
|
|
21
|
-
)
|
|
22
|
-
return (
|
|
23
|
-
<input
|
|
24
|
-
type={type}
|
|
25
|
-
data-slot="input"
|
|
26
|
-
className={cn(
|
|
27
|
-
'flex h-9 w-full min-w-0',
|
|
28
|
-
'border-border-weak shadow-input rounded-md border',
|
|
29
|
-
'px-3 py-1',
|
|
30
|
-
'bg-transparent',
|
|
31
|
-
'caret-primary text-sm',
|
|
32
|
-
'transition-[color,box-shadow] outline-none',
|
|
33
|
-
'placeholder:text-text-positive-muted',
|
|
34
|
-
'selection:bg-primary',
|
|
35
|
-
'selection:text-primary-foreground',
|
|
36
|
-
'file:inline-flex',
|
|
37
|
-
'file:h-7',
|
|
38
|
-
'file:border-0',
|
|
39
|
-
'file:bg-transparent',
|
|
40
|
-
'file:text-sm',
|
|
41
|
-
'file:text-foreground',
|
|
42
|
-
'file:font-medium',
|
|
43
|
-
'disabled:pointer-events-none',
|
|
44
|
-
'disabled:cursor-not-allowed',
|
|
45
|
-
'disabled:opacity-50',
|
|
46
|
-
'focus-visible:ring-4',
|
|
47
|
-
'focus-visible:ring-primary-weak',
|
|
48
|
-
'focus-visible:border-primary-strong',
|
|
49
|
-
'aria-invalid:ring-danger-muted',
|
|
50
|
-
'aria-invalid:border-danger',
|
|
51
|
-
className,
|
|
52
|
-
)}
|
|
53
|
-
{...props}
|
|
54
|
-
onChange={handleChange}
|
|
55
|
-
/>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export { Input }
|