@scalepad/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ai/rules/date-handling.md +39 -0
- package/.ai/rules/figma-design-system.md +372 -0
- package/.ai/rules/figma-lm-design-system-keys.md +680 -0
- package/.ai/rules/file-extensions.md +13 -0
- package/.ai/rules/modal-confirmation-mutation.md +56 -0
- package/.ai/rules/react-hooks.md +29 -0
- package/.ai/rules/styling.md +83 -0
- package/AGENTS.md +37 -0
- package/README.md +125 -0
- package/figma.config.json +9 -0
- package/package.json +127 -0
- package/scripts/install-ai-rules.mjs +136 -0
- package/src/ThemeProvider.tsx +57 -0
- package/src/charts.ts +32 -0
- package/src/components/ActionCard/ActionCard.css.ts +60 -0
- package/src/components/ActionCard/ActionCard.tsx +154 -0
- package/src/components/ActionCard/index.ts +2 -0
- package/src/components/Anchor/Anchor.tsx +47 -0
- package/src/components/Anchor/index.ts +2 -0
- package/src/components/AppliedFiltersManagerBar/AppliedFiltersManagerBar.tsx +105 -0
- package/src/components/AppliedFiltersManagerBar/FilterBadge.css.ts +23 -0
- package/src/components/AppliedFiltersManagerBar/FilterBadge.tsx +50 -0
- package/src/components/AppliedFiltersManagerBar/index.ts +5 -0
- package/src/components/Badge/Badge.css.ts +72 -0
- package/src/components/Badge/Badge.figma.tsx +43 -0
- package/src/components/Badge/Badge.tsx +159 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/BreadCrumb/BreadCrumb.tsx +62 -0
- package/src/components/BreadCrumb/index.ts +2 -0
- package/src/components/BulkActionBar/BulkActionBar.css.ts +26 -0
- package/src/components/BulkActionBar/BulkActionBar.tsx +164 -0
- package/src/components/BulkActionBar/index.ts +2 -0
- package/src/components/Button/Button.css.ts +272 -0
- package/src/components/Button/Button.figma.tsx +74 -0
- package/src/components/Button/Button.tsx +84 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Charts/ChartTooltip.figma.tsx +33 -0
- package/src/components/Charts/ChartTooltip.tsx +101 -0
- package/src/components/Charts/MiniBarSparkline.tsx +75 -0
- package/src/components/Charts/StackedPatternBarChart.tsx +494 -0
- package/src/components/Charts/TrendAreaChart.css.ts +23 -0
- package/src/components/Charts/TrendAreaChart.tsx +210 -0
- package/src/components/Charts/index.ts +12 -0
- package/src/components/CodePanel/CodePanel.css.ts +113 -0
- package/src/components/CodePanel/CodePanel.tsx +121 -0
- package/src/components/CodePanel/index.ts +2 -0
- package/src/components/CommentComposer/CommentComposer.css.ts +60 -0
- package/src/components/CommentComposer/CommentComposer.tsx +181 -0
- package/src/components/CommentComposer/index.ts +2 -0
- package/src/components/ConfirmationModal/ConfirmationModal.tsx +149 -0
- package/src/components/ConfirmationModal/index.ts +2 -0
- package/src/components/ConfirmationTooltip/ConfirmationTooltip.tsx +132 -0
- package/src/components/ConfirmationTooltip/index.ts +2 -0
- package/src/components/DataDialog.figma.tsx +33 -0
- package/src/components/DataDialog.tsx +46 -0
- package/src/components/DataTable/DataTable.tsx +1042 -0
- package/src/components/DataTable/RowExpandToggle.tsx +105 -0
- package/src/components/DataTable/RowGroupHeader.tsx +190 -0
- package/src/components/DataTable/createActionsColumn.tsx +86 -0
- package/src/components/DataTable/index.ts +25 -0
- package/src/components/DatePicker/CustomRangePicker.tsx +59 -0
- package/src/components/DatePicker/DateInput.tsx +329 -0
- package/src/components/DatePicker/DateNavigator.tsx +486 -0
- package/src/components/DatePicker/DatePicker.tsx +242 -0
- package/src/components/DatePicker/MonthlyRangePicker.tsx +231 -0
- package/src/components/DatePicker/QuarterlyRangePicker.tsx +224 -0
- package/src/components/DatePicker/QuickPicksSidebar.tsx +242 -0
- package/src/components/DatePicker/YearlyRangePicker.tsx +171 -0
- package/src/components/DatePicker/index.ts +7 -0
- package/src/components/DatePicker/types.ts +12 -0
- package/src/components/DesignSystemPrimitives/FluidGrid.tsx +44 -0
- package/src/components/DesignSystemPrimitives/InteractivePrimitives.tsx +177 -0
- package/src/components/DesignSystemPrimitives/LayoutPrimitives.tsx +220 -0
- package/src/components/DesignSystemPrimitives/LayoutPrimitives.types.tsx +15 -0
- package/src/components/DesignSystemPrimitives/SurfacePrimitives.tsx +46 -0
- package/src/components/DesignSystemPrimitives/index.ts +55 -0
- package/src/components/Details/Details.css.ts +74 -0
- package/src/components/Details/Details.tsx +140 -0
- package/src/components/Details/index.ts +2 -0
- package/src/components/DownloadCard/DownloadCard.css.ts +22 -0
- package/src/components/DownloadCard/DownloadCard.tsx +63 -0
- package/src/components/DownloadCard/index.ts +2 -0
- package/src/components/Drawer/Drawer.css.ts +32 -0
- package/src/components/Drawer/Drawer.tsx +236 -0
- package/src/components/Drawer/hooks/useDetailDrawer.ts +61 -0
- package/src/components/Drawer/hooks/useDetailDrawerNavigation.ts +125 -0
- package/src/components/Drawer/hooks/useDetailDrawerNavigationContext.ts +66 -0
- package/src/components/EditableRichText/EditableRichText.css.ts +72 -0
- package/src/components/EditableRichText/EditableRichText.tsx +324 -0
- package/src/components/EditableRichText/index.ts +2 -0
- package/src/components/EditableSelect/EditableSelect.css.ts +62 -0
- package/src/components/EditableSelect/EditableSelect.tsx +224 -0
- package/src/components/EditableSelect/index.ts +2 -0
- package/src/components/EditableText/EditableText.tsx +377 -0
- package/src/components/EditableText/index.ts +2 -0
- package/src/components/EmptyState/EmptyState.figma.tsx +33 -0
- package/src/components/EmptyState/EmptyState.tsx +230 -0
- package/src/components/EmptyState/index.ts +2 -0
- package/src/components/ErrorBoundary.tsx +135 -0
- package/src/components/ErrorState/ErrorState.tsx +197 -0
- package/src/components/ErrorState/index.ts +2 -0
- package/src/components/FeatureCard.tsx +42 -0
- package/src/components/FilterMenu/FilterMenu.figma.tsx +30 -0
- package/src/components/FilterMenu/FilterMenu.tsx +198 -0
- package/src/components/FilterMenu/FilterSubMenuTypes/BooleanFilterSubmenu.tsx +46 -0
- package/src/components/FilterMenu/FilterSubMenuTypes/SearchableFilterSubmenu.tsx +239 -0
- package/src/components/FilterMenu/FilterSubMenuTypes/index.ts +8 -0
- package/src/components/FilterMenu/defaultFilterSchemas.ts +63 -0
- package/src/components/FilterMenu/helpers.ts +115 -0
- package/src/components/FilterMenu/index.ts +35 -0
- package/src/components/FilterMenu/types.ts +101 -0
- package/src/components/IconButton/IconButton.css.ts +272 -0
- package/src/components/IconButton/IconButton.figma.tsx +47 -0
- package/src/components/IconButton/IconButton.tsx +72 -0
- package/src/components/IconButton/README.md +230 -0
- package/src/components/IconButton/index.ts +2 -0
- package/src/components/InfiniteScrollSentinel.tsx +86 -0
- package/src/components/InfiniteScrollTrigger.tsx +78 -0
- package/src/components/InfoCard.figma.tsx +47 -0
- package/src/components/InfoCard.tsx +216 -0
- package/src/components/KbdHint/KbdHint.tsx +23 -0
- package/src/components/KbdHint/index.ts +2 -0
- package/src/components/LabeledField/LabeledField.tsx +21 -0
- package/src/components/LabeledField/index.ts +2 -0
- package/src/components/LookupSelect/LookupSelect.css.ts +149 -0
- package/src/components/LookupSelect/LookupSelect.tsx +325 -0
- package/src/components/LookupSelect/index.ts +2 -0
- package/src/components/Menu/Menu.css.ts +89 -0
- package/src/components/Menu/Menu.tsx +105 -0
- package/src/components/Menu/index.ts +2 -0
- package/src/components/MessageBox/MessageBox.tsx +168 -0
- package/src/components/MessageBox/index.ts +2 -0
- package/src/components/MetricDisplay/MetricDisplay.tsx +55 -0
- package/src/components/MetricDisplay/index.ts +1 -0
- package/src/components/MultiSelect/MultiSelect.tsx +278 -0
- package/src/components/MultiSelect/index.ts +2 -0
- package/src/components/Notifications/Notifications.tsx +12 -0
- package/src/components/Notifications/README.md +93 -0
- package/src/components/Notifications/index.ts +4 -0
- package/src/components/Notifications/showToast.tsx +100 -0
- package/src/components/PropertyRow/PropertyRow.tsx +96 -0
- package/src/components/PropertyRow/index.ts +2 -0
- package/src/components/RadioTile/RadioTile.tsx +253 -0
- package/src/components/RadioTile/index.ts +2 -0
- package/src/components/RichText/FormattingToolbar.css.ts +69 -0
- package/src/components/RichText/FormattingToolbar.tsx +112 -0
- package/src/components/RichText/RichTextInline.css.ts +54 -0
- package/src/components/RichText/RichTextInline.tsx +318 -0
- package/src/components/RichText/formattingCommands.ts +181 -0
- package/src/components/RichText/formattingTypes.ts +34 -0
- package/src/components/RichText/index.ts +49 -0
- package/src/components/RichText/richTextExtensions.ts +111 -0
- package/src/components/RichText/richTextHelpers.ts +65 -0
- package/src/components/RichText/richTextImage.ts +253 -0
- package/src/components/RichText/richTextImageHandlers.ts +244 -0
- package/src/components/RichText/richTextProse.css.ts +261 -0
- package/src/components/RichTextEditor/RichTextEditor.css.ts +82 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +204 -0
- package/src/components/RichTextEditor/index.ts +2 -0
- package/src/components/RichTextView/RichTextView.css.ts +11 -0
- package/src/components/RichTextView/RichTextView.tsx +114 -0
- package/src/components/RichTextView/index.ts +2 -0
- package/src/components/Schedule/Schedule.tsx +35 -0
- package/src/components/SchedulePicker/SchedulePicker.css.ts +42 -0
- package/src/components/SchedulePicker/SchedulePicker.tsx +130 -0
- package/src/components/SchedulePicker/index.ts +2 -0
- package/src/components/SearchableList/types.ts +30 -0
- package/src/components/SearchableSubMenu/SearchableSubMenu.css.ts +25 -0
- package/src/components/SearchableSubMenu/SearchableSubMenu.tsx +139 -0
- package/src/components/SearchableSubMenu/index.ts +2 -0
- package/src/components/Select/README.md +114 -0
- package/src/components/Select/Select.css.ts +110 -0
- package/src/components/Select/Select.tsx +133 -0
- package/src/components/Select/index.ts +2 -0
- package/src/components/SelectCreatable/SelectCreatable.css.ts +16 -0
- package/src/components/SelectCreatable/SelectCreatable.tsx +203 -0
- package/src/components/SelectCreatable/index.ts +2 -0
- package/src/components/SettingsCard/SettingsCard.tsx +98 -0
- package/src/components/SettingsCard/index.ts +2 -0
- package/src/components/Sidebar/Sidebar.css.ts +91 -0
- package/src/components/Sidebar/Sidebar.tsx +129 -0
- package/src/components/Sidebar/index.ts +5 -0
- package/src/components/SimpleList/SimpleList.css.ts +12 -0
- package/src/components/SimpleList/SimpleList.tsx +44 -0
- package/src/components/SimpleList/index.ts +2 -0
- package/src/components/SimpleTable/SimpleTable.tsx +296 -0
- package/src/components/SimpleTable/index.ts +2 -0
- package/src/components/SlashRichTextEditor/SelectionBubbleMenu.css.ts +62 -0
- package/src/components/SlashRichTextEditor/SelectionBubbleMenu.tsx +85 -0
- package/src/components/SlashRichTextEditor/SlashCommandMenu.css.ts +124 -0
- package/src/components/SlashRichTextEditor/SlashCommandMenu.tsx +168 -0
- package/src/components/SlashRichTextEditor/SlashRichTextEditor.css.ts +81 -0
- package/src/components/SlashRichTextEditor/SlashRichTextEditor.tsx +538 -0
- package/src/components/SlashRichTextEditor/SlashSuggestionExtension.ts +48 -0
- package/src/components/SlashRichTextEditor/index.ts +13 -0
- package/src/components/SlashRichTextEditor/types.ts +48 -0
- package/src/components/StatCard/StatCard.css.ts +70 -0
- package/src/components/StatCard/StatCard.tsx +201 -0
- package/src/components/StatCard/index.ts +1 -0
- package/src/components/StatusBadge/StatusBadge.tsx +70 -0
- package/src/components/StatusBadge/index.ts +2 -0
- package/src/components/StatusIndicator/StatusIndicator.tsx +67 -0
- package/src/components/StatusIndicator/index.ts +6 -0
- package/src/components/SubNavigation/SubNavigation.css.ts +72 -0
- package/src/components/SubNavigation/SubNavigation.tsx +104 -0
- package/src/components/SubNavigation/index.ts +2 -0
- package/src/components/SuspenseLoader.tsx +22 -0
- package/src/components/Table/SortableColumnHeader.tsx +99 -0
- package/src/components/Table/TableSkeletonRows.figma.tsx +22 -0
- package/src/components/Table/TableSkeletonRows.tsx +113 -0
- package/src/components/Table/index.ts +9 -0
- package/src/components/TableActionsMenu.tsx +58 -0
- package/src/components/TableCard.tsx +29 -0
- package/src/components/TableContainer/TableContainer.tsx +86 -0
- package/src/components/TableContainer/index.ts +2 -0
- package/src/components/TableControlBar/TableControlBar.tsx +156 -0
- package/src/components/TableControlBar/TableSelectionButton.tsx +57 -0
- package/src/components/TableControlBar/index.ts +13 -0
- package/src/components/TableControlBar/useTableControlBar.tsx +314 -0
- package/src/components/TableSelection/TableSelection.tsx +43 -0
- package/src/components/TableSelection/index.ts +5 -0
- package/src/components/Tabs/README.md +76 -0
- package/src/components/Tabs/Tabs.css.ts +54 -0
- package/src/components/Tabs/Tabs.figma.tsx +47 -0
- package/src/components/Tabs/Tabs.tsx +96 -0
- package/src/components/Tabs/index.ts +8 -0
- package/src/components/TextInput/README.md +98 -0
- package/src/components/TextInput/SearchTextInput.figma.tsx +22 -0
- package/src/components/TextInput/SearchTextInput.tsx +150 -0
- package/src/components/TextInput/TextInput.figma.tsx +44 -0
- package/src/components/TextInput/TextInput.tsx +42 -0
- package/src/components/TextInput/index.ts +4 -0
- package/src/components/ThemeSwitcher.figma.tsx +28 -0
- package/src/components/ThemeSwitcher.tsx +69 -0
- package/src/components/TrendBadge/TrendBadge.tsx +76 -0
- package/src/components/TrendBadge/index.ts +2 -0
- package/src/components/TruncatedText.tsx +115 -0
- package/src/components/Typography/Text.tsx +74 -0
- package/src/components/Typography/Title.tsx +100 -0
- package/src/components/Typography/index.ts +4 -0
- package/src/geist-fonts.ts +48 -0
- package/src/hooks/index.ts +31 -0
- package/src/hooks/useFilters.ts +152 -0
- package/src/hooks/useInfiniteScroll.ts +62 -0
- package/src/hooks/usePlatform.ts +33 -0
- package/src/hooks/useServerTable.ts +495 -0
- package/src/hooks/useTableSelection.ts +102 -0
- package/src/hooks/useTableSort.ts +259 -0
- package/src/index.ts +483 -0
- package/src/mantine.ts +25 -0
- package/src/theme/mantineVars.ts +12 -0
- package/src/theme/themeContract.css.ts +131 -0
- package/src/theme/themeVars.ts +31 -0
- package/src/theme.ts +168 -0
- package/src/tokens/color-types.ts +107 -0
- package/src/tokens/colors.ts +243 -0
- package/src/tokens/index.ts +14 -0
- package/src/tokens/radius.ts +17 -0
- package/src/tokens/semantic-colors.ts +224 -0
- package/src/tokens/semantic-tokens-css.ts +53 -0
- package/src/tokens/shadows.ts +11 -0
- package/src/tokens/spacing.ts +20 -0
- package/src/tokens/text-styles.ts +179 -0
- package/src/tokens/typography.ts +40 -0
- package/src/tokens/zIndex.ts +27 -0
- package/src/types/mantine-theme.d.ts +17 -0
- package/src/types/tanstack-table.d.ts +22 -0
- package/src/utils/avatar.ts +150 -0
- package/src/utils/chartHelpers.ts +53 -0
- package/src/utils/color-props.ts +77 -0
- package/src/utils/createDesignComponent.tsx +104 -0
- package/src/utils/nestFlatRows.ts +111 -0
- package/src/utils/withStaticComponents.ts +6 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# TextInput Component
|
|
2
|
+
|
|
3
|
+
A custom TextInput wrapper component that matches the Figma design specifications for input fields in the design system.
|
|
4
|
+
|
|
5
|
+
## Figma Design Specifications
|
|
6
|
+
|
|
7
|
+
This component implements the following Figma design specifications from node `105:7045`:
|
|
8
|
+
|
|
9
|
+
| Property | Figma Spec | Implementation |
|
|
10
|
+
| --------------------- | ---------------------------------- | ------------------------------------------------------------- |
|
|
11
|
+
| **Border Radius** | `var(--semantic/rounded-lg, 8px)` | `radius="lg"` prop (8px) |
|
|
12
|
+
| **Padding** | `px: 12px, py: 7.5px` | CSS Module: `padding-top/bottom: 7.5px` (px: 12px from Mantine default) |
|
|
13
|
+
| **Shadow** | `0px 1px 2px 0px rgba(0,0,0,0.05)` | CSS Module: `box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05)` |
|
|
14
|
+
| **Font Size** | `14px (paragraph small)` | `size="sm"` prop (14px) |
|
|
15
|
+
| **Height** | `36px total` | Automatic via padding (21px content + 15px padding) |
|
|
16
|
+
| **Border** | `1px solid var(--general/border)` | CSS Module: `border: 1px solid var(--color-general-border)` |
|
|
17
|
+
| **Background** | `var(--general/input, white)` | CSS Module: `background-color: var(--color-general-input)` |
|
|
18
|
+
| **Placeholder Color** | `var(--general/muted-foreground)` | CSS Module: `color: var(--color-general-muted-foreground)` |
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { Search } from 'lucide-react';
|
|
24
|
+
|
|
25
|
+
import { TextInput } from '@acme/ui';
|
|
26
|
+
|
|
27
|
+
function SearchBar() {
|
|
28
|
+
return (
|
|
29
|
+
<TextInput
|
|
30
|
+
placeholder="Search by Device Name or User Name"
|
|
31
|
+
leftSection={<Search size={16} />}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- ✅ Matches Figma design specifications exactly
|
|
40
|
+
- ✅ Automatic dark mode support
|
|
41
|
+
- ✅ Consistent with design system colors and spacing
|
|
42
|
+
- ✅ Proper focus and disabled states
|
|
43
|
+
- ✅ Supports all standard Mantine TextInput props
|
|
44
|
+
- ✅ TypeScript support with full type safety
|
|
45
|
+
|
|
46
|
+
## Props
|
|
47
|
+
|
|
48
|
+
This component extends all Mantine `TextInput` props with sensible defaults:
|
|
49
|
+
|
|
50
|
+
- `size`: Defaults to `"sm"` (14px) to match Figma
|
|
51
|
+
- `radius`: Defaults to `"lg"` (8px) to match Figma
|
|
52
|
+
- All other props from `TextInputProps` are supported
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
### Basic Input
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<TextInput placeholder="Enter text" />
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### With Icon
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<TextInput placeholder="Search" leftSection={<Search size={16} />} />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### With Label and Error
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<TextInput label="Email" placeholder="Enter your email" error="Invalid email address" />
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Disabled State
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<TextInput placeholder="Disabled input" disabled />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Design System Integration
|
|
81
|
+
|
|
82
|
+
This component is part of the `@acme/ui` package and follows the same wrapper pattern as other extended components:
|
|
83
|
+
|
|
84
|
+
- **Text** and **Title**: Typography components with semantic font weights
|
|
85
|
+
- **Button**: Button component with Figma design variants
|
|
86
|
+
- **TextInput**: Input component with Figma design specifications (this component)
|
|
87
|
+
|
|
88
|
+
All components use a combination of Mantine props and CSS Modules to achieve pixel-perfect Figma designs while maintaining flexibility.
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
To view the component in Storybook:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm --filter @acme/ui storybook
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Navigate to **Components > TextInput** to see all variants and examples.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
|
|
3
|
+
import { SearchTextInput } from './SearchTextInput';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* -- This file was auto-generated by Code Connect --
|
|
7
|
+
* None of your props could be automatically mapped to Figma properties.
|
|
8
|
+
* You should update the `props` object to include a mapping from your
|
|
9
|
+
* code props to Figma properties, and update the `example` function to
|
|
10
|
+
* return the code example you'd like to see in Figma
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
figma.connect(
|
|
14
|
+
SearchTextInput,
|
|
15
|
+
'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=176%3A27332',
|
|
16
|
+
{
|
|
17
|
+
props: {},
|
|
18
|
+
example: _props => (
|
|
19
|
+
<SearchTextInput value="" onChange={() => {}} placeholder="Search" />
|
|
20
|
+
),
|
|
21
|
+
},
|
|
22
|
+
);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search TextInput with search icon and clear button
|
|
3
|
+
* Uses TextInput with a search icon in rightSection when empty;
|
|
4
|
+
* when there is a value, shows an IconButton with X to clear.
|
|
5
|
+
* All other props pass through to TextInput.
|
|
6
|
+
* Controlled: pass value and onChange.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* // Basic usage (fully controlled)
|
|
11
|
+
* <SearchTextInput
|
|
12
|
+
* placeholder="Search..."
|
|
13
|
+
* value={query}
|
|
14
|
+
* onChange={(e) => setQuery(e.currentTarget.value)}
|
|
15
|
+
* />
|
|
16
|
+
*
|
|
17
|
+
* // With debouncing for table searches
|
|
18
|
+
* <SearchTextInput
|
|
19
|
+
* placeholder="Search..."
|
|
20
|
+
* value={query}
|
|
21
|
+
* onChange={(e) => setQuery(e.currentTarget.value)}
|
|
22
|
+
* debounceMs={300}
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
forwardRef,
|
|
29
|
+
useCallback,
|
|
30
|
+
useEffect,
|
|
31
|
+
useRef,
|
|
32
|
+
useState,
|
|
33
|
+
type ChangeEvent,
|
|
34
|
+
} from 'react';
|
|
35
|
+
|
|
36
|
+
import { Search, X } from 'lucide-react';
|
|
37
|
+
|
|
38
|
+
import { useDebouncedValue } from '@scalepad/ui-utils/hooks';
|
|
39
|
+
|
|
40
|
+
import { IconButton } from '../IconButton';
|
|
41
|
+
import { TextInput, type TextInputProps } from './TextInput';
|
|
42
|
+
|
|
43
|
+
export interface SearchTextInputProps extends Omit<
|
|
44
|
+
TextInputProps,
|
|
45
|
+
'rightSection'
|
|
46
|
+
> {
|
|
47
|
+
/**
|
|
48
|
+
* Optional debounce delay in milliseconds
|
|
49
|
+
* When set, internal state is used for instant input feedback
|
|
50
|
+
* and onChange is called with debounced value
|
|
51
|
+
* @default undefined (no debouncing)
|
|
52
|
+
*/
|
|
53
|
+
debounceMs?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const SearchTextInput = forwardRef<
|
|
57
|
+
HTMLInputElement,
|
|
58
|
+
SearchTextInputProps
|
|
59
|
+
>(({ value = '', onChange, debounceMs, ...rest }, ref) => {
|
|
60
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
61
|
+
|
|
62
|
+
// Use internal state when debouncing, otherwise use controlled value
|
|
63
|
+
const [localValue, setLocalValue] = useState(value);
|
|
64
|
+
const [debouncedValue] = useDebouncedValue(localValue, debounceMs ?? 0);
|
|
65
|
+
|
|
66
|
+
const displayValue = debounceMs !== undefined ? localValue : value;
|
|
67
|
+
const hasValue = String(displayValue ?? '').length > 0;
|
|
68
|
+
|
|
69
|
+
const setRef = useCallback(
|
|
70
|
+
(el: HTMLInputElement | null) => {
|
|
71
|
+
inputRef.current = el;
|
|
72
|
+
if (typeof ref === 'function') {
|
|
73
|
+
ref(el);
|
|
74
|
+
} else if (ref) {
|
|
75
|
+
(ref as React.MutableRefObject<HTMLInputElement | null>).current = el;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[ref],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Sync debounced value to parent when debouncing is enabled
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (debounceMs !== undefined && onChange) {
|
|
84
|
+
const syntheticEvent = {
|
|
85
|
+
currentTarget: { value: debouncedValue },
|
|
86
|
+
target: { value: debouncedValue },
|
|
87
|
+
} as ChangeEvent<HTMLInputElement>;
|
|
88
|
+
onChange(syntheticEvent);
|
|
89
|
+
}
|
|
90
|
+
}, [debouncedValue, debounceMs, onChange]);
|
|
91
|
+
|
|
92
|
+
const handleChange = useCallback(
|
|
93
|
+
(e: ChangeEvent<HTMLInputElement>) => {
|
|
94
|
+
if (debounceMs !== undefined) {
|
|
95
|
+
// With debouncing: update local state, debounced effect will call parent
|
|
96
|
+
setLocalValue(e.currentTarget.value);
|
|
97
|
+
} else {
|
|
98
|
+
// Without debouncing: call parent directly
|
|
99
|
+
onChange?.(e);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
[debounceMs, onChange],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const clearInput = useCallback(() => {
|
|
106
|
+
const syntheticEvent = {
|
|
107
|
+
currentTarget: { value: '' },
|
|
108
|
+
target: { value: '' },
|
|
109
|
+
} as ChangeEvent<HTMLInputElement>;
|
|
110
|
+
|
|
111
|
+
if (debounceMs !== undefined) {
|
|
112
|
+
// With debouncing: clear local state, debounced effect will notify parent
|
|
113
|
+
setLocalValue('');
|
|
114
|
+
} else {
|
|
115
|
+
// Without debouncing: notify parent directly
|
|
116
|
+
onChange?.(syntheticEvent);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
inputRef.current?.focus();
|
|
120
|
+
}, [debounceMs, onChange]);
|
|
121
|
+
|
|
122
|
+
const getRightSection = () => {
|
|
123
|
+
if (hasValue) {
|
|
124
|
+
return (
|
|
125
|
+
<IconButton
|
|
126
|
+
variant="ghost"
|
|
127
|
+
size="xs"
|
|
128
|
+
onClick={clearInput}
|
|
129
|
+
aria-label="Clear search"
|
|
130
|
+
type="button"
|
|
131
|
+
>
|
|
132
|
+
<X size={16} />
|
|
133
|
+
</IconButton>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return <Search size={16} />;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<TextInput
|
|
141
|
+
ref={setRef}
|
|
142
|
+
value={displayValue}
|
|
143
|
+
onChange={handleChange}
|
|
144
|
+
rightSection={getRightSection()}
|
|
145
|
+
{...rest}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
SearchTextInput.displayName = 'SearchTextInput';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
|
|
3
|
+
import { TextInput } from './TextInput';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* -- This file was auto-generated by Code Connect --
|
|
7
|
+
* `props` includes a mapping from your code props to Figma properties.
|
|
8
|
+
* You should check this is correct, and update the `example` function
|
|
9
|
+
* to return the code example you'd like to see in Figma
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
figma.connect(
|
|
13
|
+
TextInput,
|
|
14
|
+
'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=16%3A1738',
|
|
15
|
+
{
|
|
16
|
+
props: {
|
|
17
|
+
// These props were automatically mapped based on your linked code:
|
|
18
|
+
size: figma.enum('Size', {
|
|
19
|
+
Small: 'sm',
|
|
20
|
+
}),
|
|
21
|
+
disabled: figma.enum('State', {
|
|
22
|
+
Disabled: true,
|
|
23
|
+
}),
|
|
24
|
+
readOnly: figma.enum('State', {
|
|
25
|
+
Disabled: true,
|
|
26
|
+
}),
|
|
27
|
+
// No matching props could be found for these Figma properties:
|
|
28
|
+
// "showDecorationLeft": figma.boolean('Show decoration left'),
|
|
29
|
+
// "showDecorationRight": figma.boolean('Show decoration right'),
|
|
30
|
+
// "showCursor": figma.boolean('Show cursor'),
|
|
31
|
+
// "roundness": figma.enum('Roundness', {
|
|
32
|
+
// "Default": "default",
|
|
33
|
+
// "Round": "round"
|
|
34
|
+
// })
|
|
35
|
+
},
|
|
36
|
+
example: props => (
|
|
37
|
+
<TextInput
|
|
38
|
+
size={props.size}
|
|
39
|
+
disabled={props.disabled}
|
|
40
|
+
readOnly={props.readOnly}
|
|
41
|
+
/>
|
|
42
|
+
),
|
|
43
|
+
},
|
|
44
|
+
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended TextInput Component
|
|
3
|
+
* Wrapper around Mantine TextInput with Figma design specifications
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <TextInput
|
|
8
|
+
* placeholder="Search by Device Name"
|
|
9
|
+
* leftSection={<Search size={16} />}
|
|
10
|
+
* />
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { forwardRef } from 'react';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
TextInput as MantineTextInput,
|
|
18
|
+
type TextInputProps as MantineTextInputProps,
|
|
19
|
+
} from '@mantine/core';
|
|
20
|
+
|
|
21
|
+
export interface TextInputProps extends Omit<MantineTextInputProps, 'size'> {
|
|
22
|
+
/** Input size - defaults to 'sm' (14px) to match Figma */
|
|
23
|
+
size?: MantineTextInputProps['size'];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
|
27
|
+
({ className, size = 'sm', radius = 'lg', ...props }, ref) => {
|
|
28
|
+
return (
|
|
29
|
+
<MantineTextInput
|
|
30
|
+
ref={ref}
|
|
31
|
+
size={size}
|
|
32
|
+
radius={radius}
|
|
33
|
+
classNames={{
|
|
34
|
+
input: className,
|
|
35
|
+
}}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
TextInput.displayName = 'TextInput';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
|
|
3
|
+
import { ThemeSwitcher } from './ThemeSwitcher';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* -- This file was auto-generated by Code Connect --
|
|
7
|
+
* None of your props could be automatically mapped to Figma properties.
|
|
8
|
+
* You should update the `props` object to include a mapping from your
|
|
9
|
+
* code props to Figma properties, and update the `example` function to
|
|
10
|
+
* return the code example you'd like to see in Figma
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
figma.connect(
|
|
14
|
+
ThemeSwitcher,
|
|
15
|
+
'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=16%3A1801',
|
|
16
|
+
{
|
|
17
|
+
props: {
|
|
18
|
+
// No matching props could be found for these Figma properties:
|
|
19
|
+
// "checked": figma.boolean('Checked?'),
|
|
20
|
+
// "state": figma.enum('State', {
|
|
21
|
+
// "Default": "default",
|
|
22
|
+
// "Focus": "focus",
|
|
23
|
+
// "Disabled": "disabled"
|
|
24
|
+
// })
|
|
25
|
+
},
|
|
26
|
+
example: _props => <ThemeSwitcher />,
|
|
27
|
+
},
|
|
28
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Monitor, Moon, Sun } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
import { Group, useMantineColorScheme } from '@mantine/core';
|
|
4
|
+
|
|
5
|
+
import { IconButton } from './IconButton';
|
|
6
|
+
|
|
7
|
+
import type { ButtonVariants } from './Button/Button';
|
|
8
|
+
|
|
9
|
+
export interface ThemeSwitcherProps {
|
|
10
|
+
/** Size of the action icons */
|
|
11
|
+
size?: string | number;
|
|
12
|
+
/** Variant of the action icons */
|
|
13
|
+
variant?: ButtonVariants;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ColorScheme = 'light' | 'dark' | 'auto';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ThemeSwitcher component for switching between light, dark, and auto themes
|
|
20
|
+
* Uses ActionIcon components to provide theme selection
|
|
21
|
+
*/
|
|
22
|
+
export function ThemeSwitcher({
|
|
23
|
+
size = 'lg',
|
|
24
|
+
variant = 'ghost',
|
|
25
|
+
}: ThemeSwitcherProps) {
|
|
26
|
+
const { setColorScheme, colorScheme } = useMantineColorScheme();
|
|
27
|
+
|
|
28
|
+
const handleThemeChange = (scheme: ColorScheme) => {
|
|
29
|
+
setColorScheme(scheme);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Group gap="xs">
|
|
34
|
+
<IconButton
|
|
35
|
+
onClick={() => handleThemeChange('light')}
|
|
36
|
+
variant={
|
|
37
|
+
colorScheme === 'light' ? ('primary' as ButtonVariants) : variant
|
|
38
|
+
}
|
|
39
|
+
size={size}
|
|
40
|
+
aria-label="Light theme"
|
|
41
|
+
title="Light theme"
|
|
42
|
+
>
|
|
43
|
+
<Sun size={18} />
|
|
44
|
+
</IconButton>
|
|
45
|
+
<IconButton
|
|
46
|
+
onClick={() => handleThemeChange('dark')}
|
|
47
|
+
variant={
|
|
48
|
+
colorScheme === 'dark' ? ('primary' as ButtonVariants) : variant
|
|
49
|
+
}
|
|
50
|
+
size={size}
|
|
51
|
+
aria-label="Dark theme"
|
|
52
|
+
title="Dark theme"
|
|
53
|
+
>
|
|
54
|
+
<Moon size={18} />
|
|
55
|
+
</IconButton>
|
|
56
|
+
<IconButton
|
|
57
|
+
onClick={() => handleThemeChange('auto')}
|
|
58
|
+
variant={
|
|
59
|
+
colorScheme === 'auto' ? ('primary' as ButtonVariants) : variant
|
|
60
|
+
}
|
|
61
|
+
size={size}
|
|
62
|
+
aria-label="Auto theme"
|
|
63
|
+
title="Auto theme (follows system)"
|
|
64
|
+
>
|
|
65
|
+
<Monitor size={18} />
|
|
66
|
+
</IconButton>
|
|
67
|
+
</Group>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Minus, TrendingDown, TrendingUp } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
import { Badge, type BadgeProps, type BadgeSize } from '../Badge';
|
|
4
|
+
|
|
5
|
+
export type TrendDirection = 'up' | 'down' | 'flat';
|
|
6
|
+
|
|
7
|
+
export interface TrendBadgeProps {
|
|
8
|
+
/**
|
|
9
|
+
* Direction of change. Drives both the icon and the default semantic color.
|
|
10
|
+
*/
|
|
11
|
+
direction: TrendDirection;
|
|
12
|
+
/**
|
|
13
|
+
* Pre-formatted value, e.g. "12.4%" or "$1.2k". Rendered after the icon.
|
|
14
|
+
*/
|
|
15
|
+
value: string;
|
|
16
|
+
/**
|
|
17
|
+
* Optional trailing qualifier, e.g. "YoY", "MoM".
|
|
18
|
+
*/
|
|
19
|
+
label?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Invert the semantic mapping — by default up=success, down=destructive.
|
|
22
|
+
* Use invert=true for metrics where a decrease is desirable (e.g. costs).
|
|
23
|
+
*/
|
|
24
|
+
invert?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Badge size
|
|
27
|
+
* @default 'xs'
|
|
28
|
+
*/
|
|
29
|
+
size?: BadgeSize;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const ICON_SIZE = 12;
|
|
33
|
+
|
|
34
|
+
function resolveVariant(
|
|
35
|
+
direction: TrendDirection,
|
|
36
|
+
invert: boolean,
|
|
37
|
+
): BadgeProps['variant'] {
|
|
38
|
+
if (direction === 'flat') return 'secondary';
|
|
39
|
+
const positive = direction === 'up';
|
|
40
|
+
const isGood = invert ? !positive : positive;
|
|
41
|
+
return isGood ? 'success' : 'destructive';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function DirectionIcon({ direction }: { direction: TrendDirection }) {
|
|
45
|
+
if (direction === 'up') return <TrendingUp size={ICON_SIZE} />;
|
|
46
|
+
if (direction === 'down') return <TrendingDown size={ICON_SIZE} />;
|
|
47
|
+
return <Minus size={ICON_SIZE} />;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* TrendBadge displays a directional delta — arrow icon + formatted value +
|
|
52
|
+
* optional qualifier — styled by semantic tone. Built on top of Badge so it
|
|
53
|
+
* inherits the design-system color tokens.
|
|
54
|
+
*/
|
|
55
|
+
export function TrendBadge({
|
|
56
|
+
direction,
|
|
57
|
+
value,
|
|
58
|
+
label,
|
|
59
|
+
invert = false,
|
|
60
|
+
size = 'xs',
|
|
61
|
+
}: TrendBadgeProps) {
|
|
62
|
+
const variant = resolveVariant(direction, invert);
|
|
63
|
+
const text = label ? `${value} ${label}` : value;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Badge
|
|
67
|
+
data-testid="trendbadge-root"
|
|
68
|
+
data-variant={variant}
|
|
69
|
+
variant={variant}
|
|
70
|
+
size={size}
|
|
71
|
+
leftSection={<DirectionIcon direction={direction} />}
|
|
72
|
+
>
|
|
73
|
+
{text}
|
|
74
|
+
</Badge>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Tooltip } from '@mantine/core';
|
|
4
|
+
|
|
5
|
+
import { Text, type TextProps } from './Typography';
|
|
6
|
+
|
|
7
|
+
export interface TruncatedTextProps extends TextProps {
|
|
8
|
+
/**
|
|
9
|
+
* Text content to display
|
|
10
|
+
*/
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Tooltip text to show on hover
|
|
14
|
+
* If not provided, uses the children text
|
|
15
|
+
* Set to null to disable tooltip
|
|
16
|
+
*/
|
|
17
|
+
tooltip?: string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to always disable the tooltip (overrides overflow detection)
|
|
20
|
+
* Useful when you want conditional tooltips
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
disableTooltip?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* TruncatedText Component
|
|
28
|
+
*
|
|
29
|
+
* Renders text with truncation (ellipsis) and automatically shows a tooltip
|
|
30
|
+
* ONLY when the text is actually truncated. This provides better UX by avoiding
|
|
31
|
+
* redundant tooltips for fully visible text.
|
|
32
|
+
*
|
|
33
|
+
* @example Basic usage (auto-tooltip only when truncated)
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <TruncatedText>Very long text that will be truncated</TruncatedText>
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example Custom tooltip
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <TruncatedText tooltip="Full version string">
|
|
41
|
+
* v1.2.3
|
|
42
|
+
* </TruncatedText>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example Force disable tooltip
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <TruncatedText disableTooltip={!hasValue}>
|
|
48
|
+
* {value || 'N/A'}
|
|
49
|
+
* </TruncatedText>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example With text props
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <TruncatedText variant="caption1.strong" c="text.subdued.default">
|
|
55
|
+
* Long text
|
|
56
|
+
* </TruncatedText>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function TruncatedText({
|
|
60
|
+
children,
|
|
61
|
+
tooltip,
|
|
62
|
+
disableTooltip = false,
|
|
63
|
+
...textProps
|
|
64
|
+
}: TruncatedTextProps) {
|
|
65
|
+
const textRef = useRef<HTMLParagraphElement>(null);
|
|
66
|
+
const [isTruncated, setIsTruncated] = useState(false);
|
|
67
|
+
|
|
68
|
+
// Check if text is truncated by comparing scrollWidth with clientWidth
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const element = textRef.current;
|
|
71
|
+
if (!element) return;
|
|
72
|
+
|
|
73
|
+
const checkTruncation = () => {
|
|
74
|
+
setIsTruncated(element.scrollWidth > element.clientWidth);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Initial check
|
|
78
|
+
checkTruncation();
|
|
79
|
+
|
|
80
|
+
// Use ResizeObserver to watch for element size changes
|
|
81
|
+
// This is more efficient than window resize listener and works for container resizes
|
|
82
|
+
const resizeObserver = new ResizeObserver(checkTruncation);
|
|
83
|
+
resizeObserver.observe(element);
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
resizeObserver.disconnect();
|
|
87
|
+
};
|
|
88
|
+
}, [children]); // Re-check when children change
|
|
89
|
+
|
|
90
|
+
// Use provided tooltip, or fallback to children if it's a string
|
|
91
|
+
const tooltipLabel =
|
|
92
|
+
tooltip !== undefined
|
|
93
|
+
? tooltip
|
|
94
|
+
: typeof children === 'string'
|
|
95
|
+
? children
|
|
96
|
+
: null;
|
|
97
|
+
|
|
98
|
+
// Only show tooltip if:
|
|
99
|
+
// 1. Not explicitly disabled
|
|
100
|
+
// 2. Text is actually truncated
|
|
101
|
+
// 3. We have valid tooltip content
|
|
102
|
+
const shouldShowTooltip =
|
|
103
|
+
!disableTooltip &&
|
|
104
|
+
isTruncated &&
|
|
105
|
+
tooltipLabel !== null &&
|
|
106
|
+
Boolean(tooltipLabel);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Tooltip label={tooltipLabel} disabled={!shouldShowTooltip}>
|
|
110
|
+
<Text ref={textRef} truncate {...textProps}>
|
|
111
|
+
{children}
|
|
112
|
+
</Text>
|
|
113
|
+
</Tooltip>
|
|
114
|
+
);
|
|
115
|
+
}
|