@gntik-ai/blocks 0.0.0-stage → 0.5.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/index.d.ts +3494 -0
- package/dist/index.js +14911 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -3
- package/src/ai/AssistantCardGrid/AssistantCardGrid.test.tsx +32 -0
- package/src/ai/AssistantCardGrid/AssistantCardGrid.tsx +98 -0
- package/src/ai/AssistantCardGrid/block.meta.ts +10 -0
- package/src/ai/AssistantCardGrid/fixtures.ts +72 -0
- package/src/ai/EvaluationScorecard/EvaluationScorecard.test.tsx +39 -0
- package/src/ai/EvaluationScorecard/EvaluationScorecard.tsx +233 -0
- package/src/ai/EvaluationScorecard/block.meta.ts +10 -0
- package/src/ai/EvaluationScorecard/fixtures.ts +79 -0
- package/src/ai/ModelKeysList/ModelKeysList.test.tsx +44 -0
- package/src/ai/ModelKeysList/ModelKeysList.tsx +203 -0
- package/src/ai/ModelKeysList/block.meta.ts +10 -0
- package/src/ai/ModelKeysList/fixtures.ts +44 -0
- package/src/ai/PromptEditor/PromptEditor.test.tsx +97 -0
- package/src/ai/PromptEditor/PromptEditor.tsx +202 -0
- package/src/ai/PromptEditor/block.meta.ts +10 -0
- package/src/ai/PromptEditor/examples/controlled-variables.tsx +23 -0
- package/src/ai/PromptEditor/fixtures.ts +12 -0
- package/src/ai/TokenCostCard/TokenCostCard.test.tsx +36 -0
- package/src/ai/TokenCostCard/TokenCostCard.tsx +130 -0
- package/src/ai/TokenCostCard/block.meta.ts +9 -0
- package/src/ai/TokenCostCard/fixtures.ts +18 -0
- package/src/ai/TraceWaterfall/TraceWaterfall.test.tsx +119 -0
- package/src/ai/TraceWaterfall/TraceWaterfall.tsx +317 -0
- package/src/ai/TraceWaterfall/block.meta.ts +10 -0
- package/src/ai/TraceWaterfall/examples/no-details.tsx +6 -0
- package/src/ai/TraceWaterfall/examples/running.tsx +16 -0
- package/src/ai/TraceWaterfall/fixtures.ts +133 -0
- package/src/ai/TraceWaterfall/span-details.tsx +51 -0
- package/src/ai/index.ts +13 -0
- package/src/auth/ForgotPasswordForm/ForgotPasswordForm.test.tsx +209 -0
- package/src/auth/ForgotPasswordForm/ForgotPasswordForm.tsx +211 -0
- package/src/auth/ForgotPasswordForm/block.meta.ts +10 -0
- package/src/auth/ForgotPasswordForm/examples/UsernameOrEmail.tsx +24 -0
- package/src/auth/MfaChallenge/MfaChallenge.test.tsx +35 -0
- package/src/auth/MfaChallenge/MfaChallenge.tsx +170 -0
- package/src/auth/MfaChallenge/block.meta.ts +9 -0
- package/src/auth/ResetPasswordForm/ResetPasswordForm.test.tsx +27 -0
- package/src/auth/ResetPasswordForm/ResetPasswordForm.tsx +100 -0
- package/src/auth/ResetPasswordForm/block.meta.ts +10 -0
- package/src/auth/SignInForm/SignInForm.test.tsx +222 -0
- package/src/auth/SignInForm/SignInForm.tsx +226 -0
- package/src/auth/SignInForm/block.meta.ts +22 -0
- package/src/auth/SignInForm/examples/Username.tsx +26 -0
- package/src/auth/SignUpForm/SignUpForm.test.tsx +33 -0
- package/src/auth/SignUpForm/SignUpForm.tsx +154 -0
- package/src/auth/SignUpForm/block.meta.ts +9 -0
- package/src/auth/SsoButtons/SsoButtons.test.tsx +23 -0
- package/src/auth/SsoButtons/SsoButtons.tsx +52 -0
- package/src/auth/SsoButtons/block.meta.ts +9 -0
- package/src/auth/SsoButtons/fixtures.ts +15 -0
- package/src/auth/index.ts +15 -0
- package/src/auth/shared.tsx +113 -0
- package/src/billing/CostBreakdown/CostBreakdown.test.tsx +24 -0
- package/src/billing/CostBreakdown/CostBreakdown.tsx +95 -0
- package/src/billing/CostBreakdown/block.meta.ts +10 -0
- package/src/billing/CostBreakdown/fixtures.ts +9 -0
- package/src/billing/InvoiceTable/InvoiceTable.test.tsx +29 -0
- package/src/billing/InvoiceTable/InvoiceTable.tsx +130 -0
- package/src/billing/InvoiceTable/block.meta.ts +10 -0
- package/src/billing/InvoiceTable/fixtures.ts +38 -0
- package/src/billing/PaymentMethodCard/PaymentMethodCard.test.tsx +28 -0
- package/src/billing/PaymentMethodCard/PaymentMethodCard.tsx +103 -0
- package/src/billing/PaymentMethodCard/block.meta.ts +10 -0
- package/src/billing/PaymentMethodCard/fixtures.ts +12 -0
- package/src/billing/PlanCard/PlanCard.baseline.test.tsx +26 -0
- package/src/billing/PlanCard/PlanCard.summary.test.tsx +227 -0
- package/src/billing/PlanCard/PlanCard.test.tsx +27 -0
- package/src/billing/PlanCard/PlanCard.tsx +140 -0
- package/src/billing/PlanCard/PlanSummary.tsx +101 -0
- package/src/billing/PlanCard/README.md +23 -0
- package/src/billing/PlanCard/__snapshots__/PlanCard.baseline.test.tsx.snap +5 -0
- package/src/billing/PlanCard/block.meta.ts +60 -0
- package/src/billing/PlanCard/examples/SummaryActionsOnly.tsx +12 -0
- package/src/billing/PlanCard/examples/SummaryFeaturesOnly.tsx +14 -0
- package/src/billing/PlanCard/examples/SummaryFull.tsx +28 -0
- package/src/billing/PlanCard/examples/SummaryMinimal.tsx +5 -0
- package/src/billing/PlanCard/examples/SummaryPriceOnly.tsx +15 -0
- package/src/billing/PlanCard/examples/SummaryRenewalOnly.tsx +14 -0
- package/src/billing/PlanCard/examples/SummaryUnassigned.tsx +5 -0
- package/src/billing/PlanCard/examples/SummaryUnassignedNoLabel.tsx +5 -0
- package/src/billing/PlanCard/examples/SummaryZeroPrice.tsx +15 -0
- package/src/billing/PlanCard/fixtures.ts +19 -0
- package/src/billing/PlanDefinitionCard/PlanDefinitionCard.test.tsx +111 -0
- package/src/billing/PlanDefinitionCard/PlanDefinitionCard.tsx +86 -0
- package/src/billing/PlanDefinitionCard/README.md +29 -0
- package/src/billing/PlanDefinitionCard/block.meta.ts +46 -0
- package/src/billing/PlanDefinitionCard/examples/Compact.tsx +23 -0
- package/src/billing/PlanDefinitionCard/examples/Full.tsx +23 -0
- package/src/billing/PlanDefinitionCard/fixtures.ts +12 -0
- package/src/billing/QuotaMeters/QuotaMeters.test.tsx +23 -0
- package/src/billing/QuotaMeters/QuotaMeters.tsx +92 -0
- package/src/billing/QuotaMeters/block.meta.ts +10 -0
- package/src/billing/QuotaMeters/fixtures.ts +9 -0
- package/src/billing/SpendVsBudget/SpendVsBudget.test.tsx +24 -0
- package/src/billing/SpendVsBudget/SpendVsBudget.tsx +105 -0
- package/src/billing/SpendVsBudget/block.meta.ts +10 -0
- package/src/billing/SpendVsBudget/fixtures.ts +18 -0
- package/src/billing/format.ts +42 -0
- package/src/billing/index.ts +21 -0
- package/src/builders/CodePanel/CodePanel.test.tsx +280 -0
- package/src/builders/CodePanel/CodePanel.tsx +239 -0
- package/src/builders/CodePanel/block.meta.ts +10 -0
- package/src/builders/CodePanel/examples/controlled.tsx +15 -0
- package/src/builders/CodePanel/examples/diagnostics.tsx +40 -0
- package/src/builders/CodePanel/fixtures.ts +95 -0
- package/src/builders/CodePanel/marker-store.ts +90 -0
- package/src/builders/CodePanel/markers.ts +62 -0
- package/src/builders/FlowBuilder/FlowBuilder.test.tsx +480 -0
- package/src/builders/FlowBuilder/FlowBuilder.tsx +343 -0
- package/src/builders/FlowBuilder/block.meta.ts +20 -0
- package/src/builders/FlowBuilder/connections.ts +67 -0
- package/src/builders/FlowBuilder/decorated-nodes.ts +80 -0
- package/src/builders/FlowBuilder/examples/extensions.tsx +120 -0
- package/src/builders/FlowBuilder/extensions.ts +37 -0
- package/src/builders/FlowBuilder/fixtures.ts +80 -0
- package/src/builders/FlowBuilder/parts.tsx +196 -0
- package/src/builders/FlowBuilder/problems.tsx +40 -0
- package/src/builders/LogViewer/LogViewer.test.tsx +97 -0
- package/src/builders/LogViewer/LogViewer.tsx +253 -0
- package/src/builders/LogViewer/block.meta.ts +10 -0
- package/src/builders/LogViewer/fixtures.ts +49 -0
- package/src/builders/index.ts +15 -0
- package/src/data-display/ActivityFeed/ActivityFeed.test.tsx +62 -0
- package/src/data-display/ActivityFeed/ActivityFeed.tsx +118 -0
- package/src/data-display/ActivityFeed/block.meta.ts +10 -0
- package/src/data-display/ActivityFeed/fixtures.ts +29 -0
- package/src/data-display/ChartCard/ChartCard.ranges.test.tsx +282 -0
- package/src/data-display/ChartCard/ChartCard.test.tsx +54 -0
- package/src/data-display/ChartCard/ChartCard.tsx +218 -0
- package/src/data-display/ChartCard/block.meta.ts +10 -0
- package/src/data-display/ChartCard/examples/custom-ranges.tsx +37 -0
- package/src/data-display/ChartCard/examples/heatmap.tsx +20 -0
- package/src/data-display/ChartCard/examples/toolbar-owned-range.tsx +34 -0
- package/src/data-display/ChartCard/fixtures.ts +82 -0
- package/src/data-display/CommentThread/CommentThread.test.tsx +29 -0
- package/src/data-display/CommentThread/CommentThread.tsx +120 -0
- package/src/data-display/CommentThread/block.meta.ts +10 -0
- package/src/data-display/CommentThread/fixtures.ts +28 -0
- package/src/data-display/DescriptionListCard/DescriptionListCard.test.tsx +28 -0
- package/src/data-display/DescriptionListCard/DescriptionListCard.tsx +120 -0
- package/src/data-display/DescriptionListCard/block.meta.ts +9 -0
- package/src/data-display/DescriptionListCard/fixtures.ts +12 -0
- package/src/data-display/KpiRow/KpiRow.test.tsx +29 -0
- package/src/data-display/KpiRow/KpiRow.tsx +101 -0
- package/src/data-display/KpiRow/block.meta.ts +10 -0
- package/src/data-display/KpiRow/fixtures.ts +34 -0
- package/src/data-display/ResourceCardGrid/ResourceCardGrid.test.tsx +22 -0
- package/src/data-display/ResourceCardGrid/ResourceCardGrid.tsx +98 -0
- package/src/data-display/ResourceCardGrid/block.meta.ts +10 -0
- package/src/data-display/ResourceCardGrid/fixtures.ts +72 -0
- package/src/data-display/StatCard/StatCard.test.tsx +30 -0
- package/src/data-display/StatCard/StatCard.tsx +138 -0
- package/src/data-display/StatCard/block.meta.ts +10 -0
- package/src/data-display/StatCard/fixtures.ts +26 -0
- package/src/data-display/StatusTimeline/StatusTimeline.test.tsx +153 -0
- package/src/data-display/StatusTimeline/StatusTimeline.tsx +114 -0
- package/src/data-display/StatusTimeline/block.meta.ts +10 -0
- package/src/data-display/StatusTimeline/examples/Empty.tsx +6 -0
- package/src/data-display/StatusTimeline/fixtures.ts +62 -0
- package/src/data-display/index.ts +34 -0
- package/src/data-display/shared/TrendDelta.tsx +60 -0
- package/src/feedback/ConfirmDestructive/ConfirmDestructive.test.tsx +47 -0
- package/src/feedback/ConfirmDestructive/ConfirmDestructive.tsx +145 -0
- package/src/feedback/ConfirmDestructive/block.meta.ts +10 -0
- package/src/feedback/EmptyStates/EmptyStates.test.tsx +48 -0
- package/src/feedback/EmptyStates/EmptyStates.tsx +191 -0
- package/src/feedback/EmptyStates/block.meta.ts +10 -0
- package/src/feedback/EmptyStates/fixtures.ts +6 -0
- package/src/feedback/ErrorPanel/ErrorPanel.test.tsx +37 -0
- package/src/feedback/ErrorPanel/ErrorPanel.tsx +146 -0
- package/src/feedback/ErrorPanel/block.meta.ts +10 -0
- package/src/feedback/InlineCallout/InlineCallout.test.tsx +33 -0
- package/src/feedback/InlineCallout/InlineCallout.tsx +81 -0
- package/src/feedback/InlineCallout/block.meta.ts +9 -0
- package/src/feedback/Skeletons/Skeletons.test.tsx +25 -0
- package/src/feedback/Skeletons/Skeletons.tsx +142 -0
- package/src/feedback/Skeletons/block.meta.ts +10 -0
- package/src/feedback/SystemBanner/SystemBanner.test.tsx +29 -0
- package/src/feedback/SystemBanner/SystemBanner.tsx +105 -0
- package/src/feedback/SystemBanner/block.meta.ts +9 -0
- package/src/feedback/index.ts +14 -0
- package/src/feedback/types.ts +13 -0
- package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.test.tsx +36 -0
- package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.tsx +197 -0
- package/src/forms/CreateApiKeyDialog/block.meta.ts +10 -0
- package/src/forms/CreateApiKeyDialog/fixtures.ts +31 -0
- package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.test.tsx +339 -0
- package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.tsx +193 -0
- package/src/forms/CredentialDisclosureDialog/block.meta.ts +10 -0
- package/src/forms/CredentialDisclosureDialog/examples/Fresh.tsx +32 -0
- package/src/forms/CredentialDisclosureDialog/examples/ResponseGating.tsx +69 -0
- package/src/forms/CredentialDisclosureDialog/examples/Stored.tsx +32 -0
- package/src/forms/CredentialDisclosureDialog/fixtures.ts +14 -0
- package/src/forms/DangerZone/ConfirmDestructiveDialog.tsx +101 -0
- package/src/forms/DangerZone/DangerZone.test.tsx +28 -0
- package/src/forms/DangerZone/DangerZone.tsx +86 -0
- package/src/forms/DangerZone/block.meta.ts +10 -0
- package/src/forms/DangerZone/fixtures.ts +36 -0
- package/src/forms/DateRangeFilter/DateRangeFilter.test.tsx +26 -0
- package/src/forms/DateRangeFilter/DateRangeFilter.tsx +83 -0
- package/src/forms/DateRangeFilter/block.meta.ts +9 -0
- package/src/forms/DialogWizard/DialogWizard.test.tsx +95 -0
- package/src/forms/DialogWizard/DialogWizard.tsx +39 -0
- package/src/forms/DialogWizard/block.meta.ts +10 -0
- package/src/forms/DialogWizard/examples/CreateResource.tsx +65 -0
- package/src/forms/DialogWizard/fixtures.ts +20 -0
- package/src/forms/FileUploadPanel/FileUploadPanel.test.tsx +43 -0
- package/src/forms/FileUploadPanel/FileUploadPanel.tsx +244 -0
- package/src/forms/FileUploadPanel/block.meta.ts +10 -0
- package/src/forms/FileUploadPanel/fixtures.ts +66 -0
- package/src/forms/FormSection/FormSection.test.tsx +34 -0
- package/src/forms/FormSection/FormSection.tsx +77 -0
- package/src/forms/FormSection/block.meta.ts +9 -0
- package/src/forms/InviteMembersDialog/InviteMembersDialog.test.tsx +37 -0
- package/src/forms/InviteMembersDialog/InviteMembersDialog.tsx +183 -0
- package/src/forms/InviteMembersDialog/block.meta.ts +10 -0
- package/src/forms/InviteMembersDialog/fixtures.ts +14 -0
- package/src/forms/SettingsRow/SettingsRow.test.tsx +50 -0
- package/src/forms/SettingsRow/SettingsRow.tsx +74 -0
- package/src/forms/SettingsRow/block.meta.ts +9 -0
- package/src/forms/ValidationSummary/ValidationSummary.test.tsx +46 -0
- package/src/forms/ValidationSummary/ValidationSummary.tsx +75 -0
- package/src/forms/ValidationSummary/block.meta.ts +9 -0
- package/src/forms/ValidationSummary/fixtures.ts +13 -0
- package/src/forms/index.ts +27 -0
- package/src/index.ts +14 -0
- package/src/marketing/ComparisonTable/ComparisonTable.test.tsx +69 -0
- package/src/marketing/ComparisonTable/ComparisonTable.tsx +245 -0
- package/src/marketing/ComparisonTable/block.meta.ts +10 -0
- package/src/marketing/ComparisonTable/examples/options.tsx +48 -0
- package/src/marketing/ComparisonTable/fixtures.ts +83 -0
- package/src/marketing/Faq/Faq.test.tsx +25 -0
- package/src/marketing/Faq/Faq.tsx +68 -0
- package/src/marketing/Faq/block.meta.ts +10 -0
- package/src/marketing/Faq/fixtures.ts +32 -0
- package/src/marketing/FeatureGrid/FeatureGrid.test.tsx +19 -0
- package/src/marketing/FeatureGrid/FeatureGrid.tsx +79 -0
- package/src/marketing/FeatureGrid/block.meta.ts +10 -0
- package/src/marketing/FeatureGrid/fixtures.ts +36 -0
- package/src/marketing/Hero/Hero.test.tsx +33 -0
- package/src/marketing/Hero/Hero.tsx +107 -0
- package/src/marketing/Hero/block.meta.ts +10 -0
- package/src/marketing/PricingTable/PricingTable.test.tsx +92 -0
- package/src/marketing/PricingTable/PricingTable.tsx +323 -0
- package/src/marketing/PricingTable/block.meta.ts +10 -0
- package/src/marketing/PricingTable/fixtures.ts +34 -0
- package/src/marketing/actions.tsx +40 -0
- package/src/marketing/index.ts +15 -0
- package/src/meta.ts +23 -0
- package/src/page-chrome/BulkActionBar/BulkActionBar.test.tsx +34 -0
- package/src/page-chrome/BulkActionBar/BulkActionBar.tsx +76 -0
- package/src/page-chrome/BulkActionBar/block.meta.ts +9 -0
- package/src/page-chrome/PageHeader/PageHeader.test.tsx +68 -0
- package/src/page-chrome/PageHeader/PageHeader.tsx +152 -0
- package/src/page-chrome/PageHeader/block.meta.ts +10 -0
- package/src/page-chrome/PageHeader/fixtures.ts +29 -0
- package/src/page-chrome/PageToolbar/PageToolbar.test.tsx +38 -0
- package/src/page-chrome/PageToolbar/PageToolbar.tsx +138 -0
- package/src/page-chrome/PageToolbar/block.meta.ts +10 -0
- package/src/page-chrome/PageToolbar/fixtures.ts +8 -0
- package/src/page-chrome/SectionHeader/SectionHeader.test.tsx +28 -0
- package/src/page-chrome/SectionHeader/SectionHeader.tsx +73 -0
- package/src/page-chrome/SectionHeader/block.meta.ts +9 -0
- package/src/page-chrome/StickyActionBar/StickyActionBar.test.tsx +29 -0
- package/src/page-chrome/StickyActionBar/StickyActionBar.tsx +66 -0
- package/src/page-chrome/StickyActionBar/block.meta.ts +9 -0
- package/src/page-chrome/index.ts +13 -0
- package/src/page-chrome/types.ts +25 -0
- package/src/scheduling/AgendaList/AgendaList.test.tsx +28 -0
- package/src/scheduling/AgendaList/AgendaList.tsx +165 -0
- package/src/scheduling/AgendaList/block.meta.ts +10 -0
- package/src/scheduling/MonthCalendar/MonthCalendar.test.tsx +32 -0
- package/src/scheduling/MonthCalendar/MonthCalendar.tsx +223 -0
- package/src/scheduling/MonthCalendar/block.meta.ts +10 -0
- package/src/scheduling/TimeSlotPicker/TimeSlotPicker.test.tsx +26 -0
- package/src/scheduling/TimeSlotPicker/TimeSlotPicker.tsx +115 -0
- package/src/scheduling/TimeSlotPicker/block.meta.ts +10 -0
- package/src/scheduling/TimeSlotPicker/fixtures.ts +10 -0
- package/src/scheduling/WeekCalendar/WeekCalendar.test.tsx +27 -0
- package/src/scheduling/WeekCalendar/WeekCalendar.tsx +274 -0
- package/src/scheduling/WeekCalendar/block.meta.ts +10 -0
- package/src/scheduling/index.ts +11 -0
- package/src/scheduling/shared/CalendarHeader.tsx +54 -0
- package/src/scheduling/shared/EventTile.tsx +21 -0
- package/src/scheduling/shared/dates.ts +98 -0
- package/src/scheduling/shared/events.ts +90 -0
- package/src/scheduling/shared/fixtures.ts +54 -0
- package/src/shell/AppSidebar/AppSidebar.test.tsx +38 -0
- package/src/shell/AppSidebar/AppSidebar.tsx +226 -0
- package/src/shell/AppSidebar/block.meta.ts +10 -0
- package/src/shell/AppSidebar/fixtures.ts +58 -0
- package/src/shell/AppTopbar/AppTopbar.test.tsx +41 -0
- package/src/shell/AppTopbar/AppTopbar.tsx +82 -0
- package/src/shell/AppTopbar/block.meta.ts +19 -0
- package/src/shell/AppTopbar/fixtures.ts +22 -0
- package/src/shell/EnvironmentBadge/EnvironmentBadge.test.tsx +22 -0
- package/src/shell/EnvironmentBadge/EnvironmentBadge.tsx +55 -0
- package/src/shell/EnvironmentBadge/block.meta.ts +10 -0
- package/src/shell/GlobalSearch/GlobalSearch.test.tsx +28 -0
- package/src/shell/GlobalSearch/GlobalSearch.tsx +80 -0
- package/src/shell/GlobalSearch/block.meta.ts +10 -0
- package/src/shell/GlobalSearch/fixtures.ts +64 -0
- package/src/shell/index.ts +11 -0
- package/src/styles.css +2 -0
- package/src/tables/DataTable/DataTable.edit-views.test.tsx +249 -0
- package/src/tables/DataTable/DataTable.layout.test.tsx +188 -0
- package/src/tables/DataTable/DataTable.single-select.test.tsx +501 -0
- package/src/tables/DataTable/DataTable.test.tsx +268 -0
- package/src/tables/DataTable/DataTable.tsx +520 -0
- package/src/tables/DataTable/block.meta.ts +27 -0
- package/src/tables/DataTable/column-menu.tsx +109 -0
- package/src/tables/DataTable/column-sizing.ts +166 -0
- package/src/tables/DataTable/data-table-body.tsx +194 -0
- package/src/tables/DataTable/data-table-props.ts +156 -0
- package/src/tables/DataTable/data-table-row.tsx +220 -0
- package/src/tables/DataTable/data-table-utils.ts +195 -0
- package/src/tables/DataTable/density-toggle.tsx +30 -0
- package/src/tables/DataTable/editable-cell.tsx +274 -0
- package/src/tables/DataTable/examples/editable.tsx +71 -0
- package/src/tables/DataTable/examples/grouped.tsx +18 -0
- package/src/tables/DataTable/examples/resizable-pinned.tsx +20 -0
- package/src/tables/DataTable/examples/saved-views.tsx +61 -0
- package/src/tables/DataTable/examples/single-selection.tsx +55 -0
- package/src/tables/DataTable/examples/virtualized.tsx +20 -0
- package/src/tables/DataTable/fixtures.ts +119 -0
- package/src/tables/DataTable/header-cell.tsx +86 -0
- package/src/tables/DataTable/resize-handle.tsx +88 -0
- package/src/tables/DataTable/single-selection.tsx +149 -0
- package/src/tables/DataTable/use-table-views.ts +79 -0
- package/src/tables/DataTable/use-virtual-rows.ts +83 -0
- package/src/tables/DataTable/views.tsx +186 -0
- package/src/tables/FilterBar/FilterBar.test.tsx +43 -0
- package/src/tables/FilterBar/FilterBar.tsx +199 -0
- package/src/tables/FilterBar/block.meta.ts +10 -0
- package/src/tables/FilterBar/fixtures.ts +32 -0
- package/src/tables/InlineEditRow/InlineEditRow.test.tsx +41 -0
- package/src/tables/InlineEditRow/InlineEditRow.tsx +215 -0
- package/src/tables/InlineEditRow/block.meta.ts +10 -0
- package/src/tables/InlineEditRow/fixtures.ts +28 -0
- package/src/tables/PaginationFooter/PaginationFooter.test.tsx +29 -0
- package/src/tables/PaginationFooter/PaginationFooter.tsx +94 -0
- package/src/tables/PaginationFooter/block.meta.ts +10 -0
- package/src/tables/SubordinateList/SubordinateList.test.tsx +351 -0
- package/src/tables/SubordinateList/SubordinateList.tsx +140 -0
- package/src/tables/SubordinateList/block.meta.ts +10 -0
- package/src/tables/SubordinateList/examples/ResourceForm.tsx +129 -0
- package/src/tables/SubordinateList/examples/empty.tsx +5 -0
- package/src/tables/SubordinateList/examples/error.tsx +5 -0
- package/src/tables/SubordinateList/examples/loading.tsx +5 -0
- package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.test.tsx +159 -0
- package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.tsx +60 -0
- package/src/tables/TruncatedCollectionFooter/block.meta.ts +10 -0
- package/src/tables/TruncatedCollectionFooter/examples/exact.tsx +6 -0
- package/src/tables/TruncatedCollectionFooter/examples/has-more.tsx +12 -0
- package/src/tables/TruncatedCollectionFooter/fixtures.ts +2 -0
- package/src/tables/index.ts +45 -0
- package/src/team/MembersTable/MembersTable.test.tsx +29 -0
- package/src/team/MembersTable/MembersTable.tsx +141 -0
- package/src/team/MembersTable/block.meta.ts +9 -0
- package/src/team/MembersTable/fixtures.ts +19 -0
- package/src/team/NotificationMatrix/NotificationMatrix.test.tsx +36 -0
- package/src/team/NotificationMatrix/NotificationMatrix.tsx +144 -0
- package/src/team/NotificationMatrix/block.meta.ts +9 -0
- package/src/team/NotificationMatrix/fixtures.ts +43 -0
- package/src/team/PendingInvitations/PendingInvitations.test.tsx +30 -0
- package/src/team/PendingInvitations/PendingInvitations.tsx +132 -0
- package/src/team/PendingInvitations/block.meta.ts +9 -0
- package/src/team/PendingInvitations/fixtures.ts +39 -0
- package/src/team/ProfileHeader/ProfileHeader.test.tsx +29 -0
- package/src/team/ProfileHeader/ProfileHeader.tsx +106 -0
- package/src/team/ProfileHeader/block.meta.ts +10 -0
- package/src/team/ProfileHeader/fixtures.ts +28 -0
- package/src/team/SessionsDevices/SessionsDevices.test.tsx +30 -0
- package/src/team/SessionsDevices/SessionsDevices.tsx +126 -0
- package/src/team/SessionsDevices/block.meta.ts +10 -0
- package/src/team/SessionsDevices/fixtures.ts +58 -0
- package/src/team/index.ts +15 -0
- package/src/test/a11y.ts +18 -0
- package/src/test/heading-levels.test.tsx +69 -0
- package/src/test/setup.ts +36 -0
- package/README.md +0 -3
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { DataTableColumn } from './data-table-utils';
|
|
3
|
+
|
|
4
|
+
/** Column widths in px by column id. */
|
|
5
|
+
export type ColumnWidths = Readonly<Record<string, number>>;
|
|
6
|
+
|
|
7
|
+
/** Columns pinned to the start (left in LTR) and end edges, in display order. */
|
|
8
|
+
export interface PinnedColumns {
|
|
9
|
+
left?: readonly string[];
|
|
10
|
+
right?: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type PinSide = 'left' | 'right';
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_COLUMN_WIDTH = 160;
|
|
16
|
+
export const MIN_COLUMN_WIDTH = 60;
|
|
17
|
+
export const MAX_COLUMN_WIDTH = 640;
|
|
18
|
+
/** Width of the selection (checkbox) and row-actions columns: `w-10` and `w-12`. */
|
|
19
|
+
export const LEAD_WIDTH = 40;
|
|
20
|
+
export const TRAIL_WIDTH = 48;
|
|
21
|
+
|
|
22
|
+
/** Width limits of a column. */
|
|
23
|
+
export function widthBounds<T>(column: DataTableColumn<T>): { min: number; max: number } {
|
|
24
|
+
const min = column.minWidth ?? MIN_COLUMN_WIDTH;
|
|
25
|
+
return { min, max: Math.max(min, column.maxWidth ?? MAX_COLUMN_WIDTH) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Clamps a width to the column's limits (rounded to whole px). */
|
|
29
|
+
export function clampColumnWidth<T>(column: DataTableColumn<T>, width: number): number {
|
|
30
|
+
const { min, max } = widthBounds(column);
|
|
31
|
+
return Math.round(Math.min(max, Math.max(min, width)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Effective width of a column: the widths map, then `column.width`, then the default. */
|
|
35
|
+
export function columnWidthOf<T>(column: DataTableColumn<T>, widths: ColumnWidths): number {
|
|
36
|
+
return clampColumnWidth(column, widths[column.id] ?? column.width ?? DEFAULT_COLUMN_WIDTH);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function pinSideOf(id: string, pinned: PinnedColumns): PinSide | null {
|
|
40
|
+
if (pinned.left?.includes(id)) return 'left';
|
|
41
|
+
if (pinned.right?.includes(id)) return 'right';
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Pins a column to a side (appended to that side) or unpins it (`null`). */
|
|
46
|
+
export function setColumnPin(pinned: PinnedColumns, id: string, side: PinSide | null): PinnedColumns {
|
|
47
|
+
const left = (pinned.left ?? []).filter((c) => c !== id);
|
|
48
|
+
const right = (pinned.right ?? []).filter((c) => c !== id);
|
|
49
|
+
if (side === 'left') left.push(id);
|
|
50
|
+
if (side === 'right') right.push(id);
|
|
51
|
+
return { left, right };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Display order: start-pinned columns (pin order), unpinned columns (column order), end-pinned columns. */
|
|
55
|
+
export function orderColumns<T>(
|
|
56
|
+
columns: ReadonlyArray<DataTableColumn<T>>,
|
|
57
|
+
pinned: PinnedColumns,
|
|
58
|
+
): Array<DataTableColumn<T>> {
|
|
59
|
+
const byId = new Map(columns.map((c) => [c.id, c]));
|
|
60
|
+
const pick = (ids: readonly string[] = []) =>
|
|
61
|
+
ids.map((id) => byId.get(id)).filter((c): c is DataTableColumn<T> => c != null);
|
|
62
|
+
const left = pick(pinned.left);
|
|
63
|
+
const right = pick(pinned.right);
|
|
64
|
+
const taken = new Set([...left, ...right].map((c) => c.id));
|
|
65
|
+
return [...left, ...columns.filter((c) => !taken.has(c.id)), ...right];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Layout of one column cell: inline style (width, sticky offset) and classes. */
|
|
69
|
+
export interface CellLayout {
|
|
70
|
+
style?: CSSProperties;
|
|
71
|
+
className?: string;
|
|
72
|
+
pinned?: PinSide;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface TableLayout {
|
|
76
|
+
/** Layout per column id. */
|
|
77
|
+
columns: ReadonlyMap<string, CellLayout>;
|
|
78
|
+
/** Selection column (sticky at the start when any column is start-pinned). */
|
|
79
|
+
lead: CellLayout;
|
|
80
|
+
/** Row-actions column (sticky at the end when any column is end-pinned). */
|
|
81
|
+
trail: CellLayout;
|
|
82
|
+
/** Table classes and style (fixed layout and total width when columns are sized). */
|
|
83
|
+
tableClassName?: string;
|
|
84
|
+
tableStyle?: CSSProperties;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Sticky cells need an opaque surface; the row tint (hover, selected) is layered under the content.
|
|
88
|
+
const STICKY = 'sticky z-[1] bg-card';
|
|
89
|
+
const START_EDGE = 'border-e border-border';
|
|
90
|
+
const END_EDGE = 'border-s border-border';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Computes widths and sticky offsets. With `sized` off and nothing pinned the table keeps its
|
|
94
|
+
* automatic layout (the original behaviour); otherwise it switches to a fixed layout whose width
|
|
95
|
+
* is the sum of the column widths (at least the container's).
|
|
96
|
+
*/
|
|
97
|
+
export function tableLayout<T>({
|
|
98
|
+
columns,
|
|
99
|
+
widths,
|
|
100
|
+
pinned,
|
|
101
|
+
sized,
|
|
102
|
+
lead,
|
|
103
|
+
trail,
|
|
104
|
+
}: {
|
|
105
|
+
columns: ReadonlyArray<DataTableColumn<T>>;
|
|
106
|
+
widths: ColumnWidths;
|
|
107
|
+
pinned: PinnedColumns;
|
|
108
|
+
sized: boolean;
|
|
109
|
+
lead: boolean;
|
|
110
|
+
trail: boolean;
|
|
111
|
+
}): TableLayout {
|
|
112
|
+
const map = new Map<string, CellLayout>();
|
|
113
|
+
const hasStart = columns.some((c) => pinSideOf(c.id, pinned) === 'left');
|
|
114
|
+
const hasEnd = columns.some((c) => pinSideOf(c.id, pinned) === 'right');
|
|
115
|
+
const active = sized || hasStart || hasEnd;
|
|
116
|
+
if (!active) {
|
|
117
|
+
for (const c of columns) map.set(c.id, {});
|
|
118
|
+
return { columns: map, lead: {}, trail: {} };
|
|
119
|
+
}
|
|
120
|
+
const widthOf = (c: DataTableColumn<T>) => columnWidthOf(c, widths);
|
|
121
|
+
const fixed = (w: number): CSSProperties => ({ width: w, minWidth: w, maxWidth: w });
|
|
122
|
+
|
|
123
|
+
let start = lead ? LEAD_WIDTH : 0;
|
|
124
|
+
const startIds = columns.filter((c) => pinSideOf(c.id, pinned) === 'left');
|
|
125
|
+
startIds.forEach((c, i) => {
|
|
126
|
+
const w = widthOf(c);
|
|
127
|
+
map.set(c.id, {
|
|
128
|
+
pinned: 'left',
|
|
129
|
+
style: { ...fixed(w), insetInlineStart: start },
|
|
130
|
+
className: [STICKY, i === startIds.length - 1 && START_EDGE].filter(Boolean).join(' '),
|
|
131
|
+
});
|
|
132
|
+
start += w;
|
|
133
|
+
});
|
|
134
|
+
let end = trail ? TRAIL_WIDTH : 0;
|
|
135
|
+
const endIds = columns.filter((c) => pinSideOf(c.id, pinned) === 'right');
|
|
136
|
+
[...endIds].reverse().forEach((c, i) => {
|
|
137
|
+
const w = widthOf(c);
|
|
138
|
+
map.set(c.id, {
|
|
139
|
+
pinned: 'right',
|
|
140
|
+
style: { ...fixed(w), insetInlineEnd: end },
|
|
141
|
+
className: [STICKY, i === endIds.length - 1 && END_EDGE].filter(Boolean).join(' '),
|
|
142
|
+
});
|
|
143
|
+
end += w;
|
|
144
|
+
});
|
|
145
|
+
for (const c of columns) if (!map.has(c.id)) map.set(c.id, { style: fixed(widthOf(c)) });
|
|
146
|
+
|
|
147
|
+
const total = columns.reduce((sum, c) => sum + widthOf(c), 0) + (lead ? LEAD_WIDTH : 0) + (trail ? TRAIL_WIDTH : 0);
|
|
148
|
+
return {
|
|
149
|
+
columns: map,
|
|
150
|
+
lead: hasStart ? { pinned: 'left', style: { insetInlineStart: 0 }, className: STICKY } : {},
|
|
151
|
+
trail: hasEnd ? { pinned: 'right', style: { insetInlineEnd: 0 }, className: STICKY } : {},
|
|
152
|
+
tableClassName: 'table-fixed',
|
|
153
|
+
tableStyle: { width: total, minWidth: '100%' },
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Under-content tint for sticky cells of a hovered or selected row (and the header surface). */
|
|
158
|
+
export function stickyTint(
|
|
159
|
+
layout: CellLayout | undefined,
|
|
160
|
+
{ selected = false, head = false } = {},
|
|
161
|
+
): string | undefined {
|
|
162
|
+
if (!layout?.pinned) return undefined;
|
|
163
|
+
const base = 'before:pointer-events-none before:absolute before:inset-0 before:-z-10';
|
|
164
|
+
if (head) return `${base} before:bg-secondary/30`;
|
|
165
|
+
return `${base} ${selected ? 'before:bg-primary/8' : '[tr:hover>&]:before:bg-accent/30'}`;
|
|
166
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Inbox } from '@gntik-ai/icons';
|
|
2
|
+
import { cn, EmptyState, Skeleton, TableBody, TableCell, TableRow, useI18n } from '@gntik-ai/ui';
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { columnAlign } from './data-table-utils';
|
|
5
|
+
import { DataTableRow, GroupHeaderRow, type DataTableBodyGroup, type RowContext } from './data-table-row';
|
|
6
|
+
import type { VirtualWindow } from './use-virtual-rows';
|
|
7
|
+
|
|
8
|
+
export type { DataTableBodyGroup } from './data-table-row';
|
|
9
|
+
|
|
10
|
+
export interface DataTableBodyProps<T> extends RowContext<T> {
|
|
11
|
+
rows: readonly T[];
|
|
12
|
+
loading: boolean;
|
|
13
|
+
loadingRows: number;
|
|
14
|
+
emptyState?: ReactNode;
|
|
15
|
+
/** Grouped rendering: one `<tbody>` per group with a header row (replaces `rows`). */
|
|
16
|
+
groups?: ReadonlyArray<DataTableBodyGroup<T>>;
|
|
17
|
+
/** Keys of collapsed groups. */
|
|
18
|
+
collapsedGroups?: ReadonlySet<string>;
|
|
19
|
+
/** Group headers render a collapse button (default true). */
|
|
20
|
+
collapsibleGroups?: boolean;
|
|
21
|
+
onToggleGroup?: (key: string) => void;
|
|
22
|
+
/** Selects or clears every row of a group. */
|
|
23
|
+
onToggleGroupRows?: (ids: readonly string[]) => void;
|
|
24
|
+
/** Renders only the items of this window (virtualized rows), with spacers for the rest. */
|
|
25
|
+
virtual?: VirtualWindow;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One rendered line of the body: a group header or a data row. */
|
|
29
|
+
export type DataTableBodyItem<T> =
|
|
30
|
+
{ kind: 'group'; group: DataTableBodyGroup<T>; open: boolean } | { kind: 'row'; row: T; groupKey?: string };
|
|
31
|
+
|
|
32
|
+
/** Flattens rows (or groups and their open rows) into the lines the body renders. */
|
|
33
|
+
export function flattenBody<T>(
|
|
34
|
+
rows: readonly T[],
|
|
35
|
+
groups: ReadonlyArray<DataTableBodyGroup<T>> | undefined,
|
|
36
|
+
collapsed: ReadonlySet<string> | undefined,
|
|
37
|
+
): Array<DataTableBodyItem<T>> {
|
|
38
|
+
if (!groups) return rows.map((row) => ({ kind: 'row', row }));
|
|
39
|
+
const items: Array<DataTableBodyItem<T>> = [];
|
|
40
|
+
for (const group of groups) {
|
|
41
|
+
const open = !collapsed?.has(group.key);
|
|
42
|
+
items.push({ kind: 'group', group, open });
|
|
43
|
+
if (open) for (const row of group.rows) items.push({ kind: 'row', row, groupKey: group.key });
|
|
44
|
+
}
|
|
45
|
+
return items;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Spacer({ height, span }: { height: number; span: number }) {
|
|
49
|
+
if (height <= 0) return null;
|
|
50
|
+
return (
|
|
51
|
+
<tbody aria-hidden data-virtual-spacer="">
|
|
52
|
+
<tr role="presentation">
|
|
53
|
+
<td colSpan={span} style={{ height, padding: 0, border: 0 }} />
|
|
54
|
+
</tr>
|
|
55
|
+
</tbody>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Body rows, plus the loading (skeleton rows) and empty (EmptyState) states. */
|
|
60
|
+
export function DataTableBody<T>(props: DataTableBodyProps<T>) {
|
|
61
|
+
const {
|
|
62
|
+
rows,
|
|
63
|
+
columns,
|
|
64
|
+
selectable,
|
|
65
|
+
singleSelection,
|
|
66
|
+
selected,
|
|
67
|
+
rowActions,
|
|
68
|
+
loading,
|
|
69
|
+
loadingRows,
|
|
70
|
+
emptyState,
|
|
71
|
+
groups,
|
|
72
|
+
collapsedGroups,
|
|
73
|
+
collapsibleGroups = true,
|
|
74
|
+
onToggleGroup,
|
|
75
|
+
onToggleGroupRows,
|
|
76
|
+
virtual,
|
|
77
|
+
} = props;
|
|
78
|
+
const { t } = useI18n();
|
|
79
|
+
const selectionColumn = selectable || singleSelection != null;
|
|
80
|
+
const span = columns.length + (selectionColumn ? 1 : 0) + (rowActions ? 1 : 0);
|
|
81
|
+
if (loading) {
|
|
82
|
+
return (
|
|
83
|
+
<TableBody>
|
|
84
|
+
{Array.from({ length: loadingRows }, (_, i) => (
|
|
85
|
+
<TableRow key={`loading-${i}`}>
|
|
86
|
+
{selectionColumn && (
|
|
87
|
+
<TableCell className="w-10">
|
|
88
|
+
<Skeleton className="size-4 rounded-[4px]" />
|
|
89
|
+
</TableCell>
|
|
90
|
+
)}
|
|
91
|
+
{columns.map((c) => (
|
|
92
|
+
<TableCell key={c.id} align={columnAlign(c)}>
|
|
93
|
+
<Skeleton className={cn('h-3.5', columnAlign(c) === 'right' ? 'ms-auto w-16' : 'w-24')} />
|
|
94
|
+
</TableCell>
|
|
95
|
+
))}
|
|
96
|
+
{rowActions && <TableCell className="w-12" />}
|
|
97
|
+
</TableRow>
|
|
98
|
+
))}
|
|
99
|
+
</TableBody>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (rows.length === 0) {
|
|
103
|
+
return (
|
|
104
|
+
<TableBody>
|
|
105
|
+
<TableRow className="hover:bg-transparent">
|
|
106
|
+
<TableCell colSpan={span} className="py-10">
|
|
107
|
+
{emptyState ?? (
|
|
108
|
+
<EmptyState
|
|
109
|
+
icon={Inbox}
|
|
110
|
+
size="sm"
|
|
111
|
+
title={t('common.noResults')}
|
|
112
|
+
description={t('table.emptyDescription')}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</TableCell>
|
|
116
|
+
</TableRow>
|
|
117
|
+
</TableBody>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
const row = (r: T, extra?: { ariaRowIndex?: number; height?: number }) => (
|
|
121
|
+
<DataTableRow
|
|
122
|
+
key={props.getRowId(r)}
|
|
123
|
+
{...props}
|
|
124
|
+
row={r}
|
|
125
|
+
ariaRowIndex={extra?.ariaRowIndex}
|
|
126
|
+
style={extra?.height ? { height: extra.height } : undefined}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
const header = (group: DataTableBodyGroup<T>, open: boolean, extra?: { ariaRowIndex?: number; height?: number }) => (
|
|
130
|
+
<GroupHeaderRow
|
|
131
|
+
key={`group-${group.key}`}
|
|
132
|
+
group={group}
|
|
133
|
+
open={open}
|
|
134
|
+
selectable={selectable}
|
|
135
|
+
selected={selected}
|
|
136
|
+
span={span - (selectable ? 1 : 0)}
|
|
137
|
+
collapsible={collapsibleGroups}
|
|
138
|
+
onToggle={onToggleGroup}
|
|
139
|
+
onToggleRows={onToggleGroupRows}
|
|
140
|
+
ariaRowIndex={extra?.ariaRowIndex}
|
|
141
|
+
style={extra?.height ? { height: extra.height } : undefined}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (virtual) {
|
|
146
|
+
const items = flattenBody(rows, groups, collapsedGroups);
|
|
147
|
+
const { start, end, rowHeight } = virtual;
|
|
148
|
+
// Consecutive items of the same group share a <tbody> (a rowgroup), as in the full render.
|
|
149
|
+
const chunks: Array<{ key: string; group?: string; items: Array<{ item: DataTableBodyItem<T>; index: number }> }> =
|
|
150
|
+
[];
|
|
151
|
+
for (let i = start; i < Math.min(end, items.length); i++) {
|
|
152
|
+
const item = items[i]!;
|
|
153
|
+
const g = item.kind === 'group' ? item.group.key : item.groupKey;
|
|
154
|
+
const lastChunk = chunks[chunks.length - 1];
|
|
155
|
+
if (lastChunk && lastChunk.group === g) lastChunk.items.push({ item, index: i });
|
|
156
|
+
else chunks.push({ key: `${g ?? 'rows'}-${i}`, group: g, items: [{ item, index: i }] });
|
|
157
|
+
}
|
|
158
|
+
return (
|
|
159
|
+
<>
|
|
160
|
+
<Spacer height={start * rowHeight} span={span} />
|
|
161
|
+
{chunks.map((chunk) => (
|
|
162
|
+
<TableBody key={chunk.key} data-group={chunk.group}>
|
|
163
|
+
{chunk.items.map(({ item, index }) =>
|
|
164
|
+
item.kind === 'group'
|
|
165
|
+
? header(item.group, item.open, { ariaRowIndex: index + 2, height: rowHeight })
|
|
166
|
+
: row(item.row, { ariaRowIndex: index + 2, height: rowHeight }),
|
|
167
|
+
)}
|
|
168
|
+
</TableBody>
|
|
169
|
+
))}
|
|
170
|
+
<Spacer height={Math.max(0, items.length - end) * rowHeight} span={span} />
|
|
171
|
+
</>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!groups) return <TableBody>{rows.map((r) => row(r))}</TableBody>;
|
|
176
|
+
return (
|
|
177
|
+
<>
|
|
178
|
+
{groups.map((group, gi) => {
|
|
179
|
+
const open = !collapsedGroups?.has(group.key);
|
|
180
|
+
return (
|
|
181
|
+
<TableBody
|
|
182
|
+
key={group.key}
|
|
183
|
+
data-group={group.key}
|
|
184
|
+
// Every group but the last keeps the bottom rule of its last row.
|
|
185
|
+
className={gi < groups.length - 1 ? '[&>tr:last-child>td]:border-b' : undefined}
|
|
186
|
+
>
|
|
187
|
+
{header(group, open)}
|
|
188
|
+
{open && group.rows.map((r) => row(r))}
|
|
189
|
+
</TableBody>
|
|
190
|
+
);
|
|
191
|
+
})}
|
|
192
|
+
</>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { MoreMenuAction, TableDensity } from '@gntik-ai/ui';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { ColumnWidths, PinnedColumns } from './column-sizing';
|
|
4
|
+
import type { DataTableColumn, DataTableGroupBy, SortState } from './data-table-utils';
|
|
5
|
+
import type { DataTableCellEditHandler } from './editable-cell';
|
|
6
|
+
import type { DataTableView } from './views';
|
|
7
|
+
|
|
8
|
+
interface DataTableCommonProps<T> {
|
|
9
|
+
rows?: readonly T[];
|
|
10
|
+
columns?: ReadonlyArray<DataTableColumn<T>>;
|
|
11
|
+
/** Stable row id (defaults to `row.id`). */
|
|
12
|
+
getRowId?: (row: T) => string;
|
|
13
|
+
/** Row name for selection and menu labels (defaults to the first column's value). */
|
|
14
|
+
getRowLabel?: (row: T) => string;
|
|
15
|
+
/** Table name (screen-reader caption unless `showCaption`). */
|
|
16
|
+
caption?: string;
|
|
17
|
+
showCaption?: boolean;
|
|
18
|
+
|
|
19
|
+
/** Row selection with a select-all header checkbox (current page). */
|
|
20
|
+
selectable?: boolean;
|
|
21
|
+
/** Native radio name in single mode. Supplied names must be unique per page. */
|
|
22
|
+
selectionName?: string;
|
|
23
|
+
selectedId?: string | null;
|
|
24
|
+
defaultSelectedId?: string | null;
|
|
25
|
+
onSelectedIdChange?: (id: string) => void;
|
|
26
|
+
isRowDisabled?: (row: T) => boolean;
|
|
27
|
+
selectedIds?: readonly string[];
|
|
28
|
+
defaultSelectedIds?: readonly string[];
|
|
29
|
+
onSelectionChange?: (ids: string[]) => void;
|
|
30
|
+
/** Rendered in the toolbar while rows are selected. */
|
|
31
|
+
bulkActions?: (selectedIds: string[]) => ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Shows the "N selected" text in the toolbar while rows are selected (default true). Turn it
|
|
34
|
+
* off when the page renders its own count, e.g. a BulkActionBar.
|
|
35
|
+
*/
|
|
36
|
+
selectionSummary?: boolean;
|
|
37
|
+
|
|
38
|
+
/** Row-actions menu items. */
|
|
39
|
+
rowActions?: (row: T) => Array<MoreMenuAction | 'separator'>;
|
|
40
|
+
|
|
41
|
+
sort?: SortState | null;
|
|
42
|
+
defaultSort?: SortState | null;
|
|
43
|
+
onSortChange?: (sort: SortState) => void;
|
|
44
|
+
/** Rows arrive sorted (server side): headers still toggle, rows are not re-sorted. */
|
|
45
|
+
manualSorting?: boolean;
|
|
46
|
+
|
|
47
|
+
defaultPageSize?: number;
|
|
48
|
+
pageSizeOptions?: readonly number[];
|
|
49
|
+
|
|
50
|
+
/** Controlled row density. */
|
|
51
|
+
density?: TableDensity;
|
|
52
|
+
/** Initial density; omitted, the table follows the surrounding DensityProvider (comfortable outside one). */
|
|
53
|
+
defaultDensity?: TableDensity;
|
|
54
|
+
onDensityChange?: (density: TableDensity) => void;
|
|
55
|
+
showDensityToggle?: boolean;
|
|
56
|
+
showColumnMenu?: boolean;
|
|
57
|
+
|
|
58
|
+
loading?: boolean;
|
|
59
|
+
loadingRows?: number;
|
|
60
|
+
emptyState?: ReactNode;
|
|
61
|
+
/**
|
|
62
|
+
* Groups rows under header rows (a field key or a function returning the group key). Groups keep
|
|
63
|
+
* the order in which their keys first appear in `rows`; sorting applies inside each group, and
|
|
64
|
+
* pagination pages through the grouped rows (each header shows the group's full count).
|
|
65
|
+
*/
|
|
66
|
+
groupBy?: DataTableGroupBy<T>;
|
|
67
|
+
/** Header text of a group (default: the key, or "No group" for rows without a value). */
|
|
68
|
+
groupLabel?: (key: string, rows: readonly T[]) => ReactNode;
|
|
69
|
+
/** Group headers render a keyboard-accessible collapse button (default true). */
|
|
70
|
+
collapsibleGroups?: boolean;
|
|
71
|
+
/** Controlled collapsed group keys. */
|
|
72
|
+
collapsedGroups?: readonly string[];
|
|
73
|
+
/** Group keys collapsed initially. */
|
|
74
|
+
defaultCollapsedGroups?: readonly string[];
|
|
75
|
+
onCollapsedGroupsChange?: (keys: string[]) => void;
|
|
76
|
+
|
|
77
|
+
/** Called when a saved view switches grouping (a field key, or `null` for none). */
|
|
78
|
+
onGroupByChange?: (groupBy: string | null) => void;
|
|
79
|
+
|
|
80
|
+
/** Controlled hidden column ids (default: columns with `defaultHidden`). */
|
|
81
|
+
hiddenColumns?: readonly string[];
|
|
82
|
+
onHiddenColumnsChange?: (ids: string[]) => void;
|
|
83
|
+
|
|
84
|
+
/** Resize handles on the column headers (pointer drag, or arrow keys on the focused handle). */
|
|
85
|
+
resizable?: boolean;
|
|
86
|
+
/** Controlled column widths in px by column id. */
|
|
87
|
+
columnWidths?: ColumnWidths;
|
|
88
|
+
defaultColumnWidths?: ColumnWidths;
|
|
89
|
+
onColumnWidthsChange?: (widths: Record<string, number>) => void;
|
|
90
|
+
|
|
91
|
+
/** Controlled pinned columns (sticky at the start or end edge while scrolling sideways). */
|
|
92
|
+
pinnedColumns?: PinnedColumns;
|
|
93
|
+
defaultPinnedColumns?: PinnedColumns;
|
|
94
|
+
onPinnedColumnsChange?: (pinned: PinnedColumns) => void;
|
|
95
|
+
/** Pin / unpin from the column menu (default true when pinning is in use: a pinned prop or callback). */
|
|
96
|
+
showPinMenu?: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Renders only the rows in view (plus overscan) inside a scrolling body with a sticky header.
|
|
100
|
+
* `'auto'` (default) turns it on above `virtualizeThreshold` rendered rows (with pagination,
|
|
101
|
+
* the rows of the current page). The table exposes `aria-rowcount` and each row its
|
|
102
|
+
* `aria-rowindex`; selection (select-all covers every row) and grouping keep working.
|
|
103
|
+
* Limits: rows get a fixed height (`rowHeight`), so multi-line cells are clipped; a row that
|
|
104
|
+
* scrolls out of view unmounts, so focus inside it (or an open inline editor) is lost; a group's
|
|
105
|
+
* header row renders only while it is in view.
|
|
106
|
+
*/
|
|
107
|
+
virtualize?: boolean | 'auto';
|
|
108
|
+
virtualizeThreshold?: number;
|
|
109
|
+
/** Row height in px for virtualization (default by density: 44 comfortable, 36 compact). */
|
|
110
|
+
rowHeight?: number;
|
|
111
|
+
/** Classes of the virtualized scroll container: its max height (default `max-h-[32rem]`). */
|
|
112
|
+
virtualContainerClassName?: string;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Inline editing of `editable` columns. Return nothing to accept (and update `rows`), or a
|
|
116
|
+
* message (or throw) to reject: the message is shown inline under the editor.
|
|
117
|
+
*/
|
|
118
|
+
onCellEdit?: DataTableCellEditHandler<T>;
|
|
119
|
+
|
|
120
|
+
/** Saved views (controlled). Each captures filters, sort, visible columns, widths, pinning and grouping. */
|
|
121
|
+
views?: readonly DataTableView[];
|
|
122
|
+
defaultViews?: readonly DataTableView[];
|
|
123
|
+
onViewsChange?: (views: DataTableView[]) => void;
|
|
124
|
+
activeViewId?: string | null;
|
|
125
|
+
defaultActiveViewId?: string | null;
|
|
126
|
+
onActiveViewChange?: (id: string | null) => void;
|
|
127
|
+
/** Shows the view switcher (default: when views are in use). */
|
|
128
|
+
showViewSwitcher?: boolean;
|
|
129
|
+
/** Opaque filter state of the page's toolbar, saved with views. */
|
|
130
|
+
filters?: unknown;
|
|
131
|
+
/** Called when a view (or a reset) restores filters. */
|
|
132
|
+
onFiltersChange?: (filters: unknown) => void;
|
|
133
|
+
|
|
134
|
+
/** Left side of the toolbar (title, search, FilterBar…). */
|
|
135
|
+
toolbar?: ReactNode;
|
|
136
|
+
className?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type DataTableProps<T> = DataTableCommonProps<T> &
|
|
140
|
+
(
|
|
141
|
+
| {
|
|
142
|
+
selectionMode: 'single';
|
|
143
|
+
paginated?: false;
|
|
144
|
+
/** Consumer-supplied accessible group label; the kit adds no default. */
|
|
145
|
+
legend?: ReactNode;
|
|
146
|
+
'aria-label'?: string;
|
|
147
|
+
'aria-labelledby'?: string;
|
|
148
|
+
}
|
|
149
|
+
| {
|
|
150
|
+
selectionMode?: 'none' | 'multiple';
|
|
151
|
+
paginated?: boolean;
|
|
152
|
+
legend?: never;
|
|
153
|
+
'aria-label'?: never;
|
|
154
|
+
'aria-labelledby'?: never;
|
|
155
|
+
}
|
|
156
|
+
);
|