@kapiuara/ui 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +764 -2
- package/dist/components/data/KapiPagination.d.ts +36 -0
- package/dist/components/data/KapiPagination.js +102 -0
- package/dist/components/data/KapiTable.d.ts +245 -0
- package/dist/components/data/KapiTable.js +748 -0
- package/dist/components/data/KapiTableFilter.d.ts +48 -0
- package/dist/components/data/KapiTableFilter.js +81 -0
- package/dist/components/data/index.d.ts +10 -0
- package/dist/components/data/tableLogic.d.ts +197 -0
- package/dist/components/data/tableLogic.js +145 -0
- package/dist/components/data/tableResizeLogic.d.ts +28 -0
- package/dist/components/data/tableResizeLogic.js +20 -0
- package/dist/components/display/KapiAnnotation.d.ts +32 -0
- package/dist/components/display/KapiAnnotation.js +130 -0
- package/dist/components/display/KapiAvatar.d.ts +27 -0
- package/dist/components/display/KapiAvatar.js +43 -0
- package/dist/components/display/KapiAvatarGroup.d.ts +19 -0
- package/dist/components/display/KapiAvatarGroup.js +38 -0
- package/dist/components/display/KapiBadge.d.ts +21 -0
- package/dist/components/display/KapiBadge.js +28 -0
- package/dist/components/display/KapiComment.d.ts +27 -0
- package/dist/components/display/KapiComment.js +66 -0
- package/dist/components/display/KapiCommentComposer.d.ts +49 -0
- package/dist/components/display/KapiCommentComposer.js +76 -0
- package/dist/components/display/KapiDescriptionList.d.ts +19 -0
- package/dist/components/display/KapiDescriptionList.js +35 -0
- package/dist/components/display/KapiFileItem.d.ts +39 -0
- package/dist/components/display/KapiFileItem.js +74 -0
- package/dist/components/display/KapiFileViewer.d.ts +51 -0
- package/dist/components/display/KapiFileViewer.js +107 -0
- package/dist/components/display/KapiIconTile.d.ts +16 -0
- package/dist/components/display/KapiIconTile.js +29 -0
- package/dist/components/display/KapiStat.d.ts +24 -0
- package/dist/components/display/KapiStat.js +41 -0
- package/dist/components/display/KapiTag.d.ts +27 -0
- package/dist/components/display/KapiTag.js +44 -0
- package/dist/components/display/KapiTimeline.d.ts +15 -0
- package/dist/components/display/KapiTimeline.js +30 -0
- package/dist/components/display/annotationLogic.d.ts +26 -0
- package/dist/components/display/annotationLogic.js +17 -0
- package/dist/components/display/avatarGroupLogic.d.ts +14 -0
- package/dist/components/display/avatarGroupLogic.js +13 -0
- package/dist/components/display/avatarLogic.d.ts +5 -0
- package/dist/components/display/avatarLogic.js +9 -0
- package/dist/components/display/commentLogic.d.ts +18 -0
- package/dist/components/display/commentLogic.js +7 -0
- package/dist/components/display/fileItemLogic.d.ts +9 -0
- package/dist/components/display/fileItemLogic.js +12 -0
- package/dist/components/display/iconTileLogic.d.ts +13 -0
- package/dist/components/display/iconTileLogic.js +13 -0
- package/dist/components/display/index.d.ts +40 -0
- package/dist/components/display/statLogic.d.ts +10 -0
- package/dist/components/display/statLogic.js +10 -0
- package/dist/components/display/tagLogic.d.ts +11 -0
- package/dist/components/display/tagLogic.js +10 -0
- package/dist/components/display/timelineLogic.d.ts +5 -0
- package/dist/components/display/timelineLogic.js +4 -0
- package/dist/components/feedback/KapiAlert.d.ts +28 -0
- package/dist/components/feedback/KapiAlert.js +53 -0
- package/dist/components/feedback/KapiBulkBar.d.ts +30 -0
- package/dist/components/feedback/KapiBulkBar.js +34 -0
- package/dist/components/feedback/KapiCountdown.d.ts +26 -0
- package/dist/components/feedback/KapiCountdown.js +43 -0
- package/dist/components/feedback/KapiEmpty.d.ts +14 -0
- package/dist/components/feedback/KapiEmpty.js +31 -0
- package/dist/components/feedback/KapiFab.d.ts +34 -0
- package/dist/components/feedback/KapiFab.js +116 -0
- package/dist/components/feedback/KapiOfflineBanner.d.ts +25 -0
- package/dist/components/feedback/KapiOfflineBanner.js +40 -0
- package/dist/components/feedback/KapiProgress.d.ts +24 -0
- package/dist/components/feedback/KapiProgress.js +94 -0
- package/dist/components/feedback/KapiSkeleton.d.ts +12 -0
- package/dist/components/feedback/KapiSkeleton.js +16 -0
- package/dist/components/feedback/KapiToastContainer.d.ts +16 -0
- package/dist/components/feedback/KapiToastContainer.js +81 -0
- package/dist/components/feedback/KapiTour.d.ts +40 -0
- package/dist/components/feedback/KapiTour.js +157 -0
- package/dist/components/feedback/KapiUnsavedBar.d.ts +35 -0
- package/dist/components/feedback/KapiUnsavedBar.js +47 -0
- package/dist/components/feedback/bulkBarLogic.d.ts +2 -0
- package/dist/components/feedback/bulkBarLogic.js +7 -0
- package/dist/components/feedback/countdownLogic.d.ts +74 -0
- package/dist/components/feedback/countdownLogic.js +70 -0
- package/dist/components/feedback/fabLogic.d.ts +17 -0
- package/dist/components/feedback/fabLogic.js +10 -0
- package/dist/components/feedback/index.d.ts +33 -0
- package/dist/components/feedback/offlineBannerLogic.d.ts +10 -0
- package/dist/components/feedback/offlineBannerLogic.js +8 -0
- package/dist/components/feedback/progressLogic.d.ts +17 -0
- package/dist/components/feedback/progressLogic.js +16 -0
- package/dist/components/feedback/toastStore.d.ts +62 -0
- package/dist/components/feedback/toastStore.js +38 -0
- package/dist/components/feedback/tourLogic.d.ts +64 -0
- package/dist/components/feedback/tourLogic.js +31 -0
- package/dist/components/feedback/unsavedBarLogic.d.ts +2 -0
- package/dist/components/feedback/unsavedBarLogic.js +8 -0
- package/dist/components/inputs/KapiAutocomplete.d.ts +81 -0
- package/dist/components/inputs/KapiAutocomplete.js +205 -0
- package/dist/components/inputs/KapiButton.d.ts +23 -0
- package/dist/components/inputs/KapiButton.js +25 -0
- package/dist/components/inputs/KapiCalendar.d.ts +35 -0
- package/dist/components/inputs/KapiCalendar.js +60 -0
- package/dist/components/inputs/KapiCalendarGrid.d.ts +29 -0
- package/dist/components/inputs/KapiCalendarGrid.js +65 -0
- package/dist/components/inputs/KapiCheckCard.d.ts +28 -0
- package/dist/components/inputs/KapiCheckCard.js +41 -0
- package/dist/components/inputs/KapiCheckbox.d.ts +7 -0
- package/dist/components/inputs/KapiCheckbox.js +5 -0
- package/dist/components/inputs/KapiDatePicker.d.ts +46 -0
- package/dist/components/inputs/KapiDatePicker.js +189 -0
- package/dist/components/inputs/KapiDateRangePicker.d.ts +49 -0
- package/dist/components/inputs/KapiDateRangePicker.js +179 -0
- package/dist/components/inputs/KapiFacetedSearch.d.ts +45 -0
- package/dist/components/inputs/KapiFacetedSearch.js +188 -0
- package/dist/components/inputs/KapiField.d.ts +33 -0
- package/dist/components/inputs/KapiField.js +60 -0
- package/dist/components/inputs/KapiFileUpload.d.ts +58 -0
- package/dist/components/inputs/KapiFileUpload.js +134 -0
- package/dist/components/inputs/KapiIconButton.d.ts +13 -0
- package/dist/components/inputs/KapiIconButton.js +16 -0
- package/dist/components/inputs/KapiInput.d.ts +92 -0
- package/dist/components/inputs/KapiInput.js +123 -0
- package/dist/components/inputs/KapiInputGroup.d.ts +37 -0
- package/dist/components/inputs/KapiInputGroup.js +29 -0
- package/dist/components/inputs/KapiMaskedInput.d.ts +52 -0
- package/dist/components/inputs/KapiMaskedInput.js +78 -0
- package/dist/components/inputs/KapiNumberStepper.d.ts +44 -0
- package/dist/components/inputs/KapiNumberStepper.js +100 -0
- package/dist/components/inputs/KapiRadio.d.ts +7 -0
- package/dist/components/inputs/KapiRadio.js +5 -0
- package/dist/components/inputs/KapiRating.d.ts +55 -0
- package/dist/components/inputs/KapiRating.js +99 -0
- package/dist/components/inputs/KapiSegmented.d.ts +41 -0
- package/dist/components/inputs/KapiSegmented.js +83 -0
- package/dist/components/inputs/KapiSelect.d.ts +109 -0
- package/dist/components/inputs/KapiSelect.js +443 -0
- package/dist/components/inputs/KapiSlider.d.ts +42 -0
- package/dist/components/inputs/KapiSlider.js +70 -0
- package/dist/components/inputs/KapiSwitch.d.ts +13 -0
- package/dist/components/inputs/KapiSwitch.js +28 -0
- package/dist/components/inputs/KapiTagInput.d.ts +79 -0
- package/dist/components/inputs/KapiTagInput.js +115 -0
- package/dist/components/inputs/KapiTextarea.d.ts +32 -0
- package/dist/components/inputs/KapiTextarea.js +67 -0
- package/dist/components/inputs/asyncSelectLogic.d.ts +42 -0
- package/dist/components/inputs/asyncSelectLogic.js +46 -0
- package/dist/components/inputs/autocompleteLogic.d.ts +73 -0
- package/dist/components/inputs/autocompleteLogic.js +67 -0
- package/dist/components/inputs/checkControl.d.ts +20 -0
- package/dist/components/inputs/checkControl.js +31 -0
- package/dist/components/inputs/datePickerLogic.d.ts +92 -0
- package/dist/components/inputs/datePickerLogic.js +174 -0
- package/dist/components/inputs/dateRangeLogic.d.ts +31 -0
- package/dist/components/inputs/dateRangeLogic.js +44 -0
- package/dist/components/inputs/facetedSearchLogic.d.ts +69 -0
- package/dist/components/inputs/facetedSearchLogic.js +39 -0
- package/dist/components/inputs/fieldLogic.d.ts +67 -0
- package/dist/components/inputs/fieldLogic.js +35 -0
- package/dist/components/inputs/fileUploadLogic.d.ts +13 -0
- package/dist/components/inputs/fileUploadLogic.js +9 -0
- package/dist/components/inputs/index.d.ts +55 -0
- package/dist/components/inputs/inputGroupLogic.d.ts +22 -0
- package/dist/components/inputs/inputGroupLogic.js +9 -0
- package/dist/components/inputs/inputLogic.d.ts +25 -0
- package/dist/components/inputs/inputLogic.js +10 -0
- package/dist/components/inputs/maskLogic.d.ts +34 -0
- package/dist/components/inputs/maskLogic.js +57 -0
- package/dist/components/inputs/numberStepLogic.d.ts +14 -0
- package/dist/components/inputs/numberStepLogic.js +21 -0
- package/dist/components/inputs/ratingLogic.d.ts +9 -0
- package/dist/components/inputs/ratingLogic.js +22 -0
- package/dist/components/inputs/segmentedLogic.d.ts +6 -0
- package/dist/components/inputs/segmentedLogic.js +14 -0
- package/dist/components/inputs/selectLogic.d.ts +89 -0
- package/dist/components/inputs/selectLogic.js +82 -0
- package/dist/components/inputs/sliderLogic.d.ts +9 -0
- package/dist/components/inputs/sliderLogic.js +17 -0
- package/dist/components/inputs/tagInputLogic.d.ts +40 -0
- package/dist/components/inputs/tagInputLogic.js +40 -0
- package/dist/components/inputs/textareaLogic.d.ts +17 -0
- package/dist/components/inputs/textareaLogic.js +13 -0
- package/dist/components/inputs/useCalendarFocus.d.ts +13 -0
- package/dist/components/inputs/useCalendarFocus.js +25 -0
- package/dist/components/inputs/useDatePopSide.d.ts +15 -0
- package/dist/components/inputs/useDatePopSide.js +20 -0
- package/dist/components/labelsLogic.d.ts +6 -0
- package/dist/components/labelsLogic.js +11 -0
- package/dist/components/layout/KapiAccessCard.d.ts +40 -0
- package/dist/components/layout/KapiAccessCard.js +66 -0
- package/dist/components/layout/KapiAccountMenu.d.ts +61 -0
- package/dist/components/layout/KapiAccountMenu.js +212 -0
- package/dist/components/layout/KapiCard.d.ts +43 -0
- package/dist/components/layout/KapiCard.js +53 -0
- package/dist/components/layout/KapiCluster.d.ts +15 -0
- package/dist/components/layout/KapiCluster.js +21 -0
- package/dist/components/layout/KapiContextSlot.d.ts +45 -0
- package/dist/components/layout/KapiContextSlot.js +92 -0
- package/dist/components/layout/KapiDivider.d.ts +16 -0
- package/dist/components/layout/KapiDivider.js +41 -0
- package/dist/components/layout/KapiGrid.d.ts +19 -0
- package/dist/components/layout/KapiGrid.js +25 -0
- package/dist/components/layout/KapiNotificationBell.d.ts +75 -0
- package/dist/components/layout/KapiNotificationBell.js +196 -0
- package/dist/components/layout/KapiPageHeader.d.ts +28 -0
- package/dist/components/layout/KapiPageHeader.js +47 -0
- package/dist/components/layout/KapiPanelParts.d.ts +35 -0
- package/dist/components/layout/KapiPanelParts.js +63 -0
- package/dist/components/layout/KapiRail.d.ts +40 -0
- package/dist/components/layout/KapiRail.js +102 -0
- package/dist/components/layout/KapiScrollArea.d.ts +40 -0
- package/dist/components/layout/KapiScrollArea.js +49 -0
- package/dist/components/layout/KapiShell.d.ts +40 -0
- package/dist/components/layout/KapiShell.js +48 -0
- package/dist/components/layout/KapiStack.d.ts +11 -0
- package/dist/components/layout/KapiStack.js +19 -0
- package/dist/components/layout/accessCardLogic.d.ts +18 -0
- package/dist/components/layout/accessCardLogic.js +11 -0
- package/dist/components/layout/cardLogic.d.ts +9 -0
- package/dist/components/layout/cardLogic.js +11 -0
- package/dist/components/layout/index.d.ts +33 -0
- package/dist/components/layout/layoutPrimitives.d.ts +51 -0
- package/dist/components/layout/layoutPrimitives.js +58 -0
- package/dist/components/layout/notificationBellLogic.d.ts +20 -0
- package/dist/components/layout/notificationBellLogic.js +14 -0
- package/dist/components/layout/railLogic.d.ts +52 -0
- package/dist/components/layout/railLogic.js +16 -0
- package/dist/components/locale/KapiLocaleProvider.d.ts +29 -0
- package/dist/components/locale/KapiLocaleProvider.js +27 -0
- package/dist/components/locale/index.d.ts +5 -0
- package/dist/components/locale/labelRegistry.d.ts +110 -0
- package/dist/components/locale/linkLabels.d.ts +6 -0
- package/dist/components/locale/linkLabels.js +4 -0
- package/dist/components/navigation/CtxSlidingList.d.ts +11 -0
- package/dist/components/navigation/CtxSlidingList.js +24 -0
- package/dist/components/navigation/KapiAccordion.d.ts +24 -0
- package/dist/components/navigation/KapiAccordion.js +68 -0
- package/dist/components/navigation/KapiBreadcrumb.d.ts +23 -0
- package/dist/components/navigation/KapiBreadcrumb.js +32 -0
- package/dist/components/navigation/KapiCollapsiblePanel.d.ts +63 -0
- package/dist/components/navigation/KapiCollapsiblePanel.js +83 -0
- package/dist/components/navigation/KapiContextPanel.d.ts +117 -0
- package/dist/components/navigation/KapiContextPanel.js +265 -0
- package/dist/components/navigation/KapiFilterChips.d.ts +30 -0
- package/dist/components/navigation/KapiFilterChips.js +40 -0
- package/dist/components/navigation/KapiNavBadge.d.ts +26 -0
- package/dist/components/navigation/KapiNavBadge.js +33 -0
- package/dist/components/navigation/KapiPanelGroup.d.ts +69 -0
- package/dist/components/navigation/KapiPanelGroup.js +149 -0
- package/dist/components/navigation/KapiSteps.d.ts +43 -0
- package/dist/components/navigation/KapiSteps.js +63 -0
- package/dist/components/navigation/KapiTabs.d.ts +56 -0
- package/dist/components/navigation/KapiTabs.js +81 -0
- package/dist/components/navigation/KapiTree.d.ts +56 -0
- package/dist/components/navigation/KapiTree.js +157 -0
- package/dist/components/navigation/KapiWizard.d.ts +49 -0
- package/dist/components/navigation/KapiWizard.js +104 -0
- package/dist/components/navigation/MiniNavItem.d.ts +35 -0
- package/dist/components/navigation/MiniNavItem.js +57 -0
- package/dist/components/navigation/accordionLogic.d.ts +4 -0
- package/dist/components/navigation/accordionLogic.js +4 -0
- package/dist/components/navigation/activeItemLogic.d.ts +7 -0
- package/dist/components/navigation/activeItemLogic.js +4 -0
- package/dist/components/navigation/breadcrumbLogic.d.ts +8 -0
- package/dist/components/navigation/breadcrumbLogic.js +4 -0
- package/dist/components/navigation/contextPanelLogic.d.ts +27 -0
- package/dist/components/navigation/contextPanelLogic.js +11 -0
- package/dist/components/navigation/filterChipsLogic.d.ts +23 -0
- package/dist/components/navigation/filterChipsLogic.js +20 -0
- package/dist/components/navigation/index.d.ts +39 -0
- package/dist/components/navigation/navBadgeLogic.d.ts +26 -0
- package/dist/components/navigation/navBadgeLogic.js +16 -0
- package/dist/components/navigation/panelGroupLogic.d.ts +15 -0
- package/dist/components/navigation/panelGroupLogic.js +13 -0
- package/dist/components/navigation/panelMini.d.ts +13 -0
- package/dist/components/navigation/panelMini.js +9 -0
- package/dist/components/navigation/panelMiniLogic.d.ts +19 -0
- package/dist/components/navigation/panelMiniLogic.js +6 -0
- package/dist/components/navigation/stepsLogic.d.ts +25 -0
- package/dist/components/navigation/stepsLogic.js +10 -0
- package/dist/components/navigation/tabsLogic.d.ts +12 -0
- package/dist/components/navigation/tabsLogic.js +14 -0
- package/dist/components/navigation/treeLogic.d.ts +80 -0
- package/dist/components/navigation/treeLogic.js +91 -0
- package/dist/components/navigation/wizardLogic.d.ts +11 -0
- package/dist/components/navigation/wizardLogic.js +7 -0
- package/dist/components/overlays/KapiCanvasForm.d.ts +42 -0
- package/dist/components/overlays/KapiCanvasForm.js +60 -0
- package/dist/components/overlays/KapiCommandPalette.d.ts +58 -0
- package/dist/components/overlays/KapiCommandPalette.js +152 -0
- package/dist/components/overlays/KapiConfirm.d.ts +48 -0
- package/dist/components/overlays/KapiConfirm.js +94 -0
- package/dist/components/overlays/KapiContextMenu.d.ts +51 -0
- package/dist/components/overlays/KapiContextMenu.js +90 -0
- package/dist/components/overlays/KapiDialogHost.d.ts +23 -0
- package/dist/components/overlays/KapiDialogHost.js +129 -0
- package/dist/components/overlays/KapiDropdown.d.ts +30 -0
- package/dist/components/overlays/KapiDropdown.js +84 -0
- package/dist/components/overlays/KapiLoading.d.ts +14 -0
- package/dist/components/overlays/KapiLoading.js +43 -0
- package/dist/components/overlays/KapiMenuButton.d.ts +25 -0
- package/dist/components/overlays/KapiMenuButton.js +109 -0
- package/dist/components/overlays/KapiModal.d.ts +61 -0
- package/dist/components/overlays/KapiModal.js +106 -0
- package/dist/components/overlays/KapiOffcanvas.d.ts +96 -0
- package/dist/components/overlays/KapiOffcanvas.js +209 -0
- package/dist/components/overlays/KapiOffcanvasStack.d.ts +58 -0
- package/dist/components/overlays/KapiOffcanvasStack.js +84 -0
- package/dist/components/overlays/KapiPhotoView.d.ts +60 -0
- package/dist/components/overlays/KapiPhotoView.js +122 -0
- package/dist/components/overlays/KapiPopover.d.ts +46 -0
- package/dist/components/overlays/KapiPopover.js +68 -0
- package/dist/components/overlays/KapiShapeSheet.d.ts +34 -0
- package/dist/components/overlays/KapiShapeSheet.js +82 -0
- package/dist/components/overlays/KapiSplitModal.d.ts +41 -0
- package/dist/components/overlays/KapiSplitModal.js +125 -0
- package/dist/components/overlays/KapiTooltip.d.ts +38 -0
- package/dist/components/overlays/KapiTooltip.js +105 -0
- package/dist/components/overlays/canvasFormLogic.d.ts +18 -0
- package/dist/components/overlays/canvasFormLogic.js +16 -0
- package/dist/components/overlays/commandPaletteLogic.d.ts +29 -0
- package/dist/components/overlays/commandPaletteLogic.js +19 -0
- package/dist/components/overlays/dialogStore.d.ts +114 -0
- package/dist/components/overlays/dialogStore.js +76 -0
- package/dist/components/overlays/dialogUtils.d.ts +72 -0
- package/dist/components/overlays/dialogUtils.js +62 -0
- package/dist/components/overlays/exitPresenceLogic.d.ts +14 -0
- package/dist/components/overlays/exitPresenceLogic.js +5 -0
- package/dist/components/overlays/index.d.ts +38 -0
- package/dist/components/overlays/menuButtonLogic.d.ts +58 -0
- package/dist/components/overlays/menuButtonLogic.js +9 -0
- package/dist/components/overlays/menuKeyboardLogic.d.ts +25 -0
- package/dist/components/overlays/menuKeyboardLogic.js +30 -0
- package/dist/components/overlays/offcanvasCompanion.d.ts +16 -0
- package/dist/components/overlays/offcanvasCompanion.js +6 -0
- package/dist/components/overlays/offcanvasResize.d.ts +22 -0
- package/dist/components/overlays/offcanvasResize.js +10 -0
- package/dist/components/overlays/offcanvasSide.d.ts +30 -0
- package/dist/components/overlays/offcanvasSide.js +29 -0
- package/dist/components/overlays/offcanvasStack.d.ts +76 -0
- package/dist/components/overlays/offcanvasStack.js +72 -0
- package/dist/components/overlays/overlayPortal.d.ts +9 -0
- package/dist/components/overlays/overlayPortal.js +6 -0
- package/dist/components/overlays/popoverLogic.d.ts +94 -0
- package/dist/components/overlays/popoverLogic.js +47 -0
- package/dist/components/overlays/splitModalLayout.d.ts +8 -0
- package/dist/components/overlays/splitModalLayout.js +5 -0
- package/dist/components/overlays/useExitPresence.d.ts +14 -0
- package/dist/components/overlays/useExitPresence.js +34 -0
- package/dist/components/overlays/useNativeDialog.d.ts +30 -0
- package/dist/components/overlays/useNativeDialog.js +56 -0
- package/dist/components/rootProps.d.ts +17 -0
- package/dist/components/rootProps.js +4 -0
- package/dist/hooks/breakpointOpenLogic.d.ts +7 -0
- package/dist/hooks/breakpointOpenLogic.js +4 -0
- package/dist/hooks/dateFormat.d.ts +12 -0
- package/dist/hooks/dateFormat.js +15 -0
- package/dist/hooks/downloadLogic.d.ts +31 -0
- package/dist/hooks/downloadLogic.js +85 -0
- package/dist/hooks/focusLogic.d.ts +18 -0
- package/dist/hooks/focusLogic.js +17 -0
- package/dist/hooks/growOriginLogic.d.ts +22 -0
- package/dist/hooks/growOriginLogic.js +10 -0
- package/dist/hooks/hotkeyLogic.d.ts +22 -0
- package/dist/hooks/hotkeyLogic.js +32 -0
- package/dist/hooks/index.d.ts +49 -0
- package/dist/hooks/kapiRoot.d.ts +16 -0
- package/dist/hooks/kapiRoot.js +9 -0
- package/dist/hooks/onlineStatusLogic.d.ts +31 -0
- package/dist/hooks/onlineStatusLogic.js +28 -0
- package/dist/hooks/persistentStateLogic.d.ts +12 -0
- package/dist/hooks/persistentStateLogic.js +17 -0
- package/dist/hooks/rafThrottle.d.ts +33 -0
- package/dist/hooks/rafThrottle.js +33 -0
- package/dist/hooks/recentLogic.d.ts +9 -0
- package/dist/hooks/recentLogic.js +10 -0
- package/dist/hooks/reducedMotion.d.ts +13 -0
- package/dist/hooks/reducedMotion.js +5 -0
- package/dist/hooks/rightClick.d.ts +37 -0
- package/dist/hooks/rightClick.js +30 -0
- package/dist/hooks/scrollCollapseLogic.d.ts +12 -0
- package/dist/hooks/scrollCollapseLogic.js +4 -0
- package/dist/hooks/scrollEdgesLogic.d.ts +49 -0
- package/dist/hooks/scrollEdgesLogic.js +19 -0
- package/dist/hooks/slidingIndicatorLogic.d.ts +25 -0
- package/dist/hooks/slidingIndicatorLogic.js +15 -0
- package/dist/hooks/storageLogic.d.ts +15 -0
- package/dist/hooks/storageLogic.js +24 -0
- package/dist/hooks/unsavedGuardLogic.d.ts +17 -0
- package/dist/hooks/unsavedGuardLogic.js +17 -0
- package/dist/hooks/useDragScroll.d.ts +12 -0
- package/dist/hooks/useDragScroll.js +32 -0
- package/dist/hooks/useGlobalRightClick.d.ts +14 -0
- package/dist/hooks/useGlobalRightClick.js +35 -0
- package/dist/hooks/useGrowOrigin.d.ts +13 -0
- package/dist/hooks/useGrowOrigin.js +16 -0
- package/dist/hooks/useHotkey.d.ts +15 -0
- package/dist/hooks/useHotkey.js +31 -0
- package/dist/hooks/useKapiTheme.d.ts +69 -0
- package/dist/hooks/useKapiTheme.js +88 -0
- package/dist/hooks/useMediaQuery.d.ts +6 -0
- package/dist/hooks/useMediaQuery.js +13 -0
- package/dist/hooks/useObjectUrl.d.ts +6 -0
- package/dist/hooks/useObjectUrl.js +19 -0
- package/dist/hooks/useOnlineStatus.d.ts +7 -0
- package/dist/hooks/useOnlineStatus.js +7 -0
- package/dist/hooks/usePersistentState.d.ts +19 -0
- package/dist/hooks/usePersistentState.js +37 -0
- package/dist/hooks/usePlacement.d.ts +36 -0
- package/dist/hooks/usePlacement.js +84 -0
- package/dist/hooks/useRecent.d.ts +17 -0
- package/dist/hooks/useRecent.js +16 -0
- package/dist/hooks/useScrollCollapse.d.ts +10 -0
- package/dist/hooks/useScrollCollapse.js +27 -0
- package/dist/hooks/useScrollEdges.d.ts +32 -0
- package/dist/hooks/useScrollEdges.js +91 -0
- package/dist/hooks/useSlidingIndicator.d.ts +23 -0
- package/dist/hooks/useSlidingIndicator.js +51 -0
- package/dist/hooks/useUnsavedGuard.d.ts +70 -0
- package/dist/hooks/useUnsavedGuard.js +47 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +162 -0
- package/dist/kapi.css +2 -0
- package/dist/kapi.layered.css +4 -0
- package/package.json +86 -4
package/README.md
CHANGED
|
@@ -1,3 +1,765 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @kapiuara/ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Design system extraído del v2 de `ucugestionfront`. React 19 + TypeScript + Vite.
|
|
4
|
+
CSS nativo propio scopeado bajo `.kapi` (tokens como custom properties). Sin Radix / Bootstrap /
|
|
5
|
+
Tailwind / Mantine. Iconos: sólo `lucide-react`. Fuente: Nunito.
|
|
6
|
+
|
|
7
|
+
> Paquete privado del monorepo `kapiuara`. Todavía no se publica: es `private: true` y no hay nada publicado en el scope `@kapiuara` de npm. Ver `BRANDING.md` en la raíz del repo.
|
|
8
|
+
|
|
9
|
+
## Comandos
|
|
10
|
+
|
|
11
|
+
| Comando | Qué hace |
|
|
12
|
+
| ------------------------------- | -------------------------------------------------------------------------------------- |
|
|
13
|
+
| `pnpm dev` | Playground (demo/) en dev server, usa `src/` directo vía alias `@kapiuara/ui` |
|
|
14
|
+
| `pnpm build` | Build de la LIBRERÍA: un `.js` + `.d.ts` por módulo (`dist/index.js`), `dist/kapi.css` |
|
|
15
|
+
| `pnpm build:demo` | Build estático del playground → `dist-demo/` |
|
|
16
|
+
| `pnpm test` / `pnpm test:watch` | Vitest (lógica pura, no markup) |
|
|
17
|
+
| `pnpm typecheck` | `tsc --noEmit` sobre src + demo + configs |
|
|
18
|
+
| `pnpm test:visual` | Regresión visual del catálogo (Playwright, en Docker); ver abajo |
|
|
19
|
+
| `pnpm test:visual:update` | Regenera las baselines de la regresión visual (en Docker) |
|
|
20
|
+
|
|
21
|
+
### Regresión visual
|
|
22
|
+
|
|
23
|
+
`pnpm test:visual` compara capturas del demo compilado (`build:demo` + `vite preview`) contra las
|
|
24
|
+
baselines de `visual/__screenshots__/`: Chromium, viewport 1280×800, tema claro en cada sección del
|
|
25
|
+
catálogo (`demo/catalog.tsx`, una sección nueva entra sola) y oscuro en la cáscara, inputs, tabla,
|
|
26
|
+
diálogos y feedback. Reloj fijo, sin animaciones y con las fuentes cargadas. Tolerancia: ningún píxel
|
|
27
|
+
distinto (`maxDiffPixels: 0`) con umbral por píxel `0.005`, que alcanza para ver un gris corrido 2–3
|
|
28
|
+
niveles; `0` no es estable (el antialiasing varía entre corridas).
|
|
29
|
+
|
|
30
|
+
- Corre **siempre dentro de la imagen** `mcr.microsoft.com/playwright:v1.63.0-noble` (la misma del
|
|
31
|
+
job `visual` del CI). `visual/run.sh` la levanta con Docker con tu uid, instala las dependencias
|
|
32
|
+
dentro del contenedor y devuelve el reporte a `playwright-report/` (gitignored). Requiere Docker.
|
|
33
|
+
- Si un cambio visual es intencional, `pnpm test:visual:update` regenera las baselines; revisá los
|
|
34
|
+
PNG en el diff y commitealos junto al cambio. Borra las baselines huérfanas (de secciones que ya
|
|
35
|
+
no existen), reescribe solo las que cambiaron y, si Playwright falla, no toca las del repo. El
|
|
36
|
+
script no acepta otros argumentos. **Nunca** las regeneres fuera de la imagen (otro SO
|
|
37
|
+
rasteriza distinto y el CI fallaría).
|
|
38
|
+
- No forma parte de `pnpm check`. Si el job `visual` falla, el artefacto `visual-report` trae el
|
|
39
|
+
reporte con las diferencias. Un test de vitest (`visual/catalogRoutes.test.ts`) verifica que la
|
|
40
|
+
lista de rutas leída de `demo/catalog.tsx` coincida con `CATALOG`.
|
|
41
|
+
|
|
42
|
+
## Estructura
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
src/components/{inputs,overlays,data,feedback,navigation,layout}/ # un index.ts por tema
|
|
46
|
+
src/hooks/ # mecánicas: drag-scroll, click derecho global, tema, media query
|
|
47
|
+
src/styles/ # kapi.css (componentes; importa los tokens de @kapiuara/tokens), shell.css, fonts.css
|
|
48
|
+
src/index.ts # barrel: re-exporta todo
|
|
49
|
+
demo/ # playground: index.html, main.tsx, App.tsx, catalog.tsx, views/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Convenciones
|
|
53
|
+
|
|
54
|
+
- Props tipadas explícitas (`XxxProps` exportado: los tipos SON el contrato). Defaults en los parámetros.
|
|
55
|
+
- Cada prop pública lleva una línea de JSDoc (con `@default` si el default no se ve en la desestructuración):
|
|
56
|
+
es la fuente de la [referencia de props](#referencia-de-props).
|
|
57
|
+
- JSX runtime automático: no hace falta `import React`.
|
|
58
|
+
- Nada hardcodeado inline: clases `.kapi-*` en `src/styles/` usando tokens `--kapi-*`. Inline sólo si es dinámico
|
|
59
|
+
(p. ej. la posición `left/top` del menú contextual).
|
|
60
|
+
- Componentes de comportamiento sobre `<dialog>` nativo cuando aplique.
|
|
61
|
+
- Tests: lógica pura exportada (ej. `buttonClasses`), colocados como `Xxx.test.ts`.
|
|
62
|
+
- Todo CSS nuevo va anidado bajo `.kapi { … }`. Es CSS plano: comentarios `/* */` (nunca `//`).
|
|
63
|
+
- Los overlays se montan DENTRO de `.kapi` (hijos del `KapiShell` o vía su prop `overlays`) para heredar tokens.
|
|
64
|
+
- Overlays controlados: `open` + `onClose()` (Escape, fondo, X y Cancelar piden cierre); paneles plegables:
|
|
65
|
+
`collapsed`/`defaultCollapsed`/`onCollapsedChange`. Ej.: `<KapiModal open={open} onClose={() => setOpen(false)}>…</KapiModal>`.
|
|
66
|
+
- `.kapi-row` ya existe (space-between); para filas con gap usá `.kapi-cluster`.
|
|
67
|
+
- Para sumar un componente al playground: una sección en `demo/catalog.tsx`.
|
|
68
|
+
|
|
69
|
+
## Referencia de props
|
|
70
|
+
|
|
71
|
+
El playground trae una vista **Referencia → Referencia de props** (`#/referencia/props/KapiButton`): elige un
|
|
72
|
+
componente y ves la tabla de sus props (nombre, tipo, si es requerida, valor por defecto y descripción) más un
|
|
73
|
+
resumen de los atributos nativos que pasa al elemento.
|
|
74
|
+
|
|
75
|
+
La tabla se genera desde el código TypeScript en cada `pnpm dev` / `pnpm build:demo` (`scripts/propsDoc.mjs`,
|
|
76
|
+
servido como módulo virtual `virtual:kapi-props-doc` por `scripts/vitePropsDoc.mjs`); no hay nada generado en
|
|
77
|
+
el repo, así que no se desactualiza. **La fuente es el JSDoc de cada prop** en el tipo `XxxProps`:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
export type KapiFooProps = {
|
|
81
|
+
/** Escala del control. */
|
|
82
|
+
size?: "sm" | "md" | "lg";
|
|
83
|
+
/**
|
|
84
|
+
* Cuántos ítems se muestran antes de "Ver más".
|
|
85
|
+
* @default 5
|
|
86
|
+
*/
|
|
87
|
+
limit?: number;
|
|
88
|
+
/** @deprecated Usa `size`. */
|
|
89
|
+
compact?: boolean;
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- El valor por defecto sale de `@default` o, si no hay, del default en la desestructuración del componente.
|
|
94
|
+
- `@deprecated` marca la prop como obsoleta en la tabla.
|
|
95
|
+
- Las props que vienen de `HTMLAttributes` y compañía no se listan una por una: se resumen en una línea.
|
|
96
|
+
- El JSDoc tiene que ir en un tipo de varias líneas: TypeScript no asocia el comentario a miembros de un tipo
|
|
97
|
+
escrito en una sola línea (`{ /** … */ a: string }`).
|
|
98
|
+
|
|
99
|
+
## Consumo de estilos
|
|
100
|
+
|
|
101
|
+
Todo el CSS (los tokens de `@kapiuara/tokens`, el shell y el CSS co-locado de cada componente:
|
|
102
|
+
`KapiSelect.css`, `overlays.css`, `KapiOffcanvas.css`, `feedback.css`, `navigation.css`, `KapiTable.css`,
|
|
103
|
+
etc.) se emite en UN solo archivo: `dist/kapi.css`, expuesto como `@kapiuara/ui/styles.css`. El build
|
|
104
|
+
inlinea el `@import` de los tokens, así que la app host **no** instala `@kapiuara/tokens`. Se importa
|
|
105
|
+
una vez en la app host:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import "@kapiuara/ui/styles.css";
|
|
109
|
+
import "@fontsource-variable/nunito"; // opcional: la fuente NO viene en la lib (ver "Fuente")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Los componentes también importan su CSS (por eso `sideEffects: ["**/*.css"]`), pero en modo lib Vite lo
|
|
113
|
+
extrae a `kapi.css`; los `.js` de `dist/` no contienen imports de CSS.
|
|
114
|
+
|
|
115
|
+
## React Server Components
|
|
116
|
+
|
|
117
|
+
Cada módulo que usa React abre con `"use client"` en `dist`, así que los componentes se pueden importar desde un Server Component (Next.js App Router) sin envolverlos. La lógica pura (`*Logic`, helpers de clases) no lleva la directiva y se puede usar en el servidor. El build falla si un módulo que importa `react` queda sin ella.
|
|
118
|
+
|
|
119
|
+
## Tree-shaking
|
|
120
|
+
|
|
121
|
+
El build de la librería emite un módulo por archivo fuente (`preserveModules`, raíz `src/`), con su `.d.ts`
|
|
122
|
+
al lado. El bundler de la app descarta lo que no importa: una app que sólo usa `KapiButton` suma ~0,6 kB
|
|
123
|
+
minificados en lugar de ~11 kB con el archivo único anterior.
|
|
124
|
+
|
|
125
|
+
## Shell composable (con y sin rail)
|
|
126
|
+
|
|
127
|
+
`KapiShell` es la raíz `.kapi` (tema, acento, fuente, click derecho global) y compone
|
|
128
|
+
`[rail?] [panel?] <main.kapi-content>`. Rail y panel son props OPCIONALES e independientes.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { KapiShell, KapiRail, KapiContextSlot, KapiPanelItem, KapiPanelList } from "@kapiuara/ui";
|
|
132
|
+
import "@kapiuara/ui/styles.css"; // + importá la fuente: @fontsource-variable/nunito
|
|
133
|
+
|
|
134
|
+
// 1) Con rail + panel contextual
|
|
135
|
+
<KapiShell
|
|
136
|
+
rail={<KapiRail brand="S" items={items} activeId={activeId} onSelect={go} />}
|
|
137
|
+
panel={
|
|
138
|
+
<KapiContextSlot title="Personas">
|
|
139
|
+
<KapiPanelList><KapiPanelItem active>Listado</KapiPanelItem></KapiPanelList>
|
|
140
|
+
</KapiContextSlot>
|
|
141
|
+
}
|
|
142
|
+
>
|
|
143
|
+
<Pagina />
|
|
144
|
+
</KapiShell>
|
|
145
|
+
|
|
146
|
+
// 2) Sin rail, con panel (otra navegación va en el contenido)
|
|
147
|
+
<KapiShell panel={<KapiContextSlot title="Sección">…</KapiContextSlot>}>
|
|
148
|
+
<MiTopNav />
|
|
149
|
+
<Pagina />
|
|
150
|
+
</KapiShell>
|
|
151
|
+
|
|
152
|
+
// 3) Sólo contenido
|
|
153
|
+
<KapiShell><Pagina /></KapiShell>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- `KapiRail` es presentacional: la app le pasa `activeId` + `onSelect` (deriva el activo de su router). `footer` para cuenta/tema.
|
|
157
|
+
- Zonas fijas del rail: `brand` (arriba) y `footer` (abajo) no se mueven nunca. Cada ítem decide con `pinned`:
|
|
158
|
+
`"start"` lo fija arriba bajo la marca, `"end"` abajo sobre el footer, y sin `pinned` va a la zona del medio, la
|
|
159
|
+
única que scrollea cuando no entra en el alto (en móvil, la barra inferior scrollea de costado con las fijas en
|
|
160
|
+
los extremos). Los divisores (`{ separator: true, pinned }`) siguen la misma regla.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<KapiRail
|
|
164
|
+
brand="S"
|
|
165
|
+
items={[
|
|
166
|
+
{ id: "inicio", label: "Inicio", icon: <Home size={21} />, pinned: "start" },
|
|
167
|
+
{ id: "tramites", label: "Trámites", icon: <Inbox size={21} /> },
|
|
168
|
+
{ id: "config", label: "Configuración", icon: <Settings size={21} />, pinned: "end" }
|
|
169
|
+
]}
|
|
170
|
+
activeId={activeId}
|
|
171
|
+
onSelect={go}
|
|
172
|
+
footer={<AccountButton />}
|
|
173
|
+
/>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- `KapiContextSlot`: plegable. Controlado (`open`/`onOpenChange`) o no (`defaultOpen`; por defecto abierto en desktop y cerrado en móvil).
|
|
177
|
+
Animación: el ancho colapsa 258px ↔ 44px (`.kapi-ctxslot`), el panel entra con `kapi-slide-in`.
|
|
178
|
+
- `contextMenuItems` en `KapiShell` habilita el menú del sistema (doble click derecho). El arrastre con click derecho scrollea siempre.
|
|
179
|
+
- Avisos de atención (vencimientos, pendientes): `KapiRailItem`, `KapiPanelItem`, los ítems de
|
|
180
|
+
`KapiContextSections` y `KapiPanelGroup` (cabecera y subítems) aceptan `badge` (número o `"dot"`), `badgeTone`
|
|
181
|
+
(variantes de `KapiBadge`: `danger` para vencidos, `warning` para por vencer) y `badgeLabel` (lo que lee un lector
|
|
182
|
+
de pantalla: "3 trámites vencidos"). Un 0 no dibuja nada; en el rail es una burbuja sobre el ícono (tope "99+") y
|
|
183
|
+
en los paneles va al final (tope "999+"). En `KapiPanelGroup` convive con `count`, que es el total neutro. La
|
|
184
|
+
pieza suelta es `KapiNavBadge`.
|
|
185
|
+
- Integración de los avisos: el rail y el panel son presentacionales, ninguna vista los muta. Los avisos salen de
|
|
186
|
+
una sola fuente compartida (un hook o store al nivel del shell que lee el backend) y los ítems se derivan de ella,
|
|
187
|
+
igual que `activeId` sale del router. Una vista que resuelve algo cambia esa fuente (o invalida su consulta) y la
|
|
188
|
+
navegación se recalcula sola:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
const avisos = useAvisos(); // { tramites: { demorados: 3 } }, del backend, compartido por contexto
|
|
192
|
+
const items = BASE_ITEMS.map((i) =>
|
|
193
|
+
i.id === "tramites"
|
|
194
|
+
? {
|
|
195
|
+
...i,
|
|
196
|
+
badge: avisos.tramites.demorados,
|
|
197
|
+
badgeTone: "danger",
|
|
198
|
+
badgeLabel: `${avisos.tramites.demorados} vencidos`
|
|
199
|
+
}
|
|
200
|
+
: i
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Ejemplo armado en el demo de Trámites (`demo/views/screens/tramites/avisos.ts`): el rail, el panel y "Mi
|
|
205
|
+
trabajo" muestran el mismo número.
|
|
206
|
+
|
|
207
|
+
- Panel plegado en modo mini: `KapiContextSlot` y `KapiContextPanel` aceptan `collapsedMode="mini"` (default
|
|
208
|
+
`"hidden"`). Plegado en desktop, el panel queda como una tira de íconos: cada `KapiPanelItem`, ítem de
|
|
209
|
+
`KapiContextSections` y cabecera de `KapiPanelGroup` muestra sólo su ícono (o sus iniciales, o la inicial de la
|
|
210
|
+
etiqueta), el aviso como burbuja en la esquina y un tooltip con el título y el aviso al pasar el mouse o llegar con
|
|
211
|
+
Tab. El clic navega sin desplegar; la cabecera de un grupo despliega el panel y abre el grupo. Se ocultan el
|
|
212
|
+
título, las etiquetas de sección, el buscador, la ayuda y el contenido propio de la app (lo que no sea una pieza
|
|
213
|
+
de navegación de kapi). Un componente propio dentro del panel puede leer el estado con `useKapiPanelMini()`. En
|
|
214
|
+
móvil el panel sigue siendo un drawer.
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<KapiContextSlot title="Trámites" collapsedMode="mini">
|
|
218
|
+
<KapiPanelList>
|
|
219
|
+
<KapiPanelItem icon={<Inbox size={15} />} badge={22} badgeLabel="22 esperando tu acción">
|
|
220
|
+
Mi trabajo
|
|
221
|
+
</KapiPanelItem>
|
|
222
|
+
</KapiPanelList>
|
|
223
|
+
</KapiContextSlot>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
- Responsive: ≤ 1024px reduce padding; ≤ 768px el rail pasa a barra inferior y el panel a drawer con backdrop
|
|
227
|
+
(botón flotante para reabrirlo). Sin rail el drawer funciona igual. Breakpoint en `shell.css` y `SHELL_MOBILE_QUERY`.
|
|
228
|
+
|
|
229
|
+
## Selección e ids tipados
|
|
230
|
+
|
|
231
|
+
- Estado controlado con nombre: `X` / `defaultX` / `onXChange(x)` (`KapiTree`: `selected`/`onSelectedChange`,
|
|
232
|
+
`expanded`/`onExpandedChange`; `KapiTable`: `selectedIds`/`onSelectedIdsChange`,
|
|
233
|
+
`expandedIds`/`onExpandedIdsChange`; `KapiAccordion`: `openIds`/`defaultOpenIds`/`onOpenIdsChange`).
|
|
234
|
+
- El valor principal de un control de elección es `value` / `onChange(value)` (`KapiTabs`, `KapiSegmented`,
|
|
235
|
+
`KapiFilterChips`, `KapiSelect`…).
|
|
236
|
+
- Las listas de navegación reciben `activeId` + `onSelect(id)` (`KapiRail`, `KapiPanelGroup`,
|
|
237
|
+
`KapiContextPanel`): la app es dueña del activo, normalmente desde el router.
|
|
238
|
+
- Los ids son genéricos y se infieren de los ítems: con un setter tipado, un id ajeno no compila.
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
type View = "list" | "grid";
|
|
242
|
+
const tabs: KapiTabItem<View>[] = [
|
|
243
|
+
{ id: "list", label: "Lista" },
|
|
244
|
+
{ id: "grid", label: "Grilla" }
|
|
245
|
+
];
|
|
246
|
+
const [view, setView] = useState<View>("list");
|
|
247
|
+
|
|
248
|
+
<KapiTabs tabs={tabs} value={view} onChange={setView} aria-label="Vista" />;
|
|
249
|
+
|
|
250
|
+
// `data` vuelve tipado en los callbacks del árbol.
|
|
251
|
+
const nodes: KapiTreeNode<{ code: number }>[] = [{ id: "fcye", label: "FCyE", data: { code: 12 } }];
|
|
252
|
+
<KapiTree nodes={nodes} onSelectedChange={(ids, node) => console.log(ids, node.data?.code)} />;
|
|
253
|
+
|
|
254
|
+
// Sin `render`, `key` tiene que ser una propiedad de la fila.
|
|
255
|
+
type Persona = { id: number; nombre: string };
|
|
256
|
+
const columns: KapiTableColumn<Persona>[] = [
|
|
257
|
+
{ key: "nombre", label: "Nombre" },
|
|
258
|
+
{ key: "acciones", label: "", render: (p) => <KapiButton>Ver {p.nombre}</KapiButton> }
|
|
259
|
+
];
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Diálogos imperativos
|
|
263
|
+
|
|
264
|
+
Para el flujo "preguntar y seguir" (lo que en otros sistemas resuelve SweetAlert) no hace falta estado
|
|
265
|
+
`open` en cada pantalla: monta `KapiDialogHost` una vez (junto a `KapiToastContainer`) y llama a las
|
|
266
|
+
funciones desde cualquier handler.
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { KapiDialogHost, kapiAlert, kapiCloseDialogs, kapiConfirm, kapiLoading } from "@kapiuara/ui";
|
|
270
|
+
|
|
271
|
+
// Una vez, en la raíz de la app (dentro de `.kapi`):
|
|
272
|
+
<KapiDialogHost />;
|
|
273
|
+
|
|
274
|
+
const eliminar = async (persona: Persona) => {
|
|
275
|
+
if (!(await kapiConfirm({ title: `¿Eliminar a ${persona.nombre}?`, message: "No se puede deshacer." })))
|
|
276
|
+
return;
|
|
277
|
+
await kapiLoading.run(api.eliminar(persona.id), "Eliminando…");
|
|
278
|
+
await kapiAlert({ title: "Eliminado", variant: "success" });
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- `kapiConfirm(options)` → `Promise<boolean>`: `true` al confirmar, `false` ante Cancelar, X, Escape o fondo.
|
|
283
|
+
Mismas opciones que `KapiConfirm` (`title`, `message`, `variant` `danger` · `warning` · `success` · `info` ·
|
|
284
|
+
`question`, `confirmLabel`, `cancelLabel`).
|
|
285
|
+
- `onConfirm` (opcional, en `kapiConfirm`): acción que corre al confirmar, como `preConfirm` de SweetAlert. Mientras
|
|
286
|
+
corre, el botón de confirmar muestra un spinner, los dos botones quedan deshabilitados y Escape/fondo/X no cierran.
|
|
287
|
+
Si resuelve, el diálogo se cierra y la promesa da `true`; si rechaza, el diálogo sigue abierto con el error adentro
|
|
288
|
+
(`role="alert"`: `error.message`, o `labels.error` de `confirm`, "No se pudo completar la acción") y se puede
|
|
289
|
+
reintentar o cancelar (`false`). Un rechazo que no es `Error` ni string muestra el texto por defecto. La promesa
|
|
290
|
+
tiene que terminar: mientras corre no hay forma de cerrar el diálogo; `kapiCloseDialogs()` lo cierra igual
|
|
291
|
+
(resuelve `false`) pero la acción ya iniciada sigue.
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
const ok = await kapiConfirm({
|
|
295
|
+
title: "¿Enviar la solicitud?",
|
|
296
|
+
variant: "question",
|
|
297
|
+
onConfirm: () => api.enviar(solicitud) // si falla, el mensaje queda en el diálogo
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- `kapiAlert(options)` → `Promise<void>`: aviso con un solo botón (`title`, `message`, `variant` `success` ·
|
|
302
|
+
`error` · `info` · `warning`, `acceptLabel`, default `labels.accept`: "Aceptar").
|
|
303
|
+
- `kapiLoading(message?)` → `{ update, close }`: carga bloqueante (sin Escape ni fondo). Varias abiertas a la vez
|
|
304
|
+
se muestran como una sola con el mensaje de la más reciente. `kapiLoading.run(promesa | fn, message?)` la cierra
|
|
305
|
+
en el `finally`.
|
|
306
|
+
- Abrir una confirmación o un aviso **cierra las cargas abiertas con `kapiLoading()`** (como SweetAlert, que muestra
|
|
307
|
+
uno por vez): `kapiLoading(); api().then(() => kapiAlert({ variant: "success", … }))` no deja ninguna carga
|
|
308
|
+
detrás, y los handles de esas cargas quedan sin efecto. La de `kapiLoading.run` no se cierra (la cierra su
|
|
309
|
+
`finally`): se oculta mientras haya un diálogo y vuelve al responderlo, así
|
|
310
|
+
`kapiLoading.run(async () => { if (await kapiConfirm(…)) await guardar(); })` sigue cubriendo `guardar()`.
|
|
311
|
+
Toda carga abierta con un diálogo visible tampoco se dibuja (quedaría encima y no se podría responder): aparece
|
|
312
|
+
al vaciarse la cola.
|
|
313
|
+
- `kapiCloseDialogs()`: cierra todo. Resuelve los diálogos en cola como cerrados (`kapiConfirm` → `false`,
|
|
314
|
+
`kapiAlert` → `undefined`) y cierra todas las cargas (p. ej. al cambiar de ruta o cerrar sesión).
|
|
315
|
+
- Las confirmaciones y avisos se encolan y se muestran de a uno; los pedidos hechos antes de montar el host esperan.
|
|
316
|
+
Si se encola sin ningún host montado, la consola avisa una vez (`console.warn`) para que la promesa no quede
|
|
317
|
+
colgada en silencio.
|
|
318
|
+
- Textos: clave `dialog` (`accept`, `loading`) y, para la confirmación, la clave `confirm` de `KapiConfirm`.
|
|
319
|
+
|
|
320
|
+
## Select async y creatable
|
|
321
|
+
|
|
322
|
+
`KapiSelect` cubre los modos de react-select sin dependencias: grupos (`{ label, options }`), búsqueda remota y
|
|
323
|
+
creación de opciones.
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
<KapiSelect
|
|
327
|
+
aria-label="Ciudad"
|
|
328
|
+
value={ciudad}
|
|
329
|
+
onChange={setCiudad}
|
|
330
|
+
loadOptions={(query, { signal }) => api.ciudades(query, { signal })}
|
|
331
|
+
/>
|
|
332
|
+
|
|
333
|
+
<KapiSelect multiple value={tags} onChange={setTags} options={opciones} onCreate={(text) => crearTag(text)} />
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
- `loadOptions(query, { signal })`: se llama tras `loadDelay` ms sin tipear (default 300); el pedido anterior se
|
|
337
|
+
aborta y una respuesta vieja nunca pisa a una nueva. `options` pasa a ser la lista inicial (query vacía). Si falla
|
|
338
|
+
(rechazo o `throw`), el menú muestra `labels.loadError`; al cerrar y reabrir se vuelve a cargar.
|
|
339
|
+
- Los resultados quedan asociados a su búsqueda: pasar otra identidad de `loadOptions` (p. ej. una función inline)
|
|
340
|
+
no los invalida ni recarga. Para forzar una recarga, remonta el select con otra `key`.
|
|
341
|
+
- Las opciones elegidas conservan su label aunque ya no estén en la lista cargada.
|
|
342
|
+
- `onCreate(text)`: ofrece `Crear "texto"` cuando ninguna opción coincide exactamente. Si devuelve (o resuelve) una
|
|
343
|
+
opción, queda elegida; si no devuelve nada, el padre actualiza `options`/`value`. Mientras una promesa de
|
|
344
|
+
`onCreate` corre, la fila se oculta (no hay duplicados) y el control queda `aria-busy`; lo tipeado mientras tanto se
|
|
345
|
+
conserva. Si la promesa se rechaza, el menú muestra `labels.createError` y se puede reintentar.
|
|
346
|
+
|
|
347
|
+
## Archivos: visor y descarga
|
|
348
|
+
|
|
349
|
+
`KapiFileViewer` muestra un archivo a partir de una URL o de un Blob (la respuesta de una API), sin dependencias:
|
|
350
|
+
|
|
351
|
+
```tsx
|
|
352
|
+
<KapiFileViewer src={blob} fileName="constancia.pdf" />
|
|
353
|
+
<KapiFileViewer src="/archivos/foto.jpg" title="Foto del DNI" height="240px" />
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
- PDF → `<object>`; si el navegador no tiene visor (p. ej. en móviles) se ve un aviso con la descarga. Imágenes →
|
|
357
|
+
`<img>` (alt = `title` o el nombre); texto plano y JSON → `<iframe sandbox>`; el resto (CSV, Excel, ZIP…) → una
|
|
358
|
+
tarjeta con el nombre y el botón de descarga.
|
|
359
|
+
- El tipo sale de `type`, del tipo del Blob o de la extensión del nombre/URL. Un Blob recibe una URL temporal que se
|
|
360
|
+
revoca al cambiarlo o al desmontar. `downloadable={false}` quita la descarga; con una URL hay además un enlace
|
|
361
|
+
para abrirla en otra pestaña. Textos: clave `fileViewer`.
|
|
362
|
+
- Solo acepta URLs relativas, `http(s):`, `blob:` o `data:` de imagen/PDF; otra (`javascript:`…) muestra el aviso
|
|
363
|
+
sin enlaces. El PDF por URL va en un `<object>` sin sandbox: si la URL es del mismo origen y el servidor devuelve
|
|
364
|
+
HTML (un archivo subido con un Content-Type errado), ese HTML corre en el origen de la app. Para archivos subidos
|
|
365
|
+
por usuarios, pasa un Blob con su `type`.
|
|
366
|
+
|
|
367
|
+
Para descargar sin mostrar, los helpers de `hooks` reemplazan a `file-saver` y al `<a download>` manual:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
const res = await fetch(url);
|
|
371
|
+
const name = fileNameFromContentDisposition(res.headers.get("Content-Disposition")) ?? "reporte.xlsx";
|
|
372
|
+
downloadBlob(await res.blob(), name);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
`fileNameFromContentDisposition` sigue la RFC 6266 (`filename*=UTF-8''…` gana sobre `filename`) y descarta rutas;
|
|
376
|
+
`fileKind(mimeOrName)` devuelve `"pdf" | "image" | "text" | "other"`; `useObjectUrl(blob)` devuelve una URL temporal
|
|
377
|
+
para usar en tu propio markup.
|
|
378
|
+
|
|
379
|
+
### Adjuntos en lista
|
|
380
|
+
|
|
381
|
+
`KapiFileList` (`<ul>`) y `KapiFileItem` (`<li>`) muestran una fila por adjunto: ícono según el tipo (MIME o
|
|
382
|
+
extensión), nombre recortado, metadatos («detalles · tamaño · fecha»), un `status` (badge) y `actions`. Con
|
|
383
|
+
`onOpen`, el nombre es un botón (p. ej. para abrir la previa en un `KapiFileViewer`).
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
<KapiFileList aria-label="Adjuntos">
|
|
387
|
+
<KapiFileItem
|
|
388
|
+
name="nota.pdf"
|
|
389
|
+
size={248320}
|
|
390
|
+
date={fecha}
|
|
391
|
+
details={["Nota", "Ana Pérez"]}
|
|
392
|
+
status={
|
|
393
|
+
<KapiBadge variant="success" size="sm">
|
|
394
|
+
Verificado
|
|
395
|
+
</KapiBadge>
|
|
396
|
+
}
|
|
397
|
+
onOpen={verPrevia}
|
|
398
|
+
actions={<KapiIconButton aria-label="Descargar nota.pdf" icon={<Download />} />}
|
|
399
|
+
/>
|
|
400
|
+
</KapiFileList>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Formateo sin dependencias (en `hooks`): `formatFileSize(1536)` → "1,5 KB" (base 1024, un decimal como máximo);
|
|
404
|
+
`formatRelativeTime(fecha, ahora?, locale?)` → "hace 5 minutos" (trunca, nunca "hace 60 minutos");
|
|
405
|
+
`formatDate(fecha)` → "12 mar 2026"; `isoDateTime(fecha)` da el valor de `<time dateTime>`. Una fecha inválida
|
|
406
|
+
devuelve "" (o `null` en `isoDateTime`).
|
|
407
|
+
|
|
408
|
+
## Comentarios
|
|
409
|
+
|
|
410
|
+
`KapiComment` es un comentario de un hilo: avatar, autor, fecha relativa (con la fecha completa en el `title`),
|
|
411
|
+
`body`, `meta` («editado»), `actions` y respuestas como `children` (se indentan un solo nivel). `now` fija la hora
|
|
412
|
+
de referencia. `KapiCommentComposer` es la caja para escribir: `onSubmit(texto)` recibe el texto recortado (si devuelve una promesa, el compositor queda ocupado y se vacía solo si resuelve: un rechazo conserva el borrador),
|
|
413
|
+
Ctrl/⌘ + Enter envía, Enter solo agrega un salto de línea y el botón queda deshabilitado con el texto vacío. Cumple
|
|
414
|
+
el contrato de campo (`name`, `onBlur`, `ref` al textarea, `invalid`/`required`); textos en la clave
|
|
415
|
+
`commentComposer`.
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
<KapiComment author="Ana Pérez" date={fecha} body="Falta la firma." meta="editado">
|
|
419
|
+
<KapiComment author="Bruno Díaz" date={otra} body="Ya la pedí." />
|
|
420
|
+
</KapiComment>
|
|
421
|
+
<KapiCommentComposer onSubmit={(texto) => agregar(texto)} />
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Toasts con acción
|
|
425
|
+
|
|
426
|
+
`kapiToast.success|error|info|warning(mensaje, opciones?)` acepta `{ duration, action: { label, onClick } }`
|
|
427
|
+
(sigue aceptando un número como duración). La acción es un botón dentro del toast: ejecuta y lo cierra, sin mover
|
|
428
|
+
el foco. Con acción y sin `duration`, el toast dura 6 s (`TOAST_ACTION_DURATION`).
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
kapiToast.success("Trámite creado", { action: { label: "Ver trámite", onClick: () => abrir(id) } });
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Campana de notificaciones
|
|
435
|
+
|
|
436
|
+
`KapiNotificationBell` (layout) es la campana de la barra superior: badge con las no leídas y un panel con la
|
|
437
|
+
lista. Los datos y el "leído" son de la app; la campana solo avisa.
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
<KapiNotificationBell
|
|
441
|
+
items={notificaciones} // { id, title, description?, date?, read?, icon?, href? }
|
|
442
|
+
onItemSelect={(item) => marcarLeida(item.id)}
|
|
443
|
+
onMarkAllRead={marcarTodas}
|
|
444
|
+
onViewAll={() => navigate("/notificaciones")}
|
|
445
|
+
loading={cargando}
|
|
446
|
+
/>
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
- Todo ítem sin `read: true` cuenta como no leído: va resaltado y suma al badge (`max`, default 9, muestra "9+").
|
|
450
|
+
El nombre accesible de la campana lleva el conteo real: "Notificaciones, 3 sin leer".
|
|
451
|
+
- `date` (`Date` o ISO) se muestra relativa con `Intl.RelativeTimeFormat` ("hace 5 minutos", "ayer") en el
|
|
452
|
+
`locale` del provider (`es` sin provider).
|
|
453
|
+
- Un ítem con `href` es un enlace; `onItemSelect(item, event)` recibe el evento para hacer `preventDefault` y
|
|
454
|
+
navegar con el router. Elegir un ítem o "Ver todas" cierra el panel.
|
|
455
|
+
- Teclado: al abrir enfoca la primera notificación; flechas, Home y End recorren la lista; Escape cierra y devuelve
|
|
456
|
+
el foco a la campana. Apertura `open`/`defaultOpen`/`onOpenChange`. Textos: clave `notificationBell`.
|
|
457
|
+
|
|
458
|
+
## Pasos de un circuito
|
|
459
|
+
|
|
460
|
+
`KapiSteps` muestra el avance de un circuito de solo lectura (por ejemplo, los estados de un trámite). Para un
|
|
461
|
+
formulario por pasos usa `KapiWizard`. Con `current`, los pasos anteriores quedan completos y los siguientes
|
|
462
|
+
pendientes; un `status` explícito (`complete` · `current` · `upcoming` · `error`) siempre gana. El paso actual lleva
|
|
463
|
+
`aria-current="step"` y cada estado se anuncia con un texto oculto (clave de textos `steps`). En horizontal se
|
|
464
|
+
acomoda en varias líneas si no entra.
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
<KapiSteps
|
|
468
|
+
aria-label="Circuito del trámite"
|
|
469
|
+
current="revision"
|
|
470
|
+
steps={[
|
|
471
|
+
{ id: "iniciado", label: "Iniciado", description: "Mesa de Entradas" },
|
|
472
|
+
{ id: "revision", label: "En revisión" },
|
|
473
|
+
{ id: "firma", label: "A la firma" }
|
|
474
|
+
]}
|
|
475
|
+
/>
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Menús, pestañas y modales
|
|
479
|
+
|
|
480
|
+
- **Ítem de menú deshabilitado:** `disabled` en un `KapiMenuItem` lo atenúa, lo anuncia con `aria-disabled`, no
|
|
481
|
+
ejecuta su acción y las flechas lo saltean. Vale para `KapiMenuButton`, `KapiContextMenu` y el menú de fila de
|
|
482
|
+
`KapiTable`.
|
|
483
|
+
- **Contador en pestañas:** `count` en un `KapiTabItem` muestra una píldora tras el label (también el `0`); el
|
|
484
|
+
nombre accesible queda «Adjuntos 3».
|
|
485
|
+
- **Nombre accesible de modales:** `KapiModal` y `KapiSplitModal` aceptan `aria-label` y `aria-labelledby`
|
|
486
|
+
nativos, que van al `<dialog>`. Precedencia: `aria-labelledby` > `aria-label` > título visible.
|
|
487
|
+
|
|
488
|
+
```tsx
|
|
489
|
+
<KapiMenuButton
|
|
490
|
+
label="Acciones"
|
|
491
|
+
items={[
|
|
492
|
+
{ label: "Derivar", onClick: derivar },
|
|
493
|
+
{ label: "Anular", disabled: !puedeAnular, onClick: anular }
|
|
494
|
+
]}
|
|
495
|
+
/>
|
|
496
|
+
<KapiTabs
|
|
497
|
+
aria-label="Secciones"
|
|
498
|
+
value={tab}
|
|
499
|
+
onChange={setTab}
|
|
500
|
+
tabs={[{ id: "datos", label: "Datos" }, { id: "adjuntos", label: "Adjuntos", count: 3 }]}
|
|
501
|
+
/>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Búsqueda facetada y tabla paginada en el cliente
|
|
505
|
+
|
|
506
|
+
`KapiFacetedSearch` se maneja con `chips` y el `apply` de cada faceta. Cada opción puede mostrar cuántos
|
|
507
|
+
resultados tiene con `count` («Pendiente, 4»). Una faceta `kind: "dateRange"` pide desde/hasta en un solo
|
|
508
|
+
calendario y entrega `yyyy-mm-dd/yyyy-mm-dd` (se lee con `parseDateRangeValue`). Con `onClearAll`, la barra
|
|
509
|
+
muestra «Limpiar todo» mientras haya filtros activos.
|
|
510
|
+
|
|
511
|
+
```tsx
|
|
512
|
+
const facets: KapiFacet[] = [
|
|
513
|
+
{
|
|
514
|
+
key: "estado",
|
|
515
|
+
label: "Estado",
|
|
516
|
+
kind: "list",
|
|
517
|
+
options: [{ value: "pend", label: "Pendiente", count: 4 }],
|
|
518
|
+
apply: (v) => set({ estado: v })
|
|
519
|
+
},
|
|
520
|
+
{ key: "creado", label: "Creado", kind: "dateRange", apply: (v) => set({ creado: parseDateRangeValue(v) }) }
|
|
521
|
+
];
|
|
522
|
+
<KapiFacetedSearch facets={facets} chips={chips} onText={(t) => set({ texto: t })} onClearAll={limpiar} />;
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
La `pagination` de `KapiTable` solo dibuja el pie: los datos los recorta quien la usa (sirve igual para paginar
|
|
526
|
+
en el servidor). Para paginar en el cliente, `sortRowsByColumn` ordena con la misma lógica que la tabla y
|
|
527
|
+
`paginateRows` recorta la página. `exportCsv.rows` (arreglo o función) exporta el conjunto completo, no solo la
|
|
528
|
+
página visible.
|
|
529
|
+
|
|
530
|
+
```tsx
|
|
531
|
+
const ordenadas = sortRowsByColumn(filas, columnas, sort);
|
|
532
|
+
<KapiTable
|
|
533
|
+
columns={columnas}
|
|
534
|
+
rows={paginateRows(ordenadas, page, pageSize)}
|
|
535
|
+
manualSort
|
|
536
|
+
sort={sort}
|
|
537
|
+
onSortChange={setSort}
|
|
538
|
+
exportCsv={{ fileName: "tramites.csv", rows: () => ordenadas }}
|
|
539
|
+
pagination={{ page, pageSize, total: filas.length, onPageChange: setPage }}
|
|
540
|
+
/>;
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Tarjetas radio y atajos de teclado
|
|
544
|
+
|
|
545
|
+
`KapiCheckCard type="radio"` es un radio nativo con forma de tarjeta: las que comparten `name` forman un grupo y
|
|
546
|
+
las flechas mueven la elección. `KapiInput shortcut="/"` muestra la tecla como `<kbd>` mientras el campo está
|
|
547
|
+
vacío y la anuncia con `aria-keyshortcuts`; la tecla la registra la app (por ejemplo con `useHotkey`).
|
|
548
|
+
|
|
549
|
+
```tsx
|
|
550
|
+
<div role="radiogroup" aria-label="Tipo de trámite">
|
|
551
|
+
{tipos.map((t) => (
|
|
552
|
+
<KapiCheckCard
|
|
553
|
+
key={t.id}
|
|
554
|
+
type="radio"
|
|
555
|
+
name="tipo"
|
|
556
|
+
value={t.id}
|
|
557
|
+
label={t.nombre}
|
|
558
|
+
checked={tipo === t.id}
|
|
559
|
+
onChange={(e) => setTipo(e.target.value)}
|
|
560
|
+
/>
|
|
561
|
+
))}
|
|
562
|
+
</div>;
|
|
563
|
+
|
|
564
|
+
const ref = useRef<HTMLInputElement>(null);
|
|
565
|
+
useHotkey("/", () => ref.current?.focus());
|
|
566
|
+
<KapiInput ref={ref} type="search" aria-label="Buscar" shortcut="/" clearable />;
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
`KapiNotificationBell now={fecha}` fija la hora de referencia de las fechas relativas (tests y capturas).
|
|
570
|
+
`KapiPanelItem icon={<Inbox size={15} />}` suma un ícono decorativo antes del texto.
|
|
571
|
+
|
|
572
|
+
## Tema y color de marca
|
|
573
|
+
|
|
574
|
+
Todos los colores salen de tokens `--kapi-*` en `.kapi`; el tema oscuro es `data-theme="dark"` en esa
|
|
575
|
+
raíz. Para usar el color de la marca alcanza con un solo token, `--kapi-primary`:
|
|
576
|
+
|
|
577
|
+
```tsx
|
|
578
|
+
const { setAccent } = useKapiThemeState(); // KapiShell lo aplica en su raíz (y lo guarda en kapi:accent)
|
|
579
|
+
setAccent("#0f766e");
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
```css
|
|
583
|
+
.kapi {
|
|
584
|
+
--kapi-primary: #0f766e;
|
|
585
|
+
} /* o directo en CSS, sobre la misma raíz */
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
- `--kapi-primary-ink` (texto), `--kapi-primary-soft` / `--kapi-primary-bg` (fondos tonales),
|
|
589
|
+
`--kapi-primary-border`, el anillo de foco `--kapi-ring` y las sombras teñidas (`--kapi-shadow-primary`,
|
|
590
|
+
`-fab`, `-shape`…) se derivan de `--kapi-primary` con `color-mix`, en claro y en oscuro: no hace falta
|
|
591
|
+
pisarlos. Con el violeta por defecto se ven igual que antes.
|
|
592
|
+
- `--kapi-primary-fg` (texto sobre el primario) no se deriva: si el acento es claro, pisalo con un color
|
|
593
|
+
oscuro para mantener el contraste.
|
|
594
|
+
- Los derivados se resuelven en `.kapi`: redefinir `--kapi-primary` en un elemento interno no los
|
|
595
|
+
recolorea. El acento va en la raíz.
|
|
596
|
+
- Un mismo acento sirve para los dos temas; para afinar el oscuro, redefiní `--kapi-primary` en
|
|
597
|
+
`.kapi[data-theme="dark"]`.
|
|
598
|
+
|
|
599
|
+
## Preferencias guardadas (namespace)
|
|
600
|
+
|
|
601
|
+
Las preferencias del design system van a `localStorage` con namespace: `kapi:theme`, `kapi:accent`, `kapi:font`,
|
|
602
|
+
`kapi:motion` y `kapi:tour-seen`. Si dos apps del design system comparten origen, cada una pasa el suyo para no pisarse:
|
|
603
|
+
|
|
604
|
+
```tsx
|
|
605
|
+
<KapiShell storageNamespace="tramites">…</KapiShell> // tramites:theme, tramites:font…
|
|
606
|
+
useKapiThemeState({ namespace: "tramites" }); // si armás tu propia raíz
|
|
607
|
+
<KapiTour markSeen="tramites:tour-seen" … />
|
|
608
|
+
usePersistentState("table-cols", {}, { namespace: "tramites" }); // tramites:table-cols
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
- Las claves viejas sin prefijo (`v2_theme`, `v2_accent`, `v2_font`, `v2_motion`, `v2_tour_seen`) se migran solas en la
|
|
612
|
+
primera lectura (se copian a la clave nueva y se borran): nadie pierde su tema.
|
|
613
|
+
- `usePersistentState` sin `namespace` sigue usando la clave tal cual (compatible con lo ya guardado).
|
|
614
|
+
|
|
615
|
+
## Sin conexión
|
|
616
|
+
|
|
617
|
+
`useOnlineStatus()` devuelve `{ online, since }` (eventos `online`/`offline` de la ventana; en SSR, conectado).
|
|
618
|
+
`KapiOfflineBanner` lo usa para mostrar un aviso flotante mientras no hay conexión y uno breve al volver;
|
|
619
|
+
con conexión no renderiza nada. Textos por `labels`, `forceOffline` para simular o para una verificación propia.
|
|
620
|
+
|
|
621
|
+
```tsx
|
|
622
|
+
<KapiShell overlays={<KapiOfflineBanner />}>…</KapiShell>
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
## Formularios
|
|
626
|
+
|
|
627
|
+
El DS no trae motor de validación: la validación es del host (react-hook-form o estado propio). Lo que
|
|
628
|
+
garantiza es un **contrato de campo** común, para cablear cualquier control sin adaptadores:
|
|
629
|
+
|
|
630
|
+
- `name`: llega al envío nativo del `<form>`. Los controles nativos lo llevan en su `<input>`; los de
|
|
631
|
+
valor que no lo son (`KapiSegmented`, `KapiRating`, `KapiSelect`) renderizan un `<input type="hidden">`.
|
|
632
|
+
- `onBlur` y `ref` al elemento enfocable (para que la librería marque `touched` y enfoque el primer error).
|
|
633
|
+
- `invalid`/`required` por prop o desde el `KapiField` que envuelve al control. Precedencia: `aria-*`
|
|
634
|
+
propios > prop > `KapiField`. Los inputs de texto (`KapiInput`, `KapiTextarea`, `KapiAutocomplete`)
|
|
635
|
+
usan `status="err"` en vez de `invalid`; `KapiSelect`, `KapiDatePicker` y `KapiDateRangePicker` toman
|
|
636
|
+
el estado solo del `KapiField`.
|
|
637
|
+
- `onChange`: los wrappers nativos (`KapiInput`, `KapiTextarea`, `KapiCheckbox`, `KapiSwitch`, `KapiRadio`)
|
|
638
|
+
pasan el evento; los de valor (`KapiSelect`, `KapiDatePicker`, `KapiSlider`, `KapiSegmented`,
|
|
639
|
+
`KapiRating`, `KapiFileUpload`) pasan el valor ya tipado.
|
|
640
|
+
|
|
641
|
+
### `KapiField`
|
|
642
|
+
|
|
643
|
+
Envuelve un control y le conecta label, ayuda y estado sin cablear ids: el control toma del contexto su id
|
|
644
|
+
(o `aria-labelledby` si no es etiquetable), `aria-describedby` hacia la ayuda, `aria-invalid` con
|
|
645
|
+
`status="err"` y `aria-required` con `required`.
|
|
646
|
+
|
|
647
|
+
```tsx
|
|
648
|
+
<KapiField label="Turno" help="Elige un turno" status="err" required>
|
|
649
|
+
<KapiSegmented name="turno" items={turnos} value={turno} onChange={setTurno} />
|
|
650
|
+
</KapiField>
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
Los radios sueltos dentro de un `KapiField` toman ayuda e `invalid`, pero no el id (un grupo comparte el
|
|
654
|
+
campo): cada uno se nombra con su `label`.
|
|
655
|
+
|
|
656
|
+
### Con react-hook-form
|
|
657
|
+
|
|
658
|
+
`react-hook-form` no es dependencia del DS; estos son los dos patrones del lado del host.
|
|
659
|
+
|
|
660
|
+
**Wrappers nativos → `register`.** `register` devuelve `name`, `onChange(event)`, `onBlur` y `ref`, que es
|
|
661
|
+
justo lo que aceptan:
|
|
662
|
+
|
|
663
|
+
```tsx
|
|
664
|
+
const { register, formState: { errors } } = useForm<Alta>();
|
|
665
|
+
|
|
666
|
+
<KapiField label="Email" help={errors.email?.message} status={errors.email ? "err" : undefined} required>
|
|
667
|
+
<KapiInput type="email" {...register("email", { required: "Ingresa tu email" })} />
|
|
668
|
+
</KapiField>
|
|
669
|
+
|
|
670
|
+
<KapiField label="Acepto los términos" help={errors.terms?.message} status={errors.terms ? "err" : undefined}>
|
|
671
|
+
<KapiCheckbox {...register("terms", { required: "Tienes que aceptarlos" })} />
|
|
672
|
+
</KapiField>
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**Controles de valor → `Controller`.** Se mapea `field` (`value`, `onChange`, `onBlur`, `ref`, `name`) y
|
|
676
|
+
`fieldState.error` al `KapiField`:
|
|
677
|
+
|
|
678
|
+
```tsx
|
|
679
|
+
<Controller
|
|
680
|
+
control={control}
|
|
681
|
+
name="sede"
|
|
682
|
+
rules={{ required: "Elige una sede" }}
|
|
683
|
+
render={({ field, fieldState }) => (
|
|
684
|
+
<KapiField label="Sede" help={fieldState.error?.message} status={fieldState.error ? "err" : undefined} required>
|
|
685
|
+
<KapiSelect
|
|
686
|
+
name={field.name}
|
|
687
|
+
ref={field.ref}
|
|
688
|
+
value={field.value ?? null}
|
|
689
|
+
onChange={field.onChange}
|
|
690
|
+
options={sedes}
|
|
691
|
+
/>
|
|
692
|
+
</KapiField>
|
|
693
|
+
)}
|
|
694
|
+
/>
|
|
695
|
+
|
|
696
|
+
<Controller
|
|
697
|
+
control={control}
|
|
698
|
+
name="volumen"
|
|
699
|
+
render={({ field, fieldState }) => (
|
|
700
|
+
<KapiField label="Volumen" help={fieldState.error?.message} status={fieldState.error ? "err" : undefined}>
|
|
701
|
+
<KapiSlider {...field} />
|
|
702
|
+
</KapiField>
|
|
703
|
+
)}
|
|
704
|
+
/>
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
`KapiDatePicker` sigue el mismo molde que `KapiSelect` (`value: Date | null`). `onBlur` de `KapiSelect`,
|
|
708
|
+
`KapiDatePicker` y `KapiDateRangePicker` se dispara solo cuando el foco sale del control: pasar a su menú
|
|
709
|
+
o calendario no cuenta. `KapiFileUpload` sincroniza su `<input type="file">` con la lista vía
|
|
710
|
+
`DataTransfer`; donde no exista, arma el `FormData` con lo que emite `onChange`.
|
|
711
|
+
|
|
712
|
+
**Qué envía cada control con `name`** (inputs ocultos salvo los nativos):
|
|
713
|
+
|
|
714
|
+
| Control | Valor en el `FormData` |
|
|
715
|
+
| ------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
716
|
+
| `KapiSelect` | el `value` elegido (vacío sin valor); con `multiple`, un input por valor |
|
|
717
|
+
| `KapiDatePicker` | `yyyy-mm-dd`, `hh:mm` o `yyyy-mm-ddThh:mm` según `mode` (como los inputs nativos) |
|
|
718
|
+
| `KapiDateRangePicker` | `name.from` y `name.to`, cada uno `yyyy-mm-dd` (vacíos sin rango) |
|
|
719
|
+
| `KapiSlider` / `KapiRating` | el número |
|
|
720
|
+
| `KapiSegmented` | el `id` elegido, siempre (no tiene `disabled`) |
|
|
721
|
+
| `KapiCheckbox` / `KapiSwitch` / `KapiRadio` | `value` (o `on`) solo si está marcado |
|
|
722
|
+
|
|
723
|
+
## Textos y locale
|
|
724
|
+
|
|
725
|
+
Cada componente con copy propio trae defaults en español y acepta `labels` (parcial, por clave).
|
|
726
|
+
`KapiLocaleProvider` pone el copy de toda una app en un solo lugar. Precedencia por clave:
|
|
727
|
+
defaults del componente < provider < prop `labels`. Una clave en `undefined` no borra el valor anterior.
|
|
728
|
+
Sin provider no cambia nada (costo cero).
|
|
729
|
+
|
|
730
|
+
```tsx
|
|
731
|
+
// Fuera del render, o memoizado: un objeto nuevo por render re-renderiza a todos.
|
|
732
|
+
const EN: KapiLabelOverrides = {
|
|
733
|
+
pagination: { of: "of", nextPage: "Next page" },
|
|
734
|
+
calendar: { pickStart: "Pick a start date" } // lo comparten KapiDatePicker y KapiDateRangePicker
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
<KapiLocaleProvider locale="en" labels={EN}>
|
|
738
|
+
<App />
|
|
739
|
+
</KapiLocaleProvider>;
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
- `link` es una clave compartida: el aviso "(se abre en una pestaña nueva)" de los enlaces con `newTab`
|
|
743
|
+
(`KapiPanelGroup`, `KapiContextPanel`, `KapiRail`, `KapiAccessCard`).
|
|
744
|
+
- Las props de texto sueltas que ya existían (`cancelLabel` de `KapiWizard`, `label` de `KapiFab`, `aria-label`,
|
|
745
|
+
`invalidMessage` de `KapiCuitInput`…) siguen ganando sobre `labels` y el provider. En las de contenido
|
|
746
|
+
(`ReactNode`, como `emptyMessage` de `KapiTable` o `footerHint` de `KapiCommandPalette`) solo `undefined` cae
|
|
747
|
+
al provider: `null` no muestra nada.
|
|
748
|
+
- `canvasForm` y `unsavedGuard` se configuran solo desde el provider (`KapiCanvasForm` ya usa `labels` para su
|
|
749
|
+
`KapiOffcanvas`; el hook tiene sus `options`).
|
|
750
|
+
- Las claves están en `KapiLabelRegistry` (una `interface`: una app puede sumar las suyas por module augmentation).
|
|
751
|
+
- `useLabels(key, defaults, labels)` resuelve los textos en un componente; `useKapiLocale()` devuelve el locale
|
|
752
|
+
(`undefined` = el del runtime). `KapiTable` lo usa para ordenar textos.
|
|
753
|
+
- `KapiTable` le pasa sus textos ya resueltos a su barra de filtro: adentro de una tabla manda la clave `table`.
|
|
754
|
+
- Fuera de alcance por ahora: la pluralización de `formatDuration` (countdown) sigue en español.
|
|
755
|
+
|
|
756
|
+
## Fuente
|
|
757
|
+
|
|
758
|
+
`src/styles/fonts.css` importa `@fontsource-variable/nunito` (familia `'Nunito Variable'`, token `--kapi-font`).
|
|
759
|
+
El CSS de la librería NO la incluye (en modo lib Vite inlinea los woff2 en base64 → 240 kB): la app consumidora
|
|
760
|
+
importa `@fontsource-variable/nunito` (o `fonts.css` desde el código fuente).
|
|
761
|
+
|
|
762
|
+
- Es una `peerDependency` **opcional** (y `devDependency` para el demo): `@kapiuara/ui` no la instala. Para usar
|
|
763
|
+
Nunito, la app la agrega (`pnpm add @fontsource-variable/nunito`) y la importa una vez.
|
|
764
|
+
- Sin ella, `--kapi-font` cae a `system-ui`. Otra fuente: cargarla en la app y pisar `--kapi-font` en la raíz
|
|
765
|
+
(`setFont` de `useKapiThemeState` elige entre las pilas predefinidas de `FONTS`).
|