@primereact/mcp 11.0.0-rc.2
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/LICENSE.md +45 -0
- package/README.md +94 -0
- package/data/llms/headless/add-ons/introduction.md +16 -0
- package/data/llms/headless/components/accordion/api.json +252 -0
- package/data/llms/headless/components/accordion.md +196 -0
- package/data/llms/headless/components/animateonscroll/api.json +88 -0
- package/data/llms/headless/components/animateonscroll.md +141 -0
- package/data/llms/headless/components/autocomplete/api.json +787 -0
- package/data/llms/headless/components/autocomplete.md +249 -0
- package/data/llms/headless/components/avatar/api.json +154 -0
- package/data/llms/headless/components/avatar.md +149 -0
- package/data/llms/headless/components/badge/api.json +68 -0
- package/data/llms/headless/components/badge.md +150 -0
- package/data/llms/headless/components/breadcrumb/api.json +142 -0
- package/data/llms/headless/components/breadcrumb.md +149 -0
- package/data/llms/headless/components/button/api.json +62 -0
- package/data/llms/headless/components/button.md +73 -0
- package/data/llms/headless/components/card/api.json +36 -0
- package/data/llms/headless/components/card.md +96 -0
- package/data/llms/headless/components/carousel/api.json +793 -0
- package/data/llms/headless/components/carousel.md +196 -0
- package/data/llms/headless/components/checkbox/api.json +593 -0
- package/data/llms/headless/components/checkbox.md +184 -0
- package/data/llms/headless/components/chip/api.json +107 -0
- package/data/llms/headless/components/chip.md +141 -0
- package/data/llms/headless/components/collapsible/api.json +301 -0
- package/data/llms/headless/components/collapsible.md +136 -0
- package/data/llms/headless/components/compare/api.json +533 -0
- package/data/llms/headless/components/compare.md +142 -0
- package/data/llms/headless/components/contextmenu/api.json +495 -0
- package/data/llms/headless/components/contextmenu.md +282 -0
- package/data/llms/headless/components/datatable/api.json +1680 -0
- package/data/llms/headless/components/datatable.md +258 -0
- package/data/llms/headless/components/dataview/api.json +199 -0
- package/data/llms/headless/components/dataview.md +147 -0
- package/data/llms/headless/components/datepicker/api.json +2796 -0
- package/data/llms/headless/components/datepicker.md +456 -0
- package/data/llms/headless/components/dialog/api.json +626 -0
- package/data/llms/headless/components/dialog.md +280 -0
- package/data/llms/headless/components/divider/api.json +130 -0
- package/data/llms/headless/components/divider.md +92 -0
- package/data/llms/headless/components/drawer/api.json +425 -0
- package/data/llms/headless/components/drawer.md +220 -0
- package/data/llms/headless/components/fieldset/api.json +195 -0
- package/data/llms/headless/components/fieldset.md +154 -0
- package/data/llms/headless/components/fileupload/api.json +905 -0
- package/data/llms/headless/components/fileupload.md +323 -0
- package/data/llms/headless/components/floatlabel/api.json +36 -0
- package/data/llms/headless/components/floatlabel.md +19 -0
- package/data/llms/headless/components/fluid/api.json +36 -0
- package/data/llms/headless/components/fluid.md +19 -0
- package/data/llms/headless/components/focustrap/api.json +135 -0
- package/data/llms/headless/components/focustrap.md +125 -0
- package/data/llms/headless/components/gallery/api.json +1349 -0
- package/data/llms/headless/components/gallery.md +355 -0
- package/data/llms/headless/components/iconfield/api.json +62 -0
- package/data/llms/headless/components/iconfield.md +19 -0
- package/data/llms/headless/components/iftalabel/api.json +62 -0
- package/data/llms/headless/components/iftalabel.md +19 -0
- package/data/llms/headless/components/inplace/api.json +340 -0
- package/data/llms/headless/components/inplace.md +153 -0
- package/data/llms/headless/components/inputcolor/api.json +469 -0
- package/data/llms/headless/components/inputcolor.md +253 -0
- package/data/llms/headless/components/inputgroup/api.json +62 -0
- package/data/llms/headless/components/inputgroup.md +19 -0
- package/data/llms/headless/components/inputnumber/api.json +554 -0
- package/data/llms/headless/components/inputnumber.md +167 -0
- package/data/llms/headless/components/inputotp/api.json +276 -0
- package/data/llms/headless/components/inputotp.md +130 -0
- package/data/llms/headless/components/inputpassword/api.json +180 -0
- package/data/llms/headless/components/inputpassword.md +117 -0
- package/data/llms/headless/components/inputtags/api.json +497 -0
- package/data/llms/headless/components/inputtags.md +298 -0
- package/data/llms/headless/components/inputtext/api.json +81 -0
- package/data/llms/headless/components/inputtext.md +109 -0
- package/data/llms/headless/components/knob/api.json +429 -0
- package/data/llms/headless/components/knob.md +151 -0
- package/data/llms/headless/components/label/api.json +62 -0
- package/data/llms/headless/components/label.md +19 -0
- package/data/llms/headless/components/listbox/api.json +814 -0
- package/data/llms/headless/components/listbox.md +194 -0
- package/data/llms/headless/components/menu/api.json +1247 -0
- package/data/llms/headless/components/menu.md +283 -0
- package/data/llms/headless/components/message/api.json +131 -0
- package/data/llms/headless/components/message.md +143 -0
- package/data/llms/headless/components/metergroup/api.json +266 -0
- package/data/llms/headless/components/metergroup.md +129 -0
- package/data/llms/headless/components/motion/api.json +237 -0
- package/data/llms/headless/components/motion.md +160 -0
- package/data/llms/headless/components/navigationmenu/api.json +220 -0
- package/data/llms/headless/components/navigationmenu.md +166 -0
- package/data/llms/headless/components/orderlist/api.json +196 -0
- package/data/llms/headless/components/orderlist.md +221 -0
- package/data/llms/headless/components/organizationchart/api.json +372 -0
- package/data/llms/headless/components/organizationchart.md +254 -0
- package/data/llms/headless/components/paginator/api.json +311 -0
- package/data/llms/headless/components/paginator.md +187 -0
- package/data/llms/headless/components/panel/api.json +195 -0
- package/data/llms/headless/components/panel.md +150 -0
- package/data/llms/headless/components/picklist/api.json +293 -0
- package/data/llms/headless/components/picklist.md +270 -0
- package/data/llms/headless/components/popover/api.json +405 -0
- package/data/llms/headless/components/popover.md +301 -0
- package/data/llms/headless/components/portal/api.json +56 -0
- package/data/llms/headless/components/portal.md +102 -0
- package/data/llms/headless/components/positioner/api.json +165 -0
- package/data/llms/headless/components/positioner.md +181 -0
- package/data/llms/headless/components/progressbar/api.json +87 -0
- package/data/llms/headless/components/progressbar.md +122 -0
- package/data/llms/headless/components/progressspinner/api.json +337 -0
- package/data/llms/headless/components/progressspinner.md +128 -0
- package/data/llms/headless/components/radiobutton/api.json +569 -0
- package/data/llms/headless/components/radiobutton.md +195 -0
- package/data/llms/headless/components/rating/api.json +347 -0
- package/data/llms/headless/components/rating.md +181 -0
- package/data/llms/headless/components/scrollarea/api.json +730 -0
- package/data/llms/headless/components/scrollarea.md +232 -0
- package/data/llms/headless/components/select/api.json +829 -0
- package/data/llms/headless/components/select.md +262 -0
- package/data/llms/headless/components/sidebar/api.json +433 -0
- package/data/llms/headless/components/sidebar.md +266 -0
- package/data/llms/headless/components/skeleton/api.json +68 -0
- package/data/llms/headless/components/skeleton.md +95 -0
- package/data/llms/headless/components/slider/api.json +559 -0
- package/data/llms/headless/components/slider.md +158 -0
- package/data/llms/headless/components/speeddial/api.json +459 -0
- package/data/llms/headless/components/speeddial.md +181 -0
- package/data/llms/headless/components/splitter/api.json +582 -0
- package/data/llms/headless/components/splitter.md +159 -0
- package/data/llms/headless/components/stepper/api.json +385 -0
- package/data/llms/headless/components/stepper.md +183 -0
- package/data/llms/headless/components/styleclass/api.json +134 -0
- package/data/llms/headless/components/styleclass.md +129 -0
- package/data/llms/headless/components/tabs/api.json +336 -0
- package/data/llms/headless/components/tabs.md +158 -0
- package/data/llms/headless/components/tag/api.json +62 -0
- package/data/llms/headless/components/tag.md +121 -0
- package/data/llms/headless/components/terminal/api.json +117 -0
- package/data/llms/headless/components/terminal.md +183 -0
- package/data/llms/headless/components/textarea/api.json +81 -0
- package/data/llms/headless/components/textarea.md +110 -0
- package/data/llms/headless/components/timeline/api.json +270 -0
- package/data/llms/headless/components/timeline.md +117 -0
- package/data/llms/headless/components/toast/api.json +455 -0
- package/data/llms/headless/components/toast.md +277 -0
- package/data/llms/headless/components/togglebutton/api.json +357 -0
- package/data/llms/headless/components/togglebutton.md +183 -0
- package/data/llms/headless/components/toggleswitch/api.json +326 -0
- package/data/llms/headless/components/toggleswitch.md +164 -0
- package/data/llms/headless/components/toolbar/api.json +68 -0
- package/data/llms/headless/components/toolbar.md +96 -0
- package/data/llms/headless/components/tooltip/api.json +478 -0
- package/data/llms/headless/components/tooltip.md +233 -0
- package/data/llms/headless/components/tree/api.json +1297 -0
- package/data/llms/headless/components/tree.md +297 -0
- package/data/llms/headless/components/treetable/api.json +1680 -0
- package/data/llms/headless/components/treetable.md +177 -0
- package/data/llms/headless/guides/ai/llms.md +25 -0
- package/data/llms/headless/guides/installation.md +126 -0
- package/data/llms/headless/guides/introduction.md +20 -0
- package/data/llms/headless/guides/misc/accessibility.md +228 -0
- package/data/llms/headless/llms.md +25 -0
- package/data/llms/headless/resources/introduction.md +15 -0
- package/data/llms/hooks/gettingstarted/introduction.md +34 -0
- package/data/llms/hooks/gettingstarted/setup.md +54 -0
- package/data/llms/hooks/use-attr-selector.md +25 -0
- package/data/llms/hooks/use-controlled-state.md +31 -0
- package/data/llms/hooks/use-event-listener.md +37 -0
- package/data/llms/hooks/use-filter.md +312 -0
- package/data/llms/hooks/use-hot-key.md +40 -0
- package/data/llms/hooks/use-id.md +24 -0
- package/data/llms/hooks/use-intersection-observer.md +42 -0
- package/data/llms/hooks/use-is-mobile.md +24 -0
- package/data/llms/hooks/use-key-filter.md +139 -0
- package/data/llms/hooks/use-local-storage.md +28 -0
- package/data/llms/hooks/use-mask.md +53 -0
- package/data/llms/hooks/use-match-media.md +28 -0
- package/data/llms/hooks/use-mount-effect.md +25 -0
- package/data/llms/hooks/use-mounted.md +36 -0
- package/data/llms/hooks/use-number-formatter.md +48 -0
- package/data/llms/hooks/use-presence.md +44 -0
- package/data/llms/hooks/use-previous.md +28 -0
- package/data/llms/hooks/use-props.md +37 -0
- package/data/llms/hooks/use-queue-task.md +29 -0
- package/data/llms/hooks/use-scrolltop.md +87 -0
- package/data/llms/hooks/use-sortable-list.md +226 -0
- package/data/llms/hooks/use-tree-filter.md +216 -0
- package/data/llms/hooks/use-unmount-effect.md +23 -0
- package/data/llms/hooks/use-update-effect.md +27 -0
- package/data/llms/hooks/use-view-transition.md +31 -0
- package/data/llms/hooks/use-visibility-change.md +30 -0
- package/data/llms/llms-full.txt +125291 -0
- package/data/llms/llms.txt +379 -0
- package/data/llms/manifest.json +12409 -0
- package/data/llms/mcp-data.json +14481 -0
- package/data/llms/primitive/add-ons/introduction.md +16 -0
- package/data/llms/primitive/components/accordion/api.json +1411 -0
- package/data/llms/primitive/components/accordion.md +231 -0
- package/data/llms/primitive/components/animateonscroll/api.json +193 -0
- package/data/llms/primitive/components/animateonscroll.md +91 -0
- package/data/llms/primitive/components/autocomplete/api.json +3167 -0
- package/data/llms/primitive/components/autocomplete.md +344 -0
- package/data/llms/primitive/components/avatar/api.json +703 -0
- package/data/llms/primitive/components/avatar.md +134 -0
- package/data/llms/primitive/components/badge/api.json +363 -0
- package/data/llms/primitive/components/badge.md +118 -0
- package/data/llms/primitive/components/breadcrumb/api.json +1162 -0
- package/data/llms/primitive/components/breadcrumb.md +196 -0
- package/data/llms/primitive/components/button/api.json +246 -0
- package/data/llms/primitive/components/button.md +114 -0
- package/data/llms/primitive/components/buttongroup/api.json +168 -0
- package/data/llms/primitive/components/buttongroup.md +43 -0
- package/data/llms/primitive/components/card/api.json +1252 -0
- package/data/llms/primitive/components/card.md +159 -0
- package/data/llms/primitive/components/carousel/api.json +1374 -0
- package/data/llms/primitive/components/carousel.md +220 -0
- package/data/llms/primitive/components/checkbox/api.json +697 -0
- package/data/llms/primitive/components/checkbox.md +160 -0
- package/data/llms/primitive/components/checkboxgroup/api.json +252 -0
- package/data/llms/primitive/components/checkboxgroup.md +58 -0
- package/data/llms/primitive/components/chip/api.json +853 -0
- package/data/llms/primitive/components/chip.md +135 -0
- package/data/llms/primitive/components/collapsible/api.json +581 -0
- package/data/llms/primitive/components/collapsible.md +133 -0
- package/data/llms/primitive/components/compare/api.json +909 -0
- package/data/llms/primitive/components/compare.md +156 -0
- package/data/llms/primitive/components/contextmenu/api.json +3454 -0
- package/data/llms/primitive/components/contextmenu.md +483 -0
- package/data/llms/primitive/components/datatable/api.json +7832 -0
- package/data/llms/primitive/components/datatable.md +561 -0
- package/data/llms/primitive/components/dataview/api.json +819 -0
- package/data/llms/primitive/components/dataview.md +163 -0
- package/data/llms/primitive/components/datepicker/api.json +9055 -0
- package/data/llms/primitive/components/datepicker.md +760 -0
- package/data/llms/primitive/components/dialog/api.json +2491 -0
- package/data/llms/primitive/components/dialog.md +276 -0
- package/data/llms/primitive/components/divider/api.json +214 -0
- package/data/llms/primitive/components/divider.md +96 -0
- package/data/llms/primitive/components/drawer/api.json +1879 -0
- package/data/llms/primitive/components/drawer.md +220 -0
- package/data/llms/primitive/components/fieldset/api.json +1116 -0
- package/data/llms/primitive/components/fieldset.md +183 -0
- package/data/llms/primitive/components/fileupload/api.json +2445 -0
- package/data/llms/primitive/components/fileupload.md +282 -0
- package/data/llms/primitive/components/floatlabel/api.json +176 -0
- package/data/llms/primitive/components/floatlabel.md +47 -0
- package/data/llms/primitive/components/focustrap/api.json +241 -0
- package/data/llms/primitive/components/focustrap.md +93 -0
- package/data/llms/primitive/components/gallery/api.json +3622 -0
- package/data/llms/primitive/components/gallery.md +387 -0
- package/data/llms/primitive/components/iconfield/api.json +322 -0
- package/data/llms/primitive/components/iconfield.md +53 -0
- package/data/llms/primitive/components/iftalabel/api.json +177 -0
- package/data/llms/primitive/components/iftalabel.md +47 -0
- package/data/llms/primitive/components/inplace/api.json +783 -0
- package/data/llms/primitive/components/inplace.md +141 -0
- package/data/llms/primitive/components/inputcolor/api.json +2211 -0
- package/data/llms/primitive/components/inputcolor.md +295 -0
- package/data/llms/primitive/components/inputnumber/api.json +469 -0
- package/data/llms/primitive/components/inputnumber.md +99 -0
- package/data/llms/primitive/components/inputotp/api.json +469 -0
- package/data/llms/primitive/components/inputotp.md +121 -0
- package/data/llms/primitive/components/inputpassword/api.json +278 -0
- package/data/llms/primitive/components/inputpassword.md +84 -0
- package/data/llms/primitive/components/inputtags/api.json +407 -0
- package/data/llms/primitive/components/inputtags.md +243 -0
- package/data/llms/primitive/components/inputtext/api.json +207 -0
- package/data/llms/primitive/components/inputtext.md +81 -0
- package/data/llms/primitive/components/knob/api.json +841 -0
- package/data/llms/primitive/components/knob.md +148 -0
- package/data/llms/primitive/components/label/api.json +177 -0
- package/data/llms/primitive/components/label.md +44 -0
- package/data/llms/primitive/components/listbox/api.json +1741 -0
- package/data/llms/primitive/components/listbox.md +206 -0
- package/data/llms/primitive/components/menu/api.json +3836 -0
- package/data/llms/primitive/components/menu.md +479 -0
- package/data/llms/primitive/components/message/api.json +863 -0
- package/data/llms/primitive/components/message.md +151 -0
- package/data/llms/primitive/components/metergroup/api.json +1222 -0
- package/data/llms/primitive/components/metergroup.md +209 -0
- package/data/llms/primitive/components/motion.md +112 -0
- package/data/llms/primitive/components/navigationmenu/api.json +298 -0
- package/data/llms/primitive/components/navigationmenu.md +184 -0
- package/data/llms/primitive/components/organizationchart.md +133 -0
- package/data/llms/primitive/components/paginator/api.json +1637 -0
- package/data/llms/primitive/components/paginator.md +223 -0
- package/data/llms/primitive/components/panel/api.json +1270 -0
- package/data/llms/primitive/components/panel.md +186 -0
- package/data/llms/primitive/components/popover/api.json +2281 -0
- package/data/llms/primitive/components/popover.md +277 -0
- package/data/llms/primitive/components/portal/api.json +176 -0
- package/data/llms/primitive/components/portal.md +79 -0
- package/data/llms/primitive/components/progressbar/api.json +857 -0
- package/data/llms/primitive/components/progressbar.md +153 -0
- package/data/llms/primitive/components/progressspinner/api.json +725 -0
- package/data/llms/primitive/components/progressspinner.md +133 -0
- package/data/llms/primitive/components/radiobutton/api.json +929 -0
- package/data/llms/primitive/components/radiobutton.md +201 -0
- package/data/llms/primitive/components/rating/api.json +878 -0
- package/data/llms/primitive/components/rating.md +160 -0
- package/data/llms/primitive/components/scrollarea/api.json +1187 -0
- package/data/llms/primitive/components/scrollarea.md +255 -0
- package/data/llms/primitive/components/select/api.json +3167 -0
- package/data/llms/primitive/components/select.md +351 -0
- package/data/llms/primitive/components/sidebar/api.json +3699 -0
- package/data/llms/primitive/components/sidebar.md +444 -0
- package/data/llms/primitive/components/skeleton/api.json +213 -0
- package/data/llms/primitive/components/skeleton.md +80 -0
- package/data/llms/primitive/components/slider/api.json +1066 -0
- package/data/llms/primitive/components/slider.md +161 -0
- package/data/llms/primitive/components/speeddial/api.json +1002 -0
- package/data/llms/primitive/components/speeddial.md +197 -0
- package/data/llms/primitive/components/splitter/api.json +901 -0
- package/data/llms/primitive/components/splitter.md +137 -0
- package/data/llms/primitive/components/stepper/api.json +2102 -0
- package/data/llms/primitive/components/stepper.md +253 -0
- package/data/llms/primitive/components/styleclass.md +399 -0
- package/data/llms/primitive/components/tabs/api.json +1705 -0
- package/data/llms/primitive/components/tabs.md +200 -0
- package/data/llms/primitive/components/tag/api.json +189 -0
- package/data/llms/primitive/components/tag.md +100 -0
- package/data/llms/primitive/components/terminal/api.json +1709 -0
- package/data/llms/primitive/components/terminal.md +227 -0
- package/data/llms/primitive/components/textarea/api.json +213 -0
- package/data/llms/primitive/components/textarea.md +83 -0
- package/data/llms/primitive/components/timeline/api.json +1180 -0
- package/data/llms/primitive/components/timeline.md +185 -0
- package/data/llms/primitive/components/toast/api.json +2102 -0
- package/data/llms/primitive/components/toast.md +291 -0
- package/data/llms/primitive/components/togglebutton/api.json +433 -0
- package/data/llms/primitive/components/togglebutton.md +161 -0
- package/data/llms/primitive/components/togglebuttongroup/api.json +276 -0
- package/data/llms/primitive/components/togglebuttongroup.md +61 -0
- package/data/llms/primitive/components/toggleswitch/api.json +681 -0
- package/data/llms/primitive/components/toggleswitch.md +149 -0
- package/data/llms/primitive/components/toolbar/api.json +646 -0
- package/data/llms/primitive/components/toolbar.md +126 -0
- package/data/llms/primitive/components/tooltip/api.json +1534 -0
- package/data/llms/primitive/components/tooltip.md +202 -0
- package/data/llms/primitive/components/tree/api.json +2952 -0
- package/data/llms/primitive/components/tree.md +292 -0
- package/data/llms/primitive/components/treetable.md +187 -0
- package/data/llms/primitive/components/visuallyhidden/api.json +186 -0
- package/data/llms/primitive/components/visuallyhidden.md +87 -0
- package/data/llms/primitive/guides/ai/llms.md +25 -0
- package/data/llms/primitive/guides/icons/customicons.md +47 -0
- package/data/llms/primitive/guides/icons/primeicons.md +112 -0
- package/data/llms/primitive/guides/installation.md +115 -0
- package/data/llms/primitive/guides/introduction.md +20 -0
- package/data/llms/primitive/guides/misc/accessibility.md +215 -0
- package/data/llms/primitive/guides/misc/passthrough.md +95 -0
- package/data/llms/primitive/guides/misc/rtl.md +27 -0
- package/data/llms/primitive/llms.md +25 -0
- package/data/llms/primitive/resources/introduction.md +15 -0
- package/data/llms/styled/add-ons/introduction.md +16 -0
- package/data/llms/styled/components/accordion.md +735 -0
- package/data/llms/styled/components/animateonscroll.md +264 -0
- package/data/llms/styled/components/autocomplete.md +1597 -0
- package/data/llms/styled/components/avatar.md +313 -0
- package/data/llms/styled/components/badge.md +213 -0
- package/data/llms/styled/components/breadcrumb.md +461 -0
- package/data/llms/styled/components/button/api.json +246 -0
- package/data/llms/styled/components/button.md +641 -0
- package/data/llms/styled/components/buttongroup.md +614 -0
- package/data/llms/styled/components/card.md +228 -0
- package/data/llms/styled/components/carousel.md +388 -0
- package/data/llms/styled/components/checkbox.md +329 -0
- package/data/llms/styled/components/checkboxgroup.md +619 -0
- package/data/llms/styled/components/chip.md +300 -0
- package/data/llms/styled/components/compare.md +271 -0
- package/data/llms/styled/components/contextmenu.md +550 -0
- package/data/llms/styled/components/datatable.md +8259 -0
- package/data/llms/styled/components/dataview.md +907 -0
- package/data/llms/styled/components/datepicker.md +2807 -0
- package/data/llms/styled/components/dialog.md +1047 -0
- package/data/llms/styled/components/divider.md +207 -0
- package/data/llms/styled/components/drawer.md +470 -0
- package/data/llms/styled/components/fieldset.md +334 -0
- package/data/llms/styled/components/fileupload.md +747 -0
- package/data/llms/styled/components/floatlabel/api.json +210 -0
- package/data/llms/styled/components/floatlabel.md +231 -0
- package/data/llms/styled/components/fluid/api.json +204 -0
- package/data/llms/styled/components/fluid.md +177 -0
- package/data/llms/styled/components/focustrap.md +203 -0
- package/data/llms/styled/components/gallery.md +553 -0
- package/data/llms/styled/components/iconfield/api.json +382 -0
- package/data/llms/styled/components/iconfield.md +385 -0
- package/data/llms/styled/components/iftalabel/api.json +237 -0
- package/data/llms/styled/components/iftalabel.md +158 -0
- package/data/llms/styled/components/inplace.md +312 -0
- package/data/llms/styled/components/inputcolor.md +674 -0
- package/data/llms/styled/components/inputgroup/api.json +399 -0
- package/data/llms/styled/components/inputgroup.md +604 -0
- package/data/llms/styled/components/inputmask.md +282 -0
- package/data/llms/styled/components/inputnumber.md +563 -0
- package/data/llms/styled/components/inputotp.md +335 -0
- package/data/llms/styled/components/inputpassword.md +630 -0
- package/data/llms/styled/components/inputtags.md +727 -0
- package/data/llms/styled/components/inputtext.md +190 -0
- package/data/llms/styled/components/keyfilter.md +289 -0
- package/data/llms/styled/components/knob.md +305 -0
- package/data/llms/styled/components/label/api.json +237 -0
- package/data/llms/styled/components/label.md +224 -0
- package/data/llms/styled/components/listbox.md +807 -0
- package/data/llms/styled/components/menu.md +1175 -0
- package/data/llms/styled/components/message.md +570 -0
- package/data/llms/styled/components/metergroup.md +449 -0
- package/data/llms/styled/components/navigationmenu.md +926 -0
- package/data/llms/styled/components/orderlist.md +519 -0
- package/data/llms/styled/components/organizationchart/api.json +1457 -0
- package/data/llms/styled/components/organizationchart.md +791 -0
- package/data/llms/styled/components/overlaymanager.md +519 -0
- package/data/llms/styled/components/paginator.md +506 -0
- package/data/llms/styled/components/panel.md +394 -0
- package/data/llms/styled/components/picklist.md +1004 -0
- package/data/llms/styled/components/popover.md +372 -0
- package/data/llms/styled/components/progressbar.md +438 -0
- package/data/llms/styled/components/progressspinner.md +128 -0
- package/data/llms/styled/components/radiobutton.md +488 -0
- package/data/llms/styled/components/rating/api.json +766 -0
- package/data/llms/styled/components/rating.md +518 -0
- package/data/llms/styled/components/scrollarea.md +627 -0
- package/data/llms/styled/components/select.md +2122 -0
- package/data/llms/styled/components/sidebar.md +1587 -0
- package/data/llms/styled/components/skeleton.md +226 -0
- package/data/llms/styled/components/slider.md +453 -0
- package/data/llms/styled/components/speeddial.md +723 -0
- package/data/llms/styled/components/splitter.md +536 -0
- package/data/llms/styled/components/stepper.md +614 -0
- package/data/llms/styled/components/tabs.md +543 -0
- package/data/llms/styled/components/tag.md +263 -0
- package/data/llms/styled/components/terminal.md +543 -0
- package/data/llms/styled/components/textarea.md +257 -0
- package/data/llms/styled/components/timeline.md +620 -0
- package/data/llms/styled/components/toast.md +1013 -0
- package/data/llms/styled/components/togglebutton.md +287 -0
- package/data/llms/styled/components/togglebuttongroup.md +328 -0
- package/data/llms/styled/components/toggleswitch.md +262 -0
- package/data/llms/styled/components/toolbar.md +187 -0
- package/data/llms/styled/components/tooltip.md +430 -0
- package/data/llms/styled/components/tree.md +1386 -0
- package/data/llms/styled/components/treetable.md +6842 -0
- package/data/llms/styled/guides/ai/llms.md +25 -0
- package/data/llms/styled/guides/configuration.md +151 -0
- package/data/llms/styled/guides/icons/customicons.md +47 -0
- package/data/llms/styled/guides/icons/primeicons.md +112 -0
- package/data/llms/styled/guides/installation/astro.md +97 -0
- package/data/llms/styled/guides/installation/manual.md +3 -0
- package/data/llms/styled/guides/installation/nextjs.md +129 -0
- package/data/llms/styled/guides/installation/react-router.md +88 -0
- package/data/llms/styled/guides/installation/tanstack.md +92 -0
- package/data/llms/styled/guides/installation/vite.md +91 -0
- package/data/llms/styled/guides/installation.md +42 -0
- package/data/llms/styled/guides/introduction.md +20 -0
- package/data/llms/styled/guides/misc/accessibility.md +211 -0
- package/data/llms/styled/guides/misc/animations.md +389 -0
- package/data/llms/styled/guides/misc/passthrough.md +95 -0
- package/data/llms/styled/guides/misc/rtl.md +27 -0
- package/data/llms/styled/guides/theming/styled.md +779 -0
- package/data/llms/styled/guides/theming/tailwind.md +473 -0
- package/data/llms/styled/guides/theming/unstyled.md +115 -0
- package/data/llms/styled/resources/introduction.md +15 -0
- package/data/llms/tailwind/add-ons/introduction.md +16 -0
- package/data/llms/tailwind/components/accordion.md +383 -0
- package/data/llms/tailwind/components/autocomplete.md +1346 -0
- package/data/llms/tailwind/components/avatar.md +305 -0
- package/data/llms/tailwind/components/badge.md +207 -0
- package/data/llms/tailwind/components/breadcrumb.md +387 -0
- package/data/llms/tailwind/components/button/api.json +246 -0
- package/data/llms/tailwind/components/button.md +617 -0
- package/data/llms/tailwind/components/buttongroup.md +599 -0
- package/data/llms/tailwind/components/card.md +224 -0
- package/data/llms/tailwind/components/carousel.md +329 -0
- package/data/llms/tailwind/components/checkbox.md +179 -0
- package/data/llms/tailwind/components/checkboxgroup.md +279 -0
- package/data/llms/tailwind/components/chip.md +304 -0
- package/data/llms/tailwind/components/compare.md +295 -0
- package/data/llms/tailwind/components/contextmenu.md +415 -0
- package/data/llms/tailwind/components/datatable.md +4062 -0
- package/data/llms/tailwind/components/dataview.md +864 -0
- package/data/llms/tailwind/components/datepicker.md +823 -0
- package/data/llms/tailwind/components/dialog.md +1216 -0
- package/data/llms/tailwind/components/divider.md +205 -0
- package/data/llms/tailwind/components/drawer.md +500 -0
- package/data/llms/tailwind/components/fieldset.md +326 -0
- package/data/llms/tailwind/components/fluid.md +117 -0
- package/data/llms/tailwind/components/iconfield.md +197 -0
- package/data/llms/tailwind/components/inplace.md +324 -0
- package/data/llms/tailwind/components/inputcolor.md +493 -0
- package/data/llms/tailwind/components/inputgroup.md +375 -0
- package/data/llms/tailwind/components/inputnumber.md +463 -0
- package/data/llms/tailwind/components/inputotp.md +255 -0
- package/data/llms/tailwind/components/inputpassword.md +543 -0
- package/data/llms/tailwind/components/inputtext.md +141 -0
- package/data/llms/tailwind/components/label.md +100 -0
- package/data/llms/tailwind/components/listbox.md +886 -0
- package/data/llms/tailwind/components/menu.md +1049 -0
- package/data/llms/tailwind/components/message.md +575 -0
- package/data/llms/tailwind/components/metergroup.md +510 -0
- package/data/llms/tailwind/components/navigationmenu.md +528 -0
- package/data/llms/tailwind/components/overlaymanager.md +567 -0
- package/data/llms/tailwind/components/paginator.md +306 -0
- package/data/llms/tailwind/components/panel.md +395 -0
- package/data/llms/tailwind/components/popover.md +424 -0
- package/data/llms/tailwind/components/progressbar.md +313 -0
- package/data/llms/tailwind/components/radiobutton.md +366 -0
- package/data/llms/tailwind/components/rating.md +300 -0
- package/data/llms/tailwind/components/scrollarea.md +638 -0
- package/data/llms/tailwind/components/select.md +1220 -0
- package/data/llms/tailwind/components/sidebar.md +1511 -0
- package/data/llms/tailwind/components/skeleton.md +236 -0
- package/data/llms/tailwind/components/slider.md +291 -0
- package/data/llms/tailwind/components/splitter.md +546 -0
- package/data/llms/tailwind/components/tabs.md +462 -0
- package/data/llms/tailwind/components/tag.md +255 -0
- package/data/llms/tailwind/components/textarea.md +170 -0
- package/data/llms/tailwind/components/timeline.md +656 -0
- package/data/llms/tailwind/components/toast.md +246 -0
- package/data/llms/tailwind/components/togglebutton.md +264 -0
- package/data/llms/tailwind/components/togglebuttongroup.md +262 -0
- package/data/llms/tailwind/components/toggleswitch.md +203 -0
- package/data/llms/tailwind/components/toolbar.md +192 -0
- package/data/llms/tailwind/components/tooltip.md +449 -0
- package/data/llms/tailwind/guides/ai/llms.md +25 -0
- package/data/llms/tailwind/guides/icons/customicons.md +47 -0
- package/data/llms/tailwind/guides/icons/primeicons.md +112 -0
- package/data/llms/tailwind/guides/installation/astro.md +165 -0
- package/data/llms/tailwind/guides/installation/manual.md +3 -0
- package/data/llms/tailwind/guides/installation/nextjs.md +169 -0
- package/data/llms/tailwind/guides/installation/react-router.md +157 -0
- package/data/llms/tailwind/guides/installation/tanstack.md +161 -0
- package/data/llms/tailwind/guides/installation/vite.md +160 -0
- package/data/llms/tailwind/guides/installation.md +42 -0
- package/data/llms/tailwind/guides/introduction.md +20 -0
- package/data/llms/tailwind/guides/misc/accessibility.md +205 -0
- package/data/llms/tailwind/guides/misc/passthrough.md +95 -0
- package/data/llms/tailwind/guides/misc/rtl.md +27 -0
- package/data/llms/tailwind/guides/theming/guide.md +179 -0
- package/data/llms/tailwind/guides/theming.md +191 -0
- package/data/llms/tailwind/llms.md +25 -0
- package/data/llms/tailwind/resources/introduction.md +15 -0
- package/data/manifest.json +12409 -0
- package/data/mcp-data.json +14481 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +2 -0
- package/package.json +61 -0
|
@@ -0,0 +1,4062 @@
|
|
|
1
|
+
# DataTable
|
|
2
|
+
|
|
3
|
+
DataTable displays tabular data with sorting, pagination, selection and filtering features.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
'use client';
|
|
7
|
+
import {
|
|
8
|
+
DataTable,
|
|
9
|
+
DataTableCell,
|
|
10
|
+
DataTableEmptyTBody,
|
|
11
|
+
DataTableRow,
|
|
12
|
+
DataTableTBody,
|
|
13
|
+
DataTableTHead,
|
|
14
|
+
DataTableTHeadCell,
|
|
15
|
+
DataTableTHeadRow,
|
|
16
|
+
DataTableTHeadTitle,
|
|
17
|
+
DataTableTable,
|
|
18
|
+
DataTableTableContainer
|
|
19
|
+
} from '@/components/ui/datatable';
|
|
20
|
+
import { Tag } from '@/components/ui/tag';
|
|
21
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
22
|
+
import { Database } from '@primeicons/react/database';
|
|
23
|
+
import Image from 'next/image';
|
|
24
|
+
import * as React from 'react';
|
|
25
|
+
|
|
26
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
27
|
+
INSTOCK: 'success',
|
|
28
|
+
LOWSTOCK: 'warn',
|
|
29
|
+
OUTOFSTOCK: 'danger'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const statusLabel: Record<string, string> = {
|
|
33
|
+
INSTOCK: 'In Stock',
|
|
34
|
+
LOWSTOCK: 'Low Stock',
|
|
35
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default function Preview() {
|
|
39
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
40
|
+
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
setProducts(ProductService.getProductsData().slice(0, 8));
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="w-full">
|
|
47
|
+
<DataTable data={products}>
|
|
48
|
+
<DataTableTableContainer>
|
|
49
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
50
|
+
<DataTableTHead>
|
|
51
|
+
<DataTableTHeadRow>
|
|
52
|
+
<DataTableTHeadCell>
|
|
53
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
54
|
+
</DataTableTHeadCell>
|
|
55
|
+
<DataTableTHeadCell>
|
|
56
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
57
|
+
</DataTableTHeadCell>
|
|
58
|
+
<DataTableTHeadCell>
|
|
59
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
60
|
+
</DataTableTHeadCell>
|
|
61
|
+
<DataTableTHeadCell>
|
|
62
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
63
|
+
</DataTableTHeadCell>
|
|
64
|
+
</DataTableTHeadRow>
|
|
65
|
+
</DataTableTHead>
|
|
66
|
+
<DataTableTBody>
|
|
67
|
+
{({ item }: { item: any }) => (
|
|
68
|
+
<DataTableRow key={item.id}>
|
|
69
|
+
<DataTableCell>
|
|
70
|
+
<div className="flex items-center gap-3">
|
|
71
|
+
<Image
|
|
72
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
73
|
+
alt={item.name}
|
|
74
|
+
width={48}
|
|
75
|
+
height={48}
|
|
76
|
+
className="rounded-md shadow"
|
|
77
|
+
/>
|
|
78
|
+
<div className="flex flex-col">
|
|
79
|
+
<span className="font-medium">{item.name}</span>
|
|
80
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</DataTableCell>
|
|
84
|
+
<DataTableCell>
|
|
85
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
86
|
+
</DataTableCell>
|
|
87
|
+
<DataTableCell>
|
|
88
|
+
<span className="font-semibold">${item.price}</span>
|
|
89
|
+
</DataTableCell>
|
|
90
|
+
<DataTableCell>
|
|
91
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
92
|
+
</DataTableCell>
|
|
93
|
+
</DataTableRow>
|
|
94
|
+
)}
|
|
95
|
+
</DataTableTBody>
|
|
96
|
+
<DataTableEmptyTBody>
|
|
97
|
+
<DataTableRow>
|
|
98
|
+
<DataTableCell colSpan={4}>
|
|
99
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
100
|
+
<div className="relative">
|
|
101
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
102
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
103
|
+
</div>
|
|
104
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
105
|
+
</div>
|
|
106
|
+
<div>
|
|
107
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
108
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
109
|
+
Hang tight, your data will appear here in a moment.
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</DataTableCell>
|
|
114
|
+
</DataTableRow>
|
|
115
|
+
</DataTableEmptyTBody>
|
|
116
|
+
</DataTableTable>
|
|
117
|
+
</DataTableTableContainer>
|
|
118
|
+
</DataTable>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Installation
|
|
125
|
+
|
|
126
|
+
<DocTabs value="CLI">
|
|
127
|
+
<DocTabsList>
|
|
128
|
+
<DocTabsTab value="CLI">Shadcn CLI</DocTabsTab>
|
|
129
|
+
<DocTabsTab value="Manual">Manual</DocTabsTab>
|
|
130
|
+
</DocTabsList>
|
|
131
|
+
<DocTabsPanel value="CLI">
|
|
132
|
+
```bash
|
|
133
|
+
npx shadcn@latest add https://primereact.dev/r/datatable.json
|
|
134
|
+
```
|
|
135
|
+
</DocTabsPanel>
|
|
136
|
+
<DocTabsPanel value="Manual">
|
|
137
|
+
Copy and paste the following code into your project
|
|
138
|
+
<DocSourceViewer name="components/ui/datatable" />
|
|
139
|
+
Update the import paths to match your project setup.
|
|
140
|
+
</DocTabsPanel>
|
|
141
|
+
</DocTabs>
|
|
142
|
+
|
|
143
|
+
## Usage
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import {
|
|
147
|
+
DataTable,
|
|
148
|
+
DataTableCell,
|
|
149
|
+
DataTableHeader,
|
|
150
|
+
DataTableFooter,
|
|
151
|
+
DataTableRow,
|
|
152
|
+
DataTableSort,
|
|
153
|
+
DataTableSortIndicator,
|
|
154
|
+
DataTableSortOrder,
|
|
155
|
+
DataTableTBody,
|
|
156
|
+
DataTableTHead,
|
|
157
|
+
DataTableTHeadCell,
|
|
158
|
+
DataTableTHeadRow,
|
|
159
|
+
DataTableTable,
|
|
160
|
+
DataTableTableContainer,
|
|
161
|
+
DataTablePagination
|
|
162
|
+
} from '@/components/ui/datatable';
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<DataTable data={items}>
|
|
167
|
+
<DataTableTableContainer>
|
|
168
|
+
<DataTableTable>
|
|
169
|
+
<DataTableTHead>
|
|
170
|
+
<DataTableTHeadRow>
|
|
171
|
+
<DataTableTHeadCell>Column</DataTableTHeadCell>
|
|
172
|
+
</DataTableTHeadRow>
|
|
173
|
+
</DataTableTHead>
|
|
174
|
+
<DataTableTBody>
|
|
175
|
+
{({ item }) => (
|
|
176
|
+
<DataTableRow>
|
|
177
|
+
<DataTableCell>{item.name}</DataTableCell>
|
|
178
|
+
</DataTableRow>
|
|
179
|
+
)}
|
|
180
|
+
</DataTableTBody>
|
|
181
|
+
</DataTableTable>
|
|
182
|
+
</DataTableTableContainer>
|
|
183
|
+
</DataTable>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Examples
|
|
187
|
+
|
|
188
|
+
### Basic
|
|
189
|
+
|
|
190
|
+
Displays a list of items in tabular format.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
'use client';
|
|
194
|
+
import {
|
|
195
|
+
DataTable,
|
|
196
|
+
DataTableCell,
|
|
197
|
+
DataTableRow,
|
|
198
|
+
DataTableTBody,
|
|
199
|
+
DataTableTHead,
|
|
200
|
+
DataTableTHeadCell,
|
|
201
|
+
DataTableTHeadRow,
|
|
202
|
+
DataTableTHeadTitle,
|
|
203
|
+
DataTableTable,
|
|
204
|
+
DataTableTableContainer
|
|
205
|
+
} from '@/components/ui/datatable';
|
|
206
|
+
import { Tag } from '@/components/ui/tag';
|
|
207
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
208
|
+
import Image from 'next/image';
|
|
209
|
+
import * as React from 'react';
|
|
210
|
+
|
|
211
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
212
|
+
INSTOCK: 'success',
|
|
213
|
+
LOWSTOCK: 'warn',
|
|
214
|
+
OUTOFSTOCK: 'danger'
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const statusLabel: Record<string, string> = {
|
|
218
|
+
INSTOCK: 'In Stock',
|
|
219
|
+
LOWSTOCK: 'Low Stock',
|
|
220
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export default function BasicDemo() {
|
|
224
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
225
|
+
|
|
226
|
+
React.useEffect(() => {
|
|
227
|
+
setProducts(ProductService.getProductsData().slice(0, 8));
|
|
228
|
+
}, []);
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<div className="w-full">
|
|
232
|
+
<DataTable data={products}>
|
|
233
|
+
<DataTableTableContainer>
|
|
234
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
235
|
+
<DataTableTHead>
|
|
236
|
+
<DataTableTHeadRow>
|
|
237
|
+
<DataTableTHeadCell>
|
|
238
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
239
|
+
</DataTableTHeadCell>
|
|
240
|
+
<DataTableTHeadCell>
|
|
241
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
242
|
+
</DataTableTHeadCell>
|
|
243
|
+
<DataTableTHeadCell>
|
|
244
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
245
|
+
</DataTableTHeadCell>
|
|
246
|
+
<DataTableTHeadCell>
|
|
247
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
248
|
+
</DataTableTHeadCell>
|
|
249
|
+
</DataTableTHeadRow>
|
|
250
|
+
</DataTableTHead>
|
|
251
|
+
<DataTableTBody>
|
|
252
|
+
{({ item }: { item: any }) => (
|
|
253
|
+
<DataTableRow key={item.id}>
|
|
254
|
+
<DataTableCell>
|
|
255
|
+
<div className="flex items-center gap-3">
|
|
256
|
+
<Image
|
|
257
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
258
|
+
alt={item.name}
|
|
259
|
+
width={40}
|
|
260
|
+
height={40}
|
|
261
|
+
className="rounded-md shadow"
|
|
262
|
+
/>
|
|
263
|
+
<div className="flex flex-col">
|
|
264
|
+
<span className="font-medium">{item.name}</span>
|
|
265
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
</DataTableCell>
|
|
269
|
+
<DataTableCell>
|
|
270
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
271
|
+
</DataTableCell>
|
|
272
|
+
<DataTableCell>
|
|
273
|
+
<span className="font-semibold">${item.price}</span>
|
|
274
|
+
</DataTableCell>
|
|
275
|
+
<DataTableCell>
|
|
276
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
277
|
+
</DataTableCell>
|
|
278
|
+
</DataTableRow>
|
|
279
|
+
)}
|
|
280
|
+
</DataTableTBody>
|
|
281
|
+
</DataTableTable>
|
|
282
|
+
</DataTableTableContainer>
|
|
283
|
+
</DataTable>
|
|
284
|
+
</div>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Striped Rows
|
|
290
|
+
|
|
291
|
+
Use the `stripedRows` prop to render rows with alternating background colors.
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
'use client';
|
|
295
|
+
import {
|
|
296
|
+
DataTable,
|
|
297
|
+
DataTableCell,
|
|
298
|
+
DataTableRow,
|
|
299
|
+
DataTableTBody,
|
|
300
|
+
DataTableTHead,
|
|
301
|
+
DataTableTHeadCell,
|
|
302
|
+
DataTableTHeadRow,
|
|
303
|
+
DataTableTable,
|
|
304
|
+
DataTableTableContainer
|
|
305
|
+
} from '@/components/ui/datatable';
|
|
306
|
+
import { Tag } from '@/components/ui/tag';
|
|
307
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
308
|
+
import * as React from 'react';
|
|
309
|
+
|
|
310
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
311
|
+
INSTOCK: 'success',
|
|
312
|
+
LOWSTOCK: 'warn',
|
|
313
|
+
OUTOFSTOCK: 'danger'
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const statusLabel: Record<string, string> = {
|
|
317
|
+
INSTOCK: 'In Stock',
|
|
318
|
+
LOWSTOCK: 'Low Stock',
|
|
319
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
function ProductTable({ stripedRows, showGridlines, size }: { stripedRows?: boolean; showGridlines?: boolean; size?: 'small' | 'large' }) {
|
|
323
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
324
|
+
|
|
325
|
+
React.useEffect(() => {
|
|
326
|
+
setProducts(ProductService.getProductsData().slice(0, 5));
|
|
327
|
+
}, []);
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<DataTable data={products} stripedRows={stripedRows} showGridlines={showGridlines} size={size}>
|
|
331
|
+
<DataTableTableContainer>
|
|
332
|
+
<DataTableTable style={{ minWidth: '32rem' }}>
|
|
333
|
+
<DataTableTHead>
|
|
334
|
+
<DataTableTHeadRow>
|
|
335
|
+
<DataTableTHeadCell>Name</DataTableTHeadCell>
|
|
336
|
+
<DataTableTHeadCell>Category</DataTableTHeadCell>
|
|
337
|
+
<DataTableTHeadCell>Price</DataTableTHeadCell>
|
|
338
|
+
<DataTableTHeadCell>Status</DataTableTHeadCell>
|
|
339
|
+
</DataTableTHeadRow>
|
|
340
|
+
</DataTableTHead>
|
|
341
|
+
<DataTableTBody>
|
|
342
|
+
{({ item }: { item: any }) => (
|
|
343
|
+
<DataTableRow key={item.id}>
|
|
344
|
+
<DataTableCell>
|
|
345
|
+
<span className="font-medium">{item.name}</span>
|
|
346
|
+
</DataTableCell>
|
|
347
|
+
<DataTableCell>
|
|
348
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
349
|
+
</DataTableCell>
|
|
350
|
+
<DataTableCell>
|
|
351
|
+
<span className="font-semibold">${item.price}</span>
|
|
352
|
+
</DataTableCell>
|
|
353
|
+
<DataTableCell>
|
|
354
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
355
|
+
</DataTableCell>
|
|
356
|
+
</DataTableRow>
|
|
357
|
+
)}
|
|
358
|
+
</DataTableTBody>
|
|
359
|
+
</DataTableTable>
|
|
360
|
+
</DataTableTableContainer>
|
|
361
|
+
</DataTable>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export default function StripedDemo() {
|
|
366
|
+
return (
|
|
367
|
+
<div className="flex flex-col gap-8 w-full">
|
|
368
|
+
<div>
|
|
369
|
+
<div className="text-xs uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-2">Striped Rows</div>
|
|
370
|
+
<ProductTable stripedRows />
|
|
371
|
+
</div>
|
|
372
|
+
<div>
|
|
373
|
+
<div className="text-xs uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-2">Gridlines</div>
|
|
374
|
+
<ProductTable showGridlines />
|
|
375
|
+
</div>
|
|
376
|
+
<div>
|
|
377
|
+
<div className="text-xs uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-2">Small + Striped + Gridlines</div>
|
|
378
|
+
<ProductTable stripedRows showGridlines size="small" />
|
|
379
|
+
</div>
|
|
380
|
+
<div>
|
|
381
|
+
<div className="text-xs uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-2">Large</div>
|
|
382
|
+
<ProductTable size="large" />
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Selection
|
|
390
|
+
|
|
391
|
+
Row selection is driven by the `selectionMode` prop (`single` or `multiple`). The `selectionKeys` prop holds a map of selected row keys and `onSelectionChange` fires when the selection changes.
|
|
392
|
+
|
|
393
|
+
#### Single
|
|
394
|
+
|
|
395
|
+
One row at a time. Clicking a different row replaces the previous selection.
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
'use client';
|
|
399
|
+
import Image from 'next/image';
|
|
400
|
+
import { Database } from '@primeicons/react/database';
|
|
401
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
402
|
+
import type { DataTableSelectionEvent } from 'primereact/datatable';
|
|
403
|
+
import {
|
|
404
|
+
DataTable,
|
|
405
|
+
DataTableCell,
|
|
406
|
+
DataTableEmptyTBody,
|
|
407
|
+
DataTableRow,
|
|
408
|
+
DataTableTBody,
|
|
409
|
+
DataTableTHead,
|
|
410
|
+
DataTableTHeadCell,
|
|
411
|
+
DataTableTHeadRow,
|
|
412
|
+
DataTableTHeadTitle,
|
|
413
|
+
DataTableTable,
|
|
414
|
+
DataTableTableContainer
|
|
415
|
+
} from '@/components/ui/datatable';
|
|
416
|
+
import { Tag } from '@/components/ui/tag';
|
|
417
|
+
import * as React from 'react';
|
|
418
|
+
|
|
419
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
420
|
+
INSTOCK: 'success',
|
|
421
|
+
LOWSTOCK: 'warn',
|
|
422
|
+
OUTOFSTOCK: 'danger'
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const statusLabel: Record<string, string> = {
|
|
426
|
+
INSTOCK: 'In Stock',
|
|
427
|
+
LOWSTOCK: 'Low Stock',
|
|
428
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
export default function SingleSelectionDemo() {
|
|
432
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
433
|
+
const [selectionKeys, setSelectionKeys] = React.useState<Record<string, boolean>>({});
|
|
434
|
+
|
|
435
|
+
React.useEffect(() => {
|
|
436
|
+
setProducts(ProductService.getProductsData().slice(0, 8));
|
|
437
|
+
}, []);
|
|
438
|
+
|
|
439
|
+
return (
|
|
440
|
+
<div className="w-full">
|
|
441
|
+
<DataTable
|
|
442
|
+
data={products}
|
|
443
|
+
dataKey="id"
|
|
444
|
+
selectionMode="single"
|
|
445
|
+
selectionKeys={selectionKeys}
|
|
446
|
+
onSelectionChange={(e: DataTableSelectionEvent) => setSelectionKeys(e.value)}
|
|
447
|
+
>
|
|
448
|
+
<DataTableTableContainer>
|
|
449
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
450
|
+
<DataTableTHead>
|
|
451
|
+
<DataTableTHeadRow>
|
|
452
|
+
<DataTableTHeadCell>
|
|
453
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
454
|
+
</DataTableTHeadCell>
|
|
455
|
+
<DataTableTHeadCell>
|
|
456
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
457
|
+
</DataTableTHeadCell>
|
|
458
|
+
<DataTableTHeadCell>
|
|
459
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
460
|
+
</DataTableTHeadCell>
|
|
461
|
+
<DataTableTHeadCell>
|
|
462
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
463
|
+
</DataTableTHeadCell>
|
|
464
|
+
<DataTableTHeadCell>
|
|
465
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
466
|
+
</DataTableTHeadCell>
|
|
467
|
+
</DataTableTHeadRow>
|
|
468
|
+
</DataTableTHead>
|
|
469
|
+
<DataTableTBody>
|
|
470
|
+
{({ item }: { item: any }) => (
|
|
471
|
+
<DataTableRow key={item.id}>
|
|
472
|
+
<DataTableCell>
|
|
473
|
+
<div className="flex items-center gap-3">
|
|
474
|
+
<Image
|
|
475
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
476
|
+
alt={item.name}
|
|
477
|
+
width={40}
|
|
478
|
+
height={40}
|
|
479
|
+
className="rounded-md shadow"
|
|
480
|
+
/>
|
|
481
|
+
<div className="flex flex-col">
|
|
482
|
+
<span className="font-medium">{item.name}</span>
|
|
483
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
484
|
+
</div>
|
|
485
|
+
</div>
|
|
486
|
+
</DataTableCell>
|
|
487
|
+
<DataTableCell>
|
|
488
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
489
|
+
</DataTableCell>
|
|
490
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
491
|
+
<DataTableCell>
|
|
492
|
+
<span className="font-semibold">${item.price}</span>
|
|
493
|
+
</DataTableCell>
|
|
494
|
+
<DataTableCell>
|
|
495
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
496
|
+
</DataTableCell>
|
|
497
|
+
</DataTableRow>
|
|
498
|
+
)}
|
|
499
|
+
</DataTableTBody>
|
|
500
|
+
<DataTableEmptyTBody>
|
|
501
|
+
<DataTableRow>
|
|
502
|
+
<DataTableCell colSpan={5}>
|
|
503
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
504
|
+
<div className="relative">
|
|
505
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
506
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
507
|
+
</div>
|
|
508
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
509
|
+
</div>
|
|
510
|
+
<div>
|
|
511
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
512
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
513
|
+
Hang tight, your data will appear here in a moment.
|
|
514
|
+
</p>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
</DataTableCell>
|
|
518
|
+
</DataTableRow>
|
|
519
|
+
</DataTableEmptyTBody>
|
|
520
|
+
</DataTableTable>
|
|
521
|
+
</DataTableTableContainer>
|
|
522
|
+
</DataTable>
|
|
523
|
+
</div>
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
#### Multiple
|
|
529
|
+
|
|
530
|
+
Multiple rows without a dedicated column. Pair with `metaKeySelection` so Ctrl/Cmd + Click toggles rows and Shift + Click selects a range.
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
'use client';
|
|
534
|
+
import Image from 'next/image';
|
|
535
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
536
|
+
import { ShoppingCart } from '@primeicons/react/shopping-cart';
|
|
537
|
+
import { Database } from '@primeicons/react/database';
|
|
538
|
+
import { Trash } from '@primeicons/react/trash';
|
|
539
|
+
import type { DataTableSelectionEvent } from 'primereact/datatable';
|
|
540
|
+
import { Badge } from '@/components/ui/badge';
|
|
541
|
+
import { Button } from '@/components/ui/button';
|
|
542
|
+
import {
|
|
543
|
+
DataTable,
|
|
544
|
+
DataTableCell,
|
|
545
|
+
DataTableEmptyTBody,
|
|
546
|
+
DataTableRow,
|
|
547
|
+
DataTableTBody,
|
|
548
|
+
DataTableTHead,
|
|
549
|
+
DataTableTHeadCell,
|
|
550
|
+
DataTableTHeadRow,
|
|
551
|
+
DataTableTHeadTitle,
|
|
552
|
+
DataTableTable,
|
|
553
|
+
DataTableTableContainer
|
|
554
|
+
} from '@/components/ui/datatable';
|
|
555
|
+
import { Tag } from '@/components/ui/tag';
|
|
556
|
+
import * as React from 'react';
|
|
557
|
+
|
|
558
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
559
|
+
INSTOCK: 'success',
|
|
560
|
+
LOWSTOCK: 'warn',
|
|
561
|
+
OUTOFSTOCK: 'danger'
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const statusLabel: Record<string, string> = {
|
|
565
|
+
INSTOCK: 'In Stock',
|
|
566
|
+
LOWSTOCK: 'Low Stock',
|
|
567
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
export default function MultipleSelectionDemo() {
|
|
571
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
572
|
+
const [selectionKeys, setSelectionKeys] = React.useState<Record<string, boolean>>({});
|
|
573
|
+
|
|
574
|
+
React.useEffect(() => {
|
|
575
|
+
setProducts(ProductService.getProductsData().slice(0, 8));
|
|
576
|
+
}, []);
|
|
577
|
+
|
|
578
|
+
const selectedIds = React.useMemo(() => Object.keys(selectionKeys).filter((k) => selectionKeys[k]), [selectionKeys]);
|
|
579
|
+
|
|
580
|
+
const totalValue = React.useMemo(
|
|
581
|
+
() => products.filter((p) => selectedIds.includes(p.id)).reduce((sum, p) => sum + p.price, 0),
|
|
582
|
+
[products, selectedIds]
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
return (
|
|
586
|
+
<div className="w-full">
|
|
587
|
+
<div className="flex items-center justify-between gap-3 mb-3 p-3 rounded-md border border-surface-200 dark:border-surface-700 bg-surface-50 dark:bg-surface-900">
|
|
588
|
+
<div className="flex items-center gap-2">
|
|
589
|
+
<span className="font-medium text-sm">Selected</span>
|
|
590
|
+
<Badge severity={selectedIds.length ? 'info' : 'secondary'}>{selectedIds.length}</Badge>
|
|
591
|
+
{selectedIds.length > 0 && (
|
|
592
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">
|
|
593
|
+
Total <span className="font-semibold text-surface-900 dark:text-surface-0">${totalValue}</span>
|
|
594
|
+
</span>
|
|
595
|
+
)}
|
|
596
|
+
</div>
|
|
597
|
+
<div className="flex items-center gap-2">
|
|
598
|
+
<Button variant="outlined" severity="danger" size="small" disabled={!selectedIds.length}>
|
|
599
|
+
<Trash />
|
|
600
|
+
Delete
|
|
601
|
+
</Button>
|
|
602
|
+
<Button size="small" disabled={!selectedIds.length}>
|
|
603
|
+
<ShoppingCart />
|
|
604
|
+
Add to cart
|
|
605
|
+
</Button>
|
|
606
|
+
</div>
|
|
607
|
+
</div>
|
|
608
|
+
<p className="mb-2 text-xs text-surface-500 dark:text-surface-400">
|
|
609
|
+
Click to replace the selection. Ctrl/Cmd + Click to toggle a row, Shift + Click to select a range.
|
|
610
|
+
</p>
|
|
611
|
+
<DataTable
|
|
612
|
+
data={products}
|
|
613
|
+
dataKey="id"
|
|
614
|
+
selectionMode="multiple"
|
|
615
|
+
metaKeySelection
|
|
616
|
+
selectionKeys={selectionKeys}
|
|
617
|
+
onSelectionChange={(e: DataTableSelectionEvent) => setSelectionKeys(e.value)}
|
|
618
|
+
>
|
|
619
|
+
<DataTableTableContainer>
|
|
620
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
621
|
+
<DataTableTHead>
|
|
622
|
+
<DataTableTHeadRow>
|
|
623
|
+
<DataTableTHeadCell>
|
|
624
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
625
|
+
</DataTableTHeadCell>
|
|
626
|
+
<DataTableTHeadCell>
|
|
627
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
628
|
+
</DataTableTHeadCell>
|
|
629
|
+
<DataTableTHeadCell>
|
|
630
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
631
|
+
</DataTableTHeadCell>
|
|
632
|
+
<DataTableTHeadCell>
|
|
633
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
634
|
+
</DataTableTHeadCell>
|
|
635
|
+
<DataTableTHeadCell>
|
|
636
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
637
|
+
</DataTableTHeadCell>
|
|
638
|
+
</DataTableTHeadRow>
|
|
639
|
+
</DataTableTHead>
|
|
640
|
+
<DataTableTBody>
|
|
641
|
+
{({ item }: { item: any }) => (
|
|
642
|
+
<DataTableRow key={item.id}>
|
|
643
|
+
<DataTableCell>
|
|
644
|
+
<div className="flex items-center gap-3">
|
|
645
|
+
<Image
|
|
646
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
647
|
+
alt={item.name}
|
|
648
|
+
width={40}
|
|
649
|
+
height={40}
|
|
650
|
+
className="rounded-md shadow"
|
|
651
|
+
/>
|
|
652
|
+
<div className="flex flex-col">
|
|
653
|
+
<span className="font-medium">{item.name}</span>
|
|
654
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
655
|
+
</div>
|
|
656
|
+
</div>
|
|
657
|
+
</DataTableCell>
|
|
658
|
+
<DataTableCell>
|
|
659
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
660
|
+
</DataTableCell>
|
|
661
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
662
|
+
<DataTableCell>
|
|
663
|
+
<span className="font-semibold">${item.price}</span>
|
|
664
|
+
</DataTableCell>
|
|
665
|
+
<DataTableCell>
|
|
666
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
667
|
+
</DataTableCell>
|
|
668
|
+
</DataTableRow>
|
|
669
|
+
)}
|
|
670
|
+
</DataTableTBody>
|
|
671
|
+
<DataTableEmptyTBody>
|
|
672
|
+
<DataTableRow>
|
|
673
|
+
<DataTableCell colSpan={5}>
|
|
674
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
675
|
+
<div className="relative">
|
|
676
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
677
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
678
|
+
</div>
|
|
679
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
680
|
+
</div>
|
|
681
|
+
<div>
|
|
682
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
683
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
684
|
+
Hang tight, your data will appear here in a moment.
|
|
685
|
+
</p>
|
|
686
|
+
</div>
|
|
687
|
+
</div>
|
|
688
|
+
</DataTableCell>
|
|
689
|
+
</DataTableRow>
|
|
690
|
+
</DataTableEmptyTBody>
|
|
691
|
+
</DataTableTable>
|
|
692
|
+
</DataTableTableContainer>
|
|
693
|
+
</DataTable>
|
|
694
|
+
</div>
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
#### Checkbox
|
|
700
|
+
|
|
701
|
+
Checkbox-based multiple selection with a header select-all.
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
'use client';
|
|
705
|
+
import { Checkbox } from '@/components/ui/checkbox';
|
|
706
|
+
import {
|
|
707
|
+
DataTable,
|
|
708
|
+
DataTableCell,
|
|
709
|
+
DataTableHeader,
|
|
710
|
+
DataTableRow,
|
|
711
|
+
DataTableTBody,
|
|
712
|
+
DataTableTHead,
|
|
713
|
+
DataTableTHeadCell,
|
|
714
|
+
DataTableTHeadRow,
|
|
715
|
+
DataTableTHeadTitle,
|
|
716
|
+
DataTableTable,
|
|
717
|
+
DataTableTableContainer
|
|
718
|
+
} from '@/components/ui/datatable';
|
|
719
|
+
import { Tag } from '@/components/ui/tag';
|
|
720
|
+
import { CustomerService, type Customer } from '@/shared/services/customer.service';
|
|
721
|
+
import { DataTable as PRDataTable, type DataTableSelectionEvent, type DataTableSelectionInstance } from '@primereact/ui/datatable';
|
|
722
|
+
import type { CheckboxRootChangeEvent } from 'primereact/checkbox';
|
|
723
|
+
import * as React from 'react';
|
|
724
|
+
|
|
725
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'info' | 'danger' | 'secondary'> = {
|
|
726
|
+
qualified: 'success',
|
|
727
|
+
unqualified: 'danger',
|
|
728
|
+
negotiation: 'warn',
|
|
729
|
+
new: 'info',
|
|
730
|
+
renewal: 'secondary',
|
|
731
|
+
proposal: 'info'
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
export default function CheckboxSelectionDemo() {
|
|
735
|
+
const [customers, setCustomers] = React.useState<Customer[]>([]);
|
|
736
|
+
const [selectionKeys, setSelectionKeys] = React.useState<Record<string, boolean>>({});
|
|
737
|
+
|
|
738
|
+
React.useEffect(() => {
|
|
739
|
+
setCustomers(CustomerService.getData().slice(0, 8));
|
|
740
|
+
}, []);
|
|
741
|
+
|
|
742
|
+
const selectedCount = Object.values(selectionKeys).filter(Boolean).length;
|
|
743
|
+
|
|
744
|
+
return (
|
|
745
|
+
<div className="w-full">
|
|
746
|
+
<DataTable
|
|
747
|
+
data={customers}
|
|
748
|
+
dataKey="id"
|
|
749
|
+
selectionKeys={selectionKeys}
|
|
750
|
+
onSelectionChange={(e: DataTableSelectionEvent) => setSelectionKeys(e.value)}
|
|
751
|
+
>
|
|
752
|
+
<DataTableHeader>
|
|
753
|
+
<div className="flex items-center justify-between">
|
|
754
|
+
<span className="text-sm font-semibold">Customers</span>
|
|
755
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">
|
|
756
|
+
{selectedCount > 0 ? `${selectedCount} selected` : `${customers.length} total`}
|
|
757
|
+
</span>
|
|
758
|
+
</div>
|
|
759
|
+
</DataTableHeader>
|
|
760
|
+
<DataTableTableContainer>
|
|
761
|
+
<DataTableTable style={{ minWidth: '40rem' }}>
|
|
762
|
+
<DataTableTHead>
|
|
763
|
+
<DataTableTHeadRow>
|
|
764
|
+
<DataTableTHeadCell style={{ width: '3rem' }}>
|
|
765
|
+
<PRDataTable.Selection>
|
|
766
|
+
{({ isAllSelected, isSomeSelected, toggleAll }: DataTableSelectionInstance) => (
|
|
767
|
+
<Checkbox
|
|
768
|
+
checked={isAllSelected}
|
|
769
|
+
indeterminate={isSomeSelected && !isAllSelected}
|
|
770
|
+
onCheckedChange={(e: CheckboxRootChangeEvent) => toggleAll(e.originalEvent)}
|
|
771
|
+
/>
|
|
772
|
+
)}
|
|
773
|
+
</PRDataTable.Selection>
|
|
774
|
+
</DataTableTHeadCell>
|
|
775
|
+
<DataTableTHeadCell>
|
|
776
|
+
<DataTableTHeadTitle>Name</DataTableTHeadTitle>
|
|
777
|
+
</DataTableTHeadCell>
|
|
778
|
+
<DataTableTHeadCell>
|
|
779
|
+
<DataTableTHeadTitle>Country</DataTableTHeadTitle>
|
|
780
|
+
</DataTableTHeadCell>
|
|
781
|
+
<DataTableTHeadCell>
|
|
782
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
783
|
+
</DataTableTHeadCell>
|
|
784
|
+
<DataTableTHeadCell>
|
|
785
|
+
<DataTableTHeadTitle>Balance</DataTableTHeadTitle>
|
|
786
|
+
</DataTableTHeadCell>
|
|
787
|
+
</DataTableTHeadRow>
|
|
788
|
+
</DataTableTHead>
|
|
789
|
+
<DataTableTBody>
|
|
790
|
+
{({ item }: { item: any }) => (
|
|
791
|
+
<DataTableRow key={item.id}>
|
|
792
|
+
<DataTableCell>
|
|
793
|
+
<PRDataTable.Selection>
|
|
794
|
+
{({ isSelected, toggle }: DataTableSelectionInstance) => (
|
|
795
|
+
<Checkbox
|
|
796
|
+
checked={isSelected}
|
|
797
|
+
onCheckedChange={(e: CheckboxRootChangeEvent) => toggle(e.originalEvent)}
|
|
798
|
+
/>
|
|
799
|
+
)}
|
|
800
|
+
</PRDataTable.Selection>
|
|
801
|
+
</DataTableCell>
|
|
802
|
+
<DataTableCell>
|
|
803
|
+
<span className="font-medium">{item.name}</span>
|
|
804
|
+
</DataTableCell>
|
|
805
|
+
<DataTableCell>{item.country.name}</DataTableCell>
|
|
806
|
+
<DataTableCell>
|
|
807
|
+
<Tag severity={statusSeverity[item.status] ?? 'secondary'}>{item.status}</Tag>
|
|
808
|
+
</DataTableCell>
|
|
809
|
+
<DataTableCell>
|
|
810
|
+
<span className="font-semibold">${item.balance.toLocaleString()}</span>
|
|
811
|
+
</DataTableCell>
|
|
812
|
+
</DataTableRow>
|
|
813
|
+
)}
|
|
814
|
+
</DataTableTBody>
|
|
815
|
+
</DataTableTable>
|
|
816
|
+
</DataTableTableContainer>
|
|
817
|
+
</DataTable>
|
|
818
|
+
</div>
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### Keyboard
|
|
824
|
+
|
|
825
|
+
Arrow Up/Down moves focus between rows, Space or Enter toggles the focused row, and Shift + Arrow extends a range.
|
|
826
|
+
|
|
827
|
+
```tsx
|
|
828
|
+
'use client';
|
|
829
|
+
import Image from 'next/image';
|
|
830
|
+
import { Database } from '@primeicons/react/database';
|
|
831
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
832
|
+
import type { DataTableSelectionEvent } from 'primereact/datatable';
|
|
833
|
+
import { Badge } from '@/components/ui/badge';
|
|
834
|
+
import {
|
|
835
|
+
DataTable,
|
|
836
|
+
DataTableCell,
|
|
837
|
+
DataTableEmptyTBody,
|
|
838
|
+
DataTableRow,
|
|
839
|
+
DataTableTBody,
|
|
840
|
+
DataTableTHead,
|
|
841
|
+
DataTableTHeadCell,
|
|
842
|
+
DataTableTHeadRow,
|
|
843
|
+
DataTableTHeadTitle,
|
|
844
|
+
DataTableTable,
|
|
845
|
+
DataTableTableContainer
|
|
846
|
+
} from '@/components/ui/datatable';
|
|
847
|
+
import { Tag } from '@/components/ui/tag';
|
|
848
|
+
import * as React from 'react';
|
|
849
|
+
|
|
850
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
851
|
+
INSTOCK: 'success',
|
|
852
|
+
LOWSTOCK: 'warn',
|
|
853
|
+
OUTOFSTOCK: 'danger'
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
const statusLabel: Record<string, string> = {
|
|
857
|
+
INSTOCK: 'In Stock',
|
|
858
|
+
LOWSTOCK: 'Low Stock',
|
|
859
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
export default function KeyboardDemo() {
|
|
863
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
864
|
+
const [selectionKeys, setSelectionKeys] = React.useState<Record<string, boolean>>({});
|
|
865
|
+
|
|
866
|
+
React.useEffect(() => {
|
|
867
|
+
setProducts(ProductService.getProductsData().slice(0, 8));
|
|
868
|
+
}, []);
|
|
869
|
+
|
|
870
|
+
const selectedCount = Object.values(selectionKeys).filter(Boolean).length;
|
|
871
|
+
|
|
872
|
+
return (
|
|
873
|
+
<div className="w-full">
|
|
874
|
+
<div className="flex items-center justify-between gap-3 mb-3 p-3 rounded-md border border-surface-200 dark:border-surface-700 bg-surface-50 dark:bg-surface-900">
|
|
875
|
+
<span className="text-sm text-surface-500 dark:text-surface-400">
|
|
876
|
+
<kbd className="px-1.5 py-0.5 text-xs rounded bg-surface-200 dark:bg-surface-700">↑</kbd>{' '}
|
|
877
|
+
<kbd className="px-1.5 py-0.5 text-xs rounded bg-surface-200 dark:bg-surface-700">↓</kbd> navigate,{' '}
|
|
878
|
+
<kbd className="px-1.5 py-0.5 text-xs rounded bg-surface-200 dark:bg-surface-700">Space</kbd> /{' '}
|
|
879
|
+
<kbd className="px-1.5 py-0.5 text-xs rounded bg-surface-200 dark:bg-surface-700">Enter</kbd> select,{' '}
|
|
880
|
+
<kbd className="px-1.5 py-0.5 text-xs rounded bg-surface-200 dark:bg-surface-700">Shift + ↑↓</kbd> range
|
|
881
|
+
</span>
|
|
882
|
+
<div className="flex items-center gap-2">
|
|
883
|
+
<span className="text-sm font-medium">Selected</span>
|
|
884
|
+
<Badge severity={selectedCount ? 'info' : 'secondary'}>{selectedCount}</Badge>
|
|
885
|
+
</div>
|
|
886
|
+
</div>
|
|
887
|
+
<DataTable
|
|
888
|
+
data={products}
|
|
889
|
+
dataKey="id"
|
|
890
|
+
selectionMode="multiple"
|
|
891
|
+
metaKeySelection
|
|
892
|
+
selectionKeys={selectionKeys}
|
|
893
|
+
onSelectionChange={(e: DataTableSelectionEvent) => setSelectionKeys(e.value)}
|
|
894
|
+
>
|
|
895
|
+
<DataTableTableContainer>
|
|
896
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
897
|
+
<DataTableTHead>
|
|
898
|
+
<DataTableTHeadRow>
|
|
899
|
+
<DataTableTHeadCell>
|
|
900
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
901
|
+
</DataTableTHeadCell>
|
|
902
|
+
<DataTableTHeadCell>
|
|
903
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
904
|
+
</DataTableTHeadCell>
|
|
905
|
+
<DataTableTHeadCell>
|
|
906
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
907
|
+
</DataTableTHeadCell>
|
|
908
|
+
<DataTableTHeadCell>
|
|
909
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
910
|
+
</DataTableTHeadCell>
|
|
911
|
+
<DataTableTHeadCell>
|
|
912
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
913
|
+
</DataTableTHeadCell>
|
|
914
|
+
</DataTableTHeadRow>
|
|
915
|
+
</DataTableTHead>
|
|
916
|
+
<DataTableTBody>
|
|
917
|
+
{({ item }: { item: any }) => (
|
|
918
|
+
<DataTableRow key={item.id}>
|
|
919
|
+
<DataTableCell>
|
|
920
|
+
<div className="flex items-center gap-3">
|
|
921
|
+
<Image
|
|
922
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
923
|
+
alt={item.name}
|
|
924
|
+
width={36}
|
|
925
|
+
height={36}
|
|
926
|
+
className="rounded-md shadow"
|
|
927
|
+
/>
|
|
928
|
+
<span className="font-medium">{item.name}</span>
|
|
929
|
+
</div>
|
|
930
|
+
</DataTableCell>
|
|
931
|
+
<DataTableCell>
|
|
932
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
933
|
+
</DataTableCell>
|
|
934
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
935
|
+
<DataTableCell>
|
|
936
|
+
<span className="font-semibold">${item.price}</span>
|
|
937
|
+
</DataTableCell>
|
|
938
|
+
<DataTableCell>
|
|
939
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
940
|
+
</DataTableCell>
|
|
941
|
+
</DataTableRow>
|
|
942
|
+
)}
|
|
943
|
+
</DataTableTBody>
|
|
944
|
+
<DataTableEmptyTBody>
|
|
945
|
+
<DataTableRow>
|
|
946
|
+
<DataTableCell colSpan={5}>
|
|
947
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
948
|
+
<div className="relative">
|
|
949
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
950
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
951
|
+
</div>
|
|
952
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
953
|
+
</div>
|
|
954
|
+
<div>
|
|
955
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
956
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
957
|
+
Hang tight, your data will appear here in a moment.
|
|
958
|
+
</p>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
</DataTableCell>
|
|
962
|
+
</DataTableRow>
|
|
963
|
+
</DataTableEmptyTBody>
|
|
964
|
+
</DataTableTable>
|
|
965
|
+
</DataTableTableContainer>
|
|
966
|
+
</DataTable>
|
|
967
|
+
</div>
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
### Sort
|
|
973
|
+
|
|
974
|
+
Wrap a column header with `DataTableSort field="…"` to make it sortable. `DataTableSortIndicator match="asc | desc | unsorted"` renders direction icons and `DataTableSortOrder` shows the priority badge in multi-sort mode.
|
|
975
|
+
|
|
976
|
+
#### Single
|
|
977
|
+
|
|
978
|
+
Clicking a column header cycles through ascending, descending and unsorted.
|
|
979
|
+
|
|
980
|
+
```tsx
|
|
981
|
+
'use client';
|
|
982
|
+
import {
|
|
983
|
+
DataTable,
|
|
984
|
+
DataTableCell,
|
|
985
|
+
DataTableEmptyTBody,
|
|
986
|
+
DataTableRow,
|
|
987
|
+
DataTableSort,
|
|
988
|
+
DataTableSortIndicator,
|
|
989
|
+
DataTableSortOrder,
|
|
990
|
+
DataTableTBody,
|
|
991
|
+
DataTableTHead,
|
|
992
|
+
DataTableTHeadCell,
|
|
993
|
+
DataTableTHeadRow,
|
|
994
|
+
DataTableTHeadTitle,
|
|
995
|
+
DataTableTable,
|
|
996
|
+
DataTableTableContainer
|
|
997
|
+
} from '@/components/ui/datatable';
|
|
998
|
+
import { Badge } from '@/components/ui/badge';
|
|
999
|
+
import { OverlayBadge } from '@/components/ui/overlaybadge';
|
|
1000
|
+
import { Tag } from '@/components/ui/tag';
|
|
1001
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
1002
|
+
import { Database } from '@primeicons/react/database';
|
|
1003
|
+
import { SortAlt } from '@primeicons/react/sort-alt';
|
|
1004
|
+
import { SortAmountDown } from '@primeicons/react/sort-amount-down';
|
|
1005
|
+
import { SortAmountUpAlt } from '@primeicons/react/sort-amount-up-alt';
|
|
1006
|
+
import type { DataTableSortOrderInstance } from 'primereact/datatable';
|
|
1007
|
+
import Image from 'next/image';
|
|
1008
|
+
import * as React from 'react';
|
|
1009
|
+
|
|
1010
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
1011
|
+
INSTOCK: 'success',
|
|
1012
|
+
LOWSTOCK: 'warn',
|
|
1013
|
+
OUTOFSTOCK: 'danger'
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
const statusLabel: Record<string, string> = {
|
|
1017
|
+
INSTOCK: 'In Stock',
|
|
1018
|
+
LOWSTOCK: 'Low Stock',
|
|
1019
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
function SortableHeader({ field, children }: { field: string; children: React.ReactNode }) {
|
|
1023
|
+
return (
|
|
1024
|
+
<DataTableSort field={field} className="inline-flex items-center gap-2">
|
|
1025
|
+
{children}
|
|
1026
|
+
<OverlayBadge className="px-1">
|
|
1027
|
+
<DataTableSortIndicator match="unsorted">
|
|
1028
|
+
<SortAlt />
|
|
1029
|
+
</DataTableSortIndicator>
|
|
1030
|
+
<DataTableSortIndicator match="asc">
|
|
1031
|
+
<SortAmountUpAlt />
|
|
1032
|
+
</DataTableSortIndicator>
|
|
1033
|
+
<DataTableSortIndicator match="desc">
|
|
1034
|
+
<SortAmountDown />
|
|
1035
|
+
</DataTableSortIndicator>
|
|
1036
|
+
<DataTableSortOrder asChild>
|
|
1037
|
+
{({ index }: DataTableSortOrderInstance) => (
|
|
1038
|
+
<Badge shape="circle" size="small" severity="info" className="mt-1">
|
|
1039
|
+
{index}
|
|
1040
|
+
</Badge>
|
|
1041
|
+
)}
|
|
1042
|
+
</DataTableSortOrder>
|
|
1043
|
+
</OverlayBadge>
|
|
1044
|
+
</DataTableSort>
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
export default function SortDemo() {
|
|
1049
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
1050
|
+
|
|
1051
|
+
React.useEffect(() => {
|
|
1052
|
+
setProducts(ProductService.getProductsData().slice(0, 10));
|
|
1053
|
+
}, []);
|
|
1054
|
+
|
|
1055
|
+
return (
|
|
1056
|
+
<div className="w-full">
|
|
1057
|
+
<DataTable data={products} removableSort>
|
|
1058
|
+
<DataTableTableContainer>
|
|
1059
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
1060
|
+
<DataTableTHead>
|
|
1061
|
+
<DataTableTHeadRow>
|
|
1062
|
+
<DataTableTHeadCell>
|
|
1063
|
+
<SortableHeader field="name">
|
|
1064
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
1065
|
+
</SortableHeader>
|
|
1066
|
+
</DataTableTHeadCell>
|
|
1067
|
+
<DataTableTHeadCell>
|
|
1068
|
+
<SortableHeader field="category">
|
|
1069
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
1070
|
+
</SortableHeader>
|
|
1071
|
+
</DataTableTHeadCell>
|
|
1072
|
+
<DataTableTHeadCell>
|
|
1073
|
+
<SortableHeader field="quantity">
|
|
1074
|
+
<DataTableTHeadTitle>Quantity</DataTableTHeadTitle>
|
|
1075
|
+
</SortableHeader>
|
|
1076
|
+
</DataTableTHeadCell>
|
|
1077
|
+
<DataTableTHeadCell>
|
|
1078
|
+
<SortableHeader field="price">
|
|
1079
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
1080
|
+
</SortableHeader>
|
|
1081
|
+
</DataTableTHeadCell>
|
|
1082
|
+
<DataTableTHeadCell>
|
|
1083
|
+
<SortableHeader field="inventoryStatus">
|
|
1084
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1085
|
+
</SortableHeader>
|
|
1086
|
+
</DataTableTHeadCell>
|
|
1087
|
+
</DataTableTHeadRow>
|
|
1088
|
+
</DataTableTHead>
|
|
1089
|
+
<DataTableTBody>
|
|
1090
|
+
{({ item }: { item: any }) => (
|
|
1091
|
+
<DataTableRow key={item.id}>
|
|
1092
|
+
<DataTableCell>
|
|
1093
|
+
<div className="flex items-center gap-3">
|
|
1094
|
+
<Image
|
|
1095
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
1096
|
+
alt={item.name}
|
|
1097
|
+
width={40}
|
|
1098
|
+
height={40}
|
|
1099
|
+
className="rounded-md shadow"
|
|
1100
|
+
/>
|
|
1101
|
+
<div className="flex flex-col">
|
|
1102
|
+
<span className="font-medium">{item.name}</span>
|
|
1103
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
1104
|
+
</div>
|
|
1105
|
+
</div>
|
|
1106
|
+
</DataTableCell>
|
|
1107
|
+
<DataTableCell>
|
|
1108
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
1109
|
+
</DataTableCell>
|
|
1110
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
1111
|
+
<DataTableCell>
|
|
1112
|
+
<span className="font-semibold">${item.price}</span>
|
|
1113
|
+
</DataTableCell>
|
|
1114
|
+
<DataTableCell>
|
|
1115
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
1116
|
+
</DataTableCell>
|
|
1117
|
+
</DataTableRow>
|
|
1118
|
+
)}
|
|
1119
|
+
</DataTableTBody>
|
|
1120
|
+
<DataTableEmptyTBody>
|
|
1121
|
+
<DataTableRow>
|
|
1122
|
+
<DataTableCell colSpan={5}>
|
|
1123
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
1124
|
+
<div className="relative">
|
|
1125
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
1126
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
1127
|
+
</div>
|
|
1128
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
1129
|
+
</div>
|
|
1130
|
+
<div>
|
|
1131
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
1132
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
1133
|
+
Hang tight, your data will appear here in a moment.
|
|
1134
|
+
</p>
|
|
1135
|
+
</div>
|
|
1136
|
+
</div>
|
|
1137
|
+
</DataTableCell>
|
|
1138
|
+
</DataTableRow>
|
|
1139
|
+
</DataTableEmptyTBody>
|
|
1140
|
+
</DataTableTable>
|
|
1141
|
+
</DataTableTableContainer>
|
|
1142
|
+
</DataTable>
|
|
1143
|
+
</div>
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
#### Multiple
|
|
1149
|
+
|
|
1150
|
+
Hold Ctrl/Cmd and click multiple column headers to sort by several fields at once.
|
|
1151
|
+
|
|
1152
|
+
```tsx
|
|
1153
|
+
'use client';
|
|
1154
|
+
import Image from 'next/image';
|
|
1155
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
1156
|
+
import type { DataTableSortOrderInstance } from 'primereact/datatable';
|
|
1157
|
+
import { SortAlt } from '@primeicons/react/sort-alt';
|
|
1158
|
+
import { Database } from '@primeicons/react/database';
|
|
1159
|
+
import { SortAmountDown } from '@primeicons/react/sort-amount-down';
|
|
1160
|
+
import { SortAmountUpAlt } from '@primeicons/react/sort-amount-up-alt';
|
|
1161
|
+
import { Badge } from '@/components/ui/badge';
|
|
1162
|
+
import {
|
|
1163
|
+
DataTable,
|
|
1164
|
+
DataTableCell,
|
|
1165
|
+
DataTableEmptyTBody,
|
|
1166
|
+
DataTableRow,
|
|
1167
|
+
DataTableSort,
|
|
1168
|
+
DataTableSortIndicator,
|
|
1169
|
+
DataTableSortOrder,
|
|
1170
|
+
DataTableTBody,
|
|
1171
|
+
DataTableTHead,
|
|
1172
|
+
DataTableTHeadCell,
|
|
1173
|
+
DataTableTHeadRow,
|
|
1174
|
+
DataTableTHeadTitle,
|
|
1175
|
+
DataTableTable,
|
|
1176
|
+
DataTableTableContainer
|
|
1177
|
+
} from '@/components/ui/datatable';
|
|
1178
|
+
import { OverlayBadge } from '@/components/ui/overlaybadge';
|
|
1179
|
+
import { Tag } from '@/components/ui/tag';
|
|
1180
|
+
import * as React from 'react';
|
|
1181
|
+
|
|
1182
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
1183
|
+
INSTOCK: 'success',
|
|
1184
|
+
LOWSTOCK: 'warn',
|
|
1185
|
+
OUTOFSTOCK: 'danger'
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
const statusLabel: Record<string, string> = {
|
|
1189
|
+
INSTOCK: 'In Stock',
|
|
1190
|
+
LOWSTOCK: 'Low Stock',
|
|
1191
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
function SortableHeader({ field, children }: { field: string; children: React.ReactNode }) {
|
|
1195
|
+
return (
|
|
1196
|
+
<DataTableSort field={field} className="inline-flex items-center gap-2">
|
|
1197
|
+
{children}
|
|
1198
|
+
<OverlayBadge className="px-1">
|
|
1199
|
+
<DataTableSortIndicator match="unsorted">
|
|
1200
|
+
<SortAlt />
|
|
1201
|
+
</DataTableSortIndicator>
|
|
1202
|
+
<DataTableSortIndicator match="asc">
|
|
1203
|
+
<SortAmountUpAlt />
|
|
1204
|
+
</DataTableSortIndicator>
|
|
1205
|
+
<DataTableSortIndicator match="desc">
|
|
1206
|
+
<SortAmountDown />
|
|
1207
|
+
</DataTableSortIndicator>
|
|
1208
|
+
<DataTableSortOrder asChild>
|
|
1209
|
+
{({ index }: DataTableSortOrderInstance) => (
|
|
1210
|
+
<Badge shape="circle" size="small" severity="info" className="mt-1">
|
|
1211
|
+
{index}
|
|
1212
|
+
</Badge>
|
|
1213
|
+
)}
|
|
1214
|
+
</DataTableSortOrder>
|
|
1215
|
+
</OverlayBadge>
|
|
1216
|
+
</DataTableSort>
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
export default function MultiSortDemo() {
|
|
1221
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
1222
|
+
|
|
1223
|
+
React.useEffect(() => {
|
|
1224
|
+
setProducts(ProductService.getProductsData().slice(0, 10));
|
|
1225
|
+
}, []);
|
|
1226
|
+
|
|
1227
|
+
return (
|
|
1228
|
+
<div className="w-full">
|
|
1229
|
+
<DataTable data={products} removableSort>
|
|
1230
|
+
<DataTableTableContainer>
|
|
1231
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
1232
|
+
<DataTableTHead>
|
|
1233
|
+
<DataTableTHeadRow>
|
|
1234
|
+
<DataTableTHeadCell>
|
|
1235
|
+
<SortableHeader field="name">
|
|
1236
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
1237
|
+
</SortableHeader>
|
|
1238
|
+
</DataTableTHeadCell>
|
|
1239
|
+
<DataTableTHeadCell>
|
|
1240
|
+
<SortableHeader field="category">
|
|
1241
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
1242
|
+
</SortableHeader>
|
|
1243
|
+
</DataTableTHeadCell>
|
|
1244
|
+
<DataTableTHeadCell>
|
|
1245
|
+
<SortableHeader field="quantity">
|
|
1246
|
+
<DataTableTHeadTitle>Quantity</DataTableTHeadTitle>
|
|
1247
|
+
</SortableHeader>
|
|
1248
|
+
</DataTableTHeadCell>
|
|
1249
|
+
<DataTableTHeadCell>
|
|
1250
|
+
<SortableHeader field="price">
|
|
1251
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
1252
|
+
</SortableHeader>
|
|
1253
|
+
</DataTableTHeadCell>
|
|
1254
|
+
<DataTableTHeadCell>
|
|
1255
|
+
<SortableHeader field="inventoryStatus">
|
|
1256
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1257
|
+
</SortableHeader>
|
|
1258
|
+
</DataTableTHeadCell>
|
|
1259
|
+
</DataTableTHeadRow>
|
|
1260
|
+
</DataTableTHead>
|
|
1261
|
+
<DataTableTBody>
|
|
1262
|
+
{({ item }: { item: any }) => (
|
|
1263
|
+
<DataTableRow key={item.id}>
|
|
1264
|
+
<DataTableCell>
|
|
1265
|
+
<div className="flex items-center gap-3">
|
|
1266
|
+
<Image
|
|
1267
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
1268
|
+
alt={item.name}
|
|
1269
|
+
width={40}
|
|
1270
|
+
height={40}
|
|
1271
|
+
className="rounded-md shadow"
|
|
1272
|
+
/>
|
|
1273
|
+
<div className="flex flex-col">
|
|
1274
|
+
<span className="font-medium">{item.name}</span>
|
|
1275
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
1276
|
+
</div>
|
|
1277
|
+
</div>
|
|
1278
|
+
</DataTableCell>
|
|
1279
|
+
<DataTableCell>
|
|
1280
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
1281
|
+
</DataTableCell>
|
|
1282
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
1283
|
+
<DataTableCell>
|
|
1284
|
+
<span className="font-semibold">${item.price}</span>
|
|
1285
|
+
</DataTableCell>
|
|
1286
|
+
<DataTableCell>
|
|
1287
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
1288
|
+
</DataTableCell>
|
|
1289
|
+
</DataTableRow>
|
|
1290
|
+
)}
|
|
1291
|
+
</DataTableTBody>
|
|
1292
|
+
<DataTableEmptyTBody>
|
|
1293
|
+
<DataTableRow>
|
|
1294
|
+
<DataTableCell colSpan={5}>
|
|
1295
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
1296
|
+
<div className="relative">
|
|
1297
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
1298
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
1299
|
+
</div>
|
|
1300
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
1301
|
+
</div>
|
|
1302
|
+
<div>
|
|
1303
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
1304
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
1305
|
+
Hang tight, your data will appear here in a moment.
|
|
1306
|
+
</p>
|
|
1307
|
+
</div>
|
|
1308
|
+
</div>
|
|
1309
|
+
</DataTableCell>
|
|
1310
|
+
</DataTableRow>
|
|
1311
|
+
</DataTableEmptyTBody>
|
|
1312
|
+
</DataTableTable>
|
|
1313
|
+
</DataTableTableContainer>
|
|
1314
|
+
</DataTable>
|
|
1315
|
+
</div>
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
#### Presort
|
|
1321
|
+
|
|
1322
|
+
Apply an initial sort on mount via `defaultSortField` + `defaultSortOrder`.
|
|
1323
|
+
|
|
1324
|
+
```tsx
|
|
1325
|
+
'use client';
|
|
1326
|
+
import Image from 'next/image';
|
|
1327
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
1328
|
+
import type { DataTableSortOrderInstance } from 'primereact/datatable';
|
|
1329
|
+
import { SortAlt } from '@primeicons/react/sort-alt';
|
|
1330
|
+
import { Database } from '@primeicons/react/database';
|
|
1331
|
+
import { SortAmountDown } from '@primeicons/react/sort-amount-down';
|
|
1332
|
+
import { SortAmountUpAlt } from '@primeicons/react/sort-amount-up-alt';
|
|
1333
|
+
import { Badge } from '@/components/ui/badge';
|
|
1334
|
+
import {
|
|
1335
|
+
DataTable,
|
|
1336
|
+
DataTableCell,
|
|
1337
|
+
DataTableEmptyTBody,
|
|
1338
|
+
DataTableRow,
|
|
1339
|
+
DataTableSort,
|
|
1340
|
+
DataTableSortIndicator,
|
|
1341
|
+
DataTableSortOrder,
|
|
1342
|
+
DataTableTBody,
|
|
1343
|
+
DataTableTHead,
|
|
1344
|
+
DataTableTHeadCell,
|
|
1345
|
+
DataTableTHeadRow,
|
|
1346
|
+
DataTableTHeadTitle,
|
|
1347
|
+
DataTableTable,
|
|
1348
|
+
DataTableTableContainer
|
|
1349
|
+
} from '@/components/ui/datatable';
|
|
1350
|
+
import { OverlayBadge } from '@/components/ui/overlaybadge';
|
|
1351
|
+
import { Tag } from '@/components/ui/tag';
|
|
1352
|
+
import * as React from 'react';
|
|
1353
|
+
|
|
1354
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
1355
|
+
INSTOCK: 'success',
|
|
1356
|
+
LOWSTOCK: 'warn',
|
|
1357
|
+
OUTOFSTOCK: 'danger'
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
const statusLabel: Record<string, string> = {
|
|
1361
|
+
INSTOCK: 'In Stock',
|
|
1362
|
+
LOWSTOCK: 'Low Stock',
|
|
1363
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1366
|
+
function SortableHeader({ field, children }: { field: string; children: React.ReactNode }) {
|
|
1367
|
+
return (
|
|
1368
|
+
<DataTableSort field={field} className="inline-flex items-center gap-2">
|
|
1369
|
+
{children}
|
|
1370
|
+
<OverlayBadge className="px-1">
|
|
1371
|
+
<DataTableSortIndicator match="unsorted">
|
|
1372
|
+
<SortAlt />
|
|
1373
|
+
</DataTableSortIndicator>
|
|
1374
|
+
<DataTableSortIndicator match="asc">
|
|
1375
|
+
<SortAmountUpAlt />
|
|
1376
|
+
</DataTableSortIndicator>
|
|
1377
|
+
<DataTableSortIndicator match="desc">
|
|
1378
|
+
<SortAmountDown />
|
|
1379
|
+
</DataTableSortIndicator>
|
|
1380
|
+
<DataTableSortOrder asChild>
|
|
1381
|
+
{({ index }: DataTableSortOrderInstance) => (
|
|
1382
|
+
<Badge shape="circle" size="small" severity="info" className="mt-1">
|
|
1383
|
+
{index}
|
|
1384
|
+
</Badge>
|
|
1385
|
+
)}
|
|
1386
|
+
</DataTableSortOrder>
|
|
1387
|
+
</OverlayBadge>
|
|
1388
|
+
</DataTableSort>
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
export default function PresortDemo() {
|
|
1393
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
1394
|
+
|
|
1395
|
+
React.useEffect(() => {
|
|
1396
|
+
setProducts(ProductService.getProductsData().slice(0, 10));
|
|
1397
|
+
}, []);
|
|
1398
|
+
|
|
1399
|
+
return (
|
|
1400
|
+
<div className="w-full">
|
|
1401
|
+
<DataTable data={products} defaultSortField="price" defaultSortOrder={-1} removableSort>
|
|
1402
|
+
<DataTableTableContainer>
|
|
1403
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
1404
|
+
<DataTableTHead>
|
|
1405
|
+
<DataTableTHeadRow>
|
|
1406
|
+
<DataTableTHeadCell>
|
|
1407
|
+
<SortableHeader field="name">
|
|
1408
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
1409
|
+
</SortableHeader>
|
|
1410
|
+
</DataTableTHeadCell>
|
|
1411
|
+
<DataTableTHeadCell>
|
|
1412
|
+
<SortableHeader field="category">
|
|
1413
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
1414
|
+
</SortableHeader>
|
|
1415
|
+
</DataTableTHeadCell>
|
|
1416
|
+
<DataTableTHeadCell>
|
|
1417
|
+
<SortableHeader field="quantity">
|
|
1418
|
+
<DataTableTHeadTitle>Quantity</DataTableTHeadTitle>
|
|
1419
|
+
</SortableHeader>
|
|
1420
|
+
</DataTableTHeadCell>
|
|
1421
|
+
<DataTableTHeadCell>
|
|
1422
|
+
<SortableHeader field="price">
|
|
1423
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
1424
|
+
</SortableHeader>
|
|
1425
|
+
</DataTableTHeadCell>
|
|
1426
|
+
<DataTableTHeadCell>
|
|
1427
|
+
<SortableHeader field="inventoryStatus">
|
|
1428
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1429
|
+
</SortableHeader>
|
|
1430
|
+
</DataTableTHeadCell>
|
|
1431
|
+
</DataTableTHeadRow>
|
|
1432
|
+
</DataTableTHead>
|
|
1433
|
+
<DataTableTBody>
|
|
1434
|
+
{({ item }: { item: any }) => (
|
|
1435
|
+
<DataTableRow key={item.id}>
|
|
1436
|
+
<DataTableCell>
|
|
1437
|
+
<div className="flex items-center gap-3">
|
|
1438
|
+
<Image
|
|
1439
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
1440
|
+
alt={item.name}
|
|
1441
|
+
width={40}
|
|
1442
|
+
height={40}
|
|
1443
|
+
className="rounded-md shadow"
|
|
1444
|
+
/>
|
|
1445
|
+
<div className="flex flex-col">
|
|
1446
|
+
<span className="font-medium">{item.name}</span>
|
|
1447
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
1448
|
+
</div>
|
|
1449
|
+
</div>
|
|
1450
|
+
</DataTableCell>
|
|
1451
|
+
<DataTableCell>
|
|
1452
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
1453
|
+
</DataTableCell>
|
|
1454
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
1455
|
+
<DataTableCell>
|
|
1456
|
+
<span className="font-semibold">${item.price}</span>
|
|
1457
|
+
</DataTableCell>
|
|
1458
|
+
<DataTableCell>
|
|
1459
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
1460
|
+
</DataTableCell>
|
|
1461
|
+
</DataTableRow>
|
|
1462
|
+
)}
|
|
1463
|
+
</DataTableTBody>
|
|
1464
|
+
<DataTableEmptyTBody>
|
|
1465
|
+
<DataTableRow>
|
|
1466
|
+
<DataTableCell colSpan={5}>
|
|
1467
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
1468
|
+
<div className="relative">
|
|
1469
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
1470
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
1471
|
+
</div>
|
|
1472
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
1473
|
+
</div>
|
|
1474
|
+
<div>
|
|
1475
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
1476
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
1477
|
+
Hang tight, your data will appear here in a moment.
|
|
1478
|
+
</p>
|
|
1479
|
+
</div>
|
|
1480
|
+
</div>
|
|
1481
|
+
</DataTableCell>
|
|
1482
|
+
</DataTableRow>
|
|
1483
|
+
</DataTableEmptyTBody>
|
|
1484
|
+
</DataTableTable>
|
|
1485
|
+
</DataTableTableContainer>
|
|
1486
|
+
</DataTable>
|
|
1487
|
+
</div>
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
For multi-column presort, use `defaultMultiSortMeta`.
|
|
1493
|
+
|
|
1494
|
+
```tsx
|
|
1495
|
+
'use client';
|
|
1496
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
1497
|
+
import type { DataTableSortOrderInstance } from 'primereact/datatable';
|
|
1498
|
+
import { SortAlt } from '@primeicons/react/sort-alt';
|
|
1499
|
+
import { Database } from '@primeicons/react/database';
|
|
1500
|
+
import { SortAmountDown } from '@primeicons/react/sort-amount-down';
|
|
1501
|
+
import { SortAmountUpAlt } from '@primeicons/react/sort-amount-up-alt';
|
|
1502
|
+
import { Badge } from '@/components/ui/badge';
|
|
1503
|
+
import {
|
|
1504
|
+
DataTable,
|
|
1505
|
+
DataTableCell,
|
|
1506
|
+
DataTableEmptyTBody,
|
|
1507
|
+
DataTableRow,
|
|
1508
|
+
DataTableSort,
|
|
1509
|
+
DataTableSortIndicator,
|
|
1510
|
+
DataTableSortOrder,
|
|
1511
|
+
DataTableTBody,
|
|
1512
|
+
DataTableTHead,
|
|
1513
|
+
DataTableTHeadCell,
|
|
1514
|
+
DataTableTHeadRow,
|
|
1515
|
+
DataTableTHeadTitle,
|
|
1516
|
+
DataTableTable,
|
|
1517
|
+
DataTableTableContainer
|
|
1518
|
+
} from '@/components/ui/datatable';
|
|
1519
|
+
import { OverlayBadge } from '@/components/ui/overlaybadge';
|
|
1520
|
+
import { Tag } from '@/components/ui/tag';
|
|
1521
|
+
import Image from 'next/image';
|
|
1522
|
+
import * as React from 'react';
|
|
1523
|
+
|
|
1524
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
1525
|
+
INSTOCK: 'success',
|
|
1526
|
+
LOWSTOCK: 'warn',
|
|
1527
|
+
OUTOFSTOCK: 'danger'
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
const statusLabel: Record<string, string> = {
|
|
1531
|
+
INSTOCK: 'In Stock',
|
|
1532
|
+
LOWSTOCK: 'Low Stock',
|
|
1533
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
function SortableHeader({ field, children }: { field: string; children: React.ReactNode }) {
|
|
1537
|
+
return (
|
|
1538
|
+
<DataTableSort field={field} className="inline-flex items-center gap-2">
|
|
1539
|
+
{children}
|
|
1540
|
+
<OverlayBadge className="px-1">
|
|
1541
|
+
<DataTableSortIndicator match="unsorted">
|
|
1542
|
+
<SortAlt />
|
|
1543
|
+
</DataTableSortIndicator>
|
|
1544
|
+
<DataTableSortIndicator match="asc">
|
|
1545
|
+
<SortAmountUpAlt />
|
|
1546
|
+
</DataTableSortIndicator>
|
|
1547
|
+
<DataTableSortIndicator match="desc">
|
|
1548
|
+
<SortAmountDown />
|
|
1549
|
+
</DataTableSortIndicator>
|
|
1550
|
+
<DataTableSortOrder asChild>
|
|
1551
|
+
{({ index }: DataTableSortOrderInstance) => (
|
|
1552
|
+
<Badge shape="circle" size="small" severity="info" className="mt-1">
|
|
1553
|
+
{index}
|
|
1554
|
+
</Badge>
|
|
1555
|
+
)}
|
|
1556
|
+
</DataTableSortOrder>
|
|
1557
|
+
</OverlayBadge>
|
|
1558
|
+
</DataTableSort>
|
|
1559
|
+
);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
export default function PresortMultiDemo() {
|
|
1563
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
1564
|
+
|
|
1565
|
+
React.useEffect(() => {
|
|
1566
|
+
setProducts(ProductService.getProductsData().slice(0, 10));
|
|
1567
|
+
}, []);
|
|
1568
|
+
|
|
1569
|
+
return (
|
|
1570
|
+
<div className="w-full">
|
|
1571
|
+
<DataTable
|
|
1572
|
+
data={products}
|
|
1573
|
+
defaultMultiSortMeta={[
|
|
1574
|
+
{ field: 'category', order: 1 },
|
|
1575
|
+
{ field: 'price', order: -1 }
|
|
1576
|
+
]}
|
|
1577
|
+
removableSort
|
|
1578
|
+
>
|
|
1579
|
+
<DataTableTableContainer>
|
|
1580
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
1581
|
+
<DataTableTHead>
|
|
1582
|
+
<DataTableTHeadRow>
|
|
1583
|
+
<DataTableTHeadCell>
|
|
1584
|
+
<SortableHeader field="name">
|
|
1585
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
1586
|
+
</SortableHeader>
|
|
1587
|
+
</DataTableTHeadCell>
|
|
1588
|
+
<DataTableTHeadCell>
|
|
1589
|
+
<SortableHeader field="category">
|
|
1590
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
1591
|
+
</SortableHeader>
|
|
1592
|
+
</DataTableTHeadCell>
|
|
1593
|
+
<DataTableTHeadCell>
|
|
1594
|
+
<SortableHeader field="quantity">
|
|
1595
|
+
<DataTableTHeadTitle>Quantity</DataTableTHeadTitle>
|
|
1596
|
+
</SortableHeader>
|
|
1597
|
+
</DataTableTHeadCell>
|
|
1598
|
+
<DataTableTHeadCell>
|
|
1599
|
+
<SortableHeader field="price">
|
|
1600
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
1601
|
+
</SortableHeader>
|
|
1602
|
+
</DataTableTHeadCell>
|
|
1603
|
+
<DataTableTHeadCell>
|
|
1604
|
+
<SortableHeader field="inventoryStatus">
|
|
1605
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1606
|
+
</SortableHeader>
|
|
1607
|
+
</DataTableTHeadCell>
|
|
1608
|
+
</DataTableTHeadRow>
|
|
1609
|
+
</DataTableTHead>
|
|
1610
|
+
<DataTableTBody>
|
|
1611
|
+
{({ item }: { item: any }) => (
|
|
1612
|
+
<DataTableRow key={item.id}>
|
|
1613
|
+
<DataTableCell>
|
|
1614
|
+
<div className="flex items-center gap-3">
|
|
1615
|
+
<Image
|
|
1616
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
1617
|
+
alt={item.name}
|
|
1618
|
+
width={40}
|
|
1619
|
+
height={40}
|
|
1620
|
+
className="rounded-md shadow"
|
|
1621
|
+
/>
|
|
1622
|
+
<div className="flex flex-col">
|
|
1623
|
+
<span className="font-medium">{item.name}</span>
|
|
1624
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
1625
|
+
</div>
|
|
1626
|
+
</div>
|
|
1627
|
+
</DataTableCell>
|
|
1628
|
+
<DataTableCell>
|
|
1629
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
1630
|
+
</DataTableCell>
|
|
1631
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
1632
|
+
<DataTableCell>
|
|
1633
|
+
<span className="font-semibold">${item.price}</span>
|
|
1634
|
+
</DataTableCell>
|
|
1635
|
+
<DataTableCell>
|
|
1636
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
1637
|
+
</DataTableCell>
|
|
1638
|
+
</DataTableRow>
|
|
1639
|
+
)}
|
|
1640
|
+
</DataTableTBody>
|
|
1641
|
+
<DataTableEmptyTBody>
|
|
1642
|
+
<DataTableRow>
|
|
1643
|
+
<DataTableCell colSpan={5}>
|
|
1644
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
1645
|
+
<div className="relative">
|
|
1646
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
1647
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
1648
|
+
</div>
|
|
1649
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
1650
|
+
</div>
|
|
1651
|
+
<div>
|
|
1652
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
1653
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
1654
|
+
Hang tight, your data will appear here in a moment.
|
|
1655
|
+
</p>
|
|
1656
|
+
</div>
|
|
1657
|
+
</div>
|
|
1658
|
+
</DataTableCell>
|
|
1659
|
+
</DataTableRow>
|
|
1660
|
+
</DataTableEmptyTBody>
|
|
1661
|
+
</DataTableTable>
|
|
1662
|
+
</DataTableTableContainer>
|
|
1663
|
+
</DataTable>
|
|
1664
|
+
</div>
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
### Pagination
|
|
1670
|
+
|
|
1671
|
+
DataTable exposes pagination state via render prop, allowing complete control over the pagination UI.
|
|
1672
|
+
|
|
1673
|
+
```tsx
|
|
1674
|
+
'use client';
|
|
1675
|
+
import {
|
|
1676
|
+
DataTable,
|
|
1677
|
+
DataTableCell,
|
|
1678
|
+
DataTablePagination,
|
|
1679
|
+
DataTableRow,
|
|
1680
|
+
DataTableTBody,
|
|
1681
|
+
DataTableTHead,
|
|
1682
|
+
DataTableTHeadCell,
|
|
1683
|
+
DataTableTHeadRow,
|
|
1684
|
+
DataTableTHeadTitle,
|
|
1685
|
+
DataTableTable,
|
|
1686
|
+
DataTableTableContainer
|
|
1687
|
+
} from '@/components/ui/datatable';
|
|
1688
|
+
import { Paginator, PaginatorFirst, PaginatorLast, PaginatorNext, PaginatorPages, PaginatorPrev } from '@/components/ui/paginator';
|
|
1689
|
+
import { Tag } from '@/components/ui/tag';
|
|
1690
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
1691
|
+
import type { DataTablePaginationInstance } from 'primereact/datatable';
|
|
1692
|
+
import type { PaginatorRootChangeEvent } from 'primereact/paginator';
|
|
1693
|
+
import * as React from 'react';
|
|
1694
|
+
|
|
1695
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
1696
|
+
INSTOCK: 'success',
|
|
1697
|
+
LOWSTOCK: 'warn',
|
|
1698
|
+
OUTOFSTOCK: 'danger'
|
|
1699
|
+
};
|
|
1700
|
+
|
|
1701
|
+
const statusLabel: Record<string, string> = {
|
|
1702
|
+
INSTOCK: 'In Stock',
|
|
1703
|
+
LOWSTOCK: 'Low Stock',
|
|
1704
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
1705
|
+
};
|
|
1706
|
+
|
|
1707
|
+
export default function PaginationDemo() {
|
|
1708
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
1709
|
+
|
|
1710
|
+
React.useEffect(() => {
|
|
1711
|
+
setProducts(ProductService.getProductsData());
|
|
1712
|
+
}, []);
|
|
1713
|
+
|
|
1714
|
+
return (
|
|
1715
|
+
<div className="w-full">
|
|
1716
|
+
<DataTable data={products} paginator defaultRows={5}>
|
|
1717
|
+
<DataTableTableContainer>
|
|
1718
|
+
<DataTableTable style={{ minWidth: '40rem' }}>
|
|
1719
|
+
<DataTableTHead>
|
|
1720
|
+
<DataTableTHeadRow>
|
|
1721
|
+
<DataTableTHeadCell>
|
|
1722
|
+
<DataTableTHeadTitle>Name</DataTableTHeadTitle>
|
|
1723
|
+
</DataTableTHeadCell>
|
|
1724
|
+
<DataTableTHeadCell>
|
|
1725
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
1726
|
+
</DataTableTHeadCell>
|
|
1727
|
+
<DataTableTHeadCell>
|
|
1728
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
1729
|
+
</DataTableTHeadCell>
|
|
1730
|
+
<DataTableTHeadCell>
|
|
1731
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1732
|
+
</DataTableTHeadCell>
|
|
1733
|
+
</DataTableTHeadRow>
|
|
1734
|
+
</DataTableTHead>
|
|
1735
|
+
<DataTableTBody>
|
|
1736
|
+
{({ item }: { item: any }) => (
|
|
1737
|
+
<DataTableRow key={item.id}>
|
|
1738
|
+
<DataTableCell>
|
|
1739
|
+
<span className="font-medium">{item.name}</span>
|
|
1740
|
+
</DataTableCell>
|
|
1741
|
+
<DataTableCell>
|
|
1742
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
1743
|
+
</DataTableCell>
|
|
1744
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
1745
|
+
<DataTableCell>
|
|
1746
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
1747
|
+
</DataTableCell>
|
|
1748
|
+
</DataTableRow>
|
|
1749
|
+
)}
|
|
1750
|
+
</DataTableTBody>
|
|
1751
|
+
</DataTableTable>
|
|
1752
|
+
</DataTableTableContainer>
|
|
1753
|
+
<DataTablePagination>
|
|
1754
|
+
{({ page, rows, totalRecords, onPageChange }: DataTablePaginationInstance) => (
|
|
1755
|
+
<Paginator
|
|
1756
|
+
page={page + 1}
|
|
1757
|
+
total={totalRecords}
|
|
1758
|
+
itemsPerPage={rows}
|
|
1759
|
+
onPageChange={(e: PaginatorRootChangeEvent) => {
|
|
1760
|
+
if (e.originalEvent) onPageChange(e.originalEvent, e.value - 1);
|
|
1761
|
+
}}
|
|
1762
|
+
>
|
|
1763
|
+
<PaginatorFirst />
|
|
1764
|
+
<PaginatorPrev />
|
|
1765
|
+
<PaginatorPages />
|
|
1766
|
+
<PaginatorNext />
|
|
1767
|
+
<PaginatorLast />
|
|
1768
|
+
</Paginator>
|
|
1769
|
+
)}
|
|
1770
|
+
</DataTablePagination>
|
|
1771
|
+
</DataTable>
|
|
1772
|
+
</div>
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
### Scroll
|
|
1778
|
+
|
|
1779
|
+
Set `scrollable` on `DataTable` to enable scrolling.
|
|
1780
|
+
|
|
1781
|
+
#### Horizontal
|
|
1782
|
+
|
|
1783
|
+
When the combined column widths exceed the container, the table scrolls horizontally.
|
|
1784
|
+
|
|
1785
|
+
```tsx
|
|
1786
|
+
'use client';
|
|
1787
|
+
import { CustomerService, type Customer } from '@/shared/services/customer.service';
|
|
1788
|
+
import { Database } from '@primeicons/react/database';
|
|
1789
|
+
import {
|
|
1790
|
+
DataTable,
|
|
1791
|
+
DataTableCell,
|
|
1792
|
+
DataTableEmptyTBody,
|
|
1793
|
+
DataTableRow,
|
|
1794
|
+
DataTableTBody,
|
|
1795
|
+
DataTableTHead,
|
|
1796
|
+
DataTableTHeadCell,
|
|
1797
|
+
DataTableTHeadRow,
|
|
1798
|
+
DataTableTHeadTitle,
|
|
1799
|
+
DataTableTable,
|
|
1800
|
+
DataTableTableContainer
|
|
1801
|
+
} from '@/components/ui/datatable';
|
|
1802
|
+
import { Tag } from '@/components/ui/tag';
|
|
1803
|
+
import * as React from 'react';
|
|
1804
|
+
|
|
1805
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'info' | 'danger' | 'secondary'> = {
|
|
1806
|
+
qualified: 'success',
|
|
1807
|
+
unqualified: 'danger',
|
|
1808
|
+
negotiation: 'warn',
|
|
1809
|
+
new: 'info',
|
|
1810
|
+
renewal: 'secondary',
|
|
1811
|
+
proposal: 'info'
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
export default function ScrollHorizontalDemo() {
|
|
1815
|
+
const [customers, setCustomers] = React.useState<Customer[]>([]);
|
|
1816
|
+
|
|
1817
|
+
React.useEffect(() => {
|
|
1818
|
+
setCustomers(CustomerService.getData());
|
|
1819
|
+
}, []);
|
|
1820
|
+
|
|
1821
|
+
return (
|
|
1822
|
+
<div className="w-full">
|
|
1823
|
+
<DataTable data={customers} scrollable scrollHeight="400px">
|
|
1824
|
+
<DataTableTableContainer>
|
|
1825
|
+
<DataTableTable>
|
|
1826
|
+
<DataTableTHead>
|
|
1827
|
+
<DataTableTHeadRow>
|
|
1828
|
+
<DataTableTHeadCell style={{ minWidth: '6rem' }}>
|
|
1829
|
+
<DataTableTHeadTitle>Id</DataTableTHeadTitle>
|
|
1830
|
+
</DataTableTHeadCell>
|
|
1831
|
+
<DataTableTHeadCell style={{ minWidth: '14rem' }}>
|
|
1832
|
+
<DataTableTHeadTitle>Name</DataTableTHeadTitle>
|
|
1833
|
+
</DataTableTHeadCell>
|
|
1834
|
+
<DataTableTHeadCell style={{ minWidth: '14rem' }}>
|
|
1835
|
+
<DataTableTHeadTitle>Country</DataTableTHeadTitle>
|
|
1836
|
+
</DataTableTHeadCell>
|
|
1837
|
+
<DataTableTHeadCell style={{ minWidth: '12rem' }}>
|
|
1838
|
+
<DataTableTHeadTitle>Date</DataTableTHeadTitle>
|
|
1839
|
+
</DataTableTHeadCell>
|
|
1840
|
+
<DataTableTHeadCell style={{ minWidth: '10rem' }}>
|
|
1841
|
+
<DataTableTHeadTitle>Balance</DataTableTHeadTitle>
|
|
1842
|
+
</DataTableTHeadCell>
|
|
1843
|
+
<DataTableTHeadCell style={{ minWidth: '14rem' }}>
|
|
1844
|
+
<DataTableTHeadTitle>Company</DataTableTHeadTitle>
|
|
1845
|
+
</DataTableTHeadCell>
|
|
1846
|
+
<DataTableTHeadCell style={{ minWidth: '10rem' }}>
|
|
1847
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1848
|
+
</DataTableTHeadCell>
|
|
1849
|
+
<DataTableTHeadCell style={{ minWidth: '8rem' }}>
|
|
1850
|
+
<DataTableTHeadTitle>Activity</DataTableTHeadTitle>
|
|
1851
|
+
</DataTableTHeadCell>
|
|
1852
|
+
<DataTableTHeadCell style={{ minWidth: '14rem' }}>
|
|
1853
|
+
<DataTableTHeadTitle>Representative</DataTableTHeadTitle>
|
|
1854
|
+
</DataTableTHeadCell>
|
|
1855
|
+
</DataTableTHeadRow>
|
|
1856
|
+
</DataTableTHead>
|
|
1857
|
+
<DataTableTBody>
|
|
1858
|
+
{({ item }: { item: any }) => (
|
|
1859
|
+
<DataTableRow key={item.id}>
|
|
1860
|
+
<DataTableCell>{item.id}</DataTableCell>
|
|
1861
|
+
<DataTableCell>
|
|
1862
|
+
<span className="font-medium">{item.name}</span>
|
|
1863
|
+
</DataTableCell>
|
|
1864
|
+
<DataTableCell>{item.country.name}</DataTableCell>
|
|
1865
|
+
<DataTableCell>{item.date}</DataTableCell>
|
|
1866
|
+
<DataTableCell>
|
|
1867
|
+
<span className="font-semibold">${Number(item.balance).toLocaleString()}</span>
|
|
1868
|
+
</DataTableCell>
|
|
1869
|
+
<DataTableCell>{item.company}</DataTableCell>
|
|
1870
|
+
<DataTableCell>
|
|
1871
|
+
<Tag severity={statusSeverity[item.status] ?? 'secondary'}>{item.status}</Tag>
|
|
1872
|
+
</DataTableCell>
|
|
1873
|
+
<DataTableCell>{item.activity}%</DataTableCell>
|
|
1874
|
+
<DataTableCell>{item.representative.name}</DataTableCell>
|
|
1875
|
+
</DataTableRow>
|
|
1876
|
+
)}
|
|
1877
|
+
</DataTableTBody>
|
|
1878
|
+
<DataTableEmptyTBody>
|
|
1879
|
+
<DataTableRow>
|
|
1880
|
+
<DataTableCell colSpan={9}>
|
|
1881
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
1882
|
+
<div className="relative">
|
|
1883
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
1884
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
1885
|
+
</div>
|
|
1886
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
1887
|
+
</div>
|
|
1888
|
+
<div>
|
|
1889
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching customers</p>
|
|
1890
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
1891
|
+
Hang tight, your data will appear here in a moment.
|
|
1892
|
+
</p>
|
|
1893
|
+
</div>
|
|
1894
|
+
</div>
|
|
1895
|
+
</DataTableCell>
|
|
1896
|
+
</DataTableRow>
|
|
1897
|
+
</DataTableEmptyTBody>
|
|
1898
|
+
</DataTableTable>
|
|
1899
|
+
</DataTableTableContainer>
|
|
1900
|
+
</DataTable>
|
|
1901
|
+
</div>
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
```
|
|
1905
|
+
|
|
1906
|
+
#### Frozen Columns
|
|
1907
|
+
|
|
1908
|
+
Pin multiple columns to either side using `frozen` and `frozenAlignment`.
|
|
1909
|
+
|
|
1910
|
+
```tsx
|
|
1911
|
+
'use client';
|
|
1912
|
+
import { CustomerService, type Customer } from '@/shared/services/customer.service';
|
|
1913
|
+
import { Database } from '@primeicons/react/database';
|
|
1914
|
+
import {
|
|
1915
|
+
DataTable,
|
|
1916
|
+
DataTableCell,
|
|
1917
|
+
DataTableEmptyTBody,
|
|
1918
|
+
DataTableRow,
|
|
1919
|
+
DataTableTBody,
|
|
1920
|
+
DataTableTHead,
|
|
1921
|
+
DataTableTHeadCell,
|
|
1922
|
+
DataTableTHeadRow,
|
|
1923
|
+
DataTableTHeadTitle,
|
|
1924
|
+
DataTableTable,
|
|
1925
|
+
DataTableTableContainer
|
|
1926
|
+
} from '@/components/ui/datatable';
|
|
1927
|
+
import { Tag } from '@/components/ui/tag';
|
|
1928
|
+
import * as React from 'react';
|
|
1929
|
+
|
|
1930
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'info' | 'danger' | 'secondary'> = {
|
|
1931
|
+
qualified: 'success',
|
|
1932
|
+
unqualified: 'danger',
|
|
1933
|
+
negotiation: 'warn',
|
|
1934
|
+
new: 'info',
|
|
1935
|
+
renewal: 'secondary',
|
|
1936
|
+
proposal: 'info'
|
|
1937
|
+
};
|
|
1938
|
+
|
|
1939
|
+
export default function ScrollFrozenColumnsMultiDemo() {
|
|
1940
|
+
const [customers, setCustomers] = React.useState<Customer[]>([]);
|
|
1941
|
+
|
|
1942
|
+
React.useEffect(() => {
|
|
1943
|
+
setCustomers(CustomerService.getData());
|
|
1944
|
+
}, []);
|
|
1945
|
+
|
|
1946
|
+
return (
|
|
1947
|
+
<div className="w-full">
|
|
1948
|
+
<DataTable data={customers} scrollable scrollHeight="400px">
|
|
1949
|
+
<DataTableTableContainer>
|
|
1950
|
+
<DataTableTable style={{ minWidth: '1400px' }}>
|
|
1951
|
+
<DataTableTHead>
|
|
1952
|
+
<DataTableTHeadRow>
|
|
1953
|
+
<DataTableTHeadCell frozen style={{ minWidth: '80px' }}>
|
|
1954
|
+
<DataTableTHeadTitle>Id</DataTableTHeadTitle>
|
|
1955
|
+
</DataTableTHeadCell>
|
|
1956
|
+
<DataTableTHeadCell style={{ minWidth: '200px' }}>
|
|
1957
|
+
<DataTableTHeadTitle>Name</DataTableTHeadTitle>
|
|
1958
|
+
</DataTableTHeadCell>
|
|
1959
|
+
<DataTableTHeadCell style={{ minWidth: '200px' }}>
|
|
1960
|
+
<DataTableTHeadTitle>Country</DataTableTHeadTitle>
|
|
1961
|
+
</DataTableTHeadCell>
|
|
1962
|
+
<DataTableTHeadCell style={{ minWidth: '220px' }}>
|
|
1963
|
+
<DataTableTHeadTitle>Company</DataTableTHeadTitle>
|
|
1964
|
+
</DataTableTHeadCell>
|
|
1965
|
+
<DataTableTHeadCell style={{ minWidth: '200px' }}>
|
|
1966
|
+
<DataTableTHeadTitle>Representative</DataTableTHeadTitle>
|
|
1967
|
+
</DataTableTHeadCell>
|
|
1968
|
+
<DataTableTHeadCell style={{ minWidth: '200px' }}>
|
|
1969
|
+
<DataTableTHeadTitle>Date</DataTableTHeadTitle>
|
|
1970
|
+
</DataTableTHeadCell>
|
|
1971
|
+
<DataTableTHeadCell style={{ minWidth: '200px' }}>
|
|
1972
|
+
<DataTableTHeadTitle>Activity</DataTableTHeadTitle>
|
|
1973
|
+
</DataTableTHeadCell>
|
|
1974
|
+
<DataTableTHeadCell style={{ minWidth: '200px' }}>
|
|
1975
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
1976
|
+
</DataTableTHeadCell>
|
|
1977
|
+
<DataTableTHeadCell frozen alignFrozen="right" style={{ minWidth: '180px' }}>
|
|
1978
|
+
<DataTableTHeadTitle>Balance</DataTableTHeadTitle>
|
|
1979
|
+
</DataTableTHeadCell>
|
|
1980
|
+
</DataTableTHeadRow>
|
|
1981
|
+
</DataTableTHead>
|
|
1982
|
+
<DataTableTBody>
|
|
1983
|
+
{({ item }: { item: any }) => (
|
|
1984
|
+
<DataTableRow key={String(item.id)}>
|
|
1985
|
+
<DataTableCell frozen>
|
|
1986
|
+
<span className="text-xs text-surface-500 dark:text-surface-400 tabular-nums">#{String(item.id)}</span>
|
|
1987
|
+
</DataTableCell>
|
|
1988
|
+
<DataTableCell>
|
|
1989
|
+
<span className="font-semibold">{String(item.name)}</span>
|
|
1990
|
+
</DataTableCell>
|
|
1991
|
+
<DataTableCell>
|
|
1992
|
+
<div className="flex items-center gap-2">
|
|
1993
|
+
<span className="text-lg leading-none">{CustomerService.getFlag(item.country.code)}</span>
|
|
1994
|
+
<span>{item.country.name}</span>
|
|
1995
|
+
</div>
|
|
1996
|
+
</DataTableCell>
|
|
1997
|
+
<DataTableCell>{String(item.company)}</DataTableCell>
|
|
1998
|
+
<DataTableCell>{String(item.representative.name)}</DataTableCell>
|
|
1999
|
+
<DataTableCell>{String(item.date)}</DataTableCell>
|
|
2000
|
+
<DataTableCell>{String(item.activity)}%</DataTableCell>
|
|
2001
|
+
<DataTableCell>
|
|
2002
|
+
<Tag severity={statusSeverity[item.status as string] ?? 'secondary'}>{String(item.status)}</Tag>
|
|
2003
|
+
</DataTableCell>
|
|
2004
|
+
<DataTableCell frozen alignFrozen="right">
|
|
2005
|
+
<span className="font-semibold">${Number(item.balance).toLocaleString()}</span>
|
|
2006
|
+
</DataTableCell>
|
|
2007
|
+
</DataTableRow>
|
|
2008
|
+
)}
|
|
2009
|
+
</DataTableTBody>
|
|
2010
|
+
<DataTableEmptyTBody>
|
|
2011
|
+
<DataTableRow>
|
|
2012
|
+
<DataTableCell colSpan={9}>
|
|
2013
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2014
|
+
<div className="relative">
|
|
2015
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2016
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2017
|
+
</div>
|
|
2018
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2019
|
+
</div>
|
|
2020
|
+
<div>
|
|
2021
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching customers</p>
|
|
2022
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2023
|
+
Hang tight, your data will appear here in a moment.
|
|
2024
|
+
</p>
|
|
2025
|
+
</div>
|
|
2026
|
+
</div>
|
|
2027
|
+
</DataTableCell>
|
|
2028
|
+
</DataTableRow>
|
|
2029
|
+
</DataTableEmptyTBody>
|
|
2030
|
+
</DataTableTable>
|
|
2031
|
+
</DataTableTableContainer>
|
|
2032
|
+
</DataTable>
|
|
2033
|
+
</div>
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
```
|
|
2037
|
+
|
|
2038
|
+
#### Frozen Rows
|
|
2039
|
+
|
|
2040
|
+
Render a `DataTableFrozenTBody` above the regular `DataTableTBody` to keep specific rows pinned to the top.
|
|
2041
|
+
|
|
2042
|
+
```tsx
|
|
2043
|
+
'use client';
|
|
2044
|
+
import { CustomerService, type Customer } from '@/shared/services/customer.service';
|
|
2045
|
+
import { Lock } from '@primeicons/react/lock';
|
|
2046
|
+
import { Database } from '@primeicons/react/database';
|
|
2047
|
+
import { LockOpen } from '@primeicons/react/lock-open';
|
|
2048
|
+
import { Button } from '@/components/ui/button';
|
|
2049
|
+
import {
|
|
2050
|
+
DataTable,
|
|
2051
|
+
DataTableCell,
|
|
2052
|
+
DataTableEmptyTBody,
|
|
2053
|
+
DataTableFrozenTBody,
|
|
2054
|
+
DataTableRow,
|
|
2055
|
+
DataTableTBody,
|
|
2056
|
+
DataTableTHead,
|
|
2057
|
+
DataTableTHeadCell,
|
|
2058
|
+
DataTableTHeadRow,
|
|
2059
|
+
DataTableTHeadTitle,
|
|
2060
|
+
DataTableTable,
|
|
2061
|
+
DataTableTableContainer
|
|
2062
|
+
} from '@/components/ui/datatable';
|
|
2063
|
+
import { Tag } from '@/components/ui/tag';
|
|
2064
|
+
import * as React from 'react';
|
|
2065
|
+
|
|
2066
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'info' | 'danger' | 'secondary'> = {
|
|
2067
|
+
qualified: 'success',
|
|
2068
|
+
unqualified: 'danger',
|
|
2069
|
+
negotiation: 'warn',
|
|
2070
|
+
new: 'info',
|
|
2071
|
+
renewal: 'secondary',
|
|
2072
|
+
proposal: 'info'
|
|
2073
|
+
};
|
|
2074
|
+
|
|
2075
|
+
export default function ScrollFrozenRowsDemo() {
|
|
2076
|
+
const [customers, setCustomers] = React.useState<Customer[]>([]);
|
|
2077
|
+
const [lockedIds, setLockedIds] = React.useState<number[]>([]);
|
|
2078
|
+
|
|
2079
|
+
React.useEffect(() => {
|
|
2080
|
+
const data = CustomerService.getData();
|
|
2081
|
+
|
|
2082
|
+
setCustomers(data);
|
|
2083
|
+
setLockedIds([data[0].id]);
|
|
2084
|
+
}, []);
|
|
2085
|
+
|
|
2086
|
+
const { lockedCustomers, unlockedCustomers } = React.useMemo(() => {
|
|
2087
|
+
const locked: Customer[] = [];
|
|
2088
|
+
const unlocked: Customer[] = [];
|
|
2089
|
+
|
|
2090
|
+
for (const c of customers) {
|
|
2091
|
+
if (lockedIds.includes(c.id)) locked.push(c);
|
|
2092
|
+
else unlocked.push(c);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
return { lockedCustomers: locked, unlockedCustomers: unlocked };
|
|
2096
|
+
}, [customers, lockedIds]);
|
|
2097
|
+
|
|
2098
|
+
const toggleLock = React.useCallback((id: number) => {
|
|
2099
|
+
setLockedIds((prev) => {
|
|
2100
|
+
if (prev.includes(id)) return prev.filter((x) => x !== id);
|
|
2101
|
+
if (prev.length >= 2) return prev;
|
|
2102
|
+
|
|
2103
|
+
return [...prev, id];
|
|
2104
|
+
});
|
|
2105
|
+
}, []);
|
|
2106
|
+
|
|
2107
|
+
const canLockMore = lockedIds.length < 2;
|
|
2108
|
+
|
|
2109
|
+
// Refs so renderRow and tbodyRender keep stable identities across toggles —
|
|
2110
|
+
// avoids invalidating TBody's render-prop and FrozenTBody children on every
|
|
2111
|
+
// lock click. The refs are read at render-time, so each row still gets the
|
|
2112
|
+
// current canLockMore value.
|
|
2113
|
+
const canLockMoreRef = React.useRef(canLockMore);
|
|
2114
|
+
|
|
2115
|
+
canLockMoreRef.current = canLockMore;
|
|
2116
|
+
|
|
2117
|
+
const toggleLockRef = React.useRef(toggleLock);
|
|
2118
|
+
|
|
2119
|
+
toggleLockRef.current = toggleLock;
|
|
2120
|
+
|
|
2121
|
+
const renderRow = React.useCallback((item: Customer, { frozen = false }: { frozen?: boolean } = {}) => {
|
|
2122
|
+
const canLock = canLockMoreRef.current;
|
|
2123
|
+
const onClick = () => toggleLockRef.current(item.id);
|
|
2124
|
+
|
|
2125
|
+
return (
|
|
2126
|
+
<DataTableRow key={item.id}>
|
|
2127
|
+
<DataTableCell>
|
|
2128
|
+
<span className={frozen ? 'font-semibold' : 'font-medium'}>{item.name}</span>
|
|
2129
|
+
</DataTableCell>
|
|
2130
|
+
<DataTableCell>
|
|
2131
|
+
<div className="flex items-center gap-2">
|
|
2132
|
+
<span className="text-lg leading-none">{CustomerService.getFlag(item.country.code)}</span>
|
|
2133
|
+
<span>{item.country.name}</span>
|
|
2134
|
+
</div>
|
|
2135
|
+
</DataTableCell>
|
|
2136
|
+
<DataTableCell>{item.representative.name}</DataTableCell>
|
|
2137
|
+
<DataTableCell>
|
|
2138
|
+
<Tag severity={statusSeverity[item.status] ?? 'secondary'}>{item.status}</Tag>
|
|
2139
|
+
</DataTableCell>
|
|
2140
|
+
<DataTableCell style={{ width: '4rem', textAlign: 'center' }}>
|
|
2141
|
+
<Button
|
|
2142
|
+
variant="text"
|
|
2143
|
+
severity="secondary"
|
|
2144
|
+
size="small"
|
|
2145
|
+
iconOnly
|
|
2146
|
+
disabled={!frozen && !canLock}
|
|
2147
|
+
onClick={onClick}
|
|
2148
|
+
aria-label={frozen ? 'Unlock row' : 'Lock row'}
|
|
2149
|
+
>
|
|
2150
|
+
{frozen ? <LockOpen /> : <Lock />}
|
|
2151
|
+
</Button>
|
|
2152
|
+
</DataTableCell>
|
|
2153
|
+
</DataTableRow>
|
|
2154
|
+
);
|
|
2155
|
+
}, []);
|
|
2156
|
+
|
|
2157
|
+
const tbodyRender = React.useCallback(({ item }: { item: any }) => renderRow(item as Customer), [renderRow]);
|
|
2158
|
+
|
|
2159
|
+
return (
|
|
2160
|
+
<div className="w-full">
|
|
2161
|
+
<DataTable data={unlockedCustomers} scrollable scrollHeight="400px">
|
|
2162
|
+
<DataTableTableContainer>
|
|
2163
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
2164
|
+
<DataTableTHead>
|
|
2165
|
+
<DataTableTHeadRow>
|
|
2166
|
+
<DataTableTHeadCell>
|
|
2167
|
+
<DataTableTHeadTitle>Name</DataTableTHeadTitle>
|
|
2168
|
+
</DataTableTHeadCell>
|
|
2169
|
+
<DataTableTHeadCell>
|
|
2170
|
+
<DataTableTHeadTitle>Country</DataTableTHeadTitle>
|
|
2171
|
+
</DataTableTHeadCell>
|
|
2172
|
+
<DataTableTHeadCell>
|
|
2173
|
+
<DataTableTHeadTitle>Representative</DataTableTHeadTitle>
|
|
2174
|
+
</DataTableTHeadCell>
|
|
2175
|
+
<DataTableTHeadCell>
|
|
2176
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
2177
|
+
</DataTableTHeadCell>
|
|
2178
|
+
<DataTableTHeadCell style={{ width: '4rem' }} />
|
|
2179
|
+
</DataTableTHeadRow>
|
|
2180
|
+
</DataTableTHead>
|
|
2181
|
+
<DataTableFrozenTBody>{lockedCustomers.map((item) => renderRow(item, { frozen: true }))}</DataTableFrozenTBody>
|
|
2182
|
+
<DataTableTBody>{tbodyRender}</DataTableTBody>
|
|
2183
|
+
<DataTableEmptyTBody>
|
|
2184
|
+
<DataTableRow>
|
|
2185
|
+
<DataTableCell colSpan={5}>
|
|
2186
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2187
|
+
<div className="relative">
|
|
2188
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2189
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2190
|
+
</div>
|
|
2191
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2192
|
+
</div>
|
|
2193
|
+
<div>
|
|
2194
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching customers</p>
|
|
2195
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2196
|
+
Hang tight, your data will appear here in a moment.
|
|
2197
|
+
</p>
|
|
2198
|
+
</div>
|
|
2199
|
+
</div>
|
|
2200
|
+
</DataTableCell>
|
|
2201
|
+
</DataTableRow>
|
|
2202
|
+
</DataTableEmptyTBody>
|
|
2203
|
+
</DataTableTable>
|
|
2204
|
+
</DataTableTableContainer>
|
|
2205
|
+
</DataTable>
|
|
2206
|
+
</div>
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
```
|
|
2210
|
+
|
|
2211
|
+
### Row Expansion
|
|
2212
|
+
|
|
2213
|
+
Expand rows to show additional detail content.
|
|
2214
|
+
|
|
2215
|
+
```tsx
|
|
2216
|
+
'use client';
|
|
2217
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
2218
|
+
import { ChevronDown } from '@primeicons/react/chevron-down';
|
|
2219
|
+
import { Database } from '@primeicons/react/database';
|
|
2220
|
+
import { ChevronRight } from '@primeicons/react/chevron-right';
|
|
2221
|
+
import { Star } from '@primeicons/react/star';
|
|
2222
|
+
import { StarFill } from '@primeicons/react/star-fill';
|
|
2223
|
+
import type { DataTableExpansionEvent } from 'primereact/datatable';
|
|
2224
|
+
import {
|
|
2225
|
+
DataTable,
|
|
2226
|
+
DataTableCell,
|
|
2227
|
+
DataTableEmptyTBody,
|
|
2228
|
+
DataTableRow,
|
|
2229
|
+
DataTableRowExpansion,
|
|
2230
|
+
DataTableRowToggle,
|
|
2231
|
+
DataTableRowToggleIndicator,
|
|
2232
|
+
DataTableTBody,
|
|
2233
|
+
DataTableTHead,
|
|
2234
|
+
DataTableTHeadCell,
|
|
2235
|
+
DataTableTHeadRow,
|
|
2236
|
+
DataTableTHeadTitle,
|
|
2237
|
+
DataTableTable,
|
|
2238
|
+
DataTableTableContainer
|
|
2239
|
+
} from '@/components/ui/datatable';
|
|
2240
|
+
import { Rating, RatingOff, RatingOn, RatingOption } from '@/components/ui/rating';
|
|
2241
|
+
import { Tag } from '@/components/ui/tag';
|
|
2242
|
+
import * as React from 'react';
|
|
2243
|
+
|
|
2244
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
2245
|
+
INSTOCK: 'success',
|
|
2246
|
+
LOWSTOCK: 'warn',
|
|
2247
|
+
OUTOFSTOCK: 'danger'
|
|
2248
|
+
};
|
|
2249
|
+
|
|
2250
|
+
const statusLabel: Record<string, string> = {
|
|
2251
|
+
INSTOCK: 'In Stock',
|
|
2252
|
+
LOWSTOCK: 'Low Stock',
|
|
2253
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
2254
|
+
};
|
|
2255
|
+
|
|
2256
|
+
function ProductRating({ value }: { value: number }) {
|
|
2257
|
+
return (
|
|
2258
|
+
<Rating value={value} readOnly>
|
|
2259
|
+
{Array.from({ length: 5 }).map((_, i) => (
|
|
2260
|
+
<RatingOption key={i} index={i}>
|
|
2261
|
+
<RatingOn>
|
|
2262
|
+
<StarFill />
|
|
2263
|
+
</RatingOn>
|
|
2264
|
+
<RatingOff>
|
|
2265
|
+
<Star />
|
|
2266
|
+
</RatingOff>
|
|
2267
|
+
</RatingOption>
|
|
2268
|
+
))}
|
|
2269
|
+
</Rating>
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
function productImageUrl(image: string): string {
|
|
2274
|
+
return `https://primefaces.org/cdn/primevue/images/product/${image}`;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
export default function ExpansionDemo() {
|
|
2278
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
2279
|
+
const [expandedKeys, setExpandedKeys] = React.useState<Record<string, boolean>>({});
|
|
2280
|
+
|
|
2281
|
+
React.useEffect(() => {
|
|
2282
|
+
const data = ProductService.getProductsData().slice(0, 6);
|
|
2283
|
+
|
|
2284
|
+
setProducts(data);
|
|
2285
|
+
|
|
2286
|
+
// Warm the browser cache so the first row-expansion open isn't blocked
|
|
2287
|
+
// on a cold image fetch. The expansion panel itself only mounts when a
|
|
2288
|
+
// row is open, so without this prefetch the user waits for the network
|
|
2289
|
+
// round-trip the first time they click the toggle.
|
|
2290
|
+
for (const product of data) {
|
|
2291
|
+
const img = new window.Image();
|
|
2292
|
+
|
|
2293
|
+
img.src = productImageUrl(product.image);
|
|
2294
|
+
}
|
|
2295
|
+
}, []);
|
|
2296
|
+
|
|
2297
|
+
return (
|
|
2298
|
+
<div className="w-full">
|
|
2299
|
+
<DataTable
|
|
2300
|
+
data={products}
|
|
2301
|
+
dataKey="id"
|
|
2302
|
+
expandedKeys={expandedKeys}
|
|
2303
|
+
onExpandedChange={(e: DataTableExpansionEvent) => setExpandedKeys(e.value)}
|
|
2304
|
+
>
|
|
2305
|
+
<DataTableTableContainer>
|
|
2306
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
2307
|
+
<DataTableTHead>
|
|
2308
|
+
<DataTableTHeadRow>
|
|
2309
|
+
<DataTableTHeadCell style={{ width: '3rem' }} />
|
|
2310
|
+
<DataTableTHeadCell>
|
|
2311
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
2312
|
+
</DataTableTHeadCell>
|
|
2313
|
+
<DataTableTHeadCell>
|
|
2314
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
2315
|
+
</DataTableTHeadCell>
|
|
2316
|
+
<DataTableTHeadCell>
|
|
2317
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
2318
|
+
</DataTableTHeadCell>
|
|
2319
|
+
<DataTableTHeadCell>
|
|
2320
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
2321
|
+
</DataTableTHeadCell>
|
|
2322
|
+
</DataTableTHeadRow>
|
|
2323
|
+
</DataTableTHead>
|
|
2324
|
+
<DataTableTBody>
|
|
2325
|
+
{({ item }: { item: any }) => (
|
|
2326
|
+
<React.Fragment key={item.id}>
|
|
2327
|
+
<DataTableRow>
|
|
2328
|
+
<DataTableCell>
|
|
2329
|
+
<DataTableRowToggle>
|
|
2330
|
+
<DataTableRowToggleIndicator match="expanded">
|
|
2331
|
+
<ChevronDown />
|
|
2332
|
+
</DataTableRowToggleIndicator>
|
|
2333
|
+
<DataTableRowToggleIndicator match="collapsed">
|
|
2334
|
+
<ChevronRight />
|
|
2335
|
+
</DataTableRowToggleIndicator>
|
|
2336
|
+
</DataTableRowToggle>
|
|
2337
|
+
</DataTableCell>
|
|
2338
|
+
<DataTableCell>
|
|
2339
|
+
<span className="font-medium">{item.name}</span>
|
|
2340
|
+
</DataTableCell>
|
|
2341
|
+
<DataTableCell>
|
|
2342
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
2343
|
+
</DataTableCell>
|
|
2344
|
+
<DataTableCell>
|
|
2345
|
+
<span className="font-semibold">${item.price}</span>
|
|
2346
|
+
</DataTableCell>
|
|
2347
|
+
<DataTableCell>
|
|
2348
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
2349
|
+
</DataTableCell>
|
|
2350
|
+
</DataTableRow>
|
|
2351
|
+
<DataTableRowExpansion>
|
|
2352
|
+
<DataTableCell colSpan={5}>
|
|
2353
|
+
<div className="flex gap-4 p-4 bg-surface-50 dark:bg-surface-900">
|
|
2354
|
+
<img
|
|
2355
|
+
src={productImageUrl(item.image)}
|
|
2356
|
+
alt={item.name}
|
|
2357
|
+
width={120}
|
|
2358
|
+
height={120}
|
|
2359
|
+
loading="eager"
|
|
2360
|
+
decoding="async"
|
|
2361
|
+
style={{ width: 120, height: 120, objectFit: 'cover', flexShrink: 0 }}
|
|
2362
|
+
className="rounded-md shadow-md"
|
|
2363
|
+
/>
|
|
2364
|
+
<div className="flex flex-col gap-2 flex-1">
|
|
2365
|
+
<h4 className="m-0 text-base font-semibold">{item.name}</h4>
|
|
2366
|
+
<div className="text-xs text-surface-500 dark:text-surface-400">SKU: {item.code}</div>
|
|
2367
|
+
<div className="flex items-center gap-2">
|
|
2368
|
+
<ProductRating value={item.rating} />
|
|
2369
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">({item.rating}/5)</span>
|
|
2370
|
+
</div>
|
|
2371
|
+
<div className="flex items-center gap-6 text-sm">
|
|
2372
|
+
<div>
|
|
2373
|
+
<div className="text-xs text-surface-500 dark:text-surface-400">Category</div>
|
|
2374
|
+
<div className="font-medium">{item.category}</div>
|
|
2375
|
+
</div>
|
|
2376
|
+
<div>
|
|
2377
|
+
<div className="text-xs text-surface-500 dark:text-surface-400">In stock</div>
|
|
2378
|
+
<div className="font-medium">{item.quantity} units</div>
|
|
2379
|
+
</div>
|
|
2380
|
+
<div>
|
|
2381
|
+
<div className="text-xs text-surface-500 dark:text-surface-400">Price</div>
|
|
2382
|
+
<div className="font-semibold">${item.price}</div>
|
|
2383
|
+
</div>
|
|
2384
|
+
</div>
|
|
2385
|
+
</div>
|
|
2386
|
+
</div>
|
|
2387
|
+
</DataTableCell>
|
|
2388
|
+
</DataTableRowExpansion>
|
|
2389
|
+
</React.Fragment>
|
|
2390
|
+
)}
|
|
2391
|
+
</DataTableTBody>
|
|
2392
|
+
<DataTableEmptyTBody>
|
|
2393
|
+
<DataTableRow>
|
|
2394
|
+
<DataTableCell colSpan={5}>
|
|
2395
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2396
|
+
<div className="relative">
|
|
2397
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2398
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2399
|
+
</div>
|
|
2400
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2401
|
+
</div>
|
|
2402
|
+
<div>
|
|
2403
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
2404
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2405
|
+
Hang tight, your data will appear here in a moment.
|
|
2406
|
+
</p>
|
|
2407
|
+
</div>
|
|
2408
|
+
</div>
|
|
2409
|
+
</DataTableCell>
|
|
2410
|
+
</DataTableRow>
|
|
2411
|
+
</DataTableEmptyTBody>
|
|
2412
|
+
</DataTableTable>
|
|
2413
|
+
</DataTableTableContainer>
|
|
2414
|
+
</DataTable>
|
|
2415
|
+
</div>
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
```
|
|
2419
|
+
|
|
2420
|
+
### Column Resize
|
|
2421
|
+
|
|
2422
|
+
Enable column resizing with drag handles via the `resizableColumns` prop.
|
|
2423
|
+
|
|
2424
|
+
#### Fit Mode
|
|
2425
|
+
|
|
2426
|
+
Dragging takes width from the adjacent column; total table width stays the same.
|
|
2427
|
+
|
|
2428
|
+
```tsx
|
|
2429
|
+
'use client';
|
|
2430
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
2431
|
+
import { Database } from '@primeicons/react/database';
|
|
2432
|
+
import {
|
|
2433
|
+
DataTable,
|
|
2434
|
+
DataTableCell,
|
|
2435
|
+
DataTableColumnResizeIndicator,
|
|
2436
|
+
DataTableColumnResizer,
|
|
2437
|
+
DataTableEmptyTBody,
|
|
2438
|
+
DataTableRow,
|
|
2439
|
+
DataTableTBody,
|
|
2440
|
+
DataTableTHead,
|
|
2441
|
+
DataTableTHeadCell,
|
|
2442
|
+
DataTableTHeadRow,
|
|
2443
|
+
DataTableTHeadTitle,
|
|
2444
|
+
DataTableTable,
|
|
2445
|
+
DataTableTableContainer
|
|
2446
|
+
} from '@/components/ui/datatable';
|
|
2447
|
+
import { Tag } from '@/components/ui/tag';
|
|
2448
|
+
import Image from 'next/image';
|
|
2449
|
+
import * as React from 'react';
|
|
2450
|
+
|
|
2451
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
2452
|
+
INSTOCK: 'success',
|
|
2453
|
+
LOWSTOCK: 'warn',
|
|
2454
|
+
OUTOFSTOCK: 'danger'
|
|
2455
|
+
};
|
|
2456
|
+
|
|
2457
|
+
const statusLabel: Record<string, string> = {
|
|
2458
|
+
INSTOCK: 'In Stock',
|
|
2459
|
+
LOWSTOCK: 'Low Stock',
|
|
2460
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
2461
|
+
};
|
|
2462
|
+
|
|
2463
|
+
export default function ResizeDemo() {
|
|
2464
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
2465
|
+
|
|
2466
|
+
React.useEffect(() => {
|
|
2467
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
2468
|
+
}, []);
|
|
2469
|
+
|
|
2470
|
+
return (
|
|
2471
|
+
<div className="w-full">
|
|
2472
|
+
<DataTable data={products} resizableColumns columnResizeMode="fit" showGridlines>
|
|
2473
|
+
<DataTableTableContainer>
|
|
2474
|
+
<DataTableColumnResizeIndicator />
|
|
2475
|
+
<DataTableTable style={{ width: '100%', tableLayout: 'fixed' }}>
|
|
2476
|
+
<DataTableTHead>
|
|
2477
|
+
<DataTableTHeadRow>
|
|
2478
|
+
<DataTableTHeadCell>
|
|
2479
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
2480
|
+
<DataTableColumnResizer />
|
|
2481
|
+
</DataTableTHeadCell>
|
|
2482
|
+
<DataTableTHeadCell>
|
|
2483
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
2484
|
+
<DataTableColumnResizer />
|
|
2485
|
+
</DataTableTHeadCell>
|
|
2486
|
+
<DataTableTHeadCell>
|
|
2487
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
2488
|
+
<DataTableColumnResizer />
|
|
2489
|
+
</DataTableTHeadCell>
|
|
2490
|
+
<DataTableTHeadCell>
|
|
2491
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
2492
|
+
<DataTableColumnResizer />
|
|
2493
|
+
</DataTableTHeadCell>
|
|
2494
|
+
</DataTableTHeadRow>
|
|
2495
|
+
</DataTableTHead>
|
|
2496
|
+
<DataTableTBody>
|
|
2497
|
+
{({ item }: { item: any }) => (
|
|
2498
|
+
<DataTableRow key={item.id}>
|
|
2499
|
+
<DataTableCell>
|
|
2500
|
+
<div className="flex items-center gap-3">
|
|
2501
|
+
<Image
|
|
2502
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
2503
|
+
alt={item.name}
|
|
2504
|
+
width={36}
|
|
2505
|
+
height={36}
|
|
2506
|
+
className="rounded-md shadow"
|
|
2507
|
+
/>
|
|
2508
|
+
<span className="font-medium truncate">{item.name}</span>
|
|
2509
|
+
</div>
|
|
2510
|
+
</DataTableCell>
|
|
2511
|
+
<DataTableCell>
|
|
2512
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
2513
|
+
</DataTableCell>
|
|
2514
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
2515
|
+
<DataTableCell>
|
|
2516
|
+
<span className="font-semibold">${item.price}</span>
|
|
2517
|
+
</DataTableCell>
|
|
2518
|
+
</DataTableRow>
|
|
2519
|
+
)}
|
|
2520
|
+
</DataTableTBody>
|
|
2521
|
+
<DataTableEmptyTBody>
|
|
2522
|
+
<DataTableRow>
|
|
2523
|
+
<DataTableCell colSpan={4}>
|
|
2524
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2525
|
+
<div className="relative">
|
|
2526
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2527
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2528
|
+
</div>
|
|
2529
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2530
|
+
</div>
|
|
2531
|
+
<div>
|
|
2532
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
2533
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2534
|
+
Hang tight, your data will appear here in a moment.
|
|
2535
|
+
</p>
|
|
2536
|
+
</div>
|
|
2537
|
+
</div>
|
|
2538
|
+
</DataTableCell>
|
|
2539
|
+
</DataTableRow>
|
|
2540
|
+
</DataTableEmptyTBody>
|
|
2541
|
+
</DataTableTable>
|
|
2542
|
+
</DataTableTableContainer>
|
|
2543
|
+
</DataTable>
|
|
2544
|
+
</div>
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
```
|
|
2548
|
+
|
|
2549
|
+
#### Expand Mode
|
|
2550
|
+
|
|
2551
|
+
Dragging grows or shrinks the whole table; adjacent columns keep their widths.
|
|
2552
|
+
|
|
2553
|
+
```tsx
|
|
2554
|
+
'use client';
|
|
2555
|
+
import Image from 'next/image';
|
|
2556
|
+
import { Database } from '@primeicons/react/database';
|
|
2557
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
2558
|
+
import {
|
|
2559
|
+
DataTable,
|
|
2560
|
+
DataTableCell,
|
|
2561
|
+
DataTableColumnResizeIndicator,
|
|
2562
|
+
DataTableColumnResizer,
|
|
2563
|
+
DataTableEmptyTBody,
|
|
2564
|
+
DataTableRow,
|
|
2565
|
+
DataTableTBody,
|
|
2566
|
+
DataTableTHead,
|
|
2567
|
+
DataTableTHeadCell,
|
|
2568
|
+
DataTableTHeadRow,
|
|
2569
|
+
DataTableTHeadTitle,
|
|
2570
|
+
DataTableTable,
|
|
2571
|
+
DataTableTableContainer
|
|
2572
|
+
} from '@/components/ui/datatable';
|
|
2573
|
+
import { Tag } from '@/components/ui/tag';
|
|
2574
|
+
import * as React from 'react';
|
|
2575
|
+
|
|
2576
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
2577
|
+
INSTOCK: 'success',
|
|
2578
|
+
LOWSTOCK: 'warn',
|
|
2579
|
+
OUTOFSTOCK: 'danger'
|
|
2580
|
+
};
|
|
2581
|
+
|
|
2582
|
+
const statusLabel: Record<string, string> = {
|
|
2583
|
+
INSTOCK: 'In Stock',
|
|
2584
|
+
LOWSTOCK: 'Low Stock',
|
|
2585
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
2586
|
+
};
|
|
2587
|
+
|
|
2588
|
+
export default function ResizeExpandDemo() {
|
|
2589
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
2590
|
+
|
|
2591
|
+
React.useEffect(() => {
|
|
2592
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
2593
|
+
}, []);
|
|
2594
|
+
|
|
2595
|
+
return (
|
|
2596
|
+
<div className="w-full">
|
|
2597
|
+
<DataTable data={products} resizableColumns columnResizeMode="expand" showGridlines scrollable scrollHeight="400px">
|
|
2598
|
+
<DataTableTableContainer>
|
|
2599
|
+
<DataTableColumnResizeIndicator />
|
|
2600
|
+
<DataTableTable style={{ tableLayout: 'fixed', minWidth: '50rem' }}>
|
|
2601
|
+
<DataTableTHead>
|
|
2602
|
+
<DataTableTHeadRow>
|
|
2603
|
+
<DataTableTHeadCell>
|
|
2604
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
2605
|
+
<DataTableColumnResizer />
|
|
2606
|
+
</DataTableTHeadCell>
|
|
2607
|
+
<DataTableTHeadCell>
|
|
2608
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
2609
|
+
<DataTableColumnResizer />
|
|
2610
|
+
</DataTableTHeadCell>
|
|
2611
|
+
<DataTableTHeadCell>
|
|
2612
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
2613
|
+
<DataTableColumnResizer />
|
|
2614
|
+
</DataTableTHeadCell>
|
|
2615
|
+
<DataTableTHeadCell>
|
|
2616
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
2617
|
+
<DataTableColumnResizer />
|
|
2618
|
+
</DataTableTHeadCell>
|
|
2619
|
+
<DataTableTHeadCell>
|
|
2620
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
2621
|
+
</DataTableTHeadCell>
|
|
2622
|
+
</DataTableTHeadRow>
|
|
2623
|
+
</DataTableTHead>
|
|
2624
|
+
<DataTableTBody>
|
|
2625
|
+
{({ item }: { item: any }) => (
|
|
2626
|
+
<DataTableRow key={item.id}>
|
|
2627
|
+
<DataTableCell>
|
|
2628
|
+
<div className="flex items-center gap-3">
|
|
2629
|
+
<Image
|
|
2630
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
2631
|
+
alt={item.name}
|
|
2632
|
+
width={36}
|
|
2633
|
+
height={36}
|
|
2634
|
+
className="rounded-md shadow"
|
|
2635
|
+
/>
|
|
2636
|
+
<span className="font-medium truncate">{item.name}</span>
|
|
2637
|
+
</div>
|
|
2638
|
+
</DataTableCell>
|
|
2639
|
+
<DataTableCell>
|
|
2640
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
2641
|
+
</DataTableCell>
|
|
2642
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
2643
|
+
<DataTableCell>
|
|
2644
|
+
<span className="font-semibold">${item.price}</span>
|
|
2645
|
+
</DataTableCell>
|
|
2646
|
+
<DataTableCell>
|
|
2647
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
2648
|
+
</DataTableCell>
|
|
2649
|
+
</DataTableRow>
|
|
2650
|
+
)}
|
|
2651
|
+
</DataTableTBody>
|
|
2652
|
+
<DataTableEmptyTBody>
|
|
2653
|
+
<DataTableRow>
|
|
2654
|
+
<DataTableCell colSpan={5}>
|
|
2655
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2656
|
+
<div className="relative">
|
|
2657
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2658
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2659
|
+
</div>
|
|
2660
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2661
|
+
</div>
|
|
2662
|
+
<div>
|
|
2663
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
2664
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2665
|
+
Hang tight, your data will appear here in a moment.
|
|
2666
|
+
</p>
|
|
2667
|
+
</div>
|
|
2668
|
+
</div>
|
|
2669
|
+
</DataTableCell>
|
|
2670
|
+
</DataTableRow>
|
|
2671
|
+
</DataTableEmptyTBody>
|
|
2672
|
+
</DataTableTable>
|
|
2673
|
+
</DataTableTableContainer>
|
|
2674
|
+
</DataTable>
|
|
2675
|
+
</div>
|
|
2676
|
+
);
|
|
2677
|
+
}
|
|
2678
|
+
```
|
|
2679
|
+
|
|
2680
|
+
### Column Reorder
|
|
2681
|
+
|
|
2682
|
+
Drag and drop column headers to reorder columns.
|
|
2683
|
+
|
|
2684
|
+
```tsx
|
|
2685
|
+
'use client';
|
|
2686
|
+
import Image from 'next/image';
|
|
2687
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
2688
|
+
import type { DataTableColumnReorderEvent } from 'primereact/datatable';
|
|
2689
|
+
import { ArrowDown } from '@primeicons/react/arrow-down';
|
|
2690
|
+
import { ArrowUp } from '@primeicons/react/arrow-up';
|
|
2691
|
+
import { Bars } from '@primeicons/react/bars';
|
|
2692
|
+
import { Database } from '@primeicons/react/database';
|
|
2693
|
+
import {
|
|
2694
|
+
DataTable,
|
|
2695
|
+
DataTableCell,
|
|
2696
|
+
DataTableColumnReorder,
|
|
2697
|
+
DataTableColumnReorderIndicatorDown,
|
|
2698
|
+
DataTableColumnReorderIndicatorUp,
|
|
2699
|
+
DataTableColumnReorderTarget,
|
|
2700
|
+
DataTableEmptyTBody,
|
|
2701
|
+
DataTableRow,
|
|
2702
|
+
DataTableTBody,
|
|
2703
|
+
DataTableTHead,
|
|
2704
|
+
DataTableTHeadCell,
|
|
2705
|
+
DataTableTHeadRow,
|
|
2706
|
+
DataTableTHeadTitle,
|
|
2707
|
+
DataTableTable,
|
|
2708
|
+
DataTableTableContainer
|
|
2709
|
+
} from '@/components/ui/datatable';
|
|
2710
|
+
import { Tag } from '@/components/ui/tag';
|
|
2711
|
+
import * as React from 'react';
|
|
2712
|
+
|
|
2713
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
2714
|
+
INSTOCK: 'success',
|
|
2715
|
+
LOWSTOCK: 'warn',
|
|
2716
|
+
OUTOFSTOCK: 'danger'
|
|
2717
|
+
};
|
|
2718
|
+
|
|
2719
|
+
const statusLabel: Record<string, string> = {
|
|
2720
|
+
INSTOCK: 'In Stock',
|
|
2721
|
+
LOWSTOCK: 'Low Stock',
|
|
2722
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
2723
|
+
};
|
|
2724
|
+
|
|
2725
|
+
interface Column {
|
|
2726
|
+
field: string;
|
|
2727
|
+
header: string;
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
const initialColumns: Column[] = [
|
|
2731
|
+
{ field: 'name', header: 'Product' },
|
|
2732
|
+
{ field: 'category', header: 'Category' },
|
|
2733
|
+
{ field: 'quantity', header: 'Quantity' },
|
|
2734
|
+
{ field: 'price', header: 'Price' },
|
|
2735
|
+
{ field: 'inventoryStatus', header: 'Status' }
|
|
2736
|
+
];
|
|
2737
|
+
|
|
2738
|
+
function renderCell(field: string, item: Product) {
|
|
2739
|
+
switch (field) {
|
|
2740
|
+
case 'name':
|
|
2741
|
+
return (
|
|
2742
|
+
<div className="flex items-center gap-3">
|
|
2743
|
+
<Image
|
|
2744
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
2745
|
+
alt={item.name}
|
|
2746
|
+
width={36}
|
|
2747
|
+
height={36}
|
|
2748
|
+
className="rounded-md shadow"
|
|
2749
|
+
/>
|
|
2750
|
+
<span className="font-medium">{item.name}</span>
|
|
2751
|
+
</div>
|
|
2752
|
+
);
|
|
2753
|
+
case 'category':
|
|
2754
|
+
return <Tag severity="secondary">{item.category}</Tag>;
|
|
2755
|
+
case 'quantity':
|
|
2756
|
+
return item.quantity;
|
|
2757
|
+
case 'price':
|
|
2758
|
+
return <span className="font-semibold">${item.price}</span>;
|
|
2759
|
+
case 'inventoryStatus':
|
|
2760
|
+
return <Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>;
|
|
2761
|
+
default:
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
export default function ReorderDemo() {
|
|
2767
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
2768
|
+
const [columns, setColumns] = React.useState<Column[]>(initialColumns);
|
|
2769
|
+
|
|
2770
|
+
React.useEffect(() => {
|
|
2771
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
2772
|
+
}, []);
|
|
2773
|
+
|
|
2774
|
+
return (
|
|
2775
|
+
<div className="w-full">
|
|
2776
|
+
<DataTable
|
|
2777
|
+
data={products}
|
|
2778
|
+
reorderableColumns
|
|
2779
|
+
onColumnReorder={(e: DataTableColumnReorderEvent) => {
|
|
2780
|
+
const reordered = [...columns];
|
|
2781
|
+
const [moved] = reordered.splice(e.dragIndex, 1);
|
|
2782
|
+
|
|
2783
|
+
reordered.splice(e.dropIndex, 0, moved);
|
|
2784
|
+
setColumns(reordered);
|
|
2785
|
+
}}
|
|
2786
|
+
>
|
|
2787
|
+
<DataTableTableContainer>
|
|
2788
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
2789
|
+
<DataTableTHead>
|
|
2790
|
+
<DataTableTHeadRow>
|
|
2791
|
+
{columns.map((col) => (
|
|
2792
|
+
<DataTableTHeadCell key={col.field}>
|
|
2793
|
+
<DataTableColumnReorderTarget className="group cursor-grab select-none">
|
|
2794
|
+
<DataTableColumnReorder className="text-surface-400 transition-colors group-hover:text-surface-600">
|
|
2795
|
+
<Bars />
|
|
2796
|
+
</DataTableColumnReorder>
|
|
2797
|
+
<DataTableTHeadTitle>{col.header}</DataTableTHeadTitle>
|
|
2798
|
+
</DataTableColumnReorderTarget>
|
|
2799
|
+
</DataTableTHeadCell>
|
|
2800
|
+
))}
|
|
2801
|
+
</DataTableTHeadRow>
|
|
2802
|
+
</DataTableTHead>
|
|
2803
|
+
<DataTableTBody key={columns.map((c) => c.field).join(',')}>
|
|
2804
|
+
{({ item }: { item: any }) => (
|
|
2805
|
+
<DataTableRow key={item.id}>
|
|
2806
|
+
{columns.map((col) => (
|
|
2807
|
+
<DataTableCell key={col.field}>{renderCell(col.field, item)}</DataTableCell>
|
|
2808
|
+
))}
|
|
2809
|
+
</DataTableRow>
|
|
2810
|
+
)}
|
|
2811
|
+
</DataTableTBody>
|
|
2812
|
+
<DataTableEmptyTBody>
|
|
2813
|
+
<DataTableRow>
|
|
2814
|
+
<DataTableCell colSpan={columns.length}>
|
|
2815
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2816
|
+
<div className="relative">
|
|
2817
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2818
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2819
|
+
</div>
|
|
2820
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2821
|
+
</div>
|
|
2822
|
+
<div>
|
|
2823
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
2824
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2825
|
+
Hang tight, your data will appear here in a moment.
|
|
2826
|
+
</p>
|
|
2827
|
+
</div>
|
|
2828
|
+
</div>
|
|
2829
|
+
</DataTableCell>
|
|
2830
|
+
</DataTableRow>
|
|
2831
|
+
</DataTableEmptyTBody>
|
|
2832
|
+
</DataTableTable>
|
|
2833
|
+
</DataTableTableContainer>
|
|
2834
|
+
<DataTableColumnReorderIndicatorUp>
|
|
2835
|
+
<ArrowDown />
|
|
2836
|
+
</DataTableColumnReorderIndicatorUp>
|
|
2837
|
+
<DataTableColumnReorderIndicatorDown>
|
|
2838
|
+
<ArrowUp />
|
|
2839
|
+
</DataTableColumnReorderIndicatorDown>
|
|
2840
|
+
</DataTable>
|
|
2841
|
+
</div>
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
```
|
|
2845
|
+
|
|
2846
|
+
### Row Reorder
|
|
2847
|
+
|
|
2848
|
+
Drag and drop rows to reorder data.
|
|
2849
|
+
|
|
2850
|
+
```tsx
|
|
2851
|
+
'use client';
|
|
2852
|
+
import Image from 'next/image';
|
|
2853
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
2854
|
+
import { Bars } from '@primeicons/react/bars';
|
|
2855
|
+
import { Database } from '@primeicons/react/database';
|
|
2856
|
+
import type { DataTableRowReorderEvent } from 'primereact/datatable';
|
|
2857
|
+
import {
|
|
2858
|
+
DataTable,
|
|
2859
|
+
DataTableCell,
|
|
2860
|
+
DataTableEmptyTBody,
|
|
2861
|
+
DataTableRow,
|
|
2862
|
+
DataTableRowReorder,
|
|
2863
|
+
DataTableTBody,
|
|
2864
|
+
DataTableTHead,
|
|
2865
|
+
DataTableTHeadCell,
|
|
2866
|
+
DataTableTHeadRow,
|
|
2867
|
+
DataTableTHeadTitle,
|
|
2868
|
+
DataTableTable,
|
|
2869
|
+
DataTableTableContainer
|
|
2870
|
+
} from '@/components/ui/datatable';
|
|
2871
|
+
import { Tag } from '@/components/ui/tag';
|
|
2872
|
+
import * as React from 'react';
|
|
2873
|
+
|
|
2874
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
2875
|
+
INSTOCK: 'success',
|
|
2876
|
+
LOWSTOCK: 'warn',
|
|
2877
|
+
OUTOFSTOCK: 'danger'
|
|
2878
|
+
};
|
|
2879
|
+
|
|
2880
|
+
const statusLabel: Record<string, string> = {
|
|
2881
|
+
INSTOCK: 'In Stock',
|
|
2882
|
+
LOWSTOCK: 'Low Stock',
|
|
2883
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
2884
|
+
};
|
|
2885
|
+
|
|
2886
|
+
export default function RowReorderDemo() {
|
|
2887
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
2888
|
+
|
|
2889
|
+
React.useEffect(() => {
|
|
2890
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
2891
|
+
}, []);
|
|
2892
|
+
|
|
2893
|
+
return (
|
|
2894
|
+
<div className="w-full">
|
|
2895
|
+
<DataTable
|
|
2896
|
+
data={products}
|
|
2897
|
+
dataKey="id"
|
|
2898
|
+
reorderableRows
|
|
2899
|
+
onRowReorder={(e: DataTableRowReorderEvent) => setProducts(e.value as unknown as Product[])}
|
|
2900
|
+
>
|
|
2901
|
+
<DataTableTableContainer>
|
|
2902
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
2903
|
+
<DataTableTHead>
|
|
2904
|
+
<DataTableTHeadRow>
|
|
2905
|
+
<DataTableTHeadCell style={{ width: '3rem' }} />
|
|
2906
|
+
<DataTableTHeadCell>
|
|
2907
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
2908
|
+
</DataTableTHeadCell>
|
|
2909
|
+
<DataTableTHeadCell>
|
|
2910
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
2911
|
+
</DataTableTHeadCell>
|
|
2912
|
+
<DataTableTHeadCell>
|
|
2913
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
2914
|
+
</DataTableTHeadCell>
|
|
2915
|
+
<DataTableTHeadCell>
|
|
2916
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
2917
|
+
</DataTableTHeadCell>
|
|
2918
|
+
</DataTableTHeadRow>
|
|
2919
|
+
</DataTableTHead>
|
|
2920
|
+
<DataTableTBody>
|
|
2921
|
+
{({ item, index }: { item: any; index: number }) => (
|
|
2922
|
+
<DataTableRow key={item.id}>
|
|
2923
|
+
<DataTableCell>
|
|
2924
|
+
<DataTableRowReorder rowIndex={index} className="cursor-grab text-surface-400 hover:text-surface-600">
|
|
2925
|
+
<Bars />
|
|
2926
|
+
</DataTableRowReorder>
|
|
2927
|
+
</DataTableCell>
|
|
2928
|
+
<DataTableCell>
|
|
2929
|
+
<div className="flex items-center gap-3">
|
|
2930
|
+
<Image
|
|
2931
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
2932
|
+
alt={item.name}
|
|
2933
|
+
width={36}
|
|
2934
|
+
height={36}
|
|
2935
|
+
className="rounded-md shadow"
|
|
2936
|
+
/>
|
|
2937
|
+
<div className="flex flex-col">
|
|
2938
|
+
<span className="font-medium">{item.name}</span>
|
|
2939
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
2940
|
+
</div>
|
|
2941
|
+
</div>
|
|
2942
|
+
</DataTableCell>
|
|
2943
|
+
<DataTableCell>
|
|
2944
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
2945
|
+
</DataTableCell>
|
|
2946
|
+
<DataTableCell>
|
|
2947
|
+
<span className="font-semibold">${item.price}</span>
|
|
2948
|
+
</DataTableCell>
|
|
2949
|
+
<DataTableCell>
|
|
2950
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
2951
|
+
</DataTableCell>
|
|
2952
|
+
</DataTableRow>
|
|
2953
|
+
)}
|
|
2954
|
+
</DataTableTBody>
|
|
2955
|
+
<DataTableEmptyTBody>
|
|
2956
|
+
<DataTableRow>
|
|
2957
|
+
<DataTableCell colSpan={5}>
|
|
2958
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
2959
|
+
<div className="relative">
|
|
2960
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
2961
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
2962
|
+
</div>
|
|
2963
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
2964
|
+
</div>
|
|
2965
|
+
<div>
|
|
2966
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
2967
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
2968
|
+
Hang tight, your data will appear here in a moment.
|
|
2969
|
+
</p>
|
|
2970
|
+
</div>
|
|
2971
|
+
</div>
|
|
2972
|
+
</DataTableCell>
|
|
2973
|
+
</DataTableRow>
|
|
2974
|
+
</DataTableEmptyTBody>
|
|
2975
|
+
</DataTableTable>
|
|
2976
|
+
</DataTableTableContainer>
|
|
2977
|
+
</DataTable>
|
|
2978
|
+
</div>
|
|
2979
|
+
);
|
|
2980
|
+
}
|
|
2981
|
+
```
|
|
2982
|
+
|
|
2983
|
+
### Column Group
|
|
2984
|
+
|
|
2985
|
+
Multi-level headers with rowspan and colspan.
|
|
2986
|
+
|
|
2987
|
+
```tsx
|
|
2988
|
+
'use client';
|
|
2989
|
+
import { ArrowDown } from '@primeicons/react/arrow-down';
|
|
2990
|
+
import { Database } from '@primeicons/react/database';
|
|
2991
|
+
import { ArrowUp } from '@primeicons/react/arrow-up';
|
|
2992
|
+
import {
|
|
2993
|
+
DataTable,
|
|
2994
|
+
DataTableCell,
|
|
2995
|
+
DataTableEmptyTBody,
|
|
2996
|
+
DataTableRow,
|
|
2997
|
+
DataTableTBody,
|
|
2998
|
+
DataTableTFoot,
|
|
2999
|
+
DataTableTFootCell,
|
|
3000
|
+
DataTableTFootRow,
|
|
3001
|
+
DataTableTHead,
|
|
3002
|
+
DataTableTHeadCell,
|
|
3003
|
+
DataTableTHeadRow,
|
|
3004
|
+
DataTableTHeadTitle,
|
|
3005
|
+
DataTableTable,
|
|
3006
|
+
DataTableTableContainer
|
|
3007
|
+
} from '@/components/ui/datatable';
|
|
3008
|
+
import { Tag } from '@/components/ui/tag';
|
|
3009
|
+
import * as React from 'react';
|
|
3010
|
+
|
|
3011
|
+
interface Sale {
|
|
3012
|
+
id: string;
|
|
3013
|
+
product: string;
|
|
3014
|
+
lastYearSale: number;
|
|
3015
|
+
thisYearSale: number;
|
|
3016
|
+
lastYearProfit: number;
|
|
3017
|
+
thisYearProfit: number;
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
const sales: Sale[] = [
|
|
3021
|
+
{ id: '1', product: 'Bamboo Watch', lastYearSale: 51, thisYearSale: 40, lastYearProfit: 54406, thisYearProfit: 43342 },
|
|
3022
|
+
{ id: '2', product: 'Black Watch', lastYearSale: 83, thisYearSale: 9, lastYearProfit: 423132, thisYearProfit: 312122 },
|
|
3023
|
+
{ id: '3', product: 'Blue Band', lastYearSale: 38, thisYearSale: 5, lastYearProfit: 12321, thisYearProfit: 8500 },
|
|
3024
|
+
{ id: '4', product: 'Blue T-Shirt', lastYearSale: 49, thisYearSale: 22, lastYearProfit: 745232, thisYearProfit: 650323 },
|
|
3025
|
+
{ id: '5', product: 'Bracelet', lastYearSale: 17, thisYearSale: 79, lastYearProfit: 643242, thisYearProfit: 500332 }
|
|
3026
|
+
];
|
|
3027
|
+
|
|
3028
|
+
function Trend({ current, previous }: { current: number; previous: number }) {
|
|
3029
|
+
const up = current >= previous;
|
|
3030
|
+
|
|
3031
|
+
return (
|
|
3032
|
+
<div className="inline-flex items-center gap-1">
|
|
3033
|
+
<span>{current}%</span>
|
|
3034
|
+
{up ? <ArrowUp className="w-3 h-3 text-green-500" /> : <ArrowDown className="w-3 h-3 text-red-500" />}
|
|
3035
|
+
</div>
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
export default function ColumnGroupDemo() {
|
|
3040
|
+
const totals = React.useMemo(() => {
|
|
3041
|
+
return sales.reduce(
|
|
3042
|
+
(acc, s) => ({
|
|
3043
|
+
lastYear: acc.lastYear + s.lastYearProfit,
|
|
3044
|
+
thisYear: acc.thisYear + s.thisYearProfit
|
|
3045
|
+
}),
|
|
3046
|
+
{ lastYear: 0, thisYear: 0 }
|
|
3047
|
+
);
|
|
3048
|
+
}, []);
|
|
3049
|
+
|
|
3050
|
+
return (
|
|
3051
|
+
<div className="w-full">
|
|
3052
|
+
<DataTable data={sales} showGridlines>
|
|
3053
|
+
<DataTableTableContainer>
|
|
3054
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
3055
|
+
<DataTableTHead>
|
|
3056
|
+
<DataTableTHeadRow>
|
|
3057
|
+
<DataTableTHeadCell rowSpan={2}>
|
|
3058
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
3059
|
+
</DataTableTHeadCell>
|
|
3060
|
+
<DataTableTHeadCell colSpan={2} style={{ textAlign: 'center' }}>
|
|
3061
|
+
<DataTableTHeadTitle>Sale Rate</DataTableTHeadTitle>
|
|
3062
|
+
</DataTableTHeadCell>
|
|
3063
|
+
<DataTableTHeadCell colSpan={2} style={{ textAlign: 'center' }}>
|
|
3064
|
+
<DataTableTHeadTitle>Profits</DataTableTHeadTitle>
|
|
3065
|
+
</DataTableTHeadCell>
|
|
3066
|
+
</DataTableTHeadRow>
|
|
3067
|
+
<DataTableTHeadRow>
|
|
3068
|
+
<DataTableTHeadCell>
|
|
3069
|
+
<DataTableTHeadTitle>Last Year</DataTableTHeadTitle>
|
|
3070
|
+
</DataTableTHeadCell>
|
|
3071
|
+
<DataTableTHeadCell>
|
|
3072
|
+
<DataTableTHeadTitle>This Year</DataTableTHeadTitle>
|
|
3073
|
+
</DataTableTHeadCell>
|
|
3074
|
+
<DataTableTHeadCell>
|
|
3075
|
+
<DataTableTHeadTitle>Last Year</DataTableTHeadTitle>
|
|
3076
|
+
</DataTableTHeadCell>
|
|
3077
|
+
<DataTableTHeadCell>
|
|
3078
|
+
<DataTableTHeadTitle>This Year</DataTableTHeadTitle>
|
|
3079
|
+
</DataTableTHeadCell>
|
|
3080
|
+
</DataTableTHeadRow>
|
|
3081
|
+
</DataTableTHead>
|
|
3082
|
+
<DataTableTBody>
|
|
3083
|
+
{({ item }: { item: any }) => (
|
|
3084
|
+
<DataTableRow key={item.id}>
|
|
3085
|
+
<DataTableCell>
|
|
3086
|
+
<span className="font-medium">{item.product}</span>
|
|
3087
|
+
</DataTableCell>
|
|
3088
|
+
<DataTableCell>{item.lastYearSale}%</DataTableCell>
|
|
3089
|
+
<DataTableCell>
|
|
3090
|
+
<Trend current={item.thisYearSale} previous={item.lastYearSale} />
|
|
3091
|
+
</DataTableCell>
|
|
3092
|
+
<DataTableCell>${item.lastYearProfit.toLocaleString()}</DataTableCell>
|
|
3093
|
+
<DataTableCell>
|
|
3094
|
+
<Tag severity={item.thisYearProfit >= item.lastYearProfit ? 'success' : 'danger'}>
|
|
3095
|
+
${item.thisYearProfit.toLocaleString()}
|
|
3096
|
+
</Tag>
|
|
3097
|
+
</DataTableCell>
|
|
3098
|
+
</DataTableRow>
|
|
3099
|
+
)}
|
|
3100
|
+
</DataTableTBody>
|
|
3101
|
+
<DataTableEmptyTBody>
|
|
3102
|
+
<DataTableRow>
|
|
3103
|
+
<DataTableCell colSpan={3}>
|
|
3104
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
3105
|
+
<div className="relative">
|
|
3106
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
3107
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
3108
|
+
</div>
|
|
3109
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
3110
|
+
</div>
|
|
3111
|
+
<div>
|
|
3112
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching sales</p>
|
|
3113
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
3114
|
+
Hang tight, the latest figures will appear here in a moment.
|
|
3115
|
+
</p>
|
|
3116
|
+
</div>
|
|
3117
|
+
</div>
|
|
3118
|
+
</DataTableCell>
|
|
3119
|
+
</DataTableRow>
|
|
3120
|
+
</DataTableEmptyTBody>
|
|
3121
|
+
<DataTableTFoot>
|
|
3122
|
+
<DataTableTFootRow>
|
|
3123
|
+
<DataTableTFootCell colSpan={3} style={{ textAlign: 'right' }}>
|
|
3124
|
+
<span className="font-semibold">Totals</span>
|
|
3125
|
+
</DataTableTFootCell>
|
|
3126
|
+
<DataTableTFootCell>
|
|
3127
|
+
<span className="font-semibold">${totals.lastYear.toLocaleString()}</span>
|
|
3128
|
+
</DataTableTFootCell>
|
|
3129
|
+
<DataTableTFootCell>
|
|
3130
|
+
<span className="font-semibold">${totals.thisYear.toLocaleString()}</span>
|
|
3131
|
+
</DataTableTFootCell>
|
|
3132
|
+
</DataTableTFootRow>
|
|
3133
|
+
</DataTableTFoot>
|
|
3134
|
+
</DataTableTable>
|
|
3135
|
+
</DataTableTableContainer>
|
|
3136
|
+
</DataTable>
|
|
3137
|
+
</div>
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
3140
|
+
```
|
|
3141
|
+
|
|
3142
|
+
Sort and filter work on any leaf header cell in a grouped layout.
|
|
3143
|
+
|
|
3144
|
+
```tsx
|
|
3145
|
+
'use client';
|
|
3146
|
+
import { ArrowDown } from '@primeicons/react/arrow-down';
|
|
3147
|
+
import { Database } from '@primeicons/react/database';
|
|
3148
|
+
import { ArrowUp } from '@primeicons/react/arrow-up';
|
|
3149
|
+
import { SortAlt } from '@primeicons/react/sort-alt';
|
|
3150
|
+
import { SortAmountDown } from '@primeicons/react/sort-amount-down';
|
|
3151
|
+
import { SortAmountUpAlt } from '@primeicons/react/sort-amount-up-alt';
|
|
3152
|
+
import { FilterMatchMode } from 'primereact/datatable';
|
|
3153
|
+
import type { DataTableFilterInstance } from 'primereact/datatable';
|
|
3154
|
+
import {
|
|
3155
|
+
DataTable,
|
|
3156
|
+
DataTableCell,
|
|
3157
|
+
DataTableEmptyTBody,
|
|
3158
|
+
DataTableFilter,
|
|
3159
|
+
DataTableRow,
|
|
3160
|
+
DataTableSort,
|
|
3161
|
+
DataTableSortIndicator,
|
|
3162
|
+
DataTableTBody,
|
|
3163
|
+
DataTableTHead,
|
|
3164
|
+
DataTableTHeadCell,
|
|
3165
|
+
DataTableTHeadRow,
|
|
3166
|
+
DataTableTHeadTitle,
|
|
3167
|
+
DataTableTable,
|
|
3168
|
+
DataTableTableContainer
|
|
3169
|
+
} from '@/components/ui/datatable';
|
|
3170
|
+
import { InputText } from '@/components/ui/inputtext';
|
|
3171
|
+
import { Tag } from '@/components/ui/tag';
|
|
3172
|
+
import * as React from 'react';
|
|
3173
|
+
|
|
3174
|
+
interface Sale {
|
|
3175
|
+
id: string;
|
|
3176
|
+
product: string;
|
|
3177
|
+
lastYearSale: number;
|
|
3178
|
+
thisYearSale: number;
|
|
3179
|
+
lastYearProfit: number;
|
|
3180
|
+
thisYearProfit: number;
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
const sales: Sale[] = [
|
|
3184
|
+
{ id: '1', product: 'Bamboo Watch', lastYearSale: 51, thisYearSale: 40, lastYearProfit: 54_406, thisYearProfit: 43_342 },
|
|
3185
|
+
{ id: '2', product: 'Black Watch', lastYearSale: 83, thisYearSale: 9, lastYearProfit: 423_132, thisYearProfit: 312_122 },
|
|
3186
|
+
{ id: '3', product: 'Blue Band', lastYearSale: 38, thisYearSale: 5, lastYearProfit: 12_321, thisYearProfit: 8_500 },
|
|
3187
|
+
{ id: '4', product: 'Blue T-Shirt', lastYearSale: 49, thisYearSale: 22, lastYearProfit: 745_232, thisYearProfit: 650_323 },
|
|
3188
|
+
{ id: '5', product: 'Bracelet', lastYearSale: 17, thisYearSale: 79, lastYearProfit: 643_242, thisYearProfit: 500_332 }
|
|
3189
|
+
];
|
|
3190
|
+
|
|
3191
|
+
function Trend({ current, previous }: { current: number; previous: number }) {
|
|
3192
|
+
const up = current >= previous;
|
|
3193
|
+
|
|
3194
|
+
return (
|
|
3195
|
+
<div className="inline-flex items-center gap-1">
|
|
3196
|
+
<span>{current}%</span>
|
|
3197
|
+
{up ? <ArrowUp className="w-3 h-3 text-green-500" /> : <ArrowDown className="w-3 h-3 text-red-500" />}
|
|
3198
|
+
</div>
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
function SortableHeader({ field, children }: { field: string; children: React.ReactNode }) {
|
|
3203
|
+
return (
|
|
3204
|
+
<DataTableSort field={field} className="inline-flex items-center gap-1">
|
|
3205
|
+
<DataTableTHeadTitle>{children}</DataTableTHeadTitle>
|
|
3206
|
+
<DataTableSortIndicator match="unsorted">
|
|
3207
|
+
<SortAlt />
|
|
3208
|
+
</DataTableSortIndicator>
|
|
3209
|
+
<DataTableSortIndicator match="asc">
|
|
3210
|
+
<SortAmountUpAlt />
|
|
3211
|
+
</DataTableSortIndicator>
|
|
3212
|
+
<DataTableSortIndicator match="desc">
|
|
3213
|
+
<SortAmountDown />
|
|
3214
|
+
</DataTableSortIndicator>
|
|
3215
|
+
</DataTableSort>
|
|
3216
|
+
);
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
export default function ColumnGroupFilterSortDemo() {
|
|
3220
|
+
const [filters, setFilters] = React.useState<Record<string, { value: unknown; matchMode: FilterMatchMode }>>({
|
|
3221
|
+
product: { value: null, matchMode: FilterMatchMode.Contains },
|
|
3222
|
+
thisYearSale: { value: null, matchMode: FilterMatchMode.Gte }
|
|
3223
|
+
});
|
|
3224
|
+
|
|
3225
|
+
return (
|
|
3226
|
+
<div className="w-full">
|
|
3227
|
+
<DataTable
|
|
3228
|
+
data={sales}
|
|
3229
|
+
removableSort
|
|
3230
|
+
filters={filters}
|
|
3231
|
+
onFilter={(e: { filters: Record<string, unknown> }) => setFilters(e.filters as typeof filters)}
|
|
3232
|
+
showGridlines
|
|
3233
|
+
>
|
|
3234
|
+
<DataTableTableContainer>
|
|
3235
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
3236
|
+
<DataTableTHead>
|
|
3237
|
+
<DataTableTHeadRow>
|
|
3238
|
+
<DataTableTHeadCell rowSpan={2}>
|
|
3239
|
+
<SortableHeader field="product">Product</SortableHeader>
|
|
3240
|
+
</DataTableTHeadCell>
|
|
3241
|
+
<DataTableTHeadCell colSpan={2} style={{ textAlign: 'center' }}>
|
|
3242
|
+
<DataTableTHeadTitle>Sale Rate</DataTableTHeadTitle>
|
|
3243
|
+
</DataTableTHeadCell>
|
|
3244
|
+
<DataTableTHeadCell colSpan={2} style={{ textAlign: 'center' }}>
|
|
3245
|
+
<DataTableTHeadTitle>Profits</DataTableTHeadTitle>
|
|
3246
|
+
</DataTableTHeadCell>
|
|
3247
|
+
</DataTableTHeadRow>
|
|
3248
|
+
<DataTableTHeadRow>
|
|
3249
|
+
<DataTableTHeadCell>
|
|
3250
|
+
<SortableHeader field="lastYearSale">Last Year</SortableHeader>
|
|
3251
|
+
</DataTableTHeadCell>
|
|
3252
|
+
<DataTableTHeadCell>
|
|
3253
|
+
<SortableHeader field="thisYearSale">This Year</SortableHeader>
|
|
3254
|
+
</DataTableTHeadCell>
|
|
3255
|
+
<DataTableTHeadCell>
|
|
3256
|
+
<SortableHeader field="lastYearProfit">Last Year</SortableHeader>
|
|
3257
|
+
</DataTableTHeadCell>
|
|
3258
|
+
<DataTableTHeadCell>
|
|
3259
|
+
<SortableHeader field="thisYearProfit">This Year</SortableHeader>
|
|
3260
|
+
</DataTableTHeadCell>
|
|
3261
|
+
</DataTableTHeadRow>
|
|
3262
|
+
<DataTableTHeadRow>
|
|
3263
|
+
<DataTableTHeadCell>
|
|
3264
|
+
<DataTableFilter field="product" display="row" dataType="text">
|
|
3265
|
+
{({ value, onChange }: DataTableFilterInstance) => (
|
|
3266
|
+
<InputText
|
|
3267
|
+
value={(value as string) ?? ''}
|
|
3268
|
+
onChange={(e: any) => onChange(e, e.target.value)}
|
|
3269
|
+
placeholder="Search"
|
|
3270
|
+
size="small"
|
|
3271
|
+
fluid
|
|
3272
|
+
/>
|
|
3273
|
+
)}
|
|
3274
|
+
</DataTableFilter>
|
|
3275
|
+
</DataTableTHeadCell>
|
|
3276
|
+
<DataTableTHeadCell />
|
|
3277
|
+
<DataTableTHeadCell>
|
|
3278
|
+
<DataTableFilter field="thisYearSale" display="row" dataType="numeric">
|
|
3279
|
+
{({ value, onChange }: DataTableFilterInstance) => (
|
|
3280
|
+
<InputText
|
|
3281
|
+
value={(value as string) ?? ''}
|
|
3282
|
+
onChange={(e: any) => onChange(e, e.target.value ? Number(e.target.value) : null, 'gte')}
|
|
3283
|
+
placeholder="≥"
|
|
3284
|
+
size="small"
|
|
3285
|
+
fluid
|
|
3286
|
+
/>
|
|
3287
|
+
)}
|
|
3288
|
+
</DataTableFilter>
|
|
3289
|
+
</DataTableTHeadCell>
|
|
3290
|
+
<DataTableTHeadCell />
|
|
3291
|
+
<DataTableTHeadCell />
|
|
3292
|
+
</DataTableTHeadRow>
|
|
3293
|
+
</DataTableTHead>
|
|
3294
|
+
<DataTableTBody>
|
|
3295
|
+
{({ item }: { item: any }) => (
|
|
3296
|
+
<DataTableRow key={item.id}>
|
|
3297
|
+
<DataTableCell>
|
|
3298
|
+
<span className="font-medium">{item.product}</span>
|
|
3299
|
+
</DataTableCell>
|
|
3300
|
+
<DataTableCell>{item.lastYearSale}%</DataTableCell>
|
|
3301
|
+
<DataTableCell>
|
|
3302
|
+
<Trend current={item.thisYearSale} previous={item.lastYearSale} />
|
|
3303
|
+
</DataTableCell>
|
|
3304
|
+
<DataTableCell>${item.lastYearProfit.toLocaleString()}</DataTableCell>
|
|
3305
|
+
<DataTableCell>
|
|
3306
|
+
<Tag severity={item.thisYearProfit >= item.lastYearProfit ? 'success' : 'danger'}>
|
|
3307
|
+
${item.thisYearProfit.toLocaleString()}
|
|
3308
|
+
</Tag>
|
|
3309
|
+
</DataTableCell>
|
|
3310
|
+
</DataTableRow>
|
|
3311
|
+
)}
|
|
3312
|
+
</DataTableTBody>
|
|
3313
|
+
<DataTableEmptyTBody>
|
|
3314
|
+
<DataTableRow>
|
|
3315
|
+
<DataTableCell colSpan={5}>
|
|
3316
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
3317
|
+
<div className="relative">
|
|
3318
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
3319
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
3320
|
+
</div>
|
|
3321
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
3322
|
+
</div>
|
|
3323
|
+
<div>
|
|
3324
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching sales</p>
|
|
3325
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
3326
|
+
Hang tight, the latest figures will appear here in a moment.
|
|
3327
|
+
</p>
|
|
3328
|
+
</div>
|
|
3329
|
+
</div>
|
|
3330
|
+
</DataTableCell>
|
|
3331
|
+
</DataTableRow>
|
|
3332
|
+
</DataTableEmptyTBody>
|
|
3333
|
+
</DataTableTable>
|
|
3334
|
+
</DataTableTableContainer>
|
|
3335
|
+
</DataTable>
|
|
3336
|
+
</div>
|
|
3337
|
+
);
|
|
3338
|
+
}
|
|
3339
|
+
```
|
|
3340
|
+
|
|
3341
|
+
### Filter
|
|
3342
|
+
|
|
3343
|
+
Filtering is driven by a `filters` object passed to `DataTable`. Global search is opt-in via `globalFilter` + `globalFilterFields`.
|
|
3344
|
+
|
|
3345
|
+
```tsx
|
|
3346
|
+
'use client';
|
|
3347
|
+
import {
|
|
3348
|
+
DataTable,
|
|
3349
|
+
DataTableCell,
|
|
3350
|
+
DataTableHeader,
|
|
3351
|
+
DataTableRow,
|
|
3352
|
+
DataTableTBody,
|
|
3353
|
+
DataTableTHead,
|
|
3354
|
+
DataTableTHeadCell,
|
|
3355
|
+
DataTableTHeadRow,
|
|
3356
|
+
DataTableTable,
|
|
3357
|
+
DataTableTableContainer
|
|
3358
|
+
} from '@/components/ui/datatable';
|
|
3359
|
+
import { IconField } from '@/components/ui/iconfield';
|
|
3360
|
+
import { InputText } from '@/components/ui/inputtext';
|
|
3361
|
+
import { Tag } from '@/components/ui/tag';
|
|
3362
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
3363
|
+
import { Search } from '@primeicons/react/search';
|
|
3364
|
+
import * as React from 'react';
|
|
3365
|
+
|
|
3366
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
3367
|
+
INSTOCK: 'success',
|
|
3368
|
+
LOWSTOCK: 'warn',
|
|
3369
|
+
OUTOFSTOCK: 'danger'
|
|
3370
|
+
};
|
|
3371
|
+
|
|
3372
|
+
const statusLabel: Record<string, string> = {
|
|
3373
|
+
INSTOCK: 'In Stock',
|
|
3374
|
+
LOWSTOCK: 'Low Stock',
|
|
3375
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
3376
|
+
};
|
|
3377
|
+
|
|
3378
|
+
export default function FilterDemo() {
|
|
3379
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
3380
|
+
const [globalFilter, setGlobalFilter] = React.useState('');
|
|
3381
|
+
|
|
3382
|
+
React.useEffect(() => {
|
|
3383
|
+
setProducts(ProductService.getProductsData());
|
|
3384
|
+
}, []);
|
|
3385
|
+
|
|
3386
|
+
return (
|
|
3387
|
+
<div className="w-full">
|
|
3388
|
+
<DataTable
|
|
3389
|
+
data={products}
|
|
3390
|
+
globalFilter={globalFilter}
|
|
3391
|
+
globalFilterFields={['name', 'category', 'inventoryStatus']}
|
|
3392
|
+
paginator
|
|
3393
|
+
defaultRows={5}
|
|
3394
|
+
>
|
|
3395
|
+
<DataTableHeader>
|
|
3396
|
+
<div className="flex justify-end">
|
|
3397
|
+
<IconField>
|
|
3398
|
+
<Search />
|
|
3399
|
+
<InputText
|
|
3400
|
+
placeholder="Search products..."
|
|
3401
|
+
value={globalFilter}
|
|
3402
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setGlobalFilter(e.target.value)}
|
|
3403
|
+
/>
|
|
3404
|
+
</IconField>
|
|
3405
|
+
</div>
|
|
3406
|
+
</DataTableHeader>
|
|
3407
|
+
<DataTableTableContainer>
|
|
3408
|
+
<DataTableTable style={{ minWidth: '40rem' }}>
|
|
3409
|
+
<DataTableTHead>
|
|
3410
|
+
<DataTableTHeadRow>
|
|
3411
|
+
<DataTableTHeadCell>Name</DataTableTHeadCell>
|
|
3412
|
+
<DataTableTHeadCell>Category</DataTableTHeadCell>
|
|
3413
|
+
<DataTableTHeadCell>Price</DataTableTHeadCell>
|
|
3414
|
+
<DataTableTHeadCell>Status</DataTableTHeadCell>
|
|
3415
|
+
</DataTableTHeadRow>
|
|
3416
|
+
</DataTableTHead>
|
|
3417
|
+
<DataTableTBody>
|
|
3418
|
+
{({ item }: { item: any }) => (
|
|
3419
|
+
<DataTableRow key={item.id}>
|
|
3420
|
+
<DataTableCell>
|
|
3421
|
+
<span className="font-medium">{item.name}</span>
|
|
3422
|
+
</DataTableCell>
|
|
3423
|
+
<DataTableCell>
|
|
3424
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
3425
|
+
</DataTableCell>
|
|
3426
|
+
<DataTableCell>
|
|
3427
|
+
<span className="font-semibold">${item.price}</span>
|
|
3428
|
+
</DataTableCell>
|
|
3429
|
+
<DataTableCell>
|
|
3430
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
3431
|
+
</DataTableCell>
|
|
3432
|
+
</DataTableRow>
|
|
3433
|
+
)}
|
|
3434
|
+
</DataTableTBody>
|
|
3435
|
+
</DataTableTable>
|
|
3436
|
+
</DataTableTableContainer>
|
|
3437
|
+
</DataTable>
|
|
3438
|
+
</div>
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
```
|
|
3442
|
+
|
|
3443
|
+
### Header & Footer
|
|
3444
|
+
|
|
3445
|
+
Use `DataTableHeader` and `DataTableFooter` to add summary information above and below the table.
|
|
3446
|
+
|
|
3447
|
+
```tsx
|
|
3448
|
+
'use client';
|
|
3449
|
+
import {
|
|
3450
|
+
DataTable,
|
|
3451
|
+
DataTableCell,
|
|
3452
|
+
DataTableFooter,
|
|
3453
|
+
DataTableHeader,
|
|
3454
|
+
DataTableRow,
|
|
3455
|
+
DataTableTBody,
|
|
3456
|
+
DataTableTHead,
|
|
3457
|
+
DataTableTHeadCell,
|
|
3458
|
+
DataTableTHeadRow,
|
|
3459
|
+
DataTableTable,
|
|
3460
|
+
DataTableTableContainer
|
|
3461
|
+
} from '@/components/ui/datatable';
|
|
3462
|
+
import { Tag } from '@/components/ui/tag';
|
|
3463
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
3464
|
+
import Image from 'next/image';
|
|
3465
|
+
import * as React from 'react';
|
|
3466
|
+
|
|
3467
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
3468
|
+
INSTOCK: 'success',
|
|
3469
|
+
LOWSTOCK: 'warn',
|
|
3470
|
+
OUTOFSTOCK: 'danger'
|
|
3471
|
+
};
|
|
3472
|
+
|
|
3473
|
+
const statusLabel: Record<string, string> = {
|
|
3474
|
+
INSTOCK: 'In Stock',
|
|
3475
|
+
LOWSTOCK: 'Low Stock',
|
|
3476
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
3477
|
+
};
|
|
3478
|
+
|
|
3479
|
+
export default function TemplateDemo() {
|
|
3480
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
3481
|
+
|
|
3482
|
+
React.useEffect(() => {
|
|
3483
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
3484
|
+
}, []);
|
|
3485
|
+
|
|
3486
|
+
return (
|
|
3487
|
+
<div className="w-full">
|
|
3488
|
+
<DataTable data={products}>
|
|
3489
|
+
<DataTableHeader>
|
|
3490
|
+
<div className="flex items-center justify-between">
|
|
3491
|
+
<span className="text-base font-semibold">Products</span>
|
|
3492
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{products.length} items</span>
|
|
3493
|
+
</div>
|
|
3494
|
+
</DataTableHeader>
|
|
3495
|
+
<DataTableTableContainer>
|
|
3496
|
+
<DataTableTable style={{ minWidth: '40rem' }}>
|
|
3497
|
+
<DataTableTHead>
|
|
3498
|
+
<DataTableTHeadRow>
|
|
3499
|
+
<DataTableTHeadCell>Product</DataTableTHeadCell>
|
|
3500
|
+
<DataTableTHeadCell>Category</DataTableTHeadCell>
|
|
3501
|
+
<DataTableTHeadCell>Price</DataTableTHeadCell>
|
|
3502
|
+
<DataTableTHeadCell>Status</DataTableTHeadCell>
|
|
3503
|
+
</DataTableTHeadRow>
|
|
3504
|
+
</DataTableTHead>
|
|
3505
|
+
<DataTableTBody>
|
|
3506
|
+
{({ item }: { item: any }) => (
|
|
3507
|
+
<DataTableRow key={item.id}>
|
|
3508
|
+
<DataTableCell>
|
|
3509
|
+
<div className="flex items-center gap-3">
|
|
3510
|
+
<Image
|
|
3511
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
3512
|
+
alt={item.name}
|
|
3513
|
+
width={40}
|
|
3514
|
+
height={40}
|
|
3515
|
+
className="rounded-md shadow"
|
|
3516
|
+
/>
|
|
3517
|
+
<span className="font-medium">{item.name}</span>
|
|
3518
|
+
</div>
|
|
3519
|
+
</DataTableCell>
|
|
3520
|
+
<DataTableCell>
|
|
3521
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
3522
|
+
</DataTableCell>
|
|
3523
|
+
<DataTableCell>
|
|
3524
|
+
<span className="font-semibold">${item.price}</span>
|
|
3525
|
+
</DataTableCell>
|
|
3526
|
+
<DataTableCell>
|
|
3527
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
3528
|
+
</DataTableCell>
|
|
3529
|
+
</DataTableRow>
|
|
3530
|
+
)}
|
|
3531
|
+
</DataTableTBody>
|
|
3532
|
+
</DataTableTable>
|
|
3533
|
+
</DataTableTableContainer>
|
|
3534
|
+
<DataTableFooter>
|
|
3535
|
+
<div className="text-xs text-surface-500 dark:text-surface-400">
|
|
3536
|
+
Total products: <span className="font-semibold text-surface-900 dark:text-surface-0">{products.length}</span>
|
|
3537
|
+
</div>
|
|
3538
|
+
</DataTableFooter>
|
|
3539
|
+
</DataTable>
|
|
3540
|
+
</div>
|
|
3541
|
+
);
|
|
3542
|
+
}
|
|
3543
|
+
```
|
|
3544
|
+
|
|
3545
|
+
### Export
|
|
3546
|
+
|
|
3547
|
+
Export table data to CSV with customizable fields and headers.
|
|
3548
|
+
|
|
3549
|
+
```tsx
|
|
3550
|
+
'use client';
|
|
3551
|
+
import Image from 'next/image';
|
|
3552
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
3553
|
+
import { FileExport } from '@primeicons/react/file-export';
|
|
3554
|
+
import { Database } from '@primeicons/react/database';
|
|
3555
|
+
import { Button } from '@/components/ui/button';
|
|
3556
|
+
import {
|
|
3557
|
+
DataTable,
|
|
3558
|
+
DataTableCell,
|
|
3559
|
+
DataTableEmptyTBody,
|
|
3560
|
+
DataTableExport,
|
|
3561
|
+
DataTableRow,
|
|
3562
|
+
DataTableTBody,
|
|
3563
|
+
DataTableTHead,
|
|
3564
|
+
DataTableTHeadCell,
|
|
3565
|
+
DataTableTHeadRow,
|
|
3566
|
+
DataTableTHeadTitle,
|
|
3567
|
+
DataTableTable,
|
|
3568
|
+
DataTableTableContainer
|
|
3569
|
+
} from '@/components/ui/datatable';
|
|
3570
|
+
import { Tag } from '@/components/ui/tag';
|
|
3571
|
+
import * as React from 'react';
|
|
3572
|
+
|
|
3573
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
3574
|
+
INSTOCK: 'success',
|
|
3575
|
+
LOWSTOCK: 'warn',
|
|
3576
|
+
OUTOFSTOCK: 'danger'
|
|
3577
|
+
};
|
|
3578
|
+
|
|
3579
|
+
const statusLabel: Record<string, string> = {
|
|
3580
|
+
INSTOCK: 'In Stock',
|
|
3581
|
+
LOWSTOCK: 'Low Stock',
|
|
3582
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
3583
|
+
};
|
|
3584
|
+
|
|
3585
|
+
export default function ExportDemo() {
|
|
3586
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
3587
|
+
|
|
3588
|
+
React.useEffect(() => {
|
|
3589
|
+
setProducts(ProductService.getProductsData().slice(0, 8));
|
|
3590
|
+
}, []);
|
|
3591
|
+
|
|
3592
|
+
return (
|
|
3593
|
+
<div className="w-full">
|
|
3594
|
+
<div className="flex items-center justify-between gap-3 mb-3">
|
|
3595
|
+
<span className="text-sm text-surface-500 dark:text-surface-400">Export visible rows to CSV with custom column headers.</span>
|
|
3596
|
+
<DataTableExport
|
|
3597
|
+
as={Button}
|
|
3598
|
+
size="small"
|
|
3599
|
+
fileName="products"
|
|
3600
|
+
headers={{ code: 'Code', name: 'Name', category: 'Category', price: 'Price', quantity: 'Quantity', inventoryStatus: 'Status' }}
|
|
3601
|
+
>
|
|
3602
|
+
<FileExport />
|
|
3603
|
+
Export CSV
|
|
3604
|
+
</DataTableExport>
|
|
3605
|
+
</div>
|
|
3606
|
+
<DataTable data={products}>
|
|
3607
|
+
<DataTableTableContainer>
|
|
3608
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
3609
|
+
<DataTableTHead>
|
|
3610
|
+
<DataTableTHeadRow>
|
|
3611
|
+
<DataTableTHeadCell>
|
|
3612
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
3613
|
+
</DataTableTHeadCell>
|
|
3614
|
+
<DataTableTHeadCell>
|
|
3615
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
3616
|
+
</DataTableTHeadCell>
|
|
3617
|
+
<DataTableTHeadCell>
|
|
3618
|
+
<DataTableTHeadTitle>Qty</DataTableTHeadTitle>
|
|
3619
|
+
</DataTableTHeadCell>
|
|
3620
|
+
<DataTableTHeadCell>
|
|
3621
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
3622
|
+
</DataTableTHeadCell>
|
|
3623
|
+
<DataTableTHeadCell>
|
|
3624
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
3625
|
+
</DataTableTHeadCell>
|
|
3626
|
+
</DataTableTHeadRow>
|
|
3627
|
+
</DataTableTHead>
|
|
3628
|
+
<DataTableTBody>
|
|
3629
|
+
{({ item }: { item: any }) => (
|
|
3630
|
+
<DataTableRow key={item.id}>
|
|
3631
|
+
<DataTableCell>
|
|
3632
|
+
<div className="flex items-center gap-3">
|
|
3633
|
+
<Image
|
|
3634
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
3635
|
+
alt={item.name}
|
|
3636
|
+
width={36}
|
|
3637
|
+
height={36}
|
|
3638
|
+
className="rounded-md shadow"
|
|
3639
|
+
/>
|
|
3640
|
+
<div className="flex flex-col">
|
|
3641
|
+
<span className="font-medium">{item.name}</span>
|
|
3642
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
3643
|
+
</div>
|
|
3644
|
+
</div>
|
|
3645
|
+
</DataTableCell>
|
|
3646
|
+
<DataTableCell>
|
|
3647
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
3648
|
+
</DataTableCell>
|
|
3649
|
+
<DataTableCell>{item.quantity}</DataTableCell>
|
|
3650
|
+
<DataTableCell>
|
|
3651
|
+
<span className="font-semibold">${item.price}</span>
|
|
3652
|
+
</DataTableCell>
|
|
3653
|
+
<DataTableCell>
|
|
3654
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
3655
|
+
</DataTableCell>
|
|
3656
|
+
</DataTableRow>
|
|
3657
|
+
)}
|
|
3658
|
+
</DataTableTBody>
|
|
3659
|
+
<DataTableEmptyTBody>
|
|
3660
|
+
<DataTableRow>
|
|
3661
|
+
<DataTableCell colSpan={5}>
|
|
3662
|
+
<div className="flex flex-col items-center justify-center gap-3 py-24 text-center">
|
|
3663
|
+
<div className="relative">
|
|
3664
|
+
<div className="w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
3665
|
+
<Database className="w-8 h-8 text-surface-400 dark:text-surface-500" />
|
|
3666
|
+
</div>
|
|
3667
|
+
<span className="absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-full border-2 border-surface-0 dark:border-surface-900 bg-primary animate-pulse" />
|
|
3668
|
+
</div>
|
|
3669
|
+
<div>
|
|
3670
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">Fetching products</p>
|
|
3671
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
3672
|
+
Hang tight, your data will appear here in a moment.
|
|
3673
|
+
</p>
|
|
3674
|
+
</div>
|
|
3675
|
+
</div>
|
|
3676
|
+
</DataTableCell>
|
|
3677
|
+
</DataTableRow>
|
|
3678
|
+
</DataTableEmptyTBody>
|
|
3679
|
+
</DataTableTable>
|
|
3680
|
+
</DataTableTableContainer>
|
|
3681
|
+
</DataTable>
|
|
3682
|
+
</div>
|
|
3683
|
+
);
|
|
3684
|
+
}
|
|
3685
|
+
```
|
|
3686
|
+
|
|
3687
|
+
### Loading
|
|
3688
|
+
|
|
3689
|
+
#### Overlay
|
|
3690
|
+
|
|
3691
|
+
Set the `loading` prop on `DataTable` and provide a spinner inside `DataTableLoading`. An absolutely positioned overlay dims the table.
|
|
3692
|
+
|
|
3693
|
+
```tsx
|
|
3694
|
+
'use client';
|
|
3695
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
3696
|
+
import { Refresh } from '@primeicons/react/refresh';
|
|
3697
|
+
import { Spinner } from '@primeicons/react/spinner';
|
|
3698
|
+
import { Button } from '@/components/ui/button';
|
|
3699
|
+
import {
|
|
3700
|
+
DataTable,
|
|
3701
|
+
DataTableCell,
|
|
3702
|
+
DataTableLoading,
|
|
3703
|
+
DataTableRow,
|
|
3704
|
+
DataTableTBody,
|
|
3705
|
+
DataTableTHead,
|
|
3706
|
+
DataTableTHeadCell,
|
|
3707
|
+
DataTableTHeadRow,
|
|
3708
|
+
DataTableTHeadTitle,
|
|
3709
|
+
DataTableTable,
|
|
3710
|
+
DataTableTableContainer
|
|
3711
|
+
} from '@/components/ui/datatable';
|
|
3712
|
+
import { Tag } from '@/components/ui/tag';
|
|
3713
|
+
import Image from 'next/image';
|
|
3714
|
+
import * as React from 'react';
|
|
3715
|
+
|
|
3716
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
3717
|
+
INSTOCK: 'success',
|
|
3718
|
+
LOWSTOCK: 'warn',
|
|
3719
|
+
OUTOFSTOCK: 'danger'
|
|
3720
|
+
};
|
|
3721
|
+
|
|
3722
|
+
const statusLabel: Record<string, string> = {
|
|
3723
|
+
INSTOCK: 'In Stock',
|
|
3724
|
+
LOWSTOCK: 'Low Stock',
|
|
3725
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
3726
|
+
};
|
|
3727
|
+
|
|
3728
|
+
export default function LoadingOverlayDemo() {
|
|
3729
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
3730
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
3731
|
+
|
|
3732
|
+
const refresh = React.useCallback(() => {
|
|
3733
|
+
setIsLoading(true);
|
|
3734
|
+
setTimeout(() => {
|
|
3735
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
3736
|
+
setIsLoading(false);
|
|
3737
|
+
}, 1500);
|
|
3738
|
+
}, []);
|
|
3739
|
+
|
|
3740
|
+
React.useEffect(() => {
|
|
3741
|
+
refresh();
|
|
3742
|
+
}, [refresh]);
|
|
3743
|
+
|
|
3744
|
+
return (
|
|
3745
|
+
<div className="w-full">
|
|
3746
|
+
<div className="mb-3 flex items-center justify-between gap-3">
|
|
3747
|
+
<span className="text-sm text-surface-500 dark:text-surface-400">Click refresh to simulate a network fetch.</span>
|
|
3748
|
+
<Button size="small" onClick={refresh} disabled={isLoading}>
|
|
3749
|
+
<Refresh />
|
|
3750
|
+
Refresh
|
|
3751
|
+
</Button>
|
|
3752
|
+
</div>
|
|
3753
|
+
<div className="relative">
|
|
3754
|
+
<DataTable data={products} loading={isLoading}>
|
|
3755
|
+
<DataTableTableContainer>
|
|
3756
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
3757
|
+
<DataTableTHead>
|
|
3758
|
+
<DataTableTHeadRow>
|
|
3759
|
+
<DataTableTHeadCell>
|
|
3760
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
3761
|
+
</DataTableTHeadCell>
|
|
3762
|
+
<DataTableTHeadCell>
|
|
3763
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
3764
|
+
</DataTableTHeadCell>
|
|
3765
|
+
<DataTableTHeadCell>
|
|
3766
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
3767
|
+
</DataTableTHeadCell>
|
|
3768
|
+
<DataTableTHeadCell>
|
|
3769
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
3770
|
+
</DataTableTHeadCell>
|
|
3771
|
+
</DataTableTHeadRow>
|
|
3772
|
+
</DataTableTHead>
|
|
3773
|
+
<DataTableTBody>
|
|
3774
|
+
{({ item }: { item: any }) => (
|
|
3775
|
+
<DataTableRow key={item.id}>
|
|
3776
|
+
<DataTableCell>
|
|
3777
|
+
<div className="flex items-center gap-3">
|
|
3778
|
+
<Image
|
|
3779
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
3780
|
+
alt={item.name}
|
|
3781
|
+
width={40}
|
|
3782
|
+
height={40}
|
|
3783
|
+
className="rounded-md shadow"
|
|
3784
|
+
/>
|
|
3785
|
+
<div className="flex flex-col">
|
|
3786
|
+
<span className="font-medium">{item.name}</span>
|
|
3787
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
3788
|
+
</div>
|
|
3789
|
+
</div>
|
|
3790
|
+
</DataTableCell>
|
|
3791
|
+
<DataTableCell>
|
|
3792
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
3793
|
+
</DataTableCell>
|
|
3794
|
+
<DataTableCell>
|
|
3795
|
+
<span className="font-semibold">${item.price}</span>
|
|
3796
|
+
</DataTableCell>
|
|
3797
|
+
<DataTableCell>
|
|
3798
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
3799
|
+
</DataTableCell>
|
|
3800
|
+
</DataTableRow>
|
|
3801
|
+
)}
|
|
3802
|
+
</DataTableTBody>
|
|
3803
|
+
</DataTableTable>
|
|
3804
|
+
</DataTableTableContainer>
|
|
3805
|
+
<DataTableLoading>
|
|
3806
|
+
<div className="flex flex-col items-center gap-2">
|
|
3807
|
+
<Spinner className="w-10! h-10! animate-spin text-primary" />
|
|
3808
|
+
<span className="text-sm text-surface-600 dark:text-surface-300">Loading products…</span>
|
|
3809
|
+
</div>
|
|
3810
|
+
</DataTableLoading>
|
|
3811
|
+
</DataTable>
|
|
3812
|
+
</div>
|
|
3813
|
+
</div>
|
|
3814
|
+
);
|
|
3815
|
+
}
|
|
3816
|
+
```
|
|
3817
|
+
|
|
3818
|
+
#### Skeleton
|
|
3819
|
+
|
|
3820
|
+
Render placeholder rows filled with `Skeleton` elements while the request is in flight.
|
|
3821
|
+
|
|
3822
|
+
```tsx
|
|
3823
|
+
'use client';
|
|
3824
|
+
import { ProductService, type Product } from '@/shared/services/product.service';
|
|
3825
|
+
import { Refresh } from '@primeicons/react/refresh';
|
|
3826
|
+
import { Button } from '@/components/ui/button';
|
|
3827
|
+
import {
|
|
3828
|
+
DataTable,
|
|
3829
|
+
DataTableCell,
|
|
3830
|
+
DataTableRow,
|
|
3831
|
+
DataTableTBody,
|
|
3832
|
+
DataTableTHead,
|
|
3833
|
+
DataTableTHeadCell,
|
|
3834
|
+
DataTableTHeadRow,
|
|
3835
|
+
DataTableTHeadTitle,
|
|
3836
|
+
DataTableTable,
|
|
3837
|
+
DataTableTableContainer
|
|
3838
|
+
} from '@/components/ui/datatable';
|
|
3839
|
+
import { Skeleton } from '@/components/ui/skeleton';
|
|
3840
|
+
import { Tag } from '@/components/ui/tag';
|
|
3841
|
+
import Image from 'next/image';
|
|
3842
|
+
import * as React from 'react';
|
|
3843
|
+
|
|
3844
|
+
const statusSeverity: Record<string, 'success' | 'warn' | 'danger'> = {
|
|
3845
|
+
INSTOCK: 'success',
|
|
3846
|
+
LOWSTOCK: 'warn',
|
|
3847
|
+
OUTOFSTOCK: 'danger'
|
|
3848
|
+
};
|
|
3849
|
+
|
|
3850
|
+
const statusLabel: Record<string, string> = {
|
|
3851
|
+
INSTOCK: 'In Stock',
|
|
3852
|
+
LOWSTOCK: 'Low Stock',
|
|
3853
|
+
OUTOFSTOCK: 'Out of Stock'
|
|
3854
|
+
};
|
|
3855
|
+
|
|
3856
|
+
const placeholders = Array.from({ length: 6 }, (_, i) => ({ id: `placeholder-${i}` }));
|
|
3857
|
+
|
|
3858
|
+
export default function LoadingSkeletonDemo() {
|
|
3859
|
+
const [products, setProducts] = React.useState<Product[]>([]);
|
|
3860
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
3861
|
+
|
|
3862
|
+
const refresh = React.useCallback(() => {
|
|
3863
|
+
setIsLoading(true);
|
|
3864
|
+
setProducts([]);
|
|
3865
|
+
setTimeout(() => {
|
|
3866
|
+
setProducts(ProductService.getProductsData().slice(0, 6));
|
|
3867
|
+
setIsLoading(false);
|
|
3868
|
+
}, 1500);
|
|
3869
|
+
}, []);
|
|
3870
|
+
|
|
3871
|
+
React.useEffect(() => {
|
|
3872
|
+
refresh();
|
|
3873
|
+
}, [refresh]);
|
|
3874
|
+
|
|
3875
|
+
const rows = isLoading ? placeholders : products;
|
|
3876
|
+
|
|
3877
|
+
return (
|
|
3878
|
+
<div className="w-full">
|
|
3879
|
+
<div className="mb-3 flex items-center justify-between gap-3">
|
|
3880
|
+
<span className="text-sm text-surface-500 dark:text-surface-400">Click refresh to simulate a network fetch.</span>
|
|
3881
|
+
<Button size="small" onClick={refresh} disabled={isLoading}>
|
|
3882
|
+
<Refresh />
|
|
3883
|
+
Refresh
|
|
3884
|
+
</Button>
|
|
3885
|
+
</div>
|
|
3886
|
+
<DataTable data={rows}>
|
|
3887
|
+
<DataTableTableContainer>
|
|
3888
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
3889
|
+
<DataTableTHead>
|
|
3890
|
+
<DataTableTHeadRow>
|
|
3891
|
+
<DataTableTHeadCell style={{ width: '40%' }}>
|
|
3892
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
3893
|
+
</DataTableTHeadCell>
|
|
3894
|
+
<DataTableTHeadCell style={{ width: '20%' }}>
|
|
3895
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
3896
|
+
</DataTableTHeadCell>
|
|
3897
|
+
<DataTableTHeadCell style={{ width: '15%' }}>
|
|
3898
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
3899
|
+
</DataTableTHeadCell>
|
|
3900
|
+
<DataTableTHeadCell style={{ width: '25%' }}>
|
|
3901
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
3902
|
+
</DataTableTHeadCell>
|
|
3903
|
+
</DataTableTHeadRow>
|
|
3904
|
+
</DataTableTHead>
|
|
3905
|
+
<DataTableTBody>
|
|
3906
|
+
{({ item }: { item: any }) =>
|
|
3907
|
+
isLoading ? (
|
|
3908
|
+
<DataTableRow key={item.id}>
|
|
3909
|
+
<DataTableCell>
|
|
3910
|
+
<div className="flex items-center gap-3">
|
|
3911
|
+
<Skeleton width="40px" height="40px" borderRadius="6px" />
|
|
3912
|
+
<div className="flex flex-col gap-1">
|
|
3913
|
+
<Skeleton width="8rem" height="0.6rem" borderRadius="4px" />
|
|
3914
|
+
<Skeleton width="5rem" height="0.55rem" borderRadius="4px" />
|
|
3915
|
+
</div>
|
|
3916
|
+
</div>
|
|
3917
|
+
</DataTableCell>
|
|
3918
|
+
<DataTableCell>
|
|
3919
|
+
<Skeleton width="5rem" height="1rem" borderRadius="16px" />
|
|
3920
|
+
</DataTableCell>
|
|
3921
|
+
<DataTableCell>
|
|
3922
|
+
<Skeleton width="3rem" height="0.6rem" borderRadius="4px" />
|
|
3923
|
+
</DataTableCell>
|
|
3924
|
+
<DataTableCell>
|
|
3925
|
+
<Skeleton width="6rem" height="1rem" borderRadius="16px" />
|
|
3926
|
+
</DataTableCell>
|
|
3927
|
+
</DataTableRow>
|
|
3928
|
+
) : (
|
|
3929
|
+
<DataTableRow key={item.id}>
|
|
3930
|
+
<DataTableCell>
|
|
3931
|
+
<div className="flex items-center gap-3">
|
|
3932
|
+
<Image
|
|
3933
|
+
src={`https://primefaces.org/cdn/primevue/images/product/${item.image}`}
|
|
3934
|
+
alt={item.name}
|
|
3935
|
+
width={40}
|
|
3936
|
+
height={40}
|
|
3937
|
+
className="rounded-md shadow"
|
|
3938
|
+
/>
|
|
3939
|
+
<div className="flex flex-col">
|
|
3940
|
+
<span className="font-medium">{item.name}</span>
|
|
3941
|
+
<span className="text-xs text-surface-500 dark:text-surface-400">{item.code}</span>
|
|
3942
|
+
</div>
|
|
3943
|
+
</div>
|
|
3944
|
+
</DataTableCell>
|
|
3945
|
+
<DataTableCell>
|
|
3946
|
+
<Tag severity="secondary">{item.category}</Tag>
|
|
3947
|
+
</DataTableCell>
|
|
3948
|
+
<DataTableCell>
|
|
3949
|
+
<span className="font-semibold">${item.price}</span>
|
|
3950
|
+
</DataTableCell>
|
|
3951
|
+
<DataTableCell>
|
|
3952
|
+
<Tag severity={statusSeverity[item.inventoryStatus]}>{statusLabel[item.inventoryStatus]}</Tag>
|
|
3953
|
+
</DataTableCell>
|
|
3954
|
+
</DataTableRow>
|
|
3955
|
+
)
|
|
3956
|
+
}
|
|
3957
|
+
</DataTableTBody>
|
|
3958
|
+
</DataTableTable>
|
|
3959
|
+
</DataTableTableContainer>
|
|
3960
|
+
</DataTable>
|
|
3961
|
+
</div>
|
|
3962
|
+
);
|
|
3963
|
+
}
|
|
3964
|
+
```
|
|
3965
|
+
|
|
3966
|
+
### Empty State
|
|
3967
|
+
|
|
3968
|
+
Custom empty state when no data is available, using `EmptyTBody`.
|
|
3969
|
+
|
|
3970
|
+
```tsx
|
|
3971
|
+
'use client';
|
|
3972
|
+
import { Inbox } from '@primeicons/react/inbox';
|
|
3973
|
+
import { Plus } from '@primeicons/react/plus';
|
|
3974
|
+
import { Button } from '@/components/ui/button';
|
|
3975
|
+
import {
|
|
3976
|
+
DataTable,
|
|
3977
|
+
DataTableCell,
|
|
3978
|
+
DataTableEmptyTBody,
|
|
3979
|
+
DataTableRow,
|
|
3980
|
+
DataTableTBody,
|
|
3981
|
+
DataTableTHead,
|
|
3982
|
+
DataTableTHeadCell,
|
|
3983
|
+
DataTableTHeadRow,
|
|
3984
|
+
DataTableTHeadTitle,
|
|
3985
|
+
DataTableTable,
|
|
3986
|
+
DataTableTableContainer
|
|
3987
|
+
} from '@/components/ui/datatable';
|
|
3988
|
+
|
|
3989
|
+
export default function EmptyDemo() {
|
|
3990
|
+
return (
|
|
3991
|
+
<div className="w-full">
|
|
3992
|
+
<DataTable data={[]}>
|
|
3993
|
+
<DataTableTableContainer>
|
|
3994
|
+
<DataTableTable style={{ minWidth: '50rem' }}>
|
|
3995
|
+
<DataTableTHead>
|
|
3996
|
+
<DataTableTHeadRow>
|
|
3997
|
+
<DataTableTHeadCell>
|
|
3998
|
+
<DataTableTHeadTitle>Product</DataTableTHeadTitle>
|
|
3999
|
+
</DataTableTHeadCell>
|
|
4000
|
+
<DataTableTHeadCell>
|
|
4001
|
+
<DataTableTHeadTitle>Category</DataTableTHeadTitle>
|
|
4002
|
+
</DataTableTHeadCell>
|
|
4003
|
+
<DataTableTHeadCell>
|
|
4004
|
+
<DataTableTHeadTitle>Price</DataTableTHeadTitle>
|
|
4005
|
+
</DataTableTHeadCell>
|
|
4006
|
+
<DataTableTHeadCell>
|
|
4007
|
+
<DataTableTHeadTitle>Status</DataTableTHeadTitle>
|
|
4008
|
+
</DataTableTHeadCell>
|
|
4009
|
+
</DataTableTHeadRow>
|
|
4010
|
+
</DataTableTHead>
|
|
4011
|
+
<DataTableTBody>
|
|
4012
|
+
{({ item }: { item: any }) => (
|
|
4013
|
+
<DataTableRow key={String(item.id)}>
|
|
4014
|
+
<DataTableCell>{String(item.name)}</DataTableCell>
|
|
4015
|
+
<DataTableCell>{String(item.category)}</DataTableCell>
|
|
4016
|
+
<DataTableCell>${String(item.price)}</DataTableCell>
|
|
4017
|
+
<DataTableCell>{String(item.status)}</DataTableCell>
|
|
4018
|
+
</DataTableRow>
|
|
4019
|
+
)}
|
|
4020
|
+
</DataTableTBody>
|
|
4021
|
+
<DataTableEmptyTBody>
|
|
4022
|
+
<DataTableRow>
|
|
4023
|
+
<DataTableCell colSpan={4}>
|
|
4024
|
+
<div className="flex flex-col items-center justify-center gap-3 py-10 text-center">
|
|
4025
|
+
<div className="w-14 h-14 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center">
|
|
4026
|
+
<Inbox className="w-7 h-7 text-surface-400 dark:text-surface-500" />
|
|
4027
|
+
</div>
|
|
4028
|
+
<div>
|
|
4029
|
+
<p className="m-0 font-semibold text-surface-900 dark:text-surface-0">No products yet</p>
|
|
4030
|
+
<p className="mt-1 text-sm text-surface-500 dark:text-surface-400">
|
|
4031
|
+
Add your first product to see it listed here.
|
|
4032
|
+
</p>
|
|
4033
|
+
</div>
|
|
4034
|
+
<Button size="small">
|
|
4035
|
+
<Plus />
|
|
4036
|
+
Add product
|
|
4037
|
+
</Button>
|
|
4038
|
+
</div>
|
|
4039
|
+
</DataTableCell>
|
|
4040
|
+
</DataTableRow>
|
|
4041
|
+
</DataTableEmptyTBody>
|
|
4042
|
+
</DataTableTable>
|
|
4043
|
+
</DataTableTableContainer>
|
|
4044
|
+
</DataTable>
|
|
4045
|
+
</div>
|
|
4046
|
+
);
|
|
4047
|
+
}
|
|
4048
|
+
```
|
|
4049
|
+
|
|
4050
|
+
## Related
|
|
4051
|
+
|
|
4052
|
+
### Sub-Components
|
|
4053
|
+
|
|
4054
|
+
See [DataTable Primitive](/docs/primitive/components/datatable#api) for the full sub-component API.
|
|
4055
|
+
|
|
4056
|
+
### Hooks
|
|
4057
|
+
|
|
4058
|
+
See [useDataTable](/docs/headless/components/datatable#api) for the headless hook API.
|
|
4059
|
+
|
|
4060
|
+
### Accessibility
|
|
4061
|
+
|
|
4062
|
+
See [DataTable Primitive](/docs/primitive/components/datatable#accessibility) for WAI-ARIA compliance details and keyboard support.
|