@miragon/mcp-toolkit-ui 2.7.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/app/adapt-data-widget.d.ts +1 -22
- package/dist/app/adapt-data-widget.d.ts.map +1 -1
- package/dist/app/adapt-data-widget.js +1 -35
- package/dist/app/adapt-data-widget.js.map +1 -1
- package/dist/app/builder/BuilderBanners.js +1 -1
- package/dist/app/builder/BuilderBanners.js.map +1 -1
- package/dist/app/builder/CanvasRow.d.ts +4 -2
- package/dist/app/builder/CanvasRow.d.ts.map +1 -1
- package/dist/app/builder/CanvasRow.js +21 -7
- package/dist/app/builder/CanvasRow.js.map +1 -1
- package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
- package/dist/app/builder/CatalogueSheet.js +11 -4
- package/dist/app/builder/CatalogueSheet.js.map +1 -1
- package/dist/app/builder/CellPropsSheet.d.ts +7 -3
- package/dist/app/builder/CellPropsSheet.d.ts.map +1 -1
- package/dist/app/builder/CellPropsSheet.js +10 -3
- package/dist/app/builder/CellPropsSheet.js.map +1 -1
- package/dist/app/builder/DiscardDialog.d.ts +18 -0
- package/dist/app/builder/DiscardDialog.d.ts.map +1 -0
- package/dist/app/builder/DiscardDialog.js +18 -0
- package/dist/app/builder/DiscardDialog.js.map +1 -0
- package/dist/app/builder/LayoutSurface.d.ts +33 -9
- package/dist/app/builder/LayoutSurface.d.ts.map +1 -1
- package/dist/app/builder/LayoutSurface.js +74 -23
- package/dist/app/builder/LayoutSurface.js.map +1 -1
- package/dist/app/builder/PipelineStrip.d.ts +2 -1
- package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
- package/dist/app/builder/PipelineStrip.js +26 -13
- package/dist/app/builder/PipelineStrip.js.map +1 -1
- package/dist/app/builder/SaveDialog.d.ts +9 -1
- package/dist/app/builder/SaveDialog.d.ts.map +1 -1
- package/dist/app/builder/SaveDialog.js +8 -2
- package/dist/app/builder/SaveDialog.js.map +1 -1
- package/dist/app/builder/Toolbar.d.ts +12 -4
- package/dist/app/builder/Toolbar.d.ts.map +1 -1
- package/dist/app/builder/Toolbar.js +18 -7
- package/dist/app/builder/Toolbar.js.map +1 -1
- package/dist/app/builder/Workspace.d.ts +23 -3
- package/dist/app/builder/Workspace.d.ts.map +1 -1
- package/dist/app/builder/Workspace.js +58 -8
- package/dist/app/builder/Workspace.js.map +1 -1
- package/dist/app/builder/builder-model.d.ts +143 -1
- package/dist/app/builder/builder-model.d.ts.map +1 -1
- package/dist/app/builder/builder-model.js +191 -1
- package/dist/app/builder/builder-model.js.map +1 -1
- package/dist/app/builder/reveal.d.ts +10 -0
- package/dist/app/builder/reveal.d.ts.map +1 -0
- package/dist/app/builder/reveal.js +10 -0
- package/dist/app/builder/reveal.js.map +1 -0
- package/dist/app/builder/use-builder-catalogue.d.ts +4 -1
- package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
- package/dist/app/builder/use-builder-catalogue.js +44 -17
- package/dist/app/builder/use-builder-catalogue.js.map +1 -1
- package/dist/app/builder/use-builder-session.d.ts +30 -0
- package/dist/app/builder/use-builder-session.d.ts.map +1 -0
- package/dist/app/builder/use-builder-session.js +58 -0
- package/dist/app/builder/use-builder-session.js.map +1 -0
- package/dist/app/builder/use-display-resolver.d.ts +17 -0
- package/dist/app/builder/use-display-resolver.d.ts.map +1 -0
- package/dist/app/builder/use-display-resolver.js +16 -0
- package/dist/app/builder/use-display-resolver.js.map +1 -0
- package/dist/app/index.d.ts +8 -3
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +8 -2
- package/dist/app/index.js.map +1 -1
- package/dist/app/layout-builder.d.ts +13 -25
- package/dist/app/layout-builder.d.ts.map +1 -1
- package/dist/app/layout-builder.js +75 -81
- package/dist/app/layout-builder.js.map +1 -1
- package/dist/app/mcp-app-view-chrome.d.ts +12 -3
- package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
- package/dist/app/mcp-app-view-chrome.js +21 -5
- package/dist/app/mcp-app-view-chrome.js.map +1 -1
- package/dist/app/mcp-app-view-states.d.ts +11 -3
- package/dist/app/mcp-app-view-states.d.ts.map +1 -1
- package/dist/app/mcp-app-view-states.js +29 -8
- package/dist/app/mcp-app-view-states.js.map +1 -1
- package/dist/app/mcp-app-view.d.ts +33 -1
- package/dist/app/mcp-app-view.d.ts.map +1 -1
- package/dist/app/mcp-app-view.js +181 -127
- package/dist/app/mcp-app-view.js.map +1 -1
- package/dist/app/mcp-toolkit-app.d.ts +6 -1
- package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
- package/dist/app/mcp-toolkit-app.js +8 -1
- package/dist/app/mcp-toolkit-app.js.map +1 -1
- package/dist/app/mcp-use-host-bridge.d.ts +15 -6
- package/dist/app/mcp-use-host-bridge.d.ts.map +1 -1
- package/dist/app/mcp-use-host-bridge.js +51 -22
- package/dist/app/mcp-use-host-bridge.js.map +1 -1
- package/dist/app/model-context.d.ts +71 -0
- package/dist/app/model-context.d.ts.map +1 -0
- package/dist/app/model-context.js +101 -0
- package/dist/app/model-context.js.map +1 -0
- package/dist/app/use-build-mode-focus.d.ts +33 -0
- package/dist/app/use-build-mode-focus.d.ts.map +1 -0
- package/dist/app/use-build-mode-focus.js +59 -0
- package/dist/app/use-build-mode-focus.js.map +1 -0
- package/dist/app/use-view-actions.d.ts +31 -0
- package/dist/app/use-view-actions.d.ts.map +1 -0
- package/dist/app/use-view-actions.js +81 -0
- package/dist/app/use-view-actions.js.map +1 -0
- package/dist/app/use-view-lifecycle.d.ts +4 -0
- package/dist/app/use-view-lifecycle.d.ts.map +1 -1
- package/dist/app/use-view-lifecycle.js +2 -0
- package/dist/app/use-view-lifecycle.js.map +1 -1
- package/dist/app/view-lifecycle.d.ts +80 -4
- package/dist/app/view-lifecycle.d.ts.map +1 -1
- package/dist/app/view-lifecycle.js +90 -3
- package/dist/app/view-lifecycle.js.map +1 -1
- package/dist/app/widget-fixture.d.ts +10 -3
- package/dist/app/widget-fixture.d.ts.map +1 -1
- package/dist/app/widget-fixture.js +21 -8
- package/dist/app/widget-fixture.js.map +1 -1
- package/dist/app/widget-props-sheet.d.ts +3 -1
- package/dist/app/widget-props-sheet.d.ts.map +1 -1
- package/dist/app/widget-props-sheet.js +22 -10
- package/dist/app/widget-props-sheet.js.map +1 -1
- package/dist/app/widget-renderer.js +1 -1
- package/dist/app/widget-renderer.js.map +1 -1
- package/dist/components/DrillButton.d.ts +14 -6
- package/dist/components/DrillButton.d.ts.map +1 -1
- package/dist/components/DrillButton.js +14 -7
- package/dist/components/DrillButton.js.map +1 -1
- package/dist/components/EmptyState.d.ts.map +1 -1
- package/dist/components/EmptyState.js +4 -2
- package/dist/components/EmptyState.js.map +1 -1
- package/dist/components/ErrorState.d.ts +5 -2
- package/dist/components/ErrorState.d.ts.map +1 -1
- package/dist/components/ErrorState.js +25 -8
- package/dist/components/ErrorState.js.map +1 -1
- package/dist/components/FilterBar.d.ts +15 -4
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +11 -6
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/KpiGrid.d.ts +66 -21
- package/dist/components/KpiGrid.d.ts.map +1 -1
- package/dist/components/KpiGrid.js +101 -34
- package/dist/components/KpiGrid.js.map +1 -1
- package/dist/components/ListFooter.d.ts +31 -4
- package/dist/components/ListFooter.d.ts.map +1 -1
- package/dist/components/ListFooter.js +30 -11
- package/dist/components/ListFooter.js.map +1 -1
- package/dist/components/PagedListFooter.d.ts +57 -0
- package/dist/components/PagedListFooter.d.ts.map +1 -0
- package/dist/components/PagedListFooter.js +27 -0
- package/dist/components/PagedListFooter.js.map +1 -0
- package/dist/components/Pills.d.ts +27 -7
- package/dist/components/Pills.d.ts.map +1 -1
- package/dist/components/Pills.js +21 -6
- package/dist/components/Pills.js.map +1 -1
- package/dist/components/SectionHeading.d.ts +3 -1
- package/dist/components/SectionHeading.d.ts.map +1 -1
- package/dist/components/SectionHeading.js +6 -2
- package/dist/components/SectionHeading.js.map +1 -1
- package/dist/components/Toast.d.ts +9 -3
- package/dist/components/Toast.d.ts.map +1 -1
- package/dist/components/Toast.js +31 -16
- package/dist/components/Toast.js.map +1 -1
- package/dist/components/WidgetHeader.d.ts +34 -10
- package/dist/components/WidgetHeader.d.ts.map +1 -1
- package/dist/components/WidgetHeader.js +11 -6
- package/dist/components/WidgetHeader.js.map +1 -1
- package/dist/hooks/index.d.ts +4 -4
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/tool-result-recovery-model.d.ts +155 -0
- package/dist/hooks/tool-result-recovery-model.d.ts.map +1 -0
- package/dist/hooks/tool-result-recovery-model.js +273 -0
- package/dist/hooks/tool-result-recovery-model.js.map +1 -0
- package/dist/hooks/use-tool-query.d.ts +56 -14
- package/dist/hooks/use-tool-query.d.ts.map +1 -1
- package/dist/hooks/use-tool-query.js +102 -25
- package/dist/hooks/use-tool-query.js.map +1 -1
- package/dist/hooks/use-tool-result-recovery.d.ts +47 -52
- package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
- package/dist/hooks/use-tool-result-recovery.js +58 -120
- package/dist/hooks/use-tool-result-recovery.js.map +1 -1
- package/dist/hooks/use-view-data.d.ts +52 -10
- package/dist/hooks/use-view-data.d.ts.map +1 -1
- package/dist/hooks/use-view-data.js +81 -16
- package/dist/hooks/use-view-data.js.map +1 -1
- package/dist/hooks/use-view-tool-query.d.ts +12 -7
- package/dist/hooks/use-view-tool-query.d.ts.map +1 -1
- package/dist/hooks/use-view-tool-query.js +15 -14
- package/dist/hooks/use-view-tool-query.js.map +1 -1
- package/dist/index.d.ts +17 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/focus-return.d.ts +61 -0
- package/dist/lib/focus-return.d.ts.map +1 -0
- package/dist/lib/focus-return.js +46 -0
- package/dist/lib/focus-return.js.map +1 -0
- package/dist/lib/parse-tool-result.d.ts +23 -1
- package/dist/lib/parse-tool-result.d.ts.map +1 -1
- package/dist/lib/parse-tool-result.js +38 -2
- package/dist/lib/parse-tool-result.js.map +1 -1
- package/dist/lib/toast-model.d.ts +41 -1
- package/dist/lib/toast-model.d.ts.map +1 -1
- package/dist/lib/toast-model.js +67 -1
- package/dist/lib/toast-model.js.map +1 -1
- package/dist/lib/tone-utils.d.ts +58 -6
- package/dist/lib/tone-utils.d.ts.map +1 -1
- package/dist/lib/tone-utils.js +66 -4
- package/dist/lib/tone-utils.js.map +1 -1
- package/dist/lib/tool-query.d.ts +107 -0
- package/dist/lib/tool-query.d.ts.map +1 -0
- package/dist/lib/tool-query.js +182 -0
- package/dist/lib/tool-query.js.map +1 -0
- package/dist/lib/tool-result-observer.d.ts +108 -0
- package/dist/lib/tool-result-observer.d.ts.map +1 -0
- package/dist/lib/tool-result-observer.js +158 -0
- package/dist/lib/tool-result-observer.js.map +1 -0
- package/dist/lib/typography.d.ts +37 -0
- package/dist/lib/typography.d.ts.map +1 -0
- package/dist/lib/typography.js +41 -0
- package/dist/lib/typography.js.map +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +11 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/locale/index.d.ts +1 -1
- package/dist/locale/index.d.ts.map +1 -1
- package/dist/locale/message-types.d.ts +117 -4
- package/dist/locale/message-types.d.ts.map +1 -1
- package/dist/locale/messages-de.d.ts.map +1 -1
- package/dist/locale/messages-de.js +44 -10
- package/dist/locale/messages-de.js.map +1 -1
- package/dist/locale/messages-en.d.ts.map +1 -1
- package/dist/locale/messages-en.js +44 -10
- package/dist/locale/messages-en.js.map +1 -1
- package/dist/locale/toolkit-messages.d.ts +19 -1
- package/dist/locale/toolkit-messages.d.ts.map +1 -1
- package/dist/locale/toolkit-messages.js +54 -1
- package/dist/locale/toolkit-messages.js.map +1 -1
- package/dist/primitives/alert.d.ts +1 -1
- package/dist/primitives/alert.d.ts.map +1 -1
- package/dist/primitives/alert.js +2 -0
- package/dist/primitives/alert.js.map +1 -1
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/badge.d.ts.map +1 -1
- package/dist/primitives/badge.js +9 -4
- package/dist/primitives/badge.js.map +1 -1
- package/dist/primitives/button.d.ts +1 -1
- package/dist/primitives/button.js +1 -1
- package/dist/primitives/button.js.map +1 -1
- package/dist/primitives/dialog.d.ts +1 -1
- package/dist/primitives/dialog.d.ts.map +1 -1
- package/dist/primitives/dialog.js +10 -3
- package/dist/primitives/dialog.js.map +1 -1
- package/dist/primitives/select.js +1 -1
- package/dist/primitives/select.js.map +1 -1
- package/dist/primitives/sheet.d.ts +1 -1
- package/dist/primitives/sheet.d.ts.map +1 -1
- package/dist/primitives/sheet.js +6 -2
- package/dist/primitives/sheet.js.map +1 -1
- package/dist/providers/query-provider.d.ts +32 -1
- package/dist/providers/query-provider.d.ts.map +1 -1
- package/dist/providers/query-provider.js +53 -4
- package/dist/providers/query-provider.js.map +1 -1
- package/dist/render-cases-primitives.js +1 -1
- package/dist/render-cases-primitives.js.map +1 -1
- package/dist/render-cases.d.ts.map +1 -1
- package/dist/render-cases.js +30 -6
- package/dist/render-cases.js.map +1 -1
- package/dist/render-effects.d.ts +42 -0
- package/dist/render-effects.d.ts.map +1 -0
- package/dist/render-effects.js +81 -0
- package/dist/render-effects.js.map +1 -0
- package/dist/theme/create-theme.d.ts +19 -1
- package/dist/theme/create-theme.d.ts.map +1 -1
- package/dist/theme/create-theme.js +15 -0
- package/dist/theme/create-theme.js.map +1 -1
- package/package.json +5 -4
- package/src/app/adapt-data-widget.tsx +2 -42
- package/src/app/builder/BuilderBanners.tsx +1 -1
- package/src/app/builder/CanvasRow.tsx +63 -17
- package/src/app/builder/CatalogueSheet.tsx +50 -29
- package/src/app/builder/CellPropsSheet.tsx +17 -2
- package/src/app/builder/DiscardDialog.tsx +59 -0
- package/src/app/builder/LayoutSurface.tsx +206 -66
- package/src/app/builder/PipelineStrip.tsx +253 -153
- package/src/app/builder/SaveDialog.tsx +14 -2
- package/src/app/builder/Toolbar.tsx +144 -59
- package/src/app/builder/Workspace.tsx +226 -58
- package/src/app/builder/builder-model.ts +341 -2
- package/src/app/builder/builder-model.ux.test.ts +456 -0
- package/src/app/builder/builder-ux.test.tsx +844 -0
- package/src/app/builder/reveal.ts +10 -0
- package/src/app/builder/use-builder-catalogue.ts +56 -19
- package/src/app/builder/use-builder-session.ts +68 -0
- package/src/app/builder/use-display-resolver.ts +24 -0
- package/src/app/index.ts +12 -2
- package/src/app/layout-builder.tsx +178 -191
- package/src/app/mcp-app-view-chrome.test.tsx +141 -1
- package/src/app/mcp-app-view-chrome.tsx +56 -6
- package/src/app/mcp-app-view-states.tsx +51 -11
- package/src/app/mcp-app-view.effects.test.tsx +292 -0
- package/src/app/mcp-app-view.test.tsx +222 -2
- package/src/app/mcp-app-view.tsx +267 -117
- package/src/app/mcp-toolkit-app.test.tsx +40 -0
- package/src/app/mcp-toolkit-app.tsx +8 -1
- package/src/app/mcp-use-host-bridge.test.tsx +180 -0
- package/src/app/mcp-use-host-bridge.tsx +67 -24
- package/src/app/model-context.test.tsx +116 -0
- package/src/app/model-context.tsx +125 -0
- package/src/app/use-build-mode-focus.ts +62 -0
- package/src/app/use-view-actions.ts +89 -0
- package/src/app/use-view-lifecycle.ts +6 -0
- package/src/app/view-lifecycle.test.ts +216 -0
- package/src/app/view-lifecycle.ts +165 -5
- package/src/app/widget-fixture.effects.test.tsx +51 -0
- package/src/app/widget-fixture.render.test.tsx +81 -0
- package/src/app/widget-fixture.tsx +34 -11
- package/src/app/widget-props-sheet.tsx +57 -16
- package/src/app/widget-renderer.tsx +1 -1
- package/src/components/DrillButton.test.tsx +48 -0
- package/src/components/DrillButton.tsx +26 -11
- package/src/components/EmptyState.tsx +6 -4
- package/src/components/ErrorState.tsx +31 -9
- package/src/components/FilterBar.test.tsx +82 -0
- package/src/components/FilterBar.tsx +52 -26
- package/src/components/KpiGrid.test.tsx +279 -0
- package/src/components/KpiGrid.tsx +297 -90
- package/src/components/ListFooter.test.tsx +116 -0
- package/src/components/ListFooter.tsx +102 -15
- package/src/components/PagedListFooter.test.tsx +140 -0
- package/src/components/PagedListFooter.tsx +107 -0
- package/src/components/Pills.test.tsx +77 -0
- package/src/components/Pills.tsx +63 -10
- package/src/components/SectionHeading.tsx +9 -3
- package/src/components/Toast.tsx +37 -19
- package/src/components/WidgetHeader.test.tsx +78 -0
- package/src/components/WidgetHeader.tsx +63 -16
- package/src/components/feedback-states.test.tsx +98 -9
- package/src/components/tone-figures.test.tsx +8 -4
- package/src/hooks/index.ts +6 -3
- package/src/hooks/tool-result-recovery-model.test.ts +744 -0
- package/src/hooks/tool-result-recovery-model.ts +418 -0
- package/src/hooks/use-tool-query.effects.test.tsx +205 -0
- package/src/hooks/use-tool-query.ts +156 -33
- package/src/hooks/use-tool-result-recovery.effects.test.tsx +141 -0
- package/src/hooks/use-tool-result-recovery.test.ts +3 -4
- package/src/hooks/use-tool-result-recovery.ts +84 -142
- package/src/hooks/use-view-data.test.tsx +118 -9
- package/src/hooks/use-view-data.ts +128 -18
- package/src/hooks/use-view-tool-query.ts +25 -19
- package/src/i18n-coverage.test.tsx +104 -2
- package/src/index.ts +48 -11
- package/src/lib/focus-return.test.ts +288 -0
- package/src/lib/focus-return.ts +96 -0
- package/src/lib/parse-tool-result.test.ts +46 -1
- package/src/lib/parse-tool-result.ts +40 -2
- package/src/lib/toast-model.test.ts +166 -1
- package/src/lib/toast-model.ts +104 -2
- package/src/lib/tone-utils.test.ts +86 -10
- package/src/lib/tone-utils.ts +89 -8
- package/src/lib/tool-query.test.ts +569 -0
- package/src/lib/tool-query.ts +253 -0
- package/src/lib/tool-result-observer.test.ts +296 -0
- package/src/lib/tool-result-observer.ts +211 -0
- package/src/lib/typography.test.ts +45 -0
- package/src/lib/typography.ts +47 -0
- package/src/lib/utils.ts +12 -1
- package/src/locale/index.ts +1 -0
- package/src/locale/message-types.ts +118 -2
- package/src/locale/messages-de.ts +49 -10
- package/src/locale/messages-en.ts +49 -10
- package/src/locale/toolkit-messages.test.ts +133 -0
- package/src/locale/toolkit-messages.ts +63 -2
- package/src/primitives/alert.tsx +3 -0
- package/src/primitives/badge.tsx +9 -4
- package/src/primitives/button.tsx +1 -1
- package/src/primitives/dialog.tsx +10 -1
- package/src/primitives/select.tsx +1 -1
- package/src/primitives/sheet.tsx +7 -0
- package/src/providers/query-provider.test.tsx +154 -0
- package/src/providers/query-provider.tsx +66 -5
- package/src/render-cases-primitives.tsx +1 -1
- package/src/render-cases.tsx +38 -6
- package/src/render-effects.tsx +96 -0
- package/src/theme/cd-tokens.test.ts +3 -1
- package/src/theme/create-theme.test.ts +10 -0
- package/src/theme/create-theme.ts +28 -0
- package/src/theme/theme-css.test.ts +51 -0
- package/src/theme.css +31 -1
- package/ui-catalog.json +448 -41
|
@@ -1,57 +1,124 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { ChevronRight, Minus, TrendingDown, TrendingUp } from "lucide-react";
|
|
4
|
+
import { FOCUS_RING_INSET, FOCUS_RING } from "../lib/focus-ring.js";
|
|
5
|
+
import { resolveTone, TONE_BORDER, TONE_DOT, TONE_ICON, TONE_TINT, } from "../lib/tone-utils.js";
|
|
6
|
+
import { MICRO_LABEL } from "../lib/typography.js";
|
|
7
|
+
import { cn } from "../lib/utils.js";
|
|
8
|
+
import { Skeleton } from "../primitives/skeleton.js";
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* Direction → tone when no `trendTone` is set (incident-biased: up reads as
|
|
11
|
+
* worse). The tone shows ONLY as the direction icon's colour or a dot; the
|
|
12
|
+
* trend words stay neutral (no coloured digits, CI §3.3).
|
|
7
13
|
*/
|
|
8
14
|
const TREND_TONE = {
|
|
9
15
|
up: "danger",
|
|
10
16
|
down: "success",
|
|
11
17
|
flat: "neutral",
|
|
12
18
|
};
|
|
19
|
+
const TREND_ICON = {
|
|
20
|
+
up: TrendingUp,
|
|
21
|
+
down: TrendingDown,
|
|
22
|
+
flat: Minus,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Columns per cell count. 4+ cells fall back to 2 columns in narrow frames
|
|
26
|
+
* (inline chat views are often under 500 px, where five cells side by side
|
|
27
|
+
* leave ~40 px each); 6 steps through 3.
|
|
28
|
+
*/
|
|
13
29
|
const COL_CLASS = {
|
|
14
30
|
1: "grid-cols-1",
|
|
15
31
|
2: "grid-cols-2",
|
|
16
32
|
3: "grid-cols-3",
|
|
17
|
-
4: "grid-cols-4",
|
|
18
|
-
5: "grid-cols-5",
|
|
19
|
-
6: "grid-cols-6",
|
|
33
|
+
4: "grid-cols-2 sm:grid-cols-4",
|
|
34
|
+
5: "grid-cols-2 sm:grid-cols-5",
|
|
35
|
+
6: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-6",
|
|
20
36
|
};
|
|
37
|
+
/** The responsive column classes for `count` cells (clamped to 1–6). */
|
|
38
|
+
export function kpiColumns(count) {
|
|
39
|
+
const cols = Math.min(Math.max(Math.trunc(count) || 1, 1), 6);
|
|
40
|
+
return COL_CLASS[cols] ?? "grid-cols-1";
|
|
41
|
+
}
|
|
21
42
|
/** A small status dot; decorative — the label next to it carries the meaning. */
|
|
22
|
-
function ToneDot({ tone,
|
|
23
|
-
return _jsx("span", { "aria-hidden": "true", className:
|
|
43
|
+
function ToneDot({ tone, className }) {
|
|
44
|
+
return (_jsx("span", { "aria-hidden": "true", "data-tone": tone, className: cn("shrink-0 rounded-full", className, TONE_DOT[tone]) }));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The trend line: neutral words (the foreground next to a state, quiet
|
|
48
|
+
* without one) and the state beside them as a direction icon or a dot in the
|
|
49
|
+
* tone — never as coloured digits.
|
|
50
|
+
*/
|
|
51
|
+
function TrendLine({ cell }) {
|
|
52
|
+
const direction = cell.trendDirection;
|
|
53
|
+
const tone = cell.trendTone !== undefined
|
|
54
|
+
? resolveTone(cell.trendTone)
|
|
55
|
+
: direction
|
|
56
|
+
? TREND_TONE[direction]
|
|
57
|
+
: "neutral";
|
|
58
|
+
const marked = tone !== "neutral";
|
|
59
|
+
const Icon = direction ? TREND_ICON[direction] : undefined;
|
|
60
|
+
return (_jsxs("div", { "data-slot": "kpi-trend", className: "text-small mt-1.5 flex items-center gap-1.5", children: [Icon ? (_jsx(Icon, { "aria-hidden": "true", className: cn("size-4 shrink-0", TONE_ICON[tone]) })) : (marked && _jsx(ToneDot, { tone: tone, className: "size-1.5" })), _jsx("span", { className: marked ? "text-foreground" : "text-muted-foreground", children: cell.trend })] }));
|
|
61
|
+
}
|
|
62
|
+
/** Label, figure, fraction and trend — shared by the strip and soft variants. */
|
|
63
|
+
function KpiCellBody({ cell, variant }) {
|
|
64
|
+
const tone = resolveTone(cell.tone);
|
|
65
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: cn("text-small flex items-center justify-between gap-2 font-medium", variant === "strip" ? "text-muted-foreground" : "text-foreground"), children: [_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [variant === "strip" && tone !== "neutral" && _jsx(ToneDot, { tone: tone, className: "size-2" }), _jsx("span", { className: "min-w-0 break-words", children: cell.label })] }), cell.onClick && _jsx(ChevronRight, { "aria-hidden": "true", className: "size-4 shrink-0" })] }), _jsxs("div", { className: "text-title text-foreground mt-1.5 leading-none tracking-tight break-words tabular-nums", children: [cell.value, cell.fraction && (_jsx("span", { className: "text-dense text-muted-foreground ml-0.5 font-normal", children: cell.fraction }))] }), cell.trend && _jsx(TrendLine, { cell: cell })] }));
|
|
66
|
+
}
|
|
67
|
+
/** One cell: a button when clickable (named by its visible text), else a plain box. */
|
|
68
|
+
function KpiCellView({ cell, variant, className, }) {
|
|
69
|
+
const hintId = useId();
|
|
70
|
+
const body = _jsx(KpiCellBody, { cell: cell, variant: variant });
|
|
71
|
+
const tone = cell.tone === undefined ? undefined : resolveTone(cell.tone);
|
|
72
|
+
if (!cell.onClick) {
|
|
73
|
+
return (_jsx("div", { "data-tone": tone, className: className, children: body }));
|
|
74
|
+
}
|
|
75
|
+
return (_jsxs("button", { type: "button", onClick: cell.onClick, "aria-label": cell.ariaLabel, "aria-describedby": cell.actionHint ? hintId : undefined, "data-tone": tone, className: cn("cursor-pointer text-left transition-colors", variant === "strip" ? cn("hover:bg-muted", FOCUS_RING_INSET) : FOCUS_RING, className), children: [body, cell.actionHint && (_jsx("span", { id: hintId, hidden: true, children: cell.actionHint }))] }));
|
|
76
|
+
}
|
|
77
|
+
/** A soft card: the tone's tint and edge around neutral text. */
|
|
78
|
+
function softCardClass(tone) {
|
|
79
|
+
const t = resolveTone(tone);
|
|
80
|
+
return cn("text-foreground rounded-lg border p-4", TONE_TINT[t], TONE_BORDER[t]);
|
|
24
81
|
}
|
|
25
82
|
/**
|
|
26
|
-
* The
|
|
27
|
-
*
|
|
83
|
+
* The strip's dividers. Each cell draws its own right and bottom rule, and the
|
|
84
|
+
* grid reaches 1 px past its clipping frame, so the rules on the outer edges
|
|
85
|
+
* fall away at any column count. An empty track in a short last row (5 cells
|
|
86
|
+
* in 2 columns, 7 in 6) keeps the card colour: a `gap-px` grid on a
|
|
87
|
+
* `bg-border` container would paint it as a solid block of the divider colour.
|
|
28
88
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
89
|
+
const STRIP_FRAME = "bg-card overflow-hidden";
|
|
90
|
+
const STRIP_GRID = "-mr-px -mb-px grid";
|
|
91
|
+
const STRIP_CELL = "border-border min-w-0 border-r border-b px-5 py-4";
|
|
92
|
+
function KpiStripHeader({ header }) {
|
|
93
|
+
return (_jsxs("div", { className: cn("border-border bg-muted text-muted-foreground flex items-center gap-2 border-b px-4 py-2", MICRO_LABEL), children: [_jsx("span", { children: header.label }), header.badge && (_jsx("span", { className: "border-border bg-card text-muted-foreground text-small rounded-sm border px-1.5 font-medium", children: header.badge }))] }));
|
|
33
94
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
95
|
+
/**
|
|
96
|
+
* A row of headline metrics — the canonical way to show KPIs in a widget
|
|
97
|
+
* (replaces the single-metric KPICard). Cells flow as columns (1–6), falling
|
|
98
|
+
* back to 2 columns in narrow frames. A cell's `tone` shows as a dot next to
|
|
99
|
+
* its label (or as a soft card's tint and edge); figures stay in the
|
|
100
|
+
* foreground colour. A clickable cell is a button whose accessible name is
|
|
101
|
+
* its visible label and value; `actionHint` describes what the click does.
|
|
102
|
+
*/
|
|
103
|
+
export function KpiGrid({ cells, boxed = false, header, variant = "strip", ariaLabel, className, }) {
|
|
104
|
+
const colClass = kpiColumns(cells.length);
|
|
105
|
+
const group = ariaLabel ? { role: "group", "aria-label": ariaLabel } : {};
|
|
106
|
+
if (variant === "soft") {
|
|
107
|
+
return (_jsx("div", { ...group, "data-slot": "kpi-grid", className: cn("grid gap-4", colClass, className), children: cells.map((cell, idx) => (_jsx(KpiCellView, { cell: cell, variant: "soft", className: cn("min-w-0", softCardClass(cell.tone)) }, idx))) }));
|
|
108
|
+
}
|
|
109
|
+
return (_jsxs("div", { ...group, "data-slot": "kpi-grid", className: cn(boxed && "border-border overflow-hidden rounded-lg border", className), children: [header && _jsx(KpiStripHeader, { header: header }), _jsx("div", { "data-slot": "kpi-strip", className: cn(STRIP_FRAME, !boxed && "border-border border-y"), children: _jsx("div", { className: cn(STRIP_GRID, colClass), children: cells.map((cell, idx) => (_jsx(KpiCellView, { cell: cell, variant: "strip", className: STRIP_CELL }, idx))) }) })] }));
|
|
37
110
|
}
|
|
38
111
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* `boxed` + `header` for a labelled metric block; unboxed (default) renders the
|
|
43
|
-
* lighter `border-y` strip. A cell's `tone` shows as a status dot next to its
|
|
44
|
-
* label; figures stay in the foreground colour.
|
|
112
|
+
* Loading placeholder with {@link KpiGrid}'s geometry (same wrapper, grid and
|
|
113
|
+
* cell padding in both variants), so the layout doesn't jump when the data
|
|
114
|
+
* arrives. `aria-busy`; pair it with a status line if the wait is long.
|
|
45
115
|
*/
|
|
46
|
-
export function
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const content = _jsx(KpiCellContent, { cell: cell });
|
|
54
|
-
return cell.onClick ? (_jsx("button", { type: "button", onClick: cell.onClick, "aria-label": cell.ariaLabel, className: `hover:bg-muted cursor-pointer px-5 py-4 text-left transition-colors ${FOCUS_RING_INSET} ${borderClass}`, children: content }, idx)) : (_jsx("div", { className: `px-5 py-4 ${borderClass}`, children: content }, idx));
|
|
55
|
-
}) })] }));
|
|
116
|
+
export function KpiGridSkeleton({ cells, variant = "strip", boxed = false, className, }) {
|
|
117
|
+
const colClass = kpiColumns(cells);
|
|
118
|
+
const count = Math.max(Math.trunc(cells) || 0, 0);
|
|
119
|
+
if (variant === "soft") {
|
|
120
|
+
return (_jsx("div", { "aria-busy": "true", className: cn("grid gap-4", colClass, className), children: Array.from({ length: count }, (_, idx) => (_jsxs("div", { className: "bg-muted border-border rounded-lg border p-4", children: [_jsx(Skeleton, { className: "h-4 w-20" }), _jsx(Skeleton, { className: "mt-2 h-7 w-14" })] }, idx))) }));
|
|
121
|
+
}
|
|
122
|
+
return (_jsx("div", { "aria-busy": "true", className: cn(boxed && "border-border overflow-hidden rounded-lg border", className), children: _jsx("div", { "data-slot": "kpi-strip", className: cn(STRIP_FRAME, !boxed && "border-border border-y"), children: _jsx("div", { className: cn(STRIP_GRID, colClass), children: Array.from({ length: count }, (_, idx) => (_jsxs("div", { className: STRIP_CELL, children: [_jsx(Skeleton, { className: "h-4 w-16" }), _jsx(Skeleton, { className: "mt-2.5 h-7 w-14" })] }, idx))) }) }) }));
|
|
56
123
|
}
|
|
57
124
|
//# sourceMappingURL=KpiGrid.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KpiGrid.js","sourceRoot":"","sources":["../../src/components/KpiGrid.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"KpiGrid.js","sourceRoot":"","sources":["../../src/components/KpiGrid.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ListFooterMessages } from "../locale/message-types.js";
|
|
1
2
|
export interface ListFooterProps {
|
|
2
3
|
/** How many rows are currently rendered. */
|
|
3
4
|
shown: number;
|
|
@@ -11,13 +12,39 @@ export interface ListFooterProps {
|
|
|
11
12
|
onLoadMore: () => void;
|
|
12
13
|
/** Plural noun for the count, e.g. `"instances"`. Default: the locale's word for rows. */
|
|
13
14
|
noun?: string;
|
|
15
|
+
/**
|
|
16
|
+
* A new first page is in flight while these rows stay on screen (a search,
|
|
17
|
+
* a filter, a refetch): the status line adds a visible "Updating…".
|
|
18
|
+
*/
|
|
19
|
+
refreshing?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The rows on screen are the PREVIOUS result (a changed search or filter is
|
|
22
|
+
* in flight or failed): the count and "Load more" hide, so the old count
|
|
23
|
+
* never reads as the new search's answer.
|
|
24
|
+
*/
|
|
25
|
+
stale?: boolean;
|
|
26
|
+
/** Override strings of the active catalog's `listFooter` section. */
|
|
27
|
+
labels?: Partial<ListFooterMessages>;
|
|
28
|
+
/**
|
|
29
|
+
* Make the count line a polite live region (`role="status"`). Default
|
|
30
|
+
* `true`. Set `false` only where the caller already announces the count in
|
|
31
|
+
* a live region of its own, so a screen reader hears it once.
|
|
32
|
+
*/
|
|
33
|
+
announce?: boolean;
|
|
34
|
+
className?: string;
|
|
14
35
|
}
|
|
15
36
|
/**
|
|
16
37
|
* Footer for a paginated list: an honest "Showing X of Y" total plus an
|
|
17
38
|
* explicit "Load more" control. Deliberately not infinite scroll — the user
|
|
18
|
-
* gets the total, a reachable footer, and control over fetching.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
39
|
+
* gets the total, a reachable footer, and control over fetching. The count
|
|
40
|
+
* is a polite live region (`role="status"`), so a keyboard or screen-reader
|
|
41
|
+
* user hears the new total after "Load more" or a filter change; while a
|
|
42
|
+
* first page is in flight it also says "Updating…". With nothing to count and
|
|
43
|
+
* nothing in flight it shows nothing, but the live region stays mounted
|
|
44
|
+
* (visually hidden) and says "No entries": a search that finds nothing is
|
|
45
|
+
* announced too, and the region is never re-created with its news already in
|
|
46
|
+
* it (a screen reader stays silent then). Text and number format follow the
|
|
47
|
+
* active locale (`LocaleProvider`, else the host's inside `McpAppView`).
|
|
21
48
|
*/
|
|
22
|
-
export declare function ListFooter({ shown, total, hasMore, loadingMore, onLoadMore, noun, }: ListFooterProps): import("react").JSX.Element | null;
|
|
49
|
+
export declare function ListFooter({ shown, total, hasMore, loadingMore, onLoadMore, noun, refreshing, stale, labels, announce, className, }: ListFooterProps): import("react").JSX.Element | null;
|
|
23
50
|
//# sourceMappingURL=ListFooter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListFooter.d.ts","sourceRoot":"","sources":["../../src/components/ListFooter.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ListFooter.d.ts","sourceRoot":"","sources":["../../src/components/ListFooter.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAGpE,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,mDAAmD;IACnD,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,OAAO,EACP,WAAmB,EACnB,UAAU,EACV,IAAI,EACJ,UAAkB,EAClB,KAAa,EACb,MAAM,EACN,QAAe,EACf,SAAS,GACV,EAAE,eAAe,sCAwCjB"}
|
|
@@ -1,23 +1,42 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { FOCUS_RING } from "../lib/focus-ring.js";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
3
4
|
import { useToolkitLocale, useToolkitMessages } from "../locale/locale-provider.js";
|
|
4
5
|
import { formatCount } from "../locale/toolkit-messages.js";
|
|
5
6
|
/**
|
|
6
7
|
* Footer for a paginated list: an honest "Showing X of Y" total plus an
|
|
7
8
|
* explicit "Load more" control. Deliberately not infinite scroll — the user
|
|
8
|
-
* gets the total, a reachable footer, and control over fetching.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* gets the total, a reachable footer, and control over fetching. The count
|
|
10
|
+
* is a polite live region (`role="status"`), so a keyboard or screen-reader
|
|
11
|
+
* user hears the new total after "Load more" or a filter change; while a
|
|
12
|
+
* first page is in flight it also says "Updating…". With nothing to count and
|
|
13
|
+
* nothing in flight it shows nothing, but the live region stays mounted
|
|
14
|
+
* (visually hidden) and says "No entries": a search that finds nothing is
|
|
15
|
+
* announced too, and the region is never re-created with its news already in
|
|
16
|
+
* it (a screen reader stays silent then). Text and number format follow the
|
|
17
|
+
* active locale (`LocaleProvider`, else the host's inside `McpAppView`).
|
|
11
18
|
*/
|
|
12
|
-
export function ListFooter({ shown, total, hasMore, loadingMore = false, onLoadMore, noun, }) {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
19
|
+
export function ListFooter({ shown, total, hasMore, loadingMore = false, onLoadMore, noun, refreshing = false, stale = false, labels, announce = true, className, }) {
|
|
20
|
+
const listFooter = { ...useToolkitMessages().listFooter, ...labels };
|
|
21
|
+
const counted = total > 0 && !stale;
|
|
22
|
+
const visible = counted || refreshing;
|
|
23
|
+
// Without a live region of its own there is nothing to keep mounted.
|
|
24
|
+
if (!visible && !announce)
|
|
16
25
|
return null;
|
|
17
|
-
return (_jsxs("div", {
|
|
26
|
+
return (_jsxs("div", { "data-slot": "list-footer",
|
|
27
|
+
// The same element tree in both states, so React keeps the live region.
|
|
28
|
+
className: visible ? cn(FOOTER_CLASS, className) : "sr-only", children: [_jsx("p", { role: announce ? "status" : undefined, "data-slot": "list-footer-status", className: "flex flex-wrap items-center gap-x-2", children: _jsx(StatusLine, { messages: listFooter, shown: shown, total: total, noun: noun, counted: counted, refreshing: refreshing,
|
|
29
|
+
// Zero rows, not a stale previous result (its failure the caller reports).
|
|
30
|
+
empty: !visible && !stale }) }), counted && hasMore && (_jsx("button", { type: "button", onClick: onLoadMore, disabled: loadingMore, className: `border-border text-foreground hover:border-focus hover:bg-accent hover:text-accent-foreground inline-flex items-center gap-1 rounded-md border px-3 py-1.5 font-medium transition-colors disabled:opacity-50 ${FOCUS_RING}`, children: loadingMore ? listFooter.loadingMore : listFooter.loadMore }))] }));
|
|
31
|
+
}
|
|
32
|
+
const FOOTER_CLASS = "text-small text-muted-foreground flex items-center justify-between gap-3 px-1 py-2";
|
|
33
|
+
/** What the live region says: the count, "Updating…", or "No entries" at zero rows. */
|
|
34
|
+
function StatusLine({ messages, shown, total, noun, counted, refreshing, empty, }) {
|
|
35
|
+
const locale = useToolkitLocale();
|
|
36
|
+
return (_jsxs(_Fragment, { children: [empty && _jsx("span", { children: messages.noResults }), counted && (_jsx("span", { children: messages.showing({
|
|
18
37
|
shown: formatCount(shown, locale),
|
|
19
38
|
total: formatCount(total, locale),
|
|
20
|
-
noun: noun ??
|
|
21
|
-
}) }),
|
|
39
|
+
noun: noun ?? messages.defaultNoun,
|
|
40
|
+
}) })), refreshing && _jsx("span", { "data-slot": "list-footer-updating", children: messages.updating })] }));
|
|
22
41
|
}
|
|
23
42
|
//# sourceMappingURL=ListFooter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListFooter.js","sourceRoot":"","sources":["../../src/components/ListFooter.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"ListFooter.js","sourceRoot":"","sources":["../../src/components/ListFooter.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAqC3D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,OAAO,EACP,WAAW,GAAG,KAAK,EACnB,UAAU,EACV,IAAI,EACJ,UAAU,GAAG,KAAK,EAClB,KAAK,GAAG,KAAK,EACb,MAAM,EACN,QAAQ,GAAG,IAAI,EACf,SAAS,GACO;IAChB,MAAM,UAAU,GAAG,EAAE,GAAG,kBAAkB,EAAE,CAAC,UAAU,EAAE,GAAG,MAAM,EAAE,CAAA;IACpE,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,CAAA;IACnC,MAAM,OAAO,GAAG,OAAO,IAAI,UAAU,CAAA;IACrC,qEAAqE;IACrE,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IACtC,OAAO,CACL,4BACY,aAAa;QACvB,wEAAwE;QACxE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,aAE5D,YACE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,eAC3B,oBAAoB,EAC9B,SAAS,EAAC,qCAAqC,YAE/C,KAAC,UAAU,IACT,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU;oBACtB,2EAA2E;oBAC3E,KAAK,EAAE,CAAC,OAAO,IAAI,CAAC,KAAK,GACzB,GACA,EACH,OAAO,IAAI,OAAO,IAAI,CACrB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,gNAAgN,UAAU,EAAE,YAEtO,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,GACpD,CACV,IACG,CACP,CAAA;AACH,CAAC;AAED,MAAM,YAAY,GAChB,oFAAoF,CAAA;AAEtF,uFAAuF;AACvF,SAAS,UAAU,CAAC,EAClB,QAAQ,EACR,KAAK,EACL,KAAK,EACL,IAAI,EACJ,OAAO,EACP,UAAU,EACV,KAAK,GASN;IACC,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAA;IACjC,OAAO,CACL,8BACG,KAAK,IAAI,yBAAO,QAAQ,CAAC,SAAS,GAAQ,EAC1C,OAAO,IAAI,CACV,yBACG,QAAQ,CAAC,OAAO,CAAC;oBAChB,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC;oBACjC,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC;oBACjC,IAAI,EAAE,IAAI,IAAI,QAAQ,CAAC,WAAW;iBACnC,CAAC,GACG,CACR,EACA,UAAU,IAAI,4BAAgB,sBAAsB,YAAE,QAAQ,CAAC,QAAQ,GAAQ,IAC/E,CACJ,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ListFooterMessages } from "../locale/message-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* What {@link PagedListFooter} reads from a paged list — structural, so a
|
|
4
|
+
* paged-query hook's result (`items`, `total`, `loadMore`, …) passes as is.
|
|
5
|
+
*/
|
|
6
|
+
export interface PagedListState {
|
|
7
|
+
/** The rows on screen (all loaded pages); its length is the "shown" count. */
|
|
8
|
+
items: readonly unknown[];
|
|
9
|
+
/** The server's total for the rows on screen. */
|
|
10
|
+
total: number;
|
|
11
|
+
/** Whether another page exists. */
|
|
12
|
+
hasMore: boolean;
|
|
13
|
+
/** Fetch the next page (also the retry of a failed load-more). */
|
|
14
|
+
loadMore: () => void;
|
|
15
|
+
/** A next page is in flight. */
|
|
16
|
+
loadingMore?: boolean;
|
|
17
|
+
/** A first page is in flight while rows are on screen (a search, a filter, a refetch). */
|
|
18
|
+
refreshing?: boolean;
|
|
19
|
+
/** The rows on screen are the previous result: a changed search or filter is in flight or failed. */
|
|
20
|
+
stale?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The first page that is on screen, if any. A first-page {@link error} only
|
|
23
|
+
* belongs in the footer while one is shown; without rows on screen it is
|
|
24
|
+
* the load error the caller's loading guard renders.
|
|
25
|
+
*/
|
|
26
|
+
firstPage?: unknown;
|
|
27
|
+
/** The first-page failure (see {@link firstPage}). */
|
|
28
|
+
error?: Error | null;
|
|
29
|
+
/** Re-run the first page — the retry of {@link error}. */
|
|
30
|
+
retry?: () => void;
|
|
31
|
+
/** The last load-more failure; {@link loadMore} retries it, the rows above stay current. */
|
|
32
|
+
loadMoreError?: Error | null;
|
|
33
|
+
}
|
|
34
|
+
export interface PagedListFooterProps {
|
|
35
|
+
/** The paged list's state. */
|
|
36
|
+
paged: PagedListState;
|
|
37
|
+
/** Plural noun for "Showing X of Y {noun}". Default: the locale's word for rows. */
|
|
38
|
+
noun?: string;
|
|
39
|
+
/** Override strings of the active catalog's `listFooter` section. */
|
|
40
|
+
labels?: Partial<ListFooterMessages>;
|
|
41
|
+
/** Retry button label of both error lines. Default: the locale's "Try again". */
|
|
42
|
+
retryLabel?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The tail of a paged list: its failures and the honest {@link ListFooter}.
|
|
46
|
+
* Two failures stay apart because their retries differ — a failed first page
|
|
47
|
+
* over the rows still on screen ("You're seeing the previous result", its
|
|
48
|
+
* retry re-runs page 0) and a failed load-more ("The entries above are
|
|
49
|
+
* current", its retry fetches the next offset). Each is an inline
|
|
50
|
+
* `ErrorState` (role="alert") with the raw reason under "Details". The
|
|
51
|
+
* footer's count line is a polite live region that says "Updating…" while a
|
|
52
|
+
* first page is in flight, hides the count while the rows are stale, and
|
|
53
|
+
* stays mounted at zero rows ("No entries", visually hidden), so a search
|
|
54
|
+
* that finds nothing is announced.
|
|
55
|
+
*/
|
|
56
|
+
export declare function PagedListFooter({ paged, noun, labels, retryLabel }: PagedListFooterProps): import("react").JSX.Element;
|
|
57
|
+
//# sourceMappingURL=PagedListFooter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PagedListFooter.d.ts","sourceRoot":"","sources":["../../src/components/PagedListFooter.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAIpE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;IACzB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,gCAAgC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qGAAqG;IACrG,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,sDAAsD;IACtD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;IAClB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,8BAA8B;IAC9B,KAAK,EAAE,cAAc,CAAA;IACrB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACpC,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,oBAAoB,+BAwCxF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useToolkitMessages } from "../locale/locale-provider.js";
|
|
3
|
+
import { ErrorState } from "./ErrorState.js";
|
|
4
|
+
import { ListFooter } from "./ListFooter.js";
|
|
5
|
+
/** The raw reason of a failure, for the "Details" disclosure (never the sentence). */
|
|
6
|
+
function rawReason(error) {
|
|
7
|
+
return error.message || error.name;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The tail of a paged list: its failures and the honest {@link ListFooter}.
|
|
11
|
+
* Two failures stay apart because their retries differ — a failed first page
|
|
12
|
+
* over the rows still on screen ("You're seeing the previous result", its
|
|
13
|
+
* retry re-runs page 0) and a failed load-more ("The entries above are
|
|
14
|
+
* current", its retry fetches the next offset). Each is an inline
|
|
15
|
+
* `ErrorState` (role="alert") with the raw reason under "Details". The
|
|
16
|
+
* footer's count line is a polite live region that says "Updating…" while a
|
|
17
|
+
* first page is in flight, hides the count while the rows are stale, and
|
|
18
|
+
* stays mounted at zero rows ("No entries", visually hidden), so a search
|
|
19
|
+
* that finds nothing is announced.
|
|
20
|
+
*/
|
|
21
|
+
export function PagedListFooter({ paged, noun, labels, retryLabel }) {
|
|
22
|
+
const catalog = useToolkitMessages().listFooter;
|
|
23
|
+
const L = { ...catalog, ...labels };
|
|
24
|
+
const refreshError = paged.firstPage != null ? (paged.error ?? null) : null;
|
|
25
|
+
return (_jsxs("div", { "data-slot": "paged-list-footer", className: "flex flex-col gap-2", children: [refreshError && (_jsx(ErrorState, { variant: "inline", tone: "warning", title: L.refreshFailed, description: L.refreshFailedHint, details: rawReason(refreshError), onRetry: paged.retry, retryLabel: retryLabel })), paged.loadMoreError && (_jsx(ErrorState, { variant: "inline", title: L.loadMoreFailed, description: L.loadMoreFailedHint, details: rawReason(paged.loadMoreError), onRetry: paged.loadMore, retryLabel: retryLabel })), _jsx(ListFooter, { shown: paged.items.length, total: paged.total, hasMore: paged.hasMore, loadingMore: paged.loadingMore, onLoadMore: paged.loadMore, noun: noun, refreshing: paged.refreshing, stale: paged.stale, labels: labels })] }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=PagedListFooter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PagedListFooter.js","sourceRoot":"","sources":["../../src/components/PagedListFooter.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAEjE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AA8C5C,sFAAsF;AACtF,SAAS,SAAS,CAAC,KAAY;IAC7B,OAAO,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAA;AACpC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAwB;IACvF,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC,UAAU,CAAA;IAC/C,MAAM,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,MAAM,EAAE,CAAA;IACnC,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3E,OAAO,CACL,4BAAe,mBAAmB,EAAC,SAAS,EAAC,qBAAqB,aAC/D,YAAY,IAAI,CACf,KAAC,UAAU,IACT,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,SAAS,EACd,KAAK,EAAE,CAAC,CAAC,aAAa,EACtB,WAAW,EAAE,CAAC,CAAC,iBAAiB,EAChC,OAAO,EAAE,SAAS,CAAC,YAAY,CAAC,EAChC,OAAO,EAAE,KAAK,CAAC,KAAK,EACpB,UAAU,EAAE,UAAU,GACtB,CACH,EACA,KAAK,CAAC,aAAa,IAAI,CACtB,KAAC,UAAU,IACT,OAAO,EAAC,QAAQ,EAChB,KAAK,EAAE,CAAC,CAAC,cAAc,EACvB,WAAW,EAAE,CAAC,CAAC,kBAAkB,EACjC,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,EACvC,OAAO,EAAE,KAAK,CAAC,QAAQ,EACvB,UAAU,EAAE,UAAU,GACtB,CACH,EACD,KAAC,UAAU,IACT,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,EACzB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,UAAU,EAAE,KAAK,CAAC,QAAQ,EAC1B,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,KAAK,CAAC,UAAU,EAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,MAAM,EAAE,MAAM,GACd,IACE,CACP,CAAA;AACH,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import { type
|
|
1
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
|
+
import { type ToneName } from "../lib/tone-utils.js";
|
|
3
3
|
export interface LivePillProps {
|
|
4
4
|
/** Status tone driving the dot, tint and border. Default `"info"`. */
|
|
5
|
-
tone?:
|
|
5
|
+
tone?: ToneName;
|
|
6
6
|
/** Pill label. Defaults to the locale's "Live". */
|
|
7
7
|
children?: ReactNode;
|
|
8
|
+
className?: string;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Small tinted pill with a pulsing dot. Use in a widget header to signal that
|
|
@@ -12,18 +13,37 @@ export interface LivePillProps {
|
|
|
12
13
|
* text in the foreground), so it inherits the active theme. The pulse stops
|
|
13
14
|
* under `prefers-reduced-motion`.
|
|
14
15
|
*/
|
|
15
|
-
export declare function LivePill({ tone, children }: LivePillProps): import("react").JSX.Element;
|
|
16
|
+
export declare function LivePill({ tone, children, className }: LivePillProps): import("react").JSX.Element;
|
|
17
|
+
/** The words a {@link StatusBadge} must carry: text, a number or an element — never nothing. */
|
|
18
|
+
export type StatusBadgeText = string | number | ReactElement | (string | number | ReactElement)[];
|
|
19
|
+
export interface StatusBadgeProps {
|
|
20
|
+
/** The state, in words ("Suspended", "3 open incidents"). Required: colour is never the only signal. */
|
|
21
|
+
children: StatusBadgeText;
|
|
22
|
+
/** The state's tone: tint, edge and dot. */
|
|
23
|
+
tone: ToneName;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A state in words, marked in its tone: tint, edge and a static dot next to
|
|
28
|
+
* foreground text (CI §3.3: the colour marks the state, the words say it).
|
|
29
|
+
* For a detail-page header (`WidgetHeader`'s `badge`) or a table cell. The
|
|
30
|
+
* text is required — a badge without words would make colour the only
|
|
31
|
+
* signal. The pulsing dot is {@link LivePill}'s live-data semantic.
|
|
32
|
+
*/
|
|
33
|
+
export declare function StatusBadge({ children, tone, className }: StatusBadgeProps): import("react").JSX.Element;
|
|
16
34
|
export interface CountPillProps {
|
|
17
35
|
/** Status tone driving the tint and border. Default `"neutral"`. */
|
|
18
|
-
tone?:
|
|
36
|
+
tone?: ToneName;
|
|
19
37
|
/** The count/value to display (tabular numbers). */
|
|
20
38
|
children: ReactNode;
|
|
39
|
+
className?: string;
|
|
21
40
|
}
|
|
22
41
|
/**
|
|
23
42
|
* Compact count badge — tabular numbers on a tinted background with a border
|
|
24
43
|
* in the tone. Use as the right-aligned indicator on a list/group row (e.g. a
|
|
25
44
|
* per-group item count). The number stays in the foreground in every tone
|
|
26
|
-
* (no coloured digits): the tint and the border carry the tone.
|
|
45
|
+
* (no coloured digits): the tint and the border carry the tone. A count
|
|
46
|
+
* without a state stays `neutral`.
|
|
27
47
|
*/
|
|
28
|
-
export declare function CountPill({ tone, children }: CountPillProps): import("react").JSX.Element;
|
|
48
|
+
export declare function CountPill({ tone, children, className }: CountPillProps): import("react").JSX.Element;
|
|
29
49
|
//# sourceMappingURL=Pills.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pills.d.ts","sourceRoot":"","sources":["../../src/components/Pills.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Pills.d.ts","sourceRoot":"","sources":["../../src/components/Pills.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEpD,OAAO,EAAiD,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAGnG,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,mDAAmD;IACnD,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,+BAmB7E;AAED,gGAAgG;AAChG,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,YAAY,CAAC,EAAE,CAAA;AAEjG,MAAM,WAAW,gBAAgB;IAC/B,wGAAwG;IACxG,QAAQ,EAAE,eAAe,CAAA;IACzB,4CAA4C;IAC5C,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAiB1E;AAED,MAAM,WAAW,cAAc;IAC7B,oEAAoE;IACpE,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,oDAAoD;IACpD,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,EAAE,IAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,cAAc,+BAclF"}
|
package/dist/components/Pills.js
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useToolkitMessages } from "../locale/locale-provider.js";
|
|
3
|
-
import { TONE_BORDER, TONE_DOT, TONE_SOFT } from "../lib/tone-utils.js";
|
|
3
|
+
import { resolveTone, TONE_BORDER, TONE_DOT, TONE_SOFT } from "../lib/tone-utils.js";
|
|
4
|
+
import { cn } from "../lib/utils.js";
|
|
4
5
|
/**
|
|
5
6
|
* Small tinted pill with a pulsing dot. Use in a widget header to signal that
|
|
6
7
|
* the data is live/real-time. Tone-based (tint, border and dot in the tone,
|
|
7
8
|
* text in the foreground), so it inherits the active theme. The pulse stops
|
|
8
9
|
* under `prefers-reduced-motion`.
|
|
9
10
|
*/
|
|
10
|
-
export function LivePill({ tone = "info", children }) {
|
|
11
|
+
export function LivePill({ tone = "info", children, className }) {
|
|
11
12
|
const messages = useToolkitMessages();
|
|
12
|
-
|
|
13
|
+
const t = resolveTone(tone, "info");
|
|
14
|
+
return (_jsxs("span", { className: cn("text-small inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 font-semibold", TONE_SOFT[t], TONE_BORDER[t], className), children: [_jsx("span", { "aria-hidden": "true", className: cn("size-1.5 rounded-full motion-safe:animate-pulse", TONE_DOT[t]) }), children ?? messages.pills.live] }));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A state in words, marked in its tone: tint, edge and a static dot next to
|
|
18
|
+
* foreground text (CI §3.3: the colour marks the state, the words say it).
|
|
19
|
+
* For a detail-page header (`WidgetHeader`'s `badge`) or a table cell. The
|
|
20
|
+
* text is required — a badge without words would make colour the only
|
|
21
|
+
* signal. The pulsing dot is {@link LivePill}'s live-data semantic.
|
|
22
|
+
*/
|
|
23
|
+
export function StatusBadge({ children, tone, className }) {
|
|
24
|
+
const t = resolveTone(tone);
|
|
25
|
+
return (_jsxs("span", { "data-slot": "status-badge", "data-tone": t, className: cn("text-small inline-flex w-fit items-center gap-1.5 rounded-sm border px-2.5 py-0.5 font-semibold", TONE_SOFT[t], TONE_BORDER[t], className), children: [_jsx("span", { "aria-hidden": "true", className: cn("size-1.5 shrink-0 rounded-full", TONE_DOT[t]) }), children] }));
|
|
13
26
|
}
|
|
14
27
|
/**
|
|
15
28
|
* Compact count badge — tabular numbers on a tinted background with a border
|
|
16
29
|
* in the tone. Use as the right-aligned indicator on a list/group row (e.g. a
|
|
17
30
|
* per-group item count). The number stays in the foreground in every tone
|
|
18
|
-
* (no coloured digits): the tint and the border carry the tone.
|
|
31
|
+
* (no coloured digits): the tint and the border carry the tone. A count
|
|
32
|
+
* without a state stays `neutral`.
|
|
19
33
|
*/
|
|
20
|
-
export function CountPill({ tone = "neutral", children }) {
|
|
21
|
-
|
|
34
|
+
export function CountPill({ tone = "neutral", children, className }) {
|
|
35
|
+
const t = resolveTone(tone);
|
|
36
|
+
return (_jsx("span", { className: cn("text-dense inline-flex min-w-10 items-center justify-center rounded-sm border px-2.5 py-0.5 font-semibold tabular-nums", TONE_SOFT[t], TONE_BORDER[t], className), children: children }));
|
|
22
37
|
}
|
|
23
38
|
//# sourceMappingURL=Pills.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pills.js","sourceRoot":"","sources":["../../src/components/Pills.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"Pills.js","sourceRoot":"","sources":["../../src/components/Pills.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAiB,MAAM,sBAAsB,CAAA;AACnG,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AAUpC;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,IAAI,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAiB;IAC5E,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;IACrC,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACnC,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CACX,yFAAyF,EACzF,SAAS,CAAC,CAAC,CAAC,EACZ,WAAW,CAAC,CAAC,CAAC,EACd,SAAS,CACV,aAED,8BACc,MAAM,EAClB,SAAS,EAAE,EAAE,CAAC,iDAAiD,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAC7E,EACD,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,IAC3B,CACR,CAAA;AACH,CAAC;AAaD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAoB;IACzE,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;IAC3B,OAAO,CACL,6BACY,cAAc,eACb,CAAC,EACZ,SAAS,EAAE,EAAE,CACX,iGAAiG,EACjG,SAAS,CAAC,CAAC,CAAC,EACZ,WAAW,CAAC,CAAC,CAAC,EACd,SAAS,CACV,aAED,8BAAkB,MAAM,EAAC,SAAS,EAAE,EAAE,CAAC,gCAAgC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAI,EACxF,QAAQ,IACJ,CACR,CAAA;AACH,CAAC;AAUD;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAkB;IACjF,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;IAC3B,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,wHAAwH,EACxH,SAAS,CAAC,CAAC,CAAC,EACZ,WAAW,CAAC,CAAC,CAAC,EACd,SAAS,CACV,YAEA,QAAQ,GACJ,CACR,CAAA;AACH,CAAC"}
|
|
@@ -10,7 +10,9 @@ export interface SectionHeadingProps {
|
|
|
10
10
|
/**
|
|
11
11
|
* Section title row — a small semibold heading with an optional muted hint or
|
|
12
12
|
* control on the right. Use to label a block inside a widget body (e.g. a list
|
|
13
|
-
* or sub-table) without the visual weight of a full card header.
|
|
13
|
+
* or sub-table) without the visual weight of a full card header. The title is
|
|
14
|
+
* a caption ({@link MICRO_LABEL}: 13 px semibold, normal case); a section that
|
|
15
|
+
* needs a real heading takes `text-section` (21 px) instead.
|
|
14
16
|
*/
|
|
15
17
|
export declare function SectionHeading({ title, hint, trailing }: SectionHeadingProps): import("react").JSX.Element;
|
|
16
18
|
//# sourceMappingURL=SectionHeading.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionHeading.d.ts","sourceRoot":"","sources":["../../src/components/SectionHeading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SectionHeading.d.ts","sourceRoot":"","sources":["../../src/components/SectionHeading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,WAAW,mBAAmB;IAClC,yBAAyB;IACzB,KAAK,EAAE,SAAS,CAAA;IAChB,kFAAkF;IAClF,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,mBAAmB,+BAW5E"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MICRO_LABEL } from "../lib/typography.js";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
2
4
|
/**
|
|
3
5
|
* Section title row — a small semibold heading with an optional muted hint or
|
|
4
6
|
* control on the right. Use to label a block inside a widget body (e.g. a list
|
|
5
|
-
* or sub-table) without the visual weight of a full card header.
|
|
7
|
+
* or sub-table) without the visual weight of a full card header. The title is
|
|
8
|
+
* a caption ({@link MICRO_LABEL}: 13 px semibold, normal case); a section that
|
|
9
|
+
* needs a real heading takes `text-section` (21 px) instead.
|
|
6
10
|
*/
|
|
7
11
|
export function SectionHeading({ title, hint, trailing }) {
|
|
8
|
-
return (_jsxs("div", { className: "text-muted-foreground mb-3 flex items-center justify-between
|
|
12
|
+
return (_jsxs("div", { className: cn("text-muted-foreground mb-3 flex items-center justify-between", MICRO_LABEL), children: [_jsx("span", { children: title }), (hint || trailing) && (_jsx("span", { className: "text-muted-foreground text-small font-medium", children: trailing ?? hint }))] }));
|
|
9
13
|
}
|
|
10
14
|
//# sourceMappingURL=SectionHeading.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionHeading.js","sourceRoot":"","sources":["../../src/components/SectionHeading.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"SectionHeading.js","sourceRoot":"","sources":["../../src/components/SectionHeading.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AAWpC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAuB;IAC3E,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CAAC,8DAA8D,EAAE,WAAW,CAAC,aAE1F,yBAAO,KAAK,GAAQ,EACnB,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CACrB,eAAM,SAAS,EAAC,8CAA8C,YAAE,QAAQ,IAAI,IAAI,GAAQ,CACzF,IACG,CACP,CAAA;AACH,CAAC"}
|