tekivex-ui 2.5.15 → 2.6.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/dist/charts.cjs +1 -1
- package/dist/charts.js +22 -553
- package/dist/chunk-B3ph66Zb.js +1 -0
- package/dist/chunk-Bc16rJT-.js +1 -0
- package/dist/chunk-Dike2DLU.js +3 -0
- package/dist/chunk-DjxOP8W4.js +1 -0
- package/dist/chunk-DkoFTL1O.js +1 -0
- package/dist/chunk-DmRB1Blb.js +1 -0
- package/dist/chunk-DnCXUUx5.js +1 -0
- package/dist/chunk-LWkKqC8d.js +1 -0
- package/dist/chunk-PJ50l0JC.js +1 -0
- package/dist/headless.cjs +1 -1
- package/dist/headless.js +1 -442
- package/dist/i18n.cjs +1 -1
- package/dist/i18n.js +1 -733
- package/dist/index.cjs +1 -152
- package/dist/index.d.ts +4 -2
- package/dist/index.js +429 -16786
- package/dist/quantum.cjs +1 -89
- package/dist/quantum.js +74 -2551
- package/dist/realtime.cjs +1 -23
- package/dist/realtime.js +1 -804
- package/dist/src/a11y/index.d.ts +0 -1
- package/dist/src/charts/TkxAreaChart.d.ts +0 -5
- package/dist/src/charts/TkxBarChart.d.ts +0 -3
- package/dist/src/charts/TkxDonutChart.d.ts +0 -5
- package/dist/src/charts/TkxLineChart.d.ts +0 -1
- package/dist/src/charts/TkxPieChart.d.ts +0 -3
- package/dist/src/charts/TkxRadarChart.d.ts +0 -3
- package/dist/src/charts/TkxScatterChart.d.ts +0 -2
- package/dist/src/charts/index.d.ts +0 -1
- package/dist/src/charts/shared.d.ts +0 -3
- package/dist/src/components/TkxAIChatBubble.d.ts +0 -9
- package/dist/src/components/TkxAIConfidenceBar.d.ts +0 -6
- package/dist/src/components/TkxAIThinking.d.ts +0 -6
- package/dist/src/components/TkxAccordion.d.ts +0 -1
- package/dist/src/components/TkxAffix.d.ts +0 -1
- package/dist/src/components/TkxAlert.d.ts +0 -1
- package/dist/src/components/TkxAnchor.d.ts +0 -1
- package/dist/src/components/TkxAppBar.d.ts +0 -1
- package/dist/src/components/TkxAutocomplete.d.ts +0 -1
- package/dist/src/components/TkxAvatar.d.ts +0 -1
- package/dist/src/components/TkxBadge.d.ts +0 -1
- package/dist/src/components/TkxBottomNav.d.ts +0 -1
- package/dist/src/components/TkxBreadcrumb.d.ts +0 -1
- package/dist/src/components/TkxButton.d.ts +0 -1
- package/dist/src/components/TkxCard.d.ts +0 -1
- package/dist/src/components/TkxCarousel.d.ts +0 -1
- package/dist/src/components/TkxCascader.d.ts +0 -1
- package/dist/src/components/TkxChat.d.ts +0 -1
- package/dist/src/components/TkxCheckbox.d.ts +0 -1
- package/dist/src/components/TkxClock.d.ts +0 -1
- package/dist/src/components/TkxColorPicker.d.ts +0 -1
- package/dist/src/components/TkxCommand.d.ts +0 -1
- package/dist/src/components/TkxConfigProvider.d.ts +0 -30
- package/dist/src/components/TkxDataGrid.d.ts +0 -15
- package/dist/src/components/TkxDatePicker.d.ts +0 -1
- package/dist/src/components/TkxDivider.d.ts +0 -1
- package/dist/src/components/TkxDrawer.d.ts +0 -1
- package/dist/src/components/TkxDropdown.d.ts +0 -1
- package/dist/src/components/TkxEmpty.d.ts +0 -1
- package/dist/src/components/TkxFileUpload.d.ts +0 -1
- package/dist/src/components/TkxForm.d.ts +0 -35
- package/dist/src/components/TkxIcon.d.ts +0 -1
- package/dist/src/components/TkxImage.d.ts +0 -1
- package/dist/src/components/TkxInput.d.ts +1 -1
- package/dist/src/components/TkxLayout.d.ts +0 -22
- package/dist/src/components/TkxList.d.ts +0 -1
- package/dist/src/components/TkxLiveFeed.d.ts +0 -1
- package/dist/src/components/TkxLiveLog.d.ts +0 -1
- package/dist/src/components/TkxLiveMetrics.d.ts +0 -1
- package/dist/src/components/TkxLogo.d.ts +0 -1
- package/dist/src/components/TkxMarkdown.d.ts +0 -10
- package/dist/src/components/TkxMasonry.d.ts +0 -1
- package/dist/src/components/TkxMentions.d.ts +0 -1
- package/dist/src/components/TkxMenu.d.ts +0 -1
- package/dist/src/components/TkxModal.d.ts +0 -1
- package/dist/src/components/TkxNumberInput.d.ts +0 -1
- package/dist/src/components/TkxOTP.d.ts +0 -1
- package/dist/src/components/TkxOrgChart.d.ts +32 -0
- package/dist/src/components/TkxPagination.d.ts +0 -1
- package/dist/src/components/TkxPlayground.d.ts +0 -13
- package/dist/src/components/TkxPopover.d.ts +0 -1
- package/dist/src/components/TkxProgress.d.ts +0 -1
- package/dist/src/components/TkxQRCode.d.ts +0 -1
- package/dist/src/components/TkxQuantumForm.d.ts +0 -1
- package/dist/src/components/TkxRadio.d.ts +0 -1
- package/dist/src/components/TkxRating.d.ts +0 -1
- package/dist/src/components/TkxRealTimeChart.d.ts +0 -1
- package/dist/src/components/TkxResult.d.ts +0 -1
- package/dist/src/components/TkxRichTextDisplay.d.ts +0 -1
- package/dist/src/components/TkxSegmented.d.ts +0 -1
- package/dist/src/components/TkxSelect.d.ts +0 -3
- package/dist/src/components/TkxSkeleton.d.ts +0 -1
- package/dist/src/components/TkxSlider.d.ts +0 -1
- package/dist/src/components/TkxSnackbar.d.ts +0 -1
- package/dist/src/components/TkxSpeedDial.d.ts +0 -1
- package/dist/src/components/TkxSpin.d.ts +0 -1
- package/dist/src/components/TkxStatistic.d.ts +0 -1
- package/dist/src/components/TkxStepper.d.ts +0 -1
- package/dist/src/components/TkxTable.d.ts +0 -1
- package/dist/src/components/TkxTabs.d.ts +0 -1
- package/dist/src/components/TkxTag.d.ts +0 -1
- package/dist/src/components/TkxThemeBuilder.d.ts +0 -1
- package/dist/src/components/TkxTimeline.d.ts +0 -1
- package/dist/src/components/TkxToast.d.ts +0 -1
- package/dist/src/components/TkxToggle.d.ts +0 -1
- package/dist/src/components/TkxToolbar.d.ts +0 -1
- package/dist/src/components/TkxTooltip.d.ts +0 -1
- package/dist/src/components/TkxTour.d.ts +0 -1
- package/dist/src/components/TkxTransferList.d.ts +0 -1
- package/dist/src/components/TkxTreeView.d.ts +0 -1
- package/dist/src/components/TkxTypography.d.ts +0 -1
- package/dist/src/components/TkxVideoPlayer.d.ts +0 -1
- package/dist/src/components/TkxWatermark.d.ts +0 -1
- package/dist/src/components/index.d.ts +1 -1
- package/dist/src/engine/css.d.ts +0 -1
- package/dist/src/engine/index.d.ts +0 -1
- package/dist/src/engine/quantum-ai.d.ts +0 -73
- package/dist/src/engine/quantum.d.ts +0 -1
- package/dist/src/engine/security.d.ts +45 -9
- package/dist/src/engine/tkx.d.ts +0 -53
- package/dist/src/engine/wcag.d.ts +0 -1
- package/dist/src/headless/index.d.ts +0 -1
- package/dist/src/headless/useControllable.d.ts +0 -15
- package/dist/src/headless/useDebounce.d.ts +0 -12
- package/dist/src/headless/useDisclosure.d.ts +0 -13
- package/dist/src/headless/useFormState.d.ts +0 -13
- package/dist/src/headless/useInfiniteQuery.d.ts +0 -11
- package/dist/src/headless/useIntersectionObserver.d.ts +0 -10
- package/dist/src/headless/useListSelection.d.ts +0 -9
- package/dist/src/headless/useLocalStorage.d.ts +0 -8
- package/dist/src/headless/useMediaQuery.d.ts +0 -10
- package/dist/src/headless/useRovingTabIndex.d.ts +0 -15
- package/dist/src/headless/useSSE.d.ts +0 -9
- package/dist/src/headless/useThrottle.d.ts +0 -7
- package/dist/src/headless/useWebSocket.d.ts +0 -9
- package/dist/src/hooks/index.d.ts +0 -1
- package/dist/src/i18n/I18nProvider.d.ts +0 -1
- package/dist/src/i18n/index.d.ts +0 -1
- package/dist/src/quantum/index.d.ts +0 -1
- package/dist/src/realtime/index.d.ts +0 -1
- package/dist/src/themes/index.d.ts +0 -1
- package/package.json +5 -3
- package/dist/TkxForm-BaO4wvXt.js +0 -320
- package/dist/TkxForm-M6ttomry.cjs +0 -12
- package/dist/chunk-Bmb41Sf3.cjs +0 -1
- package/dist/hooks-6pRsPOyG.js +0 -58
- package/dist/hooks-mivgRljH.cjs +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/src/a11y/index.d.ts.map +0 -1
- package/dist/src/charts/TkxAreaChart.d.ts.map +0 -1
- package/dist/src/charts/TkxBarChart.d.ts.map +0 -1
- package/dist/src/charts/TkxDonutChart.d.ts.map +0 -1
- package/dist/src/charts/TkxLineChart.d.ts.map +0 -1
- package/dist/src/charts/TkxPieChart.d.ts.map +0 -1
- package/dist/src/charts/TkxRadarChart.d.ts.map +0 -1
- package/dist/src/charts/TkxScatterChart.d.ts.map +0 -1
- package/dist/src/charts/index.d.ts.map +0 -1
- package/dist/src/charts/shared.d.ts.map +0 -1
- package/dist/src/components/TkxAIChatBubble.d.ts.map +0 -1
- package/dist/src/components/TkxAIConfidenceBar.d.ts.map +0 -1
- package/dist/src/components/TkxAIThinking.d.ts.map +0 -1
- package/dist/src/components/TkxAccordion.d.ts.map +0 -1
- package/dist/src/components/TkxAffix.d.ts.map +0 -1
- package/dist/src/components/TkxAlert.d.ts.map +0 -1
- package/dist/src/components/TkxAnchor.d.ts.map +0 -1
- package/dist/src/components/TkxAppBar.d.ts.map +0 -1
- package/dist/src/components/TkxAutocomplete.d.ts.map +0 -1
- package/dist/src/components/TkxAvatar.d.ts.map +0 -1
- package/dist/src/components/TkxBadge.d.ts.map +0 -1
- package/dist/src/components/TkxBottomNav.d.ts.map +0 -1
- package/dist/src/components/TkxBreadcrumb.d.ts.map +0 -1
- package/dist/src/components/TkxButton.d.ts.map +0 -1
- package/dist/src/components/TkxCard.d.ts.map +0 -1
- package/dist/src/components/TkxCarousel.d.ts.map +0 -1
- package/dist/src/components/TkxCascader.d.ts.map +0 -1
- package/dist/src/components/TkxChat.d.ts.map +0 -1
- package/dist/src/components/TkxCheckbox.d.ts.map +0 -1
- package/dist/src/components/TkxClock.d.ts.map +0 -1
- package/dist/src/components/TkxColorPicker.d.ts.map +0 -1
- package/dist/src/components/TkxCommand.d.ts.map +0 -1
- package/dist/src/components/TkxConfigProvider.d.ts.map +0 -1
- package/dist/src/components/TkxDataGrid.d.ts.map +0 -1
- package/dist/src/components/TkxDatePicker.d.ts.map +0 -1
- package/dist/src/components/TkxDivider.d.ts.map +0 -1
- package/dist/src/components/TkxDrawer.d.ts.map +0 -1
- package/dist/src/components/TkxDropdown.d.ts.map +0 -1
- package/dist/src/components/TkxEmpty.d.ts.map +0 -1
- package/dist/src/components/TkxFileUpload.d.ts.map +0 -1
- package/dist/src/components/TkxForm.d.ts.map +0 -1
- package/dist/src/components/TkxIcon.d.ts.map +0 -1
- package/dist/src/components/TkxImage.d.ts.map +0 -1
- package/dist/src/components/TkxInput.d.ts.map +0 -1
- package/dist/src/components/TkxLayout.d.ts.map +0 -1
- package/dist/src/components/TkxList.d.ts.map +0 -1
- package/dist/src/components/TkxLiveFeed.d.ts.map +0 -1
- package/dist/src/components/TkxLiveLog.d.ts.map +0 -1
- package/dist/src/components/TkxLiveMetrics.d.ts.map +0 -1
- package/dist/src/components/TkxLogo.d.ts.map +0 -1
- package/dist/src/components/TkxMarkdown.d.ts.map +0 -1
- package/dist/src/components/TkxMasonry.d.ts.map +0 -1
- package/dist/src/components/TkxMentions.d.ts.map +0 -1
- package/dist/src/components/TkxMenu.d.ts.map +0 -1
- package/dist/src/components/TkxModal.d.ts.map +0 -1
- package/dist/src/components/TkxNumberInput.d.ts.map +0 -1
- package/dist/src/components/TkxOTP.d.ts.map +0 -1
- package/dist/src/components/TkxPagination.d.ts.map +0 -1
- package/dist/src/components/TkxPlayground.d.ts.map +0 -1
- package/dist/src/components/TkxPopover.d.ts.map +0 -1
- package/dist/src/components/TkxProgress.d.ts.map +0 -1
- package/dist/src/components/TkxQRCode.d.ts.map +0 -1
- package/dist/src/components/TkxQuantumForm.d.ts.map +0 -1
- package/dist/src/components/TkxRadio.d.ts.map +0 -1
- package/dist/src/components/TkxRating.d.ts.map +0 -1
- package/dist/src/components/TkxRealTimeChart.d.ts.map +0 -1
- package/dist/src/components/TkxResult.d.ts.map +0 -1
- package/dist/src/components/TkxRichTextDisplay.d.ts.map +0 -1
- package/dist/src/components/TkxSegmented.d.ts.map +0 -1
- package/dist/src/components/TkxSelect.d.ts.map +0 -1
- package/dist/src/components/TkxSkeleton.d.ts.map +0 -1
- package/dist/src/components/TkxSlider.d.ts.map +0 -1
- package/dist/src/components/TkxSnackbar.d.ts.map +0 -1
- package/dist/src/components/TkxSpeedDial.d.ts.map +0 -1
- package/dist/src/components/TkxSpin.d.ts.map +0 -1
- package/dist/src/components/TkxStatistic.d.ts.map +0 -1
- package/dist/src/components/TkxStepper.d.ts.map +0 -1
- package/dist/src/components/TkxTable.d.ts.map +0 -1
- package/dist/src/components/TkxTabs.d.ts.map +0 -1
- package/dist/src/components/TkxTag.d.ts.map +0 -1
- package/dist/src/components/TkxThemeBuilder.d.ts.map +0 -1
- package/dist/src/components/TkxTimeline.d.ts.map +0 -1
- package/dist/src/components/TkxToast.d.ts.map +0 -1
- package/dist/src/components/TkxToggle.d.ts.map +0 -1
- package/dist/src/components/TkxToolbar.d.ts.map +0 -1
- package/dist/src/components/TkxTooltip.d.ts.map +0 -1
- package/dist/src/components/TkxTour.d.ts.map +0 -1
- package/dist/src/components/TkxTransferList.d.ts.map +0 -1
- package/dist/src/components/TkxTreeView.d.ts.map +0 -1
- package/dist/src/components/TkxTypography.d.ts.map +0 -1
- package/dist/src/components/TkxVideoPlayer.d.ts.map +0 -1
- package/dist/src/components/TkxWatermark.d.ts.map +0 -1
- package/dist/src/components/index.d.ts.map +0 -1
- package/dist/src/engine/css.d.ts.map +0 -1
- package/dist/src/engine/index.d.ts.map +0 -1
- package/dist/src/engine/quantum-ai.d.ts.map +0 -1
- package/dist/src/engine/quantum.d.ts.map +0 -1
- package/dist/src/engine/security.d.ts.map +0 -1
- package/dist/src/engine/tkx.d.ts.map +0 -1
- package/dist/src/engine/wcag.d.ts.map +0 -1
- package/dist/src/headless/index.d.ts.map +0 -1
- package/dist/src/headless/useControllable.d.ts.map +0 -1
- package/dist/src/headless/useDebounce.d.ts.map +0 -1
- package/dist/src/headless/useDisclosure.d.ts.map +0 -1
- package/dist/src/headless/useFormState.d.ts.map +0 -1
- package/dist/src/headless/useInfiniteQuery.d.ts.map +0 -1
- package/dist/src/headless/useIntersectionObserver.d.ts.map +0 -1
- package/dist/src/headless/useListSelection.d.ts.map +0 -1
- package/dist/src/headless/useLocalStorage.d.ts.map +0 -1
- package/dist/src/headless/useMediaQuery.d.ts.map +0 -1
- package/dist/src/headless/useRovingTabIndex.d.ts.map +0 -1
- package/dist/src/headless/useSSE.d.ts.map +0 -1
- package/dist/src/headless/useThrottle.d.ts.map +0 -1
- package/dist/src/headless/useWebSocket.d.ts.map +0 -1
- package/dist/src/hooks/index.d.ts.map +0 -1
- package/dist/src/i18n/I18nProvider.d.ts.map +0 -1
- package/dist/src/i18n/index.d.ts.map +0 -1
- package/dist/src/quantum/index.d.ts.map +0 -1
- package/dist/src/realtime/index.d.ts.map +0 -1
- package/dist/src/themes/index.d.ts.map +0 -1
- package/dist/themes-BbJanqiC.js +0 -498
- package/dist/themes-CMq2vWXj.cjs +0 -2
- package/dist/tkx-BoP5V5zy.cjs +0 -4
- package/dist/tkx-mOKMzvUK.js +0 -1174
- package/src/a11y/index.tsx +0 -87
- package/src/charts/TkxAreaChart.tsx +0 -121
- package/src/charts/TkxBarChart.tsx +0 -117
- package/src/charts/TkxDonutChart.tsx +0 -164
- package/src/charts/TkxLineChart.tsx +0 -137
- package/src/charts/TkxPieChart.tsx +0 -103
- package/src/charts/TkxRadarChart.tsx +0 -92
- package/src/charts/TkxScatterChart.tsx +0 -104
- package/src/charts/index.ts +0 -22
- package/src/charts/shared.ts +0 -47
- package/src/components/TkxAIChatBubble.tsx +0 -256
- package/src/components/TkxAIConfidenceBar.tsx +0 -133
- package/src/components/TkxAIThinking.tsx +0 -193
- package/src/components/TkxAccordion.tsx +0 -590
- package/src/components/TkxAffix.tsx +0 -140
- package/src/components/TkxAlert.tsx +0 -94
- package/src/components/TkxAnchor.tsx +0 -195
- package/src/components/TkxAppBar.tsx +0 -262
- package/src/components/TkxAutocomplete.tsx +0 -455
- package/src/components/TkxAvatar.tsx +0 -103
- package/src/components/TkxBadge.tsx +0 -75
- package/src/components/TkxBottomNav.tsx +0 -241
- package/src/components/TkxBreadcrumb.tsx +0 -324
- package/src/components/TkxButton.tsx +0 -155
- package/src/components/TkxCard.tsx +0 -96
- package/src/components/TkxCarousel.tsx +0 -554
- package/src/components/TkxCascader.tsx +0 -278
- package/src/components/TkxChat.tsx +0 -356
- package/src/components/TkxCheckbox.tsx +0 -220
- package/src/components/TkxClock.tsx +0 -603
- package/src/components/TkxColorPicker.tsx +0 -635
- package/src/components/TkxCommand.tsx +0 -646
- package/src/components/TkxConfigProvider.tsx +0 -460
- package/src/components/TkxDataGrid.tsx +0 -941
- package/src/components/TkxDatePicker.tsx +0 -1625
- package/src/components/TkxDivider.tsx +0 -51
- package/src/components/TkxDrawer.tsx +0 -236
- package/src/components/TkxDropdown.tsx +0 -804
- package/src/components/TkxEmpty.tsx +0 -221
- package/src/components/TkxFileUpload.tsx +0 -373
- package/src/components/TkxForm.tsx +0 -683
- package/src/components/TkxIcon.tsx +0 -685
- package/src/components/TkxImage.tsx +0 -279
- package/src/components/TkxInput.tsx +0 -103
- package/src/components/TkxLayout.tsx +0 -649
- package/src/components/TkxList.tsx +0 -244
- package/src/components/TkxLiveFeed.tsx +0 -331
- package/src/components/TkxLiveLog.tsx +0 -472
- package/src/components/TkxLiveMetrics.tsx +0 -315
- package/src/components/TkxLogo.tsx +0 -299
- package/src/components/TkxMarkdown.tsx +0 -855
- package/src/components/TkxMasonry.tsx +0 -178
- package/src/components/TkxMentions.tsx +0 -212
- package/src/components/TkxMenu.tsx +0 -1044
- package/src/components/TkxModal.tsx +0 -122
- package/src/components/TkxNumberInput.tsx +0 -410
- package/src/components/TkxOTP.tsx +0 -297
- package/src/components/TkxPagination.tsx +0 -390
- package/src/components/TkxPlayground.tsx +0 -845
- package/src/components/TkxPopover.tsx +0 -297
- package/src/components/TkxProgress.tsx +0 -114
- package/src/components/TkxQRCode.tsx +0 -176
- package/src/components/TkxQuantumForm.tsx +0 -594
- package/src/components/TkxRadio.tsx +0 -286
- package/src/components/TkxRating.tsx +0 -288
- package/src/components/TkxRealTimeChart.tsx +0 -383
- package/src/components/TkxResult.tsx +0 -130
- package/src/components/TkxRichTextDisplay.tsx +0 -396
- package/src/components/TkxSegmented.tsx +0 -171
- package/src/components/TkxSelect.tsx +0 -1130
- package/src/components/TkxSkeleton.tsx +0 -107
- package/src/components/TkxSlider.tsx +0 -495
- package/src/components/TkxSnackbar.tsx +0 -316
- package/src/components/TkxSpeedDial.tsx +0 -397
- package/src/components/TkxSpin.tsx +0 -263
- package/src/components/TkxStatistic.tsx +0 -263
- package/src/components/TkxStepper.tsx +0 -487
- package/src/components/TkxTable.tsx +0 -424
- package/src/components/TkxTabs.tsx +0 -126
- package/src/components/TkxTag.tsx +0 -321
- package/src/components/TkxThemeBuilder.tsx +0 -687
- package/src/components/TkxTimeline.tsx +0 -305
- package/src/components/TkxToast.tsx +0 -287
- package/src/components/TkxToggle.tsx +0 -95
- package/src/components/TkxToolbar.tsx +0 -262
- package/src/components/TkxTooltip.tsx +0 -85
- package/src/components/TkxTour.tsx +0 -241
- package/src/components/TkxTransferList.tsx +0 -489
- package/src/components/TkxTreeView.tsx +0 -423
- package/src/components/TkxTypography.tsx +0 -267
- package/src/components/TkxVideoPlayer.tsx +0 -1015
- package/src/components/TkxWatermark.tsx +0 -145
- package/src/components/index.ts +0 -80
- package/src/engine/css.ts +0 -126
- package/src/engine/index.ts +0 -12
- package/src/engine/quantum-ai.ts +0 -1045
- package/src/engine/quantum.ts +0 -207
- package/src/engine/security.ts +0 -232
- package/src/engine/tkx.ts +0 -1084
- package/src/engine/wcag.ts +0 -276
- package/src/headless/index.ts +0 -52
- package/src/headless/useControllable.ts +0 -42
- package/src/headless/useDebounce.ts +0 -37
- package/src/headless/useDisclosure.ts +0 -30
- package/src/headless/useFormState.ts +0 -113
- package/src/headless/useInfiniteQuery.ts +0 -155
- package/src/headless/useIntersectionObserver.ts +0 -53
- package/src/headless/useListSelection.ts +0 -103
- package/src/headless/useLocalStorage.ts +0 -48
- package/src/headless/useMediaQuery.ts +0 -39
- package/src/headless/useRovingTabIndex.ts +0 -86
- package/src/headless/useSSE.ts +0 -167
- package/src/headless/useThrottle.ts +0 -48
- package/src/headless/useWebSocket.ts +0 -231
- package/src/hooks/index.ts +0 -122
- package/src/i18n/I18nProvider.tsx +0 -36
- package/src/i18n/index.ts +0 -877
- package/src/quantum/index.ts +0 -39
- package/src/realtime/index.ts +0 -21
- package/src/styles/global.css +0 -167
- package/src/themes/index.ts +0 -294
|
@@ -1,683 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
4
|
-
// TKX FORM — Enterprise-grade form controller
|
|
5
|
-
// Context-based form state management with field-level validation,
|
|
6
|
-
// error propagation, layout control, and programmatic access.
|
|
7
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
createContext,
|
|
11
|
-
useContext,
|
|
12
|
-
useCallback,
|
|
13
|
-
useEffect,
|
|
14
|
-
useMemo,
|
|
15
|
-
useRef,
|
|
16
|
-
useState,
|
|
17
|
-
cloneElement,
|
|
18
|
-
type ReactNode,
|
|
19
|
-
type ReactElement,
|
|
20
|
-
type CSSProperties,
|
|
21
|
-
type FormEvent,
|
|
22
|
-
} from 'react';
|
|
23
|
-
import { useTheme } from '../themes';
|
|
24
|
-
import { sanitizeString } from '../engine/security';
|
|
25
|
-
import { tkx, cx } from '../engine/tkx';
|
|
26
|
-
|
|
27
|
-
// ── Validation Rule ─────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
export interface ValidationRule {
|
|
30
|
-
required?: boolean;
|
|
31
|
-
min?: number;
|
|
32
|
-
max?: number;
|
|
33
|
-
pattern?: RegExp;
|
|
34
|
-
/** Custom validator. Receives the field value and all form values for cross-field validation (e.g. "confirm password" patterns). */
|
|
35
|
-
validator?: (value: unknown, allValues?: Record<string, unknown>) => string | null | Promise<string | null>;
|
|
36
|
-
message?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ── Form Props ──────────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
export interface TkxFormProps<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
42
|
-
onSubmit?: (values: T) => void | Promise<void>;
|
|
43
|
-
onValuesChange?: (changed: Partial<T>, all: T) => void;
|
|
44
|
-
initialValues?: Partial<T>;
|
|
45
|
-
layout?: 'vertical' | 'horizontal' | 'inline';
|
|
46
|
-
disabled?: boolean;
|
|
47
|
-
children: ReactNode;
|
|
48
|
-
className?: string;
|
|
49
|
-
style?: CSSProperties;
|
|
50
|
-
/**
|
|
51
|
-
* An external FormInstance created via useTkxForm().
|
|
52
|
-
* When provided, the form exposes its internal instance through this reference,
|
|
53
|
-
* enabling programmatic control from outside the component tree.
|
|
54
|
-
*/
|
|
55
|
-
form?: FormInstance<T>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ── Form Field Props ────────────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
export interface TkxFormFieldProps {
|
|
61
|
-
name: string;
|
|
62
|
-
label?: string;
|
|
63
|
-
rules?: ValidationRule[];
|
|
64
|
-
help?: string;
|
|
65
|
-
required?: boolean;
|
|
66
|
-
children: ReactElement;
|
|
67
|
-
className?: string;
|
|
68
|
-
style?: CSSProperties;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ── Form Instance (programmatic API) ────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
export interface FormInstance<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
74
|
-
getFieldValue: <K extends keyof T>(name: K) => T[K] | undefined;
|
|
75
|
-
setFieldValue: <K extends keyof T>(name: K, value: T[K]) => void;
|
|
76
|
-
getFieldsValue: () => Partial<T>;
|
|
77
|
-
setFieldsValue: (values: Partial<T>) => void;
|
|
78
|
-
validateFields: () => Promise<T>;
|
|
79
|
-
validateField: (name: keyof T & string) => Promise<boolean>;
|
|
80
|
-
resetFields: () => void;
|
|
81
|
-
getFieldError: (name: keyof T & string) => string | null;
|
|
82
|
-
isFieldTouched: (name: keyof T & string) => boolean;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ── Internal State ──────────────────────────────────────────────────────────
|
|
86
|
-
|
|
87
|
-
interface FieldMeta {
|
|
88
|
-
rules: ValidationRule[];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
interface FormState {
|
|
92
|
-
values: Record<string, unknown>;
|
|
93
|
-
errors: Record<string, string | null>;
|
|
94
|
-
touched: Record<string, boolean>;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
interface FormContextValue {
|
|
98
|
-
state: FormState;
|
|
99
|
-
initialValues: Record<string, unknown>;
|
|
100
|
-
layout: 'vertical' | 'horizontal' | 'inline';
|
|
101
|
-
disabled: boolean;
|
|
102
|
-
fieldMeta: React.MutableRefObject<Record<string, FieldMeta>>;
|
|
103
|
-
setFieldValue: (name: string, value: unknown) => void;
|
|
104
|
-
setFieldError: (name: string, error: string | null) => void;
|
|
105
|
-
setFieldTouched: (name: string) => void;
|
|
106
|
-
registerField: (name: string, meta: FieldMeta) => void;
|
|
107
|
-
unregisterField: (name: string) => void;
|
|
108
|
-
validateField: (name: string) => Promise<boolean>;
|
|
109
|
-
instance: FormInstance;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ── Context ─────────────────────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
const FormContext = createContext<FormContextValue | null>(null);
|
|
115
|
-
|
|
116
|
-
function useFormContext(): FormContextValue {
|
|
117
|
-
const ctx = useContext(FormContext);
|
|
118
|
-
if (!ctx) {
|
|
119
|
-
throw new Error('TkxFormField must be used inside a <TkxForm>. Wrap your fields in a TkxForm component.');
|
|
120
|
-
}
|
|
121
|
-
return ctx;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ── Validation Engine ───────────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
async function runValidation(value: any, rules: ValidationRule[]): Promise<string | null> {
|
|
127
|
-
for (const rule of rules) {
|
|
128
|
-
// Required check
|
|
129
|
-
if (rule.required) {
|
|
130
|
-
const empty = value === undefined || value === null || value === '' ||
|
|
131
|
-
(Array.isArray(value) && value.length === 0);
|
|
132
|
-
if (empty) {
|
|
133
|
-
return rule.message ?? 'This field is required';
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Skip further checks if value is empty and not required
|
|
138
|
-
if (value === undefined || value === null || value === '') {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Min length / min value
|
|
143
|
-
if (rule.min !== undefined) {
|
|
144
|
-
if (typeof value === 'string' && value.length < rule.min) {
|
|
145
|
-
return rule.message ?? `Must be at least ${rule.min} characters`;
|
|
146
|
-
}
|
|
147
|
-
if (typeof value === 'number' && value < rule.min) {
|
|
148
|
-
return rule.message ?? `Must be at least ${rule.min}`;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Max length / max value
|
|
153
|
-
if (rule.max !== undefined) {
|
|
154
|
-
if (typeof value === 'string' && value.length > rule.max) {
|
|
155
|
-
return rule.message ?? `Must be no more than ${rule.max} characters`;
|
|
156
|
-
}
|
|
157
|
-
if (typeof value === 'number' && value > rule.max) {
|
|
158
|
-
return rule.message ?? `Must be no more than ${rule.max}`;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Pattern check
|
|
163
|
-
if (rule.pattern) {
|
|
164
|
-
if (typeof value === 'string' && !rule.pattern.test(value)) {
|
|
165
|
-
return rule.message ?? 'Invalid format';
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Custom async validator
|
|
170
|
-
if (rule.validator) {
|
|
171
|
-
const result = await rule.validator(value);
|
|
172
|
-
if (result) {
|
|
173
|
-
return result;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/** Merge the `required` shortcut prop into the rules array. */
|
|
182
|
-
function mergeRequiredRule(rules: ValidationRule[] | undefined, required?: boolean): ValidationRule[] {
|
|
183
|
-
const base = rules ? [...rules] : [];
|
|
184
|
-
if (required && !base.some(r => r.required)) {
|
|
185
|
-
base.unshift({ required: true, message: 'This field is required' });
|
|
186
|
-
}
|
|
187
|
-
return base;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** Check whether any rule in a set has `required: true`. */
|
|
191
|
-
function hasRequiredRule(rules: ValidationRule[]): boolean {
|
|
192
|
-
return rules.some(r => r.required);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ── useTkxForm Hook ─────────────────────────────────────────────────────────
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Returns a FormInstance for programmatic access to form state.
|
|
199
|
-
*
|
|
200
|
-
* - Inside a <TkxForm>: returns the form's live instance (reads/writes real field state).
|
|
201
|
-
* - Outside a <TkxForm>: returns a standalone instance backed by a ref store.
|
|
202
|
-
* Pass it to <TkxForm form={instance}> to connect it to a form.
|
|
203
|
-
*
|
|
204
|
-
* @example — programmatic access inside a form
|
|
205
|
-
* ```tsx
|
|
206
|
-
* function MyInnerButtons() {
|
|
207
|
-
* const form = useTkxForm(); // connected to parent TkxForm
|
|
208
|
-
* return <button onClick={() => form.resetFields()}>Reset</button>;
|
|
209
|
-
* }
|
|
210
|
-
* ```
|
|
211
|
-
*
|
|
212
|
-
* @example — external instance (Ant Design pattern)
|
|
213
|
-
* ```tsx
|
|
214
|
-
* function Parent() {
|
|
215
|
-
* const form = useTkxForm();
|
|
216
|
-
* return (
|
|
217
|
-
* <TkxForm form={form}>
|
|
218
|
-
* <TkxFormField name="email" label="Email"><TkxInput label="Email" /></TkxFormField>
|
|
219
|
-
* <button onClick={() => console.log(form.getFieldsValue())}>Log</button>
|
|
220
|
-
* </TkxForm>
|
|
221
|
-
* );
|
|
222
|
-
* }
|
|
223
|
-
* ```
|
|
224
|
-
*/
|
|
225
|
-
export function useTkxForm(): FormInstance {
|
|
226
|
-
const ctx = useContext(FormContext);
|
|
227
|
-
|
|
228
|
-
// Always call hooks unconditionally (Rules of Hooks).
|
|
229
|
-
// These refs back the standalone instance when used outside a TkxForm.
|
|
230
|
-
const valuesRef = useRef<Record<string, unknown>>({});
|
|
231
|
-
const errorsRef = useRef<Record<string, string | null>>({});
|
|
232
|
-
const touchedRef = useRef<Record<string, boolean>>({});
|
|
233
|
-
|
|
234
|
-
const standaloneInstance = useMemo<FormInstance>(() => ({
|
|
235
|
-
getFieldValue: (name: string) => valuesRef.current[name],
|
|
236
|
-
setFieldValue: (name: string, value: unknown) => { valuesRef.current[name] = value; },
|
|
237
|
-
getFieldsValue: () => ({ ...valuesRef.current }),
|
|
238
|
-
setFieldsValue: (values: Record<string, unknown>) => {
|
|
239
|
-
Object.assign(valuesRef.current, values);
|
|
240
|
-
},
|
|
241
|
-
validateFields: () => Promise.resolve({ ...valuesRef.current } as Record<string, unknown>),
|
|
242
|
-
validateField: (_name: string) => Promise.resolve(true),
|
|
243
|
-
resetFields: () => {
|
|
244
|
-
valuesRef.current = {};
|
|
245
|
-
errorsRef.current = {};
|
|
246
|
-
touchedRef.current = {};
|
|
247
|
-
},
|
|
248
|
-
getFieldError: (name: string) => errorsRef.current[name] ?? null,
|
|
249
|
-
isFieldTouched: (name: string) => touchedRef.current[name] ?? false,
|
|
250
|
-
}), []);
|
|
251
|
-
|
|
252
|
-
// If inside a TkxForm context, return the live connected instance.
|
|
253
|
-
// Otherwise return the standalone ref-backed instance.
|
|
254
|
-
return ctx ? ctx.instance : standaloneInstance;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ── TkxForm Component ───────────────────────────────────────────────────────
|
|
258
|
-
|
|
259
|
-
export function TkxForm<T extends Record<string, unknown> = Record<string, unknown>>({
|
|
260
|
-
onSubmit,
|
|
261
|
-
onValuesChange,
|
|
262
|
-
initialValues = {} as Partial<T>,
|
|
263
|
-
layout = 'vertical',
|
|
264
|
-
disabled = false,
|
|
265
|
-
children,
|
|
266
|
-
className,
|
|
267
|
-
style,
|
|
268
|
-
form: externalInstance,
|
|
269
|
-
}: TkxFormProps<T>) {
|
|
270
|
-
const theme = useTheme();
|
|
271
|
-
|
|
272
|
-
// ─── State ──────────────────────────────────────────────────────────────
|
|
273
|
-
const [state, setState] = useState<FormState>({
|
|
274
|
-
values: { ...initialValues },
|
|
275
|
-
errors: {},
|
|
276
|
-
touched: {},
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
const initialValuesRef = useRef(initialValues);
|
|
280
|
-
const fieldMetaRef = useRef<Record<string, FieldMeta>>({});
|
|
281
|
-
const stateRef = useRef(state);
|
|
282
|
-
stateRef.current = state;
|
|
283
|
-
|
|
284
|
-
// ─── Field Registration ─────────────────────────────────────────────────
|
|
285
|
-
const registerField = useCallback((name: string, meta: FieldMeta) => {
|
|
286
|
-
fieldMetaRef.current[name] = meta;
|
|
287
|
-
}, []);
|
|
288
|
-
|
|
289
|
-
const unregisterField = useCallback((name: string) => {
|
|
290
|
-
delete fieldMetaRef.current[name];
|
|
291
|
-
}, []);
|
|
292
|
-
|
|
293
|
-
// ─── Value Management ───────────────────────────────────────────────────
|
|
294
|
-
const setFieldValue = useCallback((name: string, value: unknown) => {
|
|
295
|
-
setState(prev => {
|
|
296
|
-
const next = {
|
|
297
|
-
...prev,
|
|
298
|
-
values: { ...prev.values, [name]: value },
|
|
299
|
-
};
|
|
300
|
-
onValuesChange?.({ [name]: value } as Partial<T>, next.values as T);
|
|
301
|
-
return next;
|
|
302
|
-
});
|
|
303
|
-
}, [onValuesChange]);
|
|
304
|
-
|
|
305
|
-
const setFieldError = useCallback((name: string, error: string | null) => {
|
|
306
|
-
setState(prev => ({
|
|
307
|
-
...prev,
|
|
308
|
-
errors: { ...prev.errors, [name]: error },
|
|
309
|
-
}));
|
|
310
|
-
}, []);
|
|
311
|
-
|
|
312
|
-
const setFieldTouched = useCallback((name: string) => {
|
|
313
|
-
setState(prev => ({
|
|
314
|
-
...prev,
|
|
315
|
-
touched: { ...prev.touched, [name]: true },
|
|
316
|
-
}));
|
|
317
|
-
}, []);
|
|
318
|
-
|
|
319
|
-
// ─── Validation ─────────────────────────────────────────────────────────
|
|
320
|
-
const validateField = useCallback(async (name: string): Promise<boolean> => {
|
|
321
|
-
const meta = fieldMetaRef.current[name];
|
|
322
|
-
if (!meta) return true;
|
|
323
|
-
|
|
324
|
-
const value = stateRef.current.values[name];
|
|
325
|
-
const error = await runValidation(value, meta.rules);
|
|
326
|
-
setState(prev => ({
|
|
327
|
-
...prev,
|
|
328
|
-
errors: { ...prev.errors, [name]: error },
|
|
329
|
-
touched: { ...prev.touched, [name]: true },
|
|
330
|
-
}));
|
|
331
|
-
return error === null;
|
|
332
|
-
}, []);
|
|
333
|
-
|
|
334
|
-
const validateFields = useCallback(async (): Promise<T> => {
|
|
335
|
-
const fieldNames = Object.keys(fieldMetaRef.current);
|
|
336
|
-
const results = await Promise.all(
|
|
337
|
-
fieldNames.map(async (name) => {
|
|
338
|
-
const meta = fieldMetaRef.current[name];
|
|
339
|
-
const value = stateRef.current.values[name];
|
|
340
|
-
const error = await runValidation(value, meta.rules);
|
|
341
|
-
return { name, error };
|
|
342
|
-
}),
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
const newErrors: Record<string, string | null> = {};
|
|
346
|
-
const newTouched: Record<string, boolean> = {};
|
|
347
|
-
let hasErrors = false;
|
|
348
|
-
|
|
349
|
-
for (const { name, error } of results) {
|
|
350
|
-
newErrors[name] = error;
|
|
351
|
-
newTouched[name] = true;
|
|
352
|
-
if (error) hasErrors = true;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
setState(prev => ({
|
|
356
|
-
...prev,
|
|
357
|
-
errors: { ...prev.errors, ...newErrors },
|
|
358
|
-
touched: { ...prev.touched, ...newTouched },
|
|
359
|
-
}));
|
|
360
|
-
|
|
361
|
-
if (hasErrors) {
|
|
362
|
-
const errorMap = Object.fromEntries(
|
|
363
|
-
results.filter(r => r.error).map(r => [r.name, r.error]),
|
|
364
|
-
);
|
|
365
|
-
return Promise.reject(errorMap);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return { ...stateRef.current.values } as T;
|
|
369
|
-
}, []);
|
|
370
|
-
|
|
371
|
-
const resetFields = useCallback(() => {
|
|
372
|
-
setState({
|
|
373
|
-
values: { ...initialValuesRef.current },
|
|
374
|
-
errors: {},
|
|
375
|
-
touched: {},
|
|
376
|
-
});
|
|
377
|
-
}, []);
|
|
378
|
-
|
|
379
|
-
// ─── Form Instance ─────────────────────────────────────────────────────
|
|
380
|
-
// Cast to the base FormInstance type for context compatibility.
|
|
381
|
-
// The generic T is only relevant at the props/API boundary, not internally.
|
|
382
|
-
const instance = useMemo<FormInstance>(() => ({
|
|
383
|
-
getFieldValue: (name: string) => stateRef.current.values[name],
|
|
384
|
-
setFieldValue,
|
|
385
|
-
getFieldsValue: () => ({ ...stateRef.current.values }),
|
|
386
|
-
setFieldsValue: (values: Record<string, unknown>) => {
|
|
387
|
-
setState(prev => {
|
|
388
|
-
const merged = { ...prev.values, ...values };
|
|
389
|
-
onValuesChange?.(values as Partial<T>, merged as T);
|
|
390
|
-
return { ...prev, values: merged };
|
|
391
|
-
});
|
|
392
|
-
},
|
|
393
|
-
validateFields,
|
|
394
|
-
validateField,
|
|
395
|
-
resetFields,
|
|
396
|
-
getFieldError: (name: string) => stateRef.current.errors[name] ?? null,
|
|
397
|
-
isFieldTouched: (name: string) => !!stateRef.current.touched[name],
|
|
398
|
-
}), [setFieldValue, validateFields, validateField, resetFields, onValuesChange]);
|
|
399
|
-
|
|
400
|
-
// ─── Submit Handler ─────────────────────────────────────────────────────
|
|
401
|
-
const handleSubmit = useCallback(async (e: FormEvent<HTMLFormElement>) => {
|
|
402
|
-
e.preventDefault();
|
|
403
|
-
try {
|
|
404
|
-
const values = await validateFields();
|
|
405
|
-
await onSubmit?.(values);
|
|
406
|
-
} catch {
|
|
407
|
-
// Validation failed — errors are already in state
|
|
408
|
-
}
|
|
409
|
-
}, [validateFields, onSubmit]);
|
|
410
|
-
|
|
411
|
-
// ─── Layout Styles ─────────────────────────────────────────────────────
|
|
412
|
-
const layoutClass = layout === 'inline'
|
|
413
|
-
? tkx('flex flex-row flex-wrap items-end gap-4')
|
|
414
|
-
: tkx('flex flex-col gap-5');
|
|
415
|
-
|
|
416
|
-
// ─── Context Value ─────────────────────────────────────────────────────
|
|
417
|
-
// When an external form instance is provided via the `form` prop, bind its
|
|
418
|
-
// methods to the live internal instance so programmatic calls (e.g.
|
|
419
|
-
// form.setFieldValue) drive re-renders. Without this binding the external
|
|
420
|
-
// instance is just a detached ref store and the UI never updates.
|
|
421
|
-
useEffect(() => {
|
|
422
|
-
if (!externalInstance) return;
|
|
423
|
-
const live = externalInstance as FormInstance;
|
|
424
|
-
live.getFieldValue = instance.getFieldValue;
|
|
425
|
-
live.setFieldValue = instance.setFieldValue;
|
|
426
|
-
live.getFieldsValue = instance.getFieldsValue;
|
|
427
|
-
live.setFieldsValue = instance.setFieldsValue;
|
|
428
|
-
live.validateFields = instance.validateFields;
|
|
429
|
-
live.validateField = instance.validateField;
|
|
430
|
-
live.resetFields = instance.resetFields;
|
|
431
|
-
live.getFieldError = instance.getFieldError;
|
|
432
|
-
live.isFieldTouched = instance.isFieldTouched;
|
|
433
|
-
}, [externalInstance, instance]);
|
|
434
|
-
|
|
435
|
-
const activeInstance: FormInstance = (externalInstance as FormInstance | undefined) ?? instance;
|
|
436
|
-
|
|
437
|
-
const contextValue = useMemo<FormContextValue>(() => ({
|
|
438
|
-
state,
|
|
439
|
-
initialValues: initialValuesRef.current,
|
|
440
|
-
layout,
|
|
441
|
-
disabled,
|
|
442
|
-
fieldMeta: fieldMetaRef,
|
|
443
|
-
setFieldValue,
|
|
444
|
-
setFieldError,
|
|
445
|
-
setFieldTouched,
|
|
446
|
-
registerField,
|
|
447
|
-
unregisterField,
|
|
448
|
-
validateField,
|
|
449
|
-
instance: activeInstance,
|
|
450
|
-
}), [state, layout, disabled, setFieldValue, setFieldError, setFieldTouched, registerField, unregisterField, validateField, activeInstance]);
|
|
451
|
-
|
|
452
|
-
return (
|
|
453
|
-
<FormContext.Provider value={contextValue}>
|
|
454
|
-
<form
|
|
455
|
-
noValidate
|
|
456
|
-
role="form"
|
|
457
|
-
aria-label="Form"
|
|
458
|
-
onSubmit={handleSubmit}
|
|
459
|
-
className={cx(layoutClass, className)}
|
|
460
|
-
style={{
|
|
461
|
-
color: theme.text,
|
|
462
|
-
...style,
|
|
463
|
-
}}
|
|
464
|
-
>
|
|
465
|
-
{children}
|
|
466
|
-
</form>
|
|
467
|
-
</FormContext.Provider>
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
TkxForm.displayName = 'TkxForm';
|
|
472
|
-
|
|
473
|
-
// ── TkxFormField Component ──────────────────────────────────────────────────
|
|
474
|
-
|
|
475
|
-
export function TkxFormField({
|
|
476
|
-
name,
|
|
477
|
-
label,
|
|
478
|
-
rules: rulesProp,
|
|
479
|
-
help,
|
|
480
|
-
required,
|
|
481
|
-
children,
|
|
482
|
-
className,
|
|
483
|
-
style,
|
|
484
|
-
}: TkxFormFieldProps) {
|
|
485
|
-
const theme = useTheme();
|
|
486
|
-
const ctx = useFormContext();
|
|
487
|
-
const { state, layout, disabled } = ctx;
|
|
488
|
-
|
|
489
|
-
// Merge required shortcut into rules
|
|
490
|
-
const rules = useMemo(() => mergeRequiredRule(rulesProp, required), [rulesProp, required]);
|
|
491
|
-
const isRequired = hasRequiredRule(rules);
|
|
492
|
-
|
|
493
|
-
// Register field metadata with form
|
|
494
|
-
const metaRef = useRef<FieldMeta>({ rules });
|
|
495
|
-
metaRef.current.rules = rules;
|
|
496
|
-
|
|
497
|
-
// Register / unregister on mount / unmount
|
|
498
|
-
const registeredRef = useRef(false);
|
|
499
|
-
if (!registeredRef.current) {
|
|
500
|
-
ctx.registerField(name, metaRef.current);
|
|
501
|
-
registeredRef.current = true;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Keep meta in sync
|
|
505
|
-
useMemo(() => {
|
|
506
|
-
ctx.registerField(name, metaRef.current);
|
|
507
|
-
}, [rules, name, ctx]);
|
|
508
|
-
|
|
509
|
-
// Cleanup on unmount — using a ref-based pattern since we cannot use useEffect
|
|
510
|
-
// with a stable callback for cleanup in strict mode without extra complexity.
|
|
511
|
-
// The form handles missing fields gracefully.
|
|
512
|
-
|
|
513
|
-
const value = state.values[name];
|
|
514
|
-
const error = state.touched[name] ? (state.errors[name] ?? null) : null;
|
|
515
|
-
const safeError = error ? sanitizeString(error) : null;
|
|
516
|
-
const safeLabel = label ? sanitizeString(label) : undefined;
|
|
517
|
-
const safeHelp = help ? sanitizeString(help) : undefined;
|
|
518
|
-
|
|
519
|
-
// ─── onChange handler: supports both event objects and direct values ───
|
|
520
|
-
const handleChange = useCallback(
|
|
521
|
-
(eventOrValue: any) => {
|
|
522
|
-
let nextValue: any;
|
|
523
|
-
if (
|
|
524
|
-
eventOrValue !== null &&
|
|
525
|
-
typeof eventOrValue === 'object' &&
|
|
526
|
-
'target' in eventOrValue
|
|
527
|
-
) {
|
|
528
|
-
const target = eventOrValue.target;
|
|
529
|
-
nextValue = target.type === 'checkbox' ? target.checked : target.value;
|
|
530
|
-
} else {
|
|
531
|
-
nextValue = eventOrValue;
|
|
532
|
-
}
|
|
533
|
-
ctx.setFieldValue(name, nextValue);
|
|
534
|
-
},
|
|
535
|
-
[ctx, name],
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
// ─── onBlur: trigger validation on blur ────────────────────────────────
|
|
539
|
-
const handleBlur = useCallback(() => {
|
|
540
|
-
ctx.setFieldTouched(name);
|
|
541
|
-
ctx.validateField(name);
|
|
542
|
-
}, [ctx, name]);
|
|
543
|
-
|
|
544
|
-
// ─── Clone child input with injected props ────────────────────────────
|
|
545
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
546
|
-
const typedChild = children as ReactElement<any>;
|
|
547
|
-
const childElement = cloneElement(typedChild, {
|
|
548
|
-
value: value ?? '',
|
|
549
|
-
onChange: handleChange,
|
|
550
|
-
onBlur: handleBlur,
|
|
551
|
-
error: safeError ?? undefined,
|
|
552
|
-
isInvalid: !!safeError,
|
|
553
|
-
isRequired,
|
|
554
|
-
disabled: disabled || typedChild.props.disabled,
|
|
555
|
-
name,
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
// ─── Layout-dependent rendering ───────────────────────────────────────
|
|
559
|
-
const isHorizontal = layout === 'horizontal';
|
|
560
|
-
const isInline = layout === 'inline';
|
|
561
|
-
|
|
562
|
-
// Error icon SVG (matches TkxInput pattern)
|
|
563
|
-
const errorIcon = (
|
|
564
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
565
|
-
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
|
|
566
|
-
</svg>
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
// Build the label element
|
|
570
|
-
const labelElement = safeLabel ? (
|
|
571
|
-
<label
|
|
572
|
-
className={tkx(
|
|
573
|
-
'text-sm font-medium font-sans',
|
|
574
|
-
isHorizontal ? 'min-w-[140px] pt-2.5' : '',
|
|
575
|
-
)}
|
|
576
|
-
style={{ color: theme.text }}
|
|
577
|
-
>
|
|
578
|
-
{safeLabel}
|
|
579
|
-
{isRequired && (
|
|
580
|
-
<span aria-hidden="true" className={tkx('ml-1')} style={{ color: theme.danger }}>
|
|
581
|
-
*
|
|
582
|
-
</span>
|
|
583
|
-
)}
|
|
584
|
-
</label>
|
|
585
|
-
) : null;
|
|
586
|
-
|
|
587
|
-
// Build the help / error messaging
|
|
588
|
-
const messageElement = (
|
|
589
|
-
<>
|
|
590
|
-
{safeHelp && !safeError && (
|
|
591
|
-
<span className={tkx('text-xs mt-0.5')} style={{ color: theme.textMuted }}>
|
|
592
|
-
{safeHelp}
|
|
593
|
-
</span>
|
|
594
|
-
)}
|
|
595
|
-
{safeError && (
|
|
596
|
-
<span
|
|
597
|
-
role="alert"
|
|
598
|
-
className={tkx('text-xs flex items-center gap-1 mt-0.5')}
|
|
599
|
-
style={{
|
|
600
|
-
color: theme.danger,
|
|
601
|
-
animation: 'tkxFormErrorReveal 200ms ease-out',
|
|
602
|
-
}}
|
|
603
|
-
>
|
|
604
|
-
{errorIcon}
|
|
605
|
-
{safeError}
|
|
606
|
-
</span>
|
|
607
|
-
)}
|
|
608
|
-
</>
|
|
609
|
-
);
|
|
610
|
-
|
|
611
|
-
// Inline layout: label above within a compact wrapper
|
|
612
|
-
if (isInline) {
|
|
613
|
-
return (
|
|
614
|
-
<div
|
|
615
|
-
className={cx(tkx('flex flex-col gap-1'), className)}
|
|
616
|
-
style={style}
|
|
617
|
-
>
|
|
618
|
-
{labelElement}
|
|
619
|
-
{childElement}
|
|
620
|
-
{messageElement}
|
|
621
|
-
</div>
|
|
622
|
-
);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// Horizontal layout: label left, input right
|
|
626
|
-
if (isHorizontal) {
|
|
627
|
-
return (
|
|
628
|
-
<div
|
|
629
|
-
className={cx(tkx('flex flex-row gap-4 items-start'), className)}
|
|
630
|
-
style={style}
|
|
631
|
-
>
|
|
632
|
-
{labelElement}
|
|
633
|
-
<div className={tkx('flex flex-col gap-1 flex-1 min-w-0')}>
|
|
634
|
-
{childElement}
|
|
635
|
-
{messageElement}
|
|
636
|
-
</div>
|
|
637
|
-
</div>
|
|
638
|
-
);
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// Vertical layout (default): label above input
|
|
642
|
-
return (
|
|
643
|
-
<div
|
|
644
|
-
className={cx(tkx('flex flex-col gap-1'), className)}
|
|
645
|
-
style={style}
|
|
646
|
-
>
|
|
647
|
-
{labelElement}
|
|
648
|
-
{childElement}
|
|
649
|
-
{messageElement}
|
|
650
|
-
</div>
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
TkxFormField.displayName = 'TkxFormField';
|
|
655
|
-
|
|
656
|
-
// ── Error Reveal Keyframe Injection ─────────────────────────────────────────
|
|
657
|
-
|
|
658
|
-
let stylesInjected = false;
|
|
659
|
-
|
|
660
|
-
function injectFormStyles(): void {
|
|
661
|
-
if (stylesInjected) return;
|
|
662
|
-
if (typeof document === 'undefined') return;
|
|
663
|
-
stylesInjected = true;
|
|
664
|
-
|
|
665
|
-
const style = document.createElement('style');
|
|
666
|
-
style.setAttribute('data-tkx-form', '');
|
|
667
|
-
style.textContent = `
|
|
668
|
-
@keyframes tkxFormErrorReveal {
|
|
669
|
-
from {
|
|
670
|
-
opacity: 0;
|
|
671
|
-
transform: translateY(-4px);
|
|
672
|
-
}
|
|
673
|
-
to {
|
|
674
|
-
opacity: 1;
|
|
675
|
-
transform: translateY(0);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
`;
|
|
679
|
-
document.head.appendChild(style);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// Auto-inject styles on module load (client only)
|
|
683
|
-
injectFormStyles();
|