Showing preview only (674K chars total). Download the full file or copy to clipboard to get everything.
Repository: tremorlabs/tremor-npm
Branch: main
Commit: 7613bff631f7
Files: 317
Total size: 599.3 KB
Directory structure:
gitextract_8mwyg1k6/
├── .eslintignore
├── .eslintrc
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.yaml
│ │ ├── config.yaml
│ │ └── feature-request.yaml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build.yaml
│ ├── lint.yaml
│ └── release.yaml
├── .gitignore
├── .prettierrc.json
├── .storybook/
│ ├── main.js
│ ├── manager.js
│ ├── preview.js
│ └── tremorTheme.js
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── License
├── README.md
├── babel.config.js
├── jest.config.js
├── package.json
├── postcss.config.js
├── rollup.config.js
├── setupTests.js
├── src/
│ ├── assets/
│ │ ├── ArrowDownHeadIcon.tsx
│ │ ├── ArrowDownIcon.tsx
│ │ ├── ArrowDownRightIcon.tsx
│ │ ├── ArrowLeftHeadIcon.tsx
│ │ ├── ArrowRightHeadIcon.tsx
│ │ ├── ArrowRightIcon.tsx
│ │ ├── ArrowUpHeadIcon.tsx
│ │ ├── ArrowUpIcon.tsx
│ │ ├── ArrowUpRightIcon.tsx
│ │ ├── CalendarIcon.tsx
│ │ ├── ChevronLeftFill.tsx
│ │ ├── ChevronRightFill.tsx
│ │ ├── DoubleArrowLeftHeadIcon.tsx
│ │ ├── DoubleArrowRightHeadIcon.tsx
│ │ ├── ExclamationFilledIcon.tsx
│ │ ├── EyeIcon.tsx
│ │ ├── EyeOffIcon.tsx
│ │ ├── LoadingSpinner.tsx
│ │ ├── MinusIcon.tsx
│ │ ├── PlusIcon.tsx
│ │ ├── SearchIcon.tsx
│ │ ├── XCircleIcon.tsx
│ │ ├── XIcon.tsx
│ │ └── index.ts
│ ├── components/
│ │ ├── chart-elements/
│ │ │ ├── AreaChart/
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── BarChart/
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── DonutChart/
│ │ │ │ ├── DonutChart.tsx
│ │ │ │ ├── DonutChartTooltip.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── inputParser.ts
│ │ │ ├── FunnelChart/
│ │ │ │ ├── FunnelChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── LineChart/
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ScatterChart/
│ │ │ │ ├── ScatterChart.tsx
│ │ │ │ ├── ScatterChartTooltip.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── common/
│ │ │ │ ├── BaseAnimationTimingProps.tsx
│ │ │ │ ├── BaseChartProps.tsx
│ │ │ │ ├── ChartLegend.tsx
│ │ │ │ ├── ChartTooltip.tsx
│ │ │ │ ├── CustomTooltipProps.tsx
│ │ │ │ ├── NoData.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── utils.ts
│ │ │ └── index.ts
│ │ ├── icon-elements/
│ │ │ ├── Badge/
│ │ │ │ ├── Badge.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── BadgeDelta/
│ │ │ │ ├── BadgeDelta.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── Icon/
│ │ │ │ ├── Icon.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── input-elements/
│ │ │ ├── BaseInput.tsx
│ │ │ ├── Button/
│ │ │ │ ├── Button.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── Calendar/
│ │ │ │ ├── Calendar.tsx
│ │ │ │ ├── NavButton.tsx
│ │ │ │ └── index.ts
│ │ │ ├── DatePicker/
│ │ │ │ ├── DatePicker.tsx
│ │ │ │ ├── datePickerUtils.tsx
│ │ │ │ └── index.ts
│ │ │ ├── DateRangePicker/
│ │ │ │ ├── DateRangePicker.tsx
│ │ │ │ ├── DateRangePickerItem.tsx
│ │ │ │ ├── dateRangePickerUtils.tsx
│ │ │ │ └── index.ts
│ │ │ ├── MultiSelect/
│ │ │ │ ├── MultiSelect.tsx
│ │ │ │ ├── MultiSelectItem.tsx
│ │ │ │ └── index.ts
│ │ │ ├── NumberInput/
│ │ │ │ ├── NumberInput.tsx
│ │ │ │ └── index.ts
│ │ │ ├── SearchSelect/
│ │ │ │ ├── SearchSelect.tsx
│ │ │ │ ├── SearchSelectItem.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Select/
│ │ │ │ ├── Select.tsx
│ │ │ │ ├── SelectItem.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Switch/
│ │ │ │ ├── Switch.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Tabs/
│ │ │ │ ├── Tab.tsx
│ │ │ │ ├── TabGroup.tsx
│ │ │ │ ├── TabList.tsx
│ │ │ │ ├── TabPanel.tsx
│ │ │ │ ├── TabPanels.tsx
│ │ │ │ └── index.ts
│ │ │ ├── TextInput/
│ │ │ │ ├── TextInput.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Textarea/
│ │ │ │ ├── Textarea.tsx
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ └── selectUtils.ts
│ │ ├── layout-elements/
│ │ │ ├── Accordion/
│ │ │ │ ├── Accordion.tsx
│ │ │ │ ├── AccordionBody.tsx
│ │ │ │ ├── AccordionHeader.tsx
│ │ │ │ ├── AccordionList.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Card/
│ │ │ │ ├── Card.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Dialog/
│ │ │ │ ├── Dialog.tsx
│ │ │ │ ├── DialogPanel.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Divider/
│ │ │ │ ├── Divider.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Flex/
│ │ │ │ ├── Flex.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Grid/
│ │ │ │ ├── Col.tsx
│ │ │ │ ├── Grid.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── index.ts
│ │ ├── list-elements/
│ │ │ ├── List/
│ │ │ │ ├── List.tsx
│ │ │ │ ├── ListItem.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Table/
│ │ │ │ ├── Table.tsx
│ │ │ │ ├── TableBody.tsx
│ │ │ │ ├── TableCell.tsx
│ │ │ │ ├── TableFoot.tsx
│ │ │ │ ├── TableFooterCell.tsx
│ │ │ │ ├── TableHead.tsx
│ │ │ │ ├── TableHeaderCell.tsx
│ │ │ │ ├── TableRow.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ ├── spark-elements/
│ │ │ ├── SparkAreaChart/
│ │ │ │ ├── SparkAreaChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── SparkBarChart/
│ │ │ │ ├── SparkBarChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── SparkLineChart/
│ │ │ │ ├── SparkLineChart.tsx
│ │ │ │ └── index.ts
│ │ │ ├── common/
│ │ │ │ └── BaseSparkChartProps.tsx
│ │ │ └── index.ts
│ │ ├── text-elements/
│ │ │ ├── Bold/
│ │ │ │ ├── Bold.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Callout/
│ │ │ │ ├── Callout.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Italic/
│ │ │ │ ├── Italic.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Legend/
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Metric/
│ │ │ │ ├── Metric.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Subtitle/
│ │ │ │ ├── Subtitle.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Text/
│ │ │ │ ├── Text.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Title/
│ │ │ │ ├── Title.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ ├── util-elements/
│ │ │ ├── Tooltip/
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ └── vis-elements/
│ │ ├── BarList/
│ │ │ ├── BarList.tsx
│ │ │ └── index.ts
│ │ ├── CategoryBar/
│ │ │ ├── CategoryBar.tsx
│ │ │ └── index.ts
│ │ ├── DeltaBar/
│ │ │ ├── DeltaBar.tsx
│ │ │ ├── index.ts
│ │ │ └── styles.ts
│ │ ├── MarkerBar/
│ │ │ ├── MarkerBar.tsx
│ │ │ └── index.ts
│ │ ├── ProgressBar/
│ │ │ ├── ProgressBar.tsx
│ │ │ └── index.ts
│ │ ├── ProgressCircle/
│ │ │ ├── ProgressCircle.tsx
│ │ │ └── index.ts
│ │ ├── Tracker/
│ │ │ ├── Tracker.tsx
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── contexts/
│ │ ├── BaseColorContext.tsx
│ │ ├── IndexContext.tsx
│ │ ├── RootStylesContext.tsx
│ │ ├── SelectedValueContext.tsx
│ │ └── index.ts
│ ├── hooks/
│ │ ├── index.ts
│ │ ├── useInternalState.tsx
│ │ └── useOnWindowResize.tsx
│ ├── index.ts
│ ├── lib/
│ │ ├── constants.ts
│ │ ├── index.ts
│ │ ├── inputTypes.ts
│ │ ├── theme.ts
│ │ ├── tremorTwMerge.ts
│ │ └── utils.tsx
│ ├── stories/
│ │ ├── chart-elements/
│ │ │ ├── AreaChart.stories.tsx
│ │ │ ├── BarChart.stories.tsx
│ │ │ ├── DonutChart.stories.tsx
│ │ │ ├── FunnelChart.stories.tsx
│ │ │ ├── LineChart.stories.tsx
│ │ │ ├── ScatterChart.stories.tsx
│ │ │ └── helpers/
│ │ │ ├── testData.tsx
│ │ │ ├── testDataScatterChart.tsx
│ │ │ └── utils.ts
│ │ ├── icon-elements/
│ │ │ ├── Badge.stories.tsx
│ │ │ ├── BadgeDelta.stories.tsx
│ │ │ └── Icon.stories.tsx
│ │ ├── input-elements/
│ │ │ ├── Button.stories.tsx
│ │ │ ├── DatePicker.stories.tsx
│ │ │ ├── DateRangePicker.stories.tsx
│ │ │ ├── MultiSelect.stories.tsx
│ │ │ ├── NumberInput.stories.tsx
│ │ │ ├── SearchSelect.stories.tsx
│ │ │ ├── Select.stories.tsx
│ │ │ ├── Switch.stories.tsx
│ │ │ ├── Tabs.stories.tsx
│ │ │ ├── TextArea.stories.tsx
│ │ │ ├── TextInput.stories.tsx
│ │ │ └── helpers/
│ │ │ ├── SimpleMultiSelect.tsx
│ │ │ ├── SimpleNumberInput.tsx
│ │ │ ├── SimpleSearchSelect.tsx
│ │ │ ├── SimpleSelect.tsx
│ │ │ ├── SimpleSwitch.tsx
│ │ │ ├── SimpleTextInput.tsx
│ │ │ └── testData.ts
│ │ ├── layout-elements/
│ │ │ ├── Accordion.stories.tsx
│ │ │ ├── AccordionList.stories.tsx
│ │ │ ├── Card.stories.tsx
│ │ │ ├── Dialog.stories.tsx
│ │ │ ├── Divider.stories.tsx
│ │ │ ├── Flex.stories.tsx
│ │ │ ├── Grid.stories.tsx
│ │ │ └── helpers/
│ │ │ ├── SimpleAccordion.tsx
│ │ │ ├── SimpleCard.tsx
│ │ │ └── SimpleText.tsx
│ │ ├── list-elements/
│ │ │ ├── List.stories.tsx
│ │ │ └── Table.stories.tsx
│ │ ├── spark-elements/
│ │ │ ├── SparkAreaChart.stories.tsx
│ │ │ ├── SparkBarChart.stories.tsx
│ │ │ ├── SparkLineChart.stories.tsx
│ │ │ └── helpers/
│ │ │ ├── ExampleCard.tsx
│ │ │ └── testData.ts
│ │ ├── text-elements/
│ │ │ ├── Callout.stories.tsx
│ │ │ ├── Legend.stories.tsx
│ │ │ ├── Metric.stories.tsx
│ │ │ ├── Subtitle.stories.tsx
│ │ │ ├── Text.stories.tsx
│ │ │ ├── TextElements.stories.tsx
│ │ │ └── Title.stories.tsx
│ │ └── vis-elements/
│ │ ├── BarList.stories.tsx
│ │ ├── CategoryBar.stories.tsx
│ │ ├── DeltaBar.stories.tsx
│ │ ├── MarkerBar.stories.tsx
│ │ ├── ProgressBar.stories.tsx
│ │ ├── ProgressCircle.stories.tsx
│ │ └── Tracker.stories.tsx
│ ├── styles.css
│ └── tests/
│ ├── chart-elements/
│ │ ├── AreaChart.test.tsx
│ │ └── BarChart.test.tsx
│ ├── icon-elements/
│ │ ├── Badge.test.tsx
│ │ ├── BadgeDelta.test.tsx
│ │ └── Icon.test.tsx
│ ├── input-elements/
│ │ ├── Button.test.tsx
│ │ ├── DatePicker.test.tsx
│ │ ├── DateRangePicker.test.tsx
│ │ ├── MultiSelect.test.tsx
│ │ ├── NumberInput.test.tsx
│ │ ├── SearchSelect.test.tsx
│ │ ├── Select.test.tsx
│ │ ├── Switch.text.tsx
│ │ ├── Tabs.test.tsx
│ │ ├── TextInput.test.tsx
│ │ └── Textarea.test.tsx
│ ├── layout-elements/
│ │ ├── Accordion.test.tsx
│ │ ├── AccordionList.test.tsx
│ │ ├── Card.test.tsx
│ │ ├── Dialog.test.tsx
│ │ ├── Divider.test.tsx
│ │ ├── Flex.test.tsx
│ │ └── Grid.test.tsx
│ ├── list-elements/
│ │ ├── List.test.tsx
│ │ └── Table.test.tsx
│ ├── spark-elements/
│ │ └── SparkAreaChart.test.tsx
│ ├── text-elements/
│ │ ├── Callout.test.tsx
│ │ ├── Legend.test.tsx
│ │ ├── Metric.test.tsx
│ │ ├── Subtitle.test.tsx
│ │ ├── Text.test.tsx
│ │ └── Title.test.tsx
│ └── vis-elements/
│ ├── BarList.test.tsx
│ ├── CategoryBar.test.tsx
│ ├── DeltaBar.test.tsx
│ ├── MarkerBar.test.tsx
│ ├── ProgressBar.test.tsx
│ ├── ProgressCircle.text.tsx
│ └── Tracker.test.tsx
├── tailwind.config.js
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintignore
================================================
node_modules
dist
types
================================================
FILE: .eslintrc
================================================
{
"root": true,
"extends": [
"prettier",
"plugin:prettier/recommended",
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"prettier",
"react",
"react-hooks"
],
"rules": {
"react/button-has-type": "error",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"react/prop-types": "off"
},
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"node": true
},
"globals": {
"JSX": true
}
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yaml
================================================
name: "\U0001F41E Bug report"
description: Create a report to help us improve
title: "[Bug]: "
labels: ["Triage"]
body:
- type: markdown
attributes:
value: |
**Before submitting a bug report**
The issue list is reserved for bug reports and feature requests. If you have a usage question, you can:
- Read the [documentation](https://npm.tremor.so/docs/getting-started/installation)
- Ask questions on [Slack](https://join.slack.com/t/tremor-community/shared_invite/zt-1u8jqmcmq-Fdr9B6MbnO7u8FkGh~2Ylg)
Also try to search for your issue/feature - another user may have already requested something similar, thanks!
- type: input
id: version
attributes:
label: Tremor Version
validations:
required: true
- type: input
id: reproduction-link
attributes:
label: Link to minimal reproduction
description: |
The easiest way to provide a reproduction is provide a link to a public GitHub repository or a tools like [Stackblitz](https://stackblitz.com) or [Codesandbox](https://codesandbox.io).
The reproduction should be **minimal**. This means, it should contain only the bare minimum amount of code needed
to show the bug.
placeholder: Reproduction Link
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: |
What do we need to do after opening your repro in order to make the bug happen? Clear and concise reproduction instructions are important for us to be able to triage your issue in a timely manner. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
placeholder: Steps to reproduce
validations:
required: true
- type: textarea
id: expected
attributes:
label: What is expected?
validations:
required: true
- type: textarea
id: actually-happening
attributes:
label: What is actually happening?
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Chrome
- Microsoft Edge
- Safari
- Firefox
- Vivaldi
- Brave
- Other
- type: textarea
id: additional-comments
attributes:
label: Any additional comments?
description: E.g. some background/context of how you ran into this bug.
- type: markdown
attributes:
value: |
This bug report template was inspired by the issue template from [vuejs](https://github.com/vuejs/core)
================================================
FILE: .github/ISSUE_TEMPLATE/config.yaml
================================================
blank_issues_enabled: true
contact_links:
- name: Slack Community
url: https://join.slack.com/t/tremor-community/shared_invite/zt-1u8jqmcmq-Fdr9B6MbnO7u8FkGh~2Ylg
about: Please ask and answer usage questions here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yaml
================================================
name: "\U0001F680 New feature proposal"
description: Suggest an idea for this project
title: "[Feature]: "
labels: ["Triage"]
body:
- type: markdown
attributes:
value: |
**Before submitting a feature request**
The issue list is reserved for bug reports and feature requests. If you have a usage question, you can:
- Read the [documentation](https://npm.tremor.so/docs/getting-started/installation)
- Ask questions on [Slack](https://join.slack.com/t/tremor-community/shared_invite/zt-1u8jqmcmq-Fdr9B6MbnO7u8FkGh~2Ylg)
Also try to search for your issue/feature - another user may have already requested something similar, thanks!
- type: textarea
id: problem-description
attributes:
label: What problem does this feature solve?
description: |
Explain your use case, context, and rationale behind this feature request.
An important design goal of Tremor is keeping the API small. The problem should be common enough to justify the addition.
placeholder: Problem description
validations:
required: true
- type: textarea
id: proposed-API
attributes:
label: What does the proposed API look like?
description: |
Describe how you propose to solve the problem and provide code samples of how the API would work once implemented. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format your code blocks.
placeholder: Assumed API
- type: markdown
attributes:
value: |
This bug report template was inspired by the feature template from [vuejs](https://github.com/vuejs/core)
================================================
FILE: .github/pull_request_template.md
================================================
<!--
Please make sure to read the Contribution Guidelines:
https://github.com/tremorlabs/tremor-npm/blob/main/CONTRIBUTING.md
-->
<!-- PULL REQUEST TEMPLATE -->
**Description**
<!--- Describe your changes in detail -->
**Related issue(s)**
<!--- Please link to the issue here: -->
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
**What kind of change does this PR introduce?** (check at least one)
<!-- (Update "[ ]" to "[x]" to check a box) -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New Feature (non-breaking change which adds functionality)
- [ ] New Feature (BREAKING CHANGE which adds functionality)
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:
**Does this PR introduce a breaking change?** (check one)
- [ ] Yes
- [ ] No
If yes, please describe the impact and migration path for existing applications:
**How has this been tested?**
<!--- Please describe how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
**Screenshots (if appropriate):**
**The PR fulfils these requirements:**
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] It's submitted to the `main` branch
- [ ] When resolving a specific issue, it's referenced in the related issue section above
- [ ] My change requires a change to the documentation. (Managed by Tremor Team)
- [ ] I have added tests to cover my changes
- [ ] Check the ["Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) while creating your PR.
- [ ] Add refs #XXX or fixes #XXX to the related issue section if your PR refers to or fixes an issue.
- [ ] By contributing to Tremor, you confirm that you have read and agreed to Tremor's [CONTRIBUTING.md](https://github.com/tremorlabs/tremor-npm/blob/main/CONTRIBUTING.md) guideline. You also agree that your contributions will be licensed under the [Apache License 2.0](https://github.com/tremorlabs/tremor-npm/blob/main/License) license.
================================================
FILE: .github/workflows/build.yaml
================================================
name: "Build"
on:
pull_request:
types:
- opened
- edited
- synchronize
push:
branches:
- "**"
- "!main"
jobs:
build:
name: Build dist
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: node
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
- name: install react
run: npm i react
- name: install dependencies
run: npm ci
- name: lint checks
run: npm run lint
- name: unit tests
run: npm run tests
- name: build
run: npm run build
================================================
FILE: .github/workflows/lint.yaml
================================================
name: "Lint PR"
on:
pull_request:
types:
- opened
- edited
- synchronize
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
lint-pr:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- name: Lint PR
uses: amannn/action-semantic-pull-request@v4.6.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/release.yaml
================================================
name: "Release"
on:
push:
branches:
- main
- beta
- beta-**
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: node
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
- name: install react
run: npm i react
- name: install dependencies
run: npm ci
- name: build
run: npm run build
- name: release
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
================================================
FILE: .gitignore
================================================
node_modules
dist
.DS_Store
storybook-static
package-lock.json
.vscode
yarn.lock
================================================
FILE: .prettierrc.json
================================================
{
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"printWidth": 100,
"jsxSingleQuote": false,
"endOfLine": "auto"
}
================================================
FILE: .storybook/main.js
================================================
var path = require("path");
module.exports = {
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"@storybook/addon-styling-webpack",
"@storybook/addon-themes",
"@storybook/addon-a11y",
{
name: "@storybook/addon-styling-webpack",
options: {
rules: [
{
test: /\.css$/,
sideEffects: true,
use: [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve("postcss-loader"),
options: {
implementation: require.resolve("postcss"),
},
},
],
},
],
},
},
"@storybook/addon-webpack5-compiler-babel",
"@chromatic-com/storybook",
],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
features: {
previewMdx2: true,
},
webpackFinal: async (config) => {
config.resolve.modules = [...(config.resolve.modules || []), path.resolve(__dirname, "../src")];
return config;
},
docs: {},
typescript: {
reactDocgen: "react-docgen-typescript",
},
};
================================================
FILE: .storybook/manager.js
================================================
import { addons } from "@storybook/manager-api";
import { themes } from "@storybook/theming";
import tremorTheme from "./tremorTheme";
addons.setConfig({
theme: tremorTheme,
});
================================================
FILE: .storybook/preview.js
================================================
import "../src/styles.css";
import { withThemeByDataAttribute } from "@storybook/addon-themes";
export const parameters = {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: {
default: "light",
values: [
{
name: "light",
value: "#ffffff",
},
{
name: "dark",
value: "#0f172a",
},
],
},
};
export const decorators = [
withThemeByDataAttribute({
themes: {
light: "light",
dark: "dark",
},
defaultTheme: "light",
attributeName: "data-mode",
}),
];
export const tags = ["autodocs"];
================================================
FILE: .storybook/tremorTheme.js
================================================
import { create } from "@storybook/theming/create";
export default create({
base: "light",
brandTitle: "Tremor Storybook",
brandUrl: "https://storybook.tremor.so",
// brandImage: 'images/tremor-logo.svg',
brandTarget: "_self",
//
colorSecondary: "#3b82f6",
// UI
appBg: "#ffffff",
appContentBg: "#ffffff",
// appBorderColor: '#585C6D',
appBorderRadius: 0,
//
barTextColor: "#9E9E9E",
barSelectedColor: "#3b82f6",
barBg: "#ffffff",
});
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CONTRIBUTING.md
================================================
## **Contributing to Tremor**
Thanks for your interest in contributing to Tremor. Please take a moment to review this document before submitting a pull request. This document will outline how to submit changes to this repository and which conventions to follow. If you are ever in doubt about anything we encourage you to reach out on [Slack](https://tremor-community.slack.com/join/shared_invite/zt-2a95vjndc-YCKurK3HVAkYtjialnT2_A#/shared-invite/email), [open a discussion](#discussions), or [shoot us an email](mailto:hello@tremor.so).
### **Prerequisites**
- You are familiar with [issues](#issues) and [pull requests](#pulls).
- You have read the [docs](https://npm.tremor.so/docs/getting-started/installation).
### **Issues before PRs**
1. Before you start working on a change please make sure that there is an issue for what you will be working on. You can either find an [existing issue](https://github.com/tremorlabs/tremor-npm/issues) or [open a new issue](https://github.com/tremorlabs/tremor-npm/issues/new/choose) if none exists.
2. When you are ready to start working on a change you should first [fork the Tremor repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) and [branch out](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository) from the `main` branch.
3. Make your changes.
4. [Open a pull request towards the main branch in the Tremor repo](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). Then, our team will review, comment and eventually approve your PR.
### **Branches**
All changes should be part of a branch and submitted as a pull request - your branches should be prefixed with one of:
- `fix/` for bug fixes
- `feat/` for features
### **Commits**
Strive towards keeping your commits small and isolated - this helps the reviewer understand what is going on and makes it easier to process your requests.
### **Pull Requests**
Once your changes are ready you must submit your branch as a pull request. Your pull request should be opened against the `main` branch in the main Tremor repo.
In your PR's description, you should follow the structure as outlined in the PR template:
1. Description: Describe your changes in detail.
2. Related issue(s): Please link to the issue.
3. What kind of change does this PR introduce?: Select from template options.
4. Does this PR introduce a breaking change?: Select Yes/No.
5. How has This been tested?: Please describe how you tested your changes.
6. Screenshots (if appropriate):
**The PR fulfills these requirements:**
- [ ] It's submitted to the `main` branch.
- [ ] When resolving a specific issue, it's referenced in the related issue section above.
- [ ] My change requires a change to the documentation. (Managed by Tremor Team).
- [ ] I have added tests to cover my changes.
* Be sure to check the "Allow edits from maintainers" option while creating your PR.
* If your PR refers to or fixes an issue, be sure to add refs #XXX or fixes #XXX to the related issue section. Replacing XXX with the respective issue number.
Be sure to fill the PR Template accordingly.
We encourage that you do a self-review prior to requesting a review. To do a self review click the review button in the top right corner, go through your code and annotate your changes. This makes it easier for the reviewer to process your PR.
### **Documentation**
- We generally encourage you to document your changes through comments in your code.
- If you alter user-facing behavior you must provide documentation for such changes, for reference, check out [our documentation](<[url](https://npm.tremor.so/docs/getting-started/introduction)>).
### **Licensing**
By contributing to Tremor, you agree that your contributions will be licensed under the [Apache License 2.0](https://github.com/tremorlabs/tremor-npm/blob/main/License) license. By submitting your pull request, you agree to our [Contributor License Agreement (CLA)](https://tremor.so/contributors). This agreement clarifies our ability to incorporate your contributions.
================================================
FILE: License
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
================================================
FILE: README.md
================================================
<br />
<br />
<p align="center">
<a href="https://npm.tremor.so">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/tremor-logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="images/tremor-logo-light.svg">
<img alt="Tremor Logo" src="images/tremor-logo-light.svg" height="50"/>
</picture>
</a>
</p>
<div align="center">
<br />
<br />
<div align="center">
<a href="https://npmjs.com/package/@tremor/react">
<img alt="npm" src="https://img.shields.io/npm/dm/@tremor/react?color=3b82f6&label=npm&logo=npm&labelColor=334155">
</a>
<a href="https://npm.tremor.so/docs/getting-started/installation">
<img alt="Read the documentation" src="https://img.shields.io/badge/Docs-blue?style=flat&logo=readthedocs&color=3b82f6&labelColor=334155&logoColor=f5f5f5" height="20" width="auto">
</a>
<a href="https://github.com/tremorlabs/tremor-npm/blob/main/License">
<img alt="License Apache 2.0" src="https://img.shields.io/badge/license-Apache 2.0-blue.svg?style=flat&color=3b82f6&labelColor=334155 " height="20" width="auto">
</a>
<a href="https://join.slack.com/t/tremor-community/shared_invite/zt-21ug6czv6-RckDPEAR6GdYOqfMGKOWpQ">
<img src="https://img.shields.io/badge/Join-important.svg?color=4A154B&label=Slack&logo=slack&labelColor=334155&logoColor=f5f5f5" alt="Join Slack" />
</a>
<a href="https://twitter.com/intent/follow?screen_name=tremorlabs">
<img src="https://img.shields.io/badge/Follow-important.svg?color=000000&label=@tremorlabs&logo=X&labelColor=334155&logoColor=f5f5f5" alt="Follow at Tremorlabs" />
</a>
</div>
<h3 align="center">
<a href="https://npm.tremor.so/docs/getting-started/installation">Documentation</a> •
<a href="https://npm.tremor.so">Website</a>
</h3>
<br />
<h1>React components to build charts and dashboards</h1>
</div>
[Tremor NPM](https://npm.tremor.so/) 20+ open-source components built on top of Tailwind CSS to make visualizing data simple again. Fully open-source, made by data scientists and software engineers with a sweet spot for design.
<br />

<br />
## Getting Started
See our [Installation Guide](https://npm.tremor.so/docs/getting-started/installation). To make use of the library we also need Tailwind CSS setup in the project.
## Example
With Tremor creating an analytical interface is easy.
```jsx
"use client";
import { AreaChart, Card } from "@tremor/react";
const chartdata = [
{
date: "Jan 23",
"Route Requests": 289,
"Station Requests": 233,
},
// ...
{
date: "Oct 23",
"Route Requests": 283,
"Station Requests": 247,
},
];
export default function Example() {
return (
<Card className="max-w-4xl">
<span className="text-tremor-default text-tremor-content dark:text-dark-tremor-content">
Total Requests
</span>
<p className="text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong">
6,568
</p>
<AreaChart
className="mt-2 h-80"
data={chartdata}
index="date"
categories={["Route Requests", "Station Requests"]}
colors={["indigo", "rose"]}
yAxisWidth={33}
/>
</Card>
);
}
```
<br />
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/example-dark.png">
<source media="(prefers-color-scheme: light)" srcset="images/example-light.png">
<img alt="Tremor Example" src="images/example-light.png"/>
</picture>
## Community and Contribution
We are always looking for new ideas or other ways to improve Tremor NPM. If you have developed anything cool or found a bug, send us a pull request. Check out our Contributor License Agreement [here](https://tremor.so/contributors).
## License
[Apache License 2.0](https://github.com/tremorlabs/tremor-npm/blob/main/License)
Copyright © 2025 Tremor Labs, Inc. All rights reserved.
================================================
FILE: babel.config.js
================================================
/* eslint-disable no-undef */
module.exports = {
presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
};
================================================
FILE: jest.config.js
================================================
/* eslint-disable no-undef */
module.exports = {
testEnvironment: "jsdom",
moduleDirectories: ["node_modules", "src"],
moduleNameMapper: {
".(css|less|scss)$": "identity-obj-proxy",
"components/(.*)": "<rootDir>/src/components/$1",
"assets/(.*)": "<rootDir>/src/assets/$1",
},
transformIgnorePatterns: ["<rootDir>/node_modules/(?!react-dnd|dnd-core|@react-dnd)"],
};
================================================
FILE: package.json
================================================
{
"name": "@tremor/react",
"version": "0.0.0-development",
"description": "The React library to build dashboards faster.",
"scripts": {
"prebuild": "rm -rf dist",
"build": "rollup -c",
"lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\"",
"prettier": "prettier --write \"{src,types,tests,example/src}/**/*.{js,ts,jsx,tsx}\"",
"tests": "jest",
"fix-lint": "eslint . --ext .ts --ext .tsx --fix",
"build-storybook": "storybook build",
"semantic-release": "semantic-release",
"storybook": "storybook dev -p 6006"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tremorlabs/tremor-npm.git"
},
"author": "tremor",
"license": "Apache 2.0",
"bugs": {
"url": "https://github.com/tremorlabs/tremor-npm/issues"
},
"homepage": "https://github.com/tremorlabs/tremor-npm-npm#readme",
"dependencies": {
"@floating-ui/react": "^0.19.2",
"@headlessui/react": "2.2.0",
"date-fns": "^3.6.0",
"react-day-picker": "^8.10.1",
"react-transition-state": "^2.1.2",
"recharts": "^2.13.3",
"tailwind-merge": "^2.5.2"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@chromatic-com/storybook": "^3.2.2",
"@mdx-js/react": "^2.3.0",
"@rollup/plugin-commonjs": "^21.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^8.5.0",
"@semantic-release/commit-analyzer": "^13.0.0",
"@semantic-release/github": "github:semantic-release/github",
"@semantic-release/npm": "github:semantic-release/npm",
"@storybook/addon-a11y": "^8.4.7",
"@storybook/addon-actions": "^8.4.7",
"@storybook/addon-essentials": "^8.4.7",
"@storybook/addon-interactions": "^8.4.7",
"@storybook/addon-links": "^8.4.7",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-themes": "^8.4.7",
"@storybook/addon-webpack5-compiler-babel": "^3.0.3",
"@storybook/manager-api": "^8.4.7",
"@storybook/mdx2-csf": "^1.1.0",
"@storybook/react": "^8.4.7",
"@storybook/react-vite": "^8.4.7",
"@storybook/react-webpack5": "^8.4.7",
"@storybook/test": "^8.4.7",
"@storybook/theming": "^8.4.7",
"@tailwindcss/forms": "^0.5.9",
"@testing-library/react": "^14.3.1",
"@types/jest": "^29.5.13",
"@types/node": "^22.6.1",
"@types/react": "^18.3.9",
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"autoprefixer": "^10.4.20",
"babel-jest": "^27.5.1",
"babel-loader": "^8.4.1",
"conventional-changelog-conventionalcommits": "^5.0.0",
"css-loader": "^6.11.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.36.1",
"eslint-plugin-react-hooks": "^4.6.2",
"html-webpack-plugin": "^5.6.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8.4.47",
"postcss-loader": "^7.3.4",
"prettier": "3.4.2",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"resize-observer-polyfill": "^1.5.1",
"rollup": "^2.79.1",
"rollup-plugin-dts": "^4.2.3",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-preserve-directives": "^0.1.1",
"rollup-plugin-typescript-paths": "^1.5.0",
"semantic-release": "^24.1.1",
"storybook": "^8.4.7",
"style-loader": "^3.3.4",
"tailwindcss": "^3.4.16",
"tslib": "^2.7.0",
"typescript": "^4.9.5",
"webpack": "^5.97.1"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": ">=16.6.0"
},
"main": "dist/index.cjs",
"module": "dist/index.js",
"files": [
"dist"
],
"types": "dist/index.d.ts",
"publishConfig": {
"access": "public"
},
"release": {
"branches": [
"main",
{
"name": "beta",
"prerelease": true
},
{
"name": "beta-*",
"prerelease": true
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{
"type": "build",
"release": "minor"
}
]
}
],
"@semantic-release/npm",
"@semantic-release/github"
]
}
}
================================================
FILE: postcss.config.js
================================================
/* eslint-disable no-undef */
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
================================================
FILE: rollup.config.js
================================================
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-undef */
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import { typescriptPaths } from "rollup-plugin-typescript-paths";
import terser from "@rollup/plugin-terser";
import preserveDirectives from "rollup-plugin-preserve-directives";
const outputOptions = {
sourcemap: false,
preserveModules: true,
preserveModulesRoot: "src",
};
export default [
{
input: "src/index.ts",
output: [
{
dir: "dist",
format: "cjs",
entryFileNames: "[name].cjs",
exports: "auto",
...outputOptions,
},
{
dir: "dist",
format: "esm",
...outputOptions,
},
],
external: [/node_modules/],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
preserveDirectives(),
terser(),
typescript({
tsconfig: "./tsconfig.json",
exclude: ["**/stories/**", "**/tests/**", "./styles.css"],
}),
typescriptPaths(),
],
},
{
input: "dist/index.d.ts",
output: [{ file: "dist/index.d.ts", format: "esm" }],
plugins: [dts()],
external: [/\.css$/],
},
];
================================================
FILE: setupTests.js
================================================
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable no-undef */
global.ResizeObserver = require("resize-observer-polyfill");
================================================
FILE: src/assets/ArrowDownHeadIcon.tsx
================================================
import React from "react";
const ArrowDownHeadIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"></path>
</svg>
);
export default ArrowDownHeadIcon;
================================================
FILE: src/assets/ArrowDownIcon.tsx
================================================
import React from "react";
const ArrowDownIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M13.0001 16.1716L18.3641 10.8076L19.7783 12.2218L12.0001 20L4.22192 12.2218L5.63614 10.8076L11.0001 16.1716V4H13.0001V16.1716Z"></path>
</svg>
);
export default ArrowDownIcon;
================================================
FILE: src/assets/ArrowDownRightIcon.tsx
================================================
import React from "react";
const ArrowDownRightIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M14.5895 16.0032L5.98291 7.39664L7.39712 5.98242L16.0037 14.589V7.00324H18.0037V18.0032H7.00373V16.0032H14.5895Z"></path>
</svg>
);
export default ArrowDownRightIcon;
================================================
FILE: src/assets/ArrowLeftHeadIcon.tsx
================================================
import React from "react";
const ArrowLeftHeadIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M10.8284 12.0007L15.7782 16.9504L14.364 18.3646L8 12.0007L14.364 5.63672L15.7782 7.05093L10.8284 12.0007Z"></path>
</svg>
);
export default ArrowLeftHeadIcon;
================================================
FILE: src/assets/ArrowRightHeadIcon.tsx
================================================
import React from "react";
const ArrowRightHeadIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M13.1717 12.0007L8.22192 7.05093L9.63614 5.63672L16.0001 12.0007L9.63614 18.3646L8.22192 16.9504L13.1717 12.0007Z"></path>
</svg>
);
export default ArrowRightHeadIcon;
================================================
FILE: src/assets/ArrowRightIcon.tsx
================================================
import React from "react";
const ArrowRightIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M16.1716 10.9999L10.8076 5.63589L12.2218 4.22168L20 11.9999L12.2218 19.778L10.8076 18.3638L16.1716 12.9999H4V10.9999H16.1716Z"></path>
</svg>
);
export default ArrowRightIcon;
================================================
FILE: src/assets/ArrowUpHeadIcon.tsx
================================================
import React from "react";
const ArrowUpHeadIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M11.9999 10.8284L7.0502 15.7782L5.63599 14.364L11.9999 8L18.3639 14.364L16.9497 15.7782L11.9999 10.8284Z"></path>
</svg>
);
export default ArrowUpHeadIcon;
================================================
FILE: src/assets/ArrowUpIcon.tsx
================================================
import React from "react";
const ArrowUpIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M13.0001 7.82843V20H11.0001V7.82843L5.63614 13.1924L4.22192 11.7782L12.0001 4L19.7783 11.7782L18.3641 13.1924L13.0001 7.82843Z"></path>
</svg>
);
export default ArrowUpIcon;
================================================
FILE: src/assets/ArrowUpRightIcon.tsx
================================================
import React from "react";
const ArrowUpRightIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M16.0037 9.41421L7.39712 18.0208L5.98291 16.6066L14.5895 8H7.00373V6H18.0037V17H16.0037V9.41421Z"></path>
</svg>
);
export default ArrowUpRightIcon;
================================================
FILE: src/assets/CalendarIcon.tsx
================================================
import React from "react";
const CalendarIcon = ({ ...props }) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path
fillRule="evenodd"
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
clipRule="evenodd"
/>
</svg>
);
export default CalendarIcon;
================================================
FILE: src/assets/ChevronLeftFill.tsx
================================================
import React from "react";
const ChevronLeftFill = ({ ...props }) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M8 12L14 6V18L8 12Z"></path>
</svg>
);
export default ChevronLeftFill;
================================================
FILE: src/assets/ChevronRightFill.tsx
================================================
import React from "react";
const ChevronRightFill = ({ ...props }) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M16 12L10 18V6L16 12Z"></path>
</svg>
);
export default ChevronRightFill;
================================================
FILE: src/assets/DoubleArrowLeftHeadIcon.tsx
================================================
import React from "react";
const DoubleArrowLeftHeadIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M4.83582 12L11.0429 18.2071L12.4571 16.7929L7.66424 12L12.4571 7.20712L11.0429 5.79291L4.83582 12ZM10.4857 12L16.6928 18.2071L18.107 16.7929L13.3141 12L18.107 7.20712L16.6928 5.79291L10.4857 12Z"></path>
</svg>
);
export default DoubleArrowLeftHeadIcon;
================================================
FILE: src/assets/DoubleArrowRightHeadIcon.tsx
================================================
import React from "react";
const DoubleArrowRightHeadIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M19.1642 12L12.9571 5.79291L11.5429 7.20712L16.3358 12L11.5429 16.7929L12.9571 18.2071L19.1642 12ZM13.5143 12L7.30722 5.79291L5.89301 7.20712L10.6859 12L5.89301 16.7929L7.30722 18.2071L13.5143 12Z"></path>
</svg>
);
export default DoubleArrowRightHeadIcon;
================================================
FILE: src/assets/ExclamationFilledIcon.tsx
================================================
import React from "react";
const ExclamationFilledIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM11 15V17H13V15H11ZM11 7V13H13V7H11Z"></path>
</svg>
);
export default ExclamationFilledIcon;
================================================
FILE: src/assets/EyeIcon.tsx
================================================
import React from "react";
const EyeIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M1.18164 12C2.12215 6.87976 6.60812 3 12.0003 3C17.3924 3 21.8784 6.87976 22.8189 12C21.8784 17.1202 17.3924 21 12.0003 21C6.60812 21 2.12215 17.1202 1.18164 12ZM12.0003 17C14.7617 17 17.0003 14.7614 17.0003 12C17.0003 9.23858 14.7617 7 12.0003 7C9.23884 7 7.00026 9.23858 7.00026 12C7.00026 14.7614 9.23884 17 12.0003 17ZM12.0003 15C10.3434 15 9.00026 13.6569 9.00026 12C9.00026 10.3431 10.3434 9 12.0003 9C13.6571 9 15.0003 10.3431 15.0003 12C15.0003 13.6569 13.6571 15 12.0003 15Z"></path>
</svg>
);
export default EyeIcon;
================================================
FILE: src/assets/EyeOffIcon.tsx
================================================
import React from "react";
const EyeOffIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M4.52047 5.93457L1.39366 2.80777L2.80788 1.39355L22.6069 21.1925L21.1927 22.6068L17.8827 19.2968C16.1814 20.3755 14.1638 21.0002 12.0003 21.0002C6.60812 21.0002 2.12215 17.1204 1.18164 12.0002C1.61832 9.62282 2.81932 7.5129 4.52047 5.93457ZM14.7577 16.1718L13.2937 14.7078C12.902 14.8952 12.4634 15.0002 12.0003 15.0002C10.3434 15.0002 9.00026 13.657 9.00026 12.0002C9.00026 11.537 9.10522 11.0984 9.29263 10.7067L7.82866 9.24277C7.30514 10.0332 7.00026 10.9811 7.00026 12.0002C7.00026 14.7616 9.23884 17.0002 12.0003 17.0002C13.0193 17.0002 13.9672 16.6953 14.7577 16.1718ZM7.97446 3.76015C9.22127 3.26959 10.5793 3.00016 12.0003 3.00016C17.3924 3.00016 21.8784 6.87992 22.8189 12.0002C22.5067 13.6998 21.8038 15.2628 20.8068 16.5925L16.947 12.7327C16.9821 12.4936 17.0003 12.249 17.0003 12.0002C17.0003 9.23873 14.7617 7.00016 12.0003 7.00016C11.7514 7.00016 11.5068 7.01833 11.2677 7.05343L7.97446 3.76015Z"></path>
</svg>
);
export default EyeOffIcon;
================================================
FILE: src/assets/LoadingSpinner.tsx
================================================
import React from "react";
const LoadingSpinner = ({ ...props }) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z" />
</svg>
);
export default LoadingSpinner;
================================================
FILE: src/assets/MinusIcon.tsx
================================================
import React from "react";
const MinusIcon = ({ ...props }) => (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2.5"
>
<path d="M20 12H4" />
</svg>
);
export default MinusIcon;
================================================
FILE: src/assets/PlusIcon.tsx
================================================
import React from "react";
const PlusIcon = ({ ...props }) => (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2.5"
>
<path d="M12 4v16m8-8H4" />
</svg>
);
export default PlusIcon;
================================================
FILE: src/assets/SearchIcon.tsx
================================================
import React from "react";
const SearchIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"></path>
</svg>
);
export default SearchIcon;
================================================
FILE: src/assets/XCircleIcon.tsx
================================================
import React from "react";
const XCircleIcon = ({ ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 10.5858L9.17157 7.75736L7.75736 9.17157L10.5858 12L7.75736 14.8284L9.17157 16.2426L12 13.4142L14.8284 16.2426L16.2426 14.8284L13.4142 12L16.2426 9.17157L14.8284 7.75736L12 10.5858Z"></path>
</svg>
);
export default XCircleIcon;
================================================
FILE: src/assets/XIcon.tsx
================================================
import React from "react";
const XIcon = ({ ...props }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
);
};
export default XIcon;
================================================
FILE: src/assets/index.ts
================================================
export { default as ArrowDownHeadIcon } from "./ArrowDownHeadIcon";
export { default as ArrowDownIcon } from "./ArrowDownIcon";
export { default as ArrowDownRightIcon } from "./ArrowDownRightIcon";
export { default as ArrowLeftHeadIcon } from "./ArrowLeftHeadIcon";
export { default as ArrowRightHeadIcon } from "./ArrowRightHeadIcon";
export { default as ArrowRightIcon } from "./ArrowRightIcon";
export { default as ArrowUpHeadIcon } from "./ArrowUpHeadIcon";
export { default as ArrowUpIcon } from "./ArrowUpIcon";
export { default as ArrowUpRightIcon } from "./ArrowUpRightIcon";
export { default as CalendarIcon } from "./CalendarIcon";
export { default as DoubleArrowLeftHeadIcon } from "./DoubleArrowLeftHeadIcon";
export { default as DoubleArrowRightHeadIcon } from "./DoubleArrowRightHeadIcon";
export { default as ExclamationFilledIcon } from "./ExclamationFilledIcon";
export { default as EyeIcon } from "./EyeIcon";
export { default as EyeOffIcon } from "./EyeOffIcon";
export { default as LoadingSpinner } from "./LoadingSpinner";
export { default as SearchIcon } from "./SearchIcon";
export { default as XCircleIcon } from "./XCircleIcon";
export { default as PlusIcon } from "./PlusIcon";
export { default as MinusIcon } from "./MinusIcon";
export { default as ChevronLeftFill } from "./ChevronLeftFill";
export { default as ChevronRightFill } from "./ChevronRightFill";
================================================
FILE: src/components/chart-elements/AreaChart/AreaChart.tsx
================================================
"use client";
import React, { Fragment, useState } from "react";
import {
Area,
AreaChart as ReChartsAreaChart,
CartesianGrid,
Dot,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
Label,
} from "recharts";
import { AxisDomain } from "recharts/types/util/types";
import BaseChartProps from "../common/BaseChartProps";
import ChartLegend from "../common/ChartLegend";
import ChartTooltip from "../common/ChartTooltip";
import NoData from "../common/NoData";
import {
constructCategoryColors,
getYAxisDomain,
hasOnlyOneValueForThisKey,
} from "../common/utils";
import {
BaseColors,
colorPalette,
defaultValueFormatter,
getColorClassNames,
themeColorRange,
tremorTwMerge,
} from "lib";
import { CurveType } from "../../../lib/inputTypes";
export interface AreaChartProps extends BaseChartProps {
stack?: boolean;
curveType?: CurveType;
connectNulls?: boolean;
showGradient?: boolean;
}
interface ActiveDot {
index?: number;
dataKey?: string;
}
const AreaChart = React.forwardRef<HTMLDivElement, AreaChartProps>((props, ref) => {
const {
data = [],
categories = [],
index,
stack = false,
colors = themeColorRange,
valueFormatter = defaultValueFormatter,
startEndOnly = false,
showXAxis = true,
showYAxis = true,
yAxisWidth = 56,
intervalType = "equidistantPreserveStart",
showAnimation = false,
animationDuration = 900,
showTooltip = true,
showLegend = true,
showGridLines = true,
showGradient = true,
autoMinValue = false,
curveType = "linear",
minValue,
maxValue,
connectNulls = false,
allowDecimals = true,
noDataText,
className,
onValueChange,
enableLegendSlider = false,
customTooltip,
rotateLabelX,
padding = (!showXAxis && !showYAxis) || (startEndOnly && !showYAxis)
? { left: 0, right: 0 }
: { left: 20, right: 20 },
tickGap = 5,
xAxisLabel,
yAxisLabel,
...other
} = props;
const CustomTooltip = customTooltip;
const [legendHeight, setLegendHeight] = useState(60);
const [activeDot, setActiveDot] = useState<ActiveDot | undefined>(undefined);
const [activeLegend, setActiveLegend] = useState<string | undefined>(undefined);
const categoryColors = constructCategoryColors(categories, colors);
const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue);
const hasOnValueChange = !!onValueChange;
function onDotClick(itemData: any, event: React.MouseEvent) {
event.stopPropagation();
if (!hasOnValueChange) return;
if (
(itemData.index === activeDot?.index && itemData.dataKey === activeDot?.dataKey) ||
(hasOnlyOneValueForThisKey(data, itemData.dataKey) &&
activeLegend &&
activeLegend === itemData.dataKey)
) {
setActiveLegend(undefined);
setActiveDot(undefined);
onValueChange?.(null);
} else {
setActiveLegend(itemData.dataKey);
setActiveDot({
index: itemData.index,
dataKey: itemData.dataKey,
});
onValueChange?.({
eventType: "dot",
categoryClicked: itemData.dataKey,
...itemData.payload,
});
}
}
function onCategoryClick(dataKey: string) {
if (!hasOnValueChange) return;
if (
(dataKey === activeLegend && !activeDot) ||
(hasOnlyOneValueForThisKey(data, dataKey) && activeDot && activeDot.dataKey === dataKey)
) {
setActiveLegend(undefined);
onValueChange?.(null);
} else {
setActiveLegend(dataKey);
onValueChange?.({
eventType: "category",
categoryClicked: dataKey,
});
}
setActiveDot(undefined);
}
return (
<div ref={ref} className={tremorTwMerge("w-full h-80", className)} {...other}>
<ResponsiveContainer className="h-full w-full">
{data?.length ? (
<ReChartsAreaChart
data={data}
onClick={
hasOnValueChange && (activeLegend || activeDot)
? () => {
setActiveDot(undefined);
setActiveLegend(undefined);
onValueChange?.(null);
}
: undefined
}
margin={{
bottom: xAxisLabel ? 30 : undefined,
left: yAxisLabel ? 20 : undefined,
right: yAxisLabel ? 5 : undefined,
top: 5,
}}
>
{showGridLines ? (
<CartesianGrid
className={tremorTwMerge(
// common
"stroke-1",
// light
"stroke-tremor-border",
// dark
"dark:stroke-dark-tremor-border",
)}
horizontal={true}
vertical={false}
/>
) : null}
<XAxis
padding={padding}
hide={!showXAxis}
dataKey={index}
tick={{ transform: "translate(0, 6)" }}
ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
interval={startEndOnly ? "preserveStartEnd" : intervalType}
tickLine={false}
axisLine={false}
minTickGap={tickGap}
angle={rotateLabelX?.angle}
dy={rotateLabelX?.verticalShift}
height={rotateLabelX?.xAxisHeight}
>
{xAxisLabel && (
<Label
position="insideBottom"
offset={-20}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{xAxisLabel}
</Label>
)}
</XAxis>
<YAxis
width={yAxisWidth}
hide={!showYAxis}
axisLine={false}
tickLine={false}
type="number"
domain={yAxisDomain as AxisDomain}
tick={{ transform: "translate(-3, 0)" }}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickFormatter={valueFormatter}
allowDecimals={allowDecimals}
>
{yAxisLabel && (
<Label
position="insideLeft"
style={{ textAnchor: "middle" }}
angle={-90}
offset={-15}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{yAxisLabel}
</Label>
)}
</YAxis>
<Tooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
content={
showTooltip ? (
({ active, payload, label }) =>
CustomTooltip ? (
<CustomTooltip
payload={payload?.map((payloadItem: any) => ({
...payloadItem,
color: categoryColors.get(payloadItem.dataKey) ?? BaseColors.Gray,
}))}
active={active}
label={label}
/>
) : (
<ChartTooltip
active={active}
payload={payload}
label={label}
valueFormatter={valueFormatter}
categoryColors={categoryColors}
/>
)
) : (
<></>
)
}
position={{ y: 0 }}
/>
{showLegend ? (
<Legend
verticalAlign="top"
height={legendHeight}
content={({ payload }) =>
ChartLegend(
{ payload },
categoryColors,
setLegendHeight,
activeLegend,
hasOnValueChange
? (clickedLegendItem: string) => onCategoryClick(clickedLegendItem)
: undefined,
enableLegendSlider,
)
}
/>
) : null}
{categories.map((category) => {
const gradientId = (categoryColors.get(category) ?? BaseColors.Gray).replace("#", "");
return (
<defs key={category}>
{showGradient ? (
<linearGradient
className={
getColorClassNames(
categoryColors.get(category) ?? BaseColors.Gray,
colorPalette.text,
).textColor
}
id={gradientId}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor="currentColor"
stopOpacity={
activeDot || (activeLegend && activeLegend !== category) ? 0.15 : 0.4
}
/>
<stop offset="95%" stopColor="currentColor" stopOpacity={0} />
</linearGradient>
) : (
<linearGradient
className={
getColorClassNames(
categoryColors.get(category) ?? BaseColors.Gray,
colorPalette.text,
).textColor
}
id={gradientId}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
stopColor="currentColor"
stopOpacity={
activeDot || (activeLegend && activeLegend !== category) ? 0.1 : 0.3
}
/>
</linearGradient>
)}
</defs>
);
})}
{categories.map((category) => {
const gradientId = (categoryColors.get(category) ?? BaseColors.Gray).replace("#", "");
return (
<Area
className={
getColorClassNames(
categoryColors.get(category) ?? BaseColors.Gray,
colorPalette.text,
).strokeColor
}
strokeOpacity={activeDot || (activeLegend && activeLegend !== category) ? 0.3 : 1}
activeDot={(props: any) => {
const { cx, cy, stroke, strokeLinecap, strokeLinejoin, strokeWidth, dataKey } =
props;
return (
<Dot
className={tremorTwMerge(
"stroke-tremor-background dark:stroke-dark-tremor-background",
onValueChange ? "cursor-pointer" : "",
getColorClassNames(
categoryColors.get(dataKey) ?? BaseColors.Gray,
colorPalette.text,
).fillColor,
)}
cx={cx}
cy={cy}
r={5}
fill=""
stroke={stroke}
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
strokeWidth={strokeWidth}
onClick={(dotProps: any, event) => onDotClick(props, event)}
/>
);
}}
dot={(props: any) => {
const {
stroke,
strokeLinecap,
strokeLinejoin,
strokeWidth,
cx,
cy,
dataKey,
index,
} = props;
if (
(hasOnlyOneValueForThisKey(data, category) &&
!(activeDot || (activeLegend && activeLegend !== category))) ||
(activeDot?.index === index && activeDot?.dataKey === category)
) {
return (
<Dot
key={index}
cx={cx}
cy={cy}
r={5}
stroke={stroke}
fill=""
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
strokeWidth={strokeWidth}
className={tremorTwMerge(
"stroke-tremor-background dark:stroke-dark-tremor-background",
onValueChange ? "cursor-pointer" : "",
getColorClassNames(
categoryColors.get(dataKey) ?? BaseColors.Gray,
colorPalette.text,
).fillColor,
)}
/>
);
}
return <Fragment key={index}></Fragment>;
}}
key={category}
name={category}
type={curveType}
dataKey={category}
stroke=""
fill={`url(#${gradientId})`}
strokeWidth={2}
strokeLinejoin="round"
strokeLinecap="round"
isAnimationActive={showAnimation}
animationDuration={animationDuration}
stackId={stack ? "a" : undefined}
connectNulls={connectNulls}
/>
);
})}
{onValueChange
? categories.map((category) => (
<Line
className={tremorTwMerge("cursor-pointer")}
strokeOpacity={0}
key={category}
name={category}
type={curveType}
dataKey={category}
stroke="transparent"
fill="transparent"
legendType="none"
tooltipType="none"
strokeWidth={12}
connectNulls={connectNulls}
onClick={(props: any, event) => {
event.stopPropagation();
const { name } = props;
onCategoryClick(name);
}}
/>
))
: null}
</ReChartsAreaChart>
) : (
<NoData noDataText={noDataText} />
)}
</ResponsiveContainer>
</div>
);
});
AreaChart.displayName = "AreaChart";
export default AreaChart;
================================================
FILE: src/components/chart-elements/AreaChart/index.ts
================================================
export { default as AreaChart } from "./AreaChart";
export type { AreaChartProps } from "./AreaChart";
================================================
FILE: src/components/chart-elements/BarChart/BarChart.tsx
================================================
"use client";
import { colorPalette, getColorClassNames, tremorTwMerge } from "lib";
import React, { useState } from "react";
import {
Bar,
BarChart as ReChartsBarChart,
CartesianGrid,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
Label,
} from "recharts";
import BaseChartProps from "../common/BaseChartProps";
import ChartLegend from "../common/ChartLegend";
import ChartTooltip from "../common/ChartTooltip";
import NoData from "../common/NoData";
import { constructCategoryColors, deepEqual, getYAxisDomain } from "../common/utils";
import { BaseColors, defaultValueFormatter, themeColorRange } from "lib";
import { AxisDomain } from "recharts/types/util/types";
const renderShape = (
props: any,
activeBar: any | undefined,
activeLegend: string | undefined,
layout: string,
) => {
const { fillOpacity, name, payload, value } = props;
let { x, width, y, height } = props;
if (layout === "horizontal" && height < 0) {
y += height;
height = Math.abs(height); // height must be a positive number
} else if (layout === "vertical" && width < 0) {
x += width;
width = Math.abs(width); // width must be a positive number
}
return (
<rect
x={x}
y={y}
width={width}
height={height}
opacity={
activeBar || (activeLegend && activeLegend !== name)
? deepEqual(activeBar, { ...payload, value })
? fillOpacity
: 0.3
: fillOpacity
}
/>
);
};
export interface BarChartProps extends BaseChartProps {
layout?: "vertical" | "horizontal";
stack?: boolean;
relative?: boolean;
barCategoryGap?: string | number;
}
const BarChart = React.forwardRef<HTMLDivElement, BarChartProps>((props, ref) => {
const {
data = [],
categories = [],
index,
colors = themeColorRange,
valueFormatter = defaultValueFormatter,
layout = "horizontal",
stack = false,
relative = false,
startEndOnly = false,
animationDuration = 900,
showAnimation = false,
showXAxis = true,
showYAxis = true,
yAxisWidth = 56,
intervalType = "equidistantPreserveStart",
showTooltip = true,
showLegend = true,
showGridLines = true,
autoMinValue = false,
minValue,
maxValue,
allowDecimals = true,
noDataText,
onValueChange,
enableLegendSlider = false,
customTooltip,
rotateLabelX,
barCategoryGap,
tickGap = 5,
xAxisLabel,
yAxisLabel,
className,
padding = !showXAxis && !showYAxis ? { left: 0, right: 0 } : { left: 20, right: 20 },
...other
} = props;
const CustomTooltip = customTooltip;
const [legendHeight, setLegendHeight] = useState(60);
const categoryColors = constructCategoryColors(categories, colors);
const [activeBar, setActiveBar] = React.useState<any | undefined>(undefined);
const [activeLegend, setActiveLegend] = useState<string | undefined>(undefined);
const hasOnValueChange = !!onValueChange;
function onBarClick(data: any, idx: number, event: React.MouseEvent) {
event.stopPropagation();
if (!onValueChange) return;
if (deepEqual(activeBar, { ...data.payload, value: data.value })) {
setActiveLegend(undefined);
setActiveBar(undefined);
onValueChange?.(null);
} else {
setActiveLegend(data.tooltipPayload?.[0]?.dataKey);
setActiveBar({
...data.payload,
value: data.value,
});
onValueChange?.({
eventType: "bar",
categoryClicked: data.tooltipPayload?.[0]?.dataKey,
...data.payload,
});
}
}
function onCategoryClick(dataKey: string) {
if (!hasOnValueChange) return;
if (dataKey === activeLegend && !activeBar) {
setActiveLegend(undefined);
onValueChange?.(null);
} else {
setActiveLegend(dataKey);
onValueChange?.({
eventType: "category",
categoryClicked: dataKey,
});
}
setActiveBar(undefined);
}
const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue);
return (
<div ref={ref} className={tremorTwMerge("w-full h-80", className)} {...other}>
<ResponsiveContainer className="h-full w-full">
{data?.length ? (
<ReChartsBarChart
barCategoryGap={barCategoryGap}
data={data}
stackOffset={stack ? "sign" : relative ? "expand" : "none"}
layout={layout === "vertical" ? "vertical" : "horizontal"}
onClick={
hasOnValueChange && (activeLegend || activeBar)
? () => {
setActiveBar(undefined);
setActiveLegend(undefined);
onValueChange?.(null);
}
: undefined
}
margin={{
bottom: xAxisLabel ? 30 : undefined,
left: yAxisLabel ? 20 : undefined,
right: yAxisLabel ? 5 : undefined,
top: 5,
}}
>
{showGridLines ? (
<CartesianGrid
className={tremorTwMerge(
// common
"stroke-1",
// light
"stroke-tremor-border",
// dark
"dark:stroke-dark-tremor-border",
)}
horizontal={layout !== "vertical"}
vertical={layout === "vertical"}
/>
) : null}
{layout !== "vertical" ? (
<XAxis
padding={padding}
hide={!showXAxis}
dataKey={index}
interval={startEndOnly ? "preserveStartEnd" : intervalType}
tick={{ transform: "translate(0, 6)" }}
ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined}
fill=""
stroke=""
className={tremorTwMerge(
// common
"mt-4 text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickLine={false}
axisLine={false}
angle={rotateLabelX?.angle}
dy={rotateLabelX?.verticalShift}
height={rotateLabelX?.xAxisHeight}
minTickGap={tickGap}
>
{xAxisLabel && (
<Label
position="insideBottom"
offset={-20}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{xAxisLabel}
</Label>
)}
</XAxis>
) : (
<XAxis
hide={!showXAxis}
type="number"
tick={{ transform: "translate(-3, 0)" }}
domain={yAxisDomain as AxisDomain}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickLine={false}
axisLine={false}
tickFormatter={valueFormatter}
minTickGap={tickGap}
allowDecimals={allowDecimals}
angle={rotateLabelX?.angle}
dy={rotateLabelX?.verticalShift}
height={rotateLabelX?.xAxisHeight}
>
{xAxisLabel && (
<Label
position="insideBottom"
offset={-20}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{xAxisLabel}
</Label>
)}
</XAxis>
)}
{layout !== "vertical" ? (
<YAxis
width={yAxisWidth}
hide={!showYAxis}
axisLine={false}
tickLine={false}
type="number"
domain={yAxisDomain as AxisDomain}
tick={{ transform: "translate(-3, 0)" }}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickFormatter={
relative ? (value: number) => `${(value * 100).toString()} %` : valueFormatter
}
allowDecimals={allowDecimals}
>
{yAxisLabel && (
<Label
position="insideLeft"
style={{ textAnchor: "middle" }}
angle={-90}
offset={-15}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{yAxisLabel}
</Label>
)}
</YAxis>
) : (
<YAxis
width={yAxisWidth}
hide={!showYAxis}
dataKey={index}
axisLine={false}
tickLine={false}
ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined}
type="category"
interval="preserveStartEnd"
tick={{ transform: "translate(0, 6)" }}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
>
{yAxisLabel && (
<Label
position="insideLeft"
style={{ textAnchor: "middle" }}
angle={-90}
offset={-15}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{yAxisLabel}
</Label>
)}
</YAxis>
)}
<Tooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
cursor={{ fill: "#d1d5db", opacity: "0.15" }}
content={
showTooltip ? (
({ active, payload, label }) =>
CustomTooltip ? (
<CustomTooltip
payload={payload?.map((payloadItem: any) => ({
...payloadItem,
color: categoryColors.get(payloadItem.dataKey) ?? BaseColors.Gray,
}))}
active={active}
label={label}
/>
) : (
<ChartTooltip
active={active}
payload={payload}
label={label}
valueFormatter={valueFormatter}
categoryColors={categoryColors}
/>
)
) : (
<></>
)
}
position={{ y: 0 }}
/>
{showLegend ? (
<Legend
verticalAlign="top"
height={legendHeight}
content={({ payload }) =>
ChartLegend(
{ payload },
categoryColors,
setLegendHeight,
activeLegend,
hasOnValueChange
? (clickedLegendItem: string) => onCategoryClick(clickedLegendItem)
: undefined,
enableLegendSlider,
)
}
/>
) : null}
{categories.map((category) => (
<Bar
className={tremorTwMerge(
getColorClassNames(
categoryColors.get(category) ?? BaseColors.Gray,
colorPalette.background,
).fillColor,
onValueChange ? "cursor-pointer" : "",
)}
key={category}
name={category}
type="linear"
stackId={stack || relative ? "a" : undefined}
dataKey={category}
fill=""
isAnimationActive={showAnimation}
animationDuration={animationDuration}
shape={(props: any) => renderShape(props, activeBar, activeLegend, layout)}
onClick={onBarClick}
/>
))}
</ReChartsBarChart>
) : (
<NoData noDataText={noDataText} />
)}
</ResponsiveContainer>
</div>
);
});
BarChart.displayName = "BarChart";
export default BarChart;
================================================
FILE: src/components/chart-elements/BarChart/index.ts
================================================
export { default as BarChart } from "./BarChart";
export type { BarChartProps } from "./BarChart";
================================================
FILE: src/components/chart-elements/DonutChart/DonutChart.tsx
================================================
"use client";
import { BaseColors, defaultValueFormatter, themeColorRange, tremorTwMerge } from "lib";
import React, { useEffect } from "react";
import {
Pie,
PieChart as ReChartsDonutChart,
ResponsiveContainer,
Sector,
Tooltip,
} from "recharts";
import { Color, ValueFormatter } from "../../../lib/inputTypes";
import NoData from "../common/NoData";
import { DonutChartTooltip } from "./DonutChartTooltip";
import { parseData, parseLabelInput } from "./inputParser";
import type { EventProps } from "components/chart-elements/common";
import { CustomTooltipProps } from "components/chart-elements/common/CustomTooltipProps";
import type BaseAnimationTimingProps from "../common/BaseAnimationTimingProps";
type DonutChartVariant = "donut" | "pie";
export interface DonutChartProps extends BaseAnimationTimingProps {
data: any[];
category?: string;
index?: string;
colors?: (Color | string)[];
variant?: DonutChartVariant;
valueFormatter?: ValueFormatter;
label?: string;
showLabel?: boolean;
showAnimation?: boolean;
showTooltip?: boolean;
noDataText?: string;
className?: string;
onValueChange?: (value: EventProps) => void;
customTooltip?: React.ComponentType<CustomTooltipProps>;
}
const renderInactiveShape = (props: any) => {
const {
cx,
cy,
// midAngle,
innerRadius,
outerRadius,
startAngle,
endAngle,
// fill,
// payload,
// percent,
// value,
// activeIndex,
className,
} = props;
return (
<g>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
className={className}
fill=""
opacity={0.3}
style={{ outline: "none" }}
/>
</g>
);
};
const DonutChart = React.forwardRef<HTMLDivElement, DonutChartProps>((props, ref) => {
const {
data = [],
category = "value",
index = "name",
colors = themeColorRange,
variant = "donut",
valueFormatter = defaultValueFormatter,
label,
showLabel = true,
animationDuration = 900,
showAnimation = false,
showTooltip = true,
noDataText,
onValueChange,
customTooltip,
className,
...other
} = props;
const CustomTooltip = customTooltip;
const isDonut = variant == "donut";
const parsedLabelInput = parseLabelInput(label, valueFormatter, data, category);
const [activeIndex, setActiveIndex] = React.useState<number | undefined>(undefined);
const hasOnValueChange = !!onValueChange;
function onShapeClick(data: any, index: number, event: React.MouseEvent) {
event.stopPropagation();
if (!hasOnValueChange) return;
if (activeIndex === index) {
setActiveIndex(undefined);
onValueChange?.(null);
} else {
setActiveIndex(index);
onValueChange?.({
eventType: "slice",
...data.payload.payload,
});
}
}
useEffect(() => {
const pieSectors = document.querySelectorAll(".recharts-pie-sector");
if (pieSectors) {
pieSectors.forEach((sector) => {
sector.setAttribute("style", "outline: none");
});
}
}, [activeIndex]);
return (
<div ref={ref} className={tremorTwMerge("w-full h-40", className)} {...other}>
<ResponsiveContainer className="h-full w-full">
{data?.length ? (
<ReChartsDonutChart
onClick={
hasOnValueChange && activeIndex
? () => {
setActiveIndex(undefined);
onValueChange?.(null);
}
: undefined
}
margin={{ top: 0, left: 0, right: 0, bottom: 0 }}
>
{showLabel && isDonut ? (
<text
className={tremorTwMerge(
// light
"fill-tremor-content-emphasis",
// dark
"dark:fill-dark-tremor-content-emphasis",
)}
x="50%"
y="50%"
textAnchor="middle"
dominantBaseline="middle"
>
{parsedLabelInput}
</text>
) : null}
<Pie
className={tremorTwMerge(
"stroke-tremor-background dark:stroke-dark-tremor-background",
onValueChange ? "cursor-pointer" : "cursor-default",
)}
data={parseData(data, colors)}
cx="50%"
cy="50%"
startAngle={90}
endAngle={-270}
innerRadius={isDonut ? "75%" : "0%"}
outerRadius="100%"
stroke=""
strokeLinejoin="round"
dataKey={category}
nameKey={index}
isAnimationActive={showAnimation}
animationDuration={animationDuration}
onClick={onShapeClick}
activeIndex={activeIndex}
inactiveShape={renderInactiveShape}
style={{ outline: "none" }}
/>
{/* {showTooltip ? (
<Tooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
content={({ active, payload }) => (
<DonutChartTooltip
active={active}
payload={payload}
valueFormatter={valueFormatter}
/>
)}
/>
) : null} */}
<Tooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
content={
showTooltip ? (
({ active, payload }) =>
CustomTooltip ? (
<CustomTooltip
payload={payload?.map((payloadItem) => ({
...payloadItem,
color: payload?.[0]?.payload?.color ?? BaseColors.Gray,
}))}
active={active}
label={payload?.[0]?.name}
/>
) : (
<DonutChartTooltip
active={active}
payload={payload}
valueFormatter={valueFormatter}
/>
)
) : (
<></>
)
}
/>
</ReChartsDonutChart>
) : (
<NoData noDataText={noDataText} />
)}
</ResponsiveContainer>
</div>
);
});
DonutChart.displayName = "DonutChart";
export default DonutChart;
================================================
FILE: src/components/chart-elements/DonutChart/DonutChartTooltip.tsx
================================================
import React from "react";
import { tremorTwMerge, ValueFormatter } from "lib";
import { ChartTooltipFrame, ChartTooltipRow } from "components/chart-elements/common/ChartTooltip";
export interface DonutChartTooltipProps {
active: boolean | undefined;
payload: any;
valueFormatter: ValueFormatter;
}
export const DonutChartTooltip = ({ active, payload, valueFormatter }: DonutChartTooltipProps) => {
if (active && payload?.[0]) {
const payloadRow = payload?.[0];
return (
<ChartTooltipFrame>
<div className={tremorTwMerge("px-4 py-2")}>
<ChartTooltipRow
value={valueFormatter(payloadRow.value)}
name={payloadRow.name}
color={payloadRow.payload.color}
/>
</div>
</ChartTooltipFrame>
);
}
return null;
};
================================================
FILE: src/components/chart-elements/DonutChart/index.ts
================================================
export { default as DonutChart } from "./DonutChart";
export type { DonutChartProps } from "./DonutChart";
================================================
FILE: src/components/chart-elements/DonutChart/inputParser.ts
================================================
import { BaseColors, colorPalette, getColorClassNames, sumNumericArray } from "lib";
import { Color, ValueFormatter } from "../../../lib/inputTypes";
export const parseData = (data: any[], colors: (Color | string)[]) =>
data.map((dataPoint: any, idx: number) => {
const baseColor = idx < colors.length ? colors[idx] : BaseColors.Gray;
return {
...dataPoint,
// explicitly adding color key if not present for tooltip coloring
color: baseColor,
className: getColorClassNames(baseColor ?? BaseColors.Gray, colorPalette.background)
.fillColor,
fill: "",
};
});
const calculateDefaultLabel = (data: any[], category: string) =>
sumNumericArray(data.map((dataPoint) => dataPoint[category]));
export const parseLabelInput = (
labelInput: string | undefined,
valueFormatter: ValueFormatter,
data: any[],
category: string,
) => (labelInput ? labelInput : valueFormatter(calculateDefaultLabel(data, category)));
================================================
FILE: src/components/chart-elements/FunnelChart/FunnelChart.tsx
================================================
import React from "react";
import { ChartTooltipFrame, ChartTooltipRow } from "../common/ChartTooltip";
import {
BaseColors,
Color,
FunnelVariantType,
colorPalette,
defaultValueFormatter,
getColorClassNames,
tremorTwMerge,
} from "lib";
import { CustomTooltipProps, EventProps } from "../common";
import NoData from "../common/NoData";
import { ArrowRightIcon } from "assets";
type FormattedDataT = DataT & {
normalizedValue: number;
startX: number;
startY: number;
barHeight: number;
nextValue: number;
nextNormalizedValue: number;
nextBarHeight: number;
nextStartX: number;
};
type CalculateFrom = "first" | "previous";
type Tooltip = {
x: number;
y: number;
data?: {
className?: string;
name: string;
fill?: string;
dataKey: string;
color?: Color | string;
value: number;
payload?: any;
};
index?: number;
};
type DataT = {
value: number;
name: string;
};
const GLOBAL_PADDING = 10;
const HALF_PADDING = GLOBAL_PADDING / 2;
const Y_AXIS_LABELS = ["100%", "75%", "50%", "25%", "0%"];
export interface FunnelChartProps extends React.HTMLAttributes<HTMLDivElement> {
data: DataT[];
evolutionGradient?: boolean;
gradient?: boolean;
valueFormatter?: (value: number) => string;
calculateFrom?: CalculateFrom;
color?: Color | string;
variant?: FunnelVariantType;
yAxisPadding?: number;
showYAxis?: boolean;
showXAxis?: boolean;
showArrow?: boolean;
showGridLines?: boolean;
showTooltip?: boolean;
onValueChange?: (value: EventProps) => void;
customTooltip?: React.ComponentType<CustomTooltipProps>;
noDataText?: string;
rotateLabelX?: {
angle: number;
verticalShift?: number;
xAxisHeight?: number;
};
barGap?: number | `${number}%`;
xAxisLabel?: string;
yAxisLabel?: string;
}
//#region Funnel Chart Primitive
const FunnelChartPrimitive = React.forwardRef<HTMLDivElement, FunnelChartProps>(
(props: FunnelChartProps, ref) => {
const {
data,
evolutionGradient = false,
gradient = true,
valueFormatter = defaultValueFormatter,
className,
calculateFrom = "first",
color,
variant = "base",
showGridLines = true,
showYAxis = calculateFrom === "previous" ? false : true,
showXAxis = true,
showArrow = true,
xAxisLabel = "",
yAxisLabel = "",
yAxisPadding = showYAxis ? (yAxisLabel ? 70 : 45) : 0,
showTooltip = true,
onValueChange,
customTooltip,
noDataText,
rotateLabelX,
barGap = "20%",
...other
} = props;
const DEFAULT_X_AXIS_HEIGHT = showXAxis && xAxisLabel ? 25 : 15;
const CustomTooltip = customTooltip;
const svgRef = React.useRef<SVGSVGElement>(null);
const tooltipRef = React.useRef<HTMLDivElement>(null);
const [width, setWidth] = React.useState(0);
const [height, setHeight] = React.useState(0);
const [tooltip, setTooltip] = React.useState<Tooltip>({ x: 0, y: 0 });
const [activeBar, setActiveBar] = React.useState<any | undefined>(undefined);
const hasOnValueChange = !!onValueChange;
function onBarClick(data: any, idx: number, event: React.MouseEvent) {
event.stopPropagation();
if (!hasOnValueChange) return;
if (idx === activeBar?.index) {
setActiveBar(undefined);
onValueChange(undefined);
} else {
setActiveBar({ data, index: idx });
onValueChange({
eventType: "bar",
categoryClicked: data.name,
[data.name]: data.value,
percentage: data.normalizedValue,
});
}
}
const maxValue = React.useMemo(() => Math.max(...data.map((item) => item.value)), [data]);
const widthWithoutPadding = width - GLOBAL_PADDING - yAxisPadding;
const gap = React.useMemo(() => {
if (typeof barGap === "number") {
return barGap;
} else if (typeof barGap === "string" && barGap.endsWith("%")) {
const percentage = parseFloat(barGap.slice(0, -1));
const totalWidthForGaps = (widthWithoutPadding * percentage) / 100;
const numberOfGaps = data.length - 1;
return totalWidthForGaps / numberOfGaps;
} else {
console.error(
'Invalid barGap value. It must be a number or a percentage string (e.g., "10%").',
);
return 30;
}
}, [widthWithoutPadding, data.length, barGap]);
const barWidth = React.useMemo(
() => (widthWithoutPadding - (data.length - 1) * gap - gap) / data.length,
[widthWithoutPadding, gap, data.length],
);
const realHeight =
height -
GLOBAL_PADDING -
(showXAxis
? (rotateLabelX?.xAxisHeight || DEFAULT_X_AXIS_HEIGHT) + (showXAxis && xAxisLabel ? 30 : 10)
: 0);
const isPreviousCalculation = calculateFrom === "previous";
const isVariantCenter = variant === "center";
React.useLayoutEffect(() => {
const handleResize = () => {
if (svgRef.current) {
const boundingBox = svgRef.current.getBoundingClientRect();
setWidth(boundingBox.width);
setHeight(boundingBox.height);
}
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [className]);
React.useEffect(() => {
const handleTooltipOverflows = () => {
if (tooltipRef.current) {
const boundingBox = tooltipRef.current.getBoundingClientRect();
if (boundingBox.right > window.innerWidth) {
tooltipRef.current.style.left = `${width - boundingBox.width}px`;
}
}
};
handleTooltipOverflows();
window.addEventListener("resize", handleTooltipOverflows);
return () => {
window.removeEventListener("resize", handleTooltipOverflows);
};
}, [tooltip, width]);
const formattedData = React.useMemo(() => {
if (realHeight <= 0) return [];
return data.reduce((acc: FormattedDataT[], item, index) => {
const prev = acc[index - 1];
const value = item.value;
const valueToCompareWith = isPreviousCalculation ? (prev?.value ?? maxValue) : maxValue;
const calculationHeight = isPreviousCalculation
? (prev?.barHeight ?? realHeight)
: realHeight;
const normalizedValue = value / valueToCompareWith;
const barHeight = normalizedValue * calculationHeight;
const startX = index * (barWidth + gap) + 0.5 * gap;
const startY =
calculationHeight -
barHeight +
(isPreviousCalculation ? realHeight - (prev?.barHeight ?? realHeight) : 0);
const nextValue = data[index + 1]?.value;
const nextNormalizedValue = nextValue / valueToCompareWith;
const nextBarHeight = nextNormalizedValue * calculationHeight;
const nextStartX = (index + 1) * (barWidth + gap) + 0.5 * gap;
acc.push({
value,
normalizedValue,
name: item.name,
startX,
startY,
barHeight,
nextValue,
nextNormalizedValue,
nextBarHeight,
nextStartX,
});
return acc;
}, []);
}, [data, realHeight, isPreviousCalculation, barWidth, gap, maxValue]);
const handleTooltip = (touch: React.Touch) => {
const chartBoundingRect = svgRef.current?.getBoundingClientRect();
if (!chartBoundingRect) return;
const chartX = chartBoundingRect.x;
const chartY = chartBoundingRect.y;
const chartTop = chartY + window.scrollY;
const chartLeft = chartX + window.scrollX + yAxisPadding + HALF_PADDING;
const chartWidth = chartBoundingRect.width - yAxisPadding - HALF_PADDING;
const chartHeight =
chartBoundingRect.height - HALF_PADDING - (showXAxis ? DEFAULT_X_AXIS_HEIGHT : 0);
const chartRight = chartLeft + chartWidth;
const chartBottom = chartTop + chartHeight;
if (
touch.pageX < chartLeft ||
touch.pageX > chartRight ||
touch.pageY < chartTop ||
touch.pageY > chartBottom
) {
console.log("out of bounds");
return setTooltip({ x: 0, y: 0 });
}
const pageX = touch.pageX - chartX - barWidth / 2 - yAxisPadding - HALF_PADDING;
const closestBar = formattedData.reduce((acc, current) => {
const currentDistance = Math.abs(current.startX - pageX);
const accDistance = Math.abs(acc.startX - pageX);
return currentDistance < accDistance ? current : acc;
});
const closestBarIndex = formattedData.findIndex((bar) => bar === closestBar);
setTooltip({
x: closestBar.startX,
y: closestBar.startY,
data: {
dataKey: closestBar.name,
name: closestBar.name,
value: closestBar.value,
color: color ?? BaseColors.Blue,
className: tremorTwMerge(
getColorClassNames(color ?? BaseColors.Blue, colorPalette.text).textColor,
hasOnValueChange ? "cursor-pointer" : "cursor-default",
),
fill: "",
payload: closestBar,
},
index: closestBarIndex,
});
};
return (
<div
ref={ref}
className={tremorTwMerge("tremor-wrapper relative w-full h-80", className)}
{...other}
>
{data?.length ? (
<>
<svg
ref={svgRef}
xmlns="http://www.w3.org/2000/svg"
className={tremorTwMerge("w-full h-full")}
onMouseMove={(e) => {
const fakeTouch = {
clientX: e.clientX,
clientY: e.clientY,
pageX: e.pageX,
pageY: e.pageY,
} as React.Touch;
handleTooltip(fakeTouch);
}}
onTouchMove={(e) => {
const touch = e.touches[0];
handleTooltip(touch);
}}
onMouseLeave={() => setTooltip({ x: 0, y: 0 })}
onTouchEnd={() => setTooltip({ x: 0, y: 0 })}
>
{/* Draw Y axis labels and lines */}
{Y_AXIS_LABELS.map((label, index) => (
<React.Fragment key={`y-axis-${index}`}>
{showGridLines ? (
<line
x1={yAxisPadding + HALF_PADDING}
y1={(index * realHeight) / 4 + HALF_PADDING}
x2={width - HALF_PADDING}
y2={(index * realHeight) / 4 + HALF_PADDING}
stroke="currentColor"
className={tremorTwMerge(
// common
"stroke-1",
// light
"stroke-tremor-border",
// dark
"dark:stroke-dark-tremor-border",
)}
/>
) : null}
<text
x={yAxisPadding - 10 + HALF_PADDING}
y={(index * realHeight) / 4 + 5 + HALF_PADDING}
textAnchor="end"
fill=""
stroke=""
className={tremorTwMerge(
// base
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
>
{label}
</text>
</React.Fragment>
))}
{formattedData.map((item, index) => (
<g key={`bar-${index}`}>
{/* Hover gray rect */}
<rect
x={item.startX - gap * 0.5 + HALF_PADDING + yAxisPadding}
y={HALF_PADDING}
width={barWidth + gap}
height={realHeight}
fill="currentColor"
className={tremorTwMerge(
"z-0",
tooltip.index === index ? "text-[#d1d5db]/15" : "text-transparent",
)}
/>
{/* Draw gradient bar to fill space */}
{gradient ? (
<rect
x={item.startX + HALF_PADDING + yAxisPadding}
y={
realHeight -
(isPreviousCalculation
? formattedData[index - 1]?.barHeight || realHeight
: realHeight) +
HALF_PADDING
}
width={barWidth}
height={
(realHeight -
item.barHeight -
(isPreviousCalculation
? realHeight - formattedData[index - 1]?.barHeight || 0
: 0)) /
(isVariantCenter ? 2 : 1)
}
fill={`url(#base-gradient)`}
className={tremorTwMerge(
!activeBar || activeBar.index === index ? "" : "opacity-30",
)}
/>
) : null}
{/* Draw bar */}
<rect
x={item.startX + HALF_PADDING + yAxisPadding}
y={
(isVariantCenter ? realHeight / 2 - item.barHeight / 2 : item.startY) +
HALF_PADDING
}
width={barWidth}
height={item.barHeight}
fill="currentColor"
className={tremorTwMerge(
getColorClassNames(color ?? BaseColors.Blue, colorPalette.text).textColor,
!activeBar || activeBar.index === index ? "" : "opacity-30",
hasOnValueChange ? "cursor-pointer" : "cursor-default",
)}
onClick={(e) => onBarClick(item, index, e)}
/>
{/* Draw bottom gradient bar to fill space */}
{gradient && isVariantCenter ? (
<rect
x={item.startX + HALF_PADDING + yAxisPadding}
y={realHeight / 2 + item.barHeight / 2 + HALF_PADDING}
width={barWidth}
height={(realHeight - item.barHeight) / 2}
fill={`url(#base-gradient-revert)`}
className={tremorTwMerge(
!activeBar || activeBar.index === index ? "" : "opacity-30",
)}
/>
) : null}
{/* Draw label */}
{showXAxis ? (
<foreignObject
x={item.startX + HALF_PADDING + yAxisPadding}
y={realHeight + HALF_PADDING + 10}
width={barWidth}
height={rotateLabelX?.xAxisHeight || DEFAULT_X_AXIS_HEIGHT}
transform={
rotateLabelX
? `rotate(${rotateLabelX?.angle}, ${
item.startX + barWidth / 2 + HALF_PADDING + yAxisPadding
}, ${
realHeight +
(rotateLabelX?.xAxisHeight || DEFAULT_X_AXIS_HEIGHT) / 2 +
HALF_PADDING +
(rotateLabelX?.verticalShift || 0)
})`
: undefined
}
>
<div
className={tremorTwMerge(
//common
"truncate text-center !text-tremor-label",
// light
"text-tremor-content",
// dark
"dark:text-dark-tremor-content",
)}
title={item.name}
>
{item.name}
</div>
</foreignObject>
) : null}
</g>
))}
{/* Draw gradient polygon between bars */}
{formattedData.map((item, index) => (
<React.Fragment key={`gradient-${index}`}>
{index < data.length - 1 && evolutionGradient ? (
<>
{isVariantCenter ? (
<>
<polygon
points={`
${item.startX + barWidth + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 + item.nextBarHeight / 4 + HALF_PADDING
}
${item.nextStartX + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 + item.nextBarHeight / 4 + HALF_PADDING
}
${item.nextStartX + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 - item.nextBarHeight / 2 + HALF_PADDING
}
${item.startX + barWidth + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 - item.barHeight / 2 + HALF_PADDING
}
`}
fill={`url(#base-gradient)`}
className={tremorTwMerge(
"z-10",
!activeBar || activeBar.index === index ? "" : "opacity-30",
)}
/>
<polygon
points={`
${item.startX + barWidth + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 + item.barHeight / 2 + HALF_PADDING
}
${item.nextStartX + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 + item.nextBarHeight / 2 + HALF_PADDING
}
${item.nextStartX + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 - item.nextBarHeight / 4 + HALF_PADDING
}
${item.startX + barWidth + HALF_PADDING + yAxisPadding}, ${
realHeight / 2 - item.nextBarHeight / 4 + HALF_PADDING
}
`}
fill={`url(#base-gradient-revert)`}
className={tremorTwMerge(
"z-10",
!activeBar || activeBar.index === index ? "" : "opacity-30",
)}
/>
</>
) : (
<polygon
points={`
${item.startX + barWidth + HALF_PADDING + yAxisPadding}, ${
item.startY + HALF_PADDING
}
${item.nextStartX + HALF_PADDING + yAxisPadding}, ${
realHeight - item.nextBarHeight + HALF_PADDING
}
${item.nextStartX + HALF_PADDING + yAxisPadding}, ${
realHeight + HALF_PADDING
}
${item.startX + barWidth + HALF_PADDING + yAxisPadding}, ${
realHeight + HALF_PADDING
}
`}
fill={`url(#base-gradient)`}
className={tremorTwMerge(
"z-10",
!activeBar || activeBar.index === index ? "" : "opacity-30",
)}
/>
)}
</>
) : null}
{/* Hover transparent rect for tooltip */}
{/*<rect
x={item.startX - 0.5 * gap + HALF_PADDING + yAxisPadding}
y={HALF_PADDING}
width={barWidth + gap}
height={realHeight}
fill="transparent"
onMouseEnter={() => handleTooltip(index, item)}
onTouchStart={() => handleTooltip(index, item)}
onTouchMove={(e) => {
const touch = e.touches[0];
const distance = barWidth + gap * 2 + yAxisPadding - touch.clientX;
const closestBar = formattedData.reduce((acc, current) => {
const currentDistance = Math.abs(current.startX + distance);
const accDistance = Math.abs(acc.startX + distance);
return currentDistance < accDistance ? current : acc;
});
const closestBarIndex = formattedData.findIndex((bar) => bar === closestBar);
handleTooltip(closestBarIndex, closestBar);
}}
onMouseLeave={() => setTooltip({ x: 0, y: 0 })}
onTouchEnd={() => setTooltip({ x: 0, y: 0 })}
onClick={(e) => onBarClick(item, index, e)}
className={tremorTwMerge(
hasOnValueChange ? "cursor-pointer" : "cursor-default",
)}
/> */}
{/* Add arrow between labels */}
{index < data.length - 1 && showXAxis && showArrow && gap >= 14 ? (
<foreignObject
x={item.startX + barWidth + HALF_PADDING + yAxisPadding - 6 + gap / 2}
y={realHeight + HALF_PADDING + 11}
width={12}
height={rotateLabelX?.xAxisHeight || DEFAULT_X_AXIS_HEIGHT}
>
<div
className={tremorTwMerge(
// light
"text-tremor-content",
// dark
"dark:text-dark-tremor-content",
)}
>
<ArrowRightIcon className="size-3.5 shrink-0" />
</div>
</foreignObject>
) : null}
</React.Fragment>
))}
<linearGradient
id={"base-gradient"}
x1="0%"
y1="0%"
x2="0%"
y2="100%"
className={tremorTwMerge(
getColorClassNames(color ?? BaseColors.Blue, colorPalette.text).textColor,
)}
>
<stop offset="5%" stopColor="currentColor" stopOpacity={0.4} />
<stop offset="95%" stopColor="currentColor" stopOpacity={0} />
</linearGradient>
<linearGradient
id={"base-gradient-revert"}
x1="0%"
y1="0%"
x2="0%"
y2="100%"
className={tremorTwMerge(
getColorClassNames(color ?? BaseColors.Blue, colorPalette.text).textColor,
)}
>
<stop offset="5%" stopColor="currentColor" stopOpacity={0} />
<stop offset="95%" stopColor="currentColor" stopOpacity={0.4} />
</linearGradient>
{showXAxis && xAxisLabel ? (
<text
x={width / 2 + yAxisPadding / 2}
y={realHeight + HALF_PADDING + 50}
style={{ textAnchor: "middle" }}
fill=""
stroke=""
className={tremorTwMerge(
// base
"text-tremor-default cursor-default font-medium",
// light
"fill-tremor-content-emphasis",
// dark
"dark:fill-dark-tremor-content-emphasis",
)}
>
{xAxisLabel}
</text>
) : null}
{showYAxis && yAxisLabel ? (
<text
x={-5}
y={realHeight / 2 + 10}
textAnchor="middle"
style={{ textAnchor: "middle" }}
transform={`rotate(-90, 0, ${realHeight / 2})`}
fill=""
stroke=""
className={tremorTwMerge(
// base
"text-tremor-default cursor-default font-medium",
// light
"fill-tremor-content-emphasis",
// dark
"dark:fill-dark-tremor-content-emphasis",
)}
>
{yAxisLabel}
</text>
) : null}
</svg>
{showTooltip ? (
<div
ref={tooltipRef}
className={tremorTwMerge(
"absolute top-0 pointer-events-none",
tooltip.data ? "visible" : "hidden",
)}
tabIndex={-1}
role="dialog"
style={{
left: tooltip.x + barWidth * 0.66,
}}
>
{CustomTooltip ? (
<CustomTooltip
payload={tooltip.data ? [tooltip.data] : []}
active={!!tooltip.data}
label={tooltip.data?.name}
/>
) : (
<ChartTooltipFrame>
<div
className={tremorTwMerge(
// light
"border-tremor-border border-b px-4 py-2",
// dark
"dark:border-dark-tremor-border",
)}
>
<p
className={tremorTwMerge(
// common
"font-medium",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
>
{tooltip?.data?.name}
</p>
</div>
<div className={tremorTwMerge("px-4 py-2 space-y-1")}>
{tooltip.data ? (
<ChartTooltipRow
value={valueFormatter(tooltip.data.value)}
name={`${(tooltip.data.payload.normalizedValue * 100).toFixed(2)}%`}
color={color ?? BaseColors.Blue}
/>
) : null}
</div>
</ChartTooltipFrame>
)}
</div>
) : null}
</>
) : (
<NoData noDataText={noDataText} />
)}
</div>
);
},
);
FunnelChartPrimitive.displayName = "FunnelChart";
//#region Data Validation
const validateData = (data: DataT[], calculatedFrom?: CalculateFrom): string | null => {
if (data && data.length > 0) {
if (calculatedFrom === "previous" && data[0].value <= 0) {
return `The value of the first item "${data[0].name}" is not greater than 0. This is not allowed when setting the "calculateFrom" prop to "previous". Please enter a value greater than 0.`;
}
for (const item of data) {
if (item.value < 0) {
return `Item "${item.name}" has a negative value: ${item.value}. This is not allowed. The value must be greater than or equal to 0.`;
}
}
}
return null;
};
//#region Exports
const FunnelChart = ({ data, ...props }: FunnelChartProps) => {
const errorMessage = data ? validateData(data, props.calculateFrom) : null;
return errorMessage ? (
<NoData className="h-full w-full p-6" noDataText={`Calculation error: ${errorMessage}`} />
) : (
<FunnelChartPrimitive data={data} {...props} />
);
};
export default FunnelChart;
================================================
FILE: src/components/chart-elements/FunnelChart/index.ts
================================================
export { default as FunnelChart } from "./FunnelChart";
export type { FunnelChartProps } from "./FunnelChart";
================================================
FILE: src/components/chart-elements/LineChart/LineChart.tsx
================================================
"use client";
import React, { Fragment, useState } from "react";
import {
CartesianGrid,
Dot,
Legend,
Line,
LineChart as ReChartsLineChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
Label,
} from "recharts";
import { AxisDomain } from "recharts/types/util/types";
import BaseChartProps from "../common/BaseChartProps";
import ChartLegend from "../common/ChartLegend";
import ChartTooltip from "../common/ChartTooltip";
import NoData from "../common/NoData";
import {
constructCategoryColors,
getYAxisDomain,
hasOnlyOneValueForThisKey,
} from "../common/utils";
import {
BaseColors,
colorPalette,
defaultValueFormatter,
getColorClassNames,
themeColorRange,
tremorTwMerge,
} from "lib";
import { CurveType } from "../../../lib/inputTypes";
export interface LineChartProps extends BaseChartProps {
curveType?: CurveType;
connectNulls?: boolean;
}
interface ActiveDot {
index?: number;
dataKey?: string;
}
const LineChart = React.forwardRef<HTMLDivElement, LineChartProps>((props, ref) => {
const {
data = [],
categories = [],
index,
colors = themeColorRange,
valueFormatter = defaultValueFormatter,
startEndOnly = false,
showXAxis = true,
showYAxis = true,
yAxisWidth = 56,
intervalType = "equidistantPreserveStart",
animationDuration = 900,
showAnimation = false,
showTooltip = true,
showLegend = true,
showGridLines = true,
autoMinValue = false,
curveType = "linear",
minValue,
maxValue,
connectNulls = false,
allowDecimals = true,
noDataText,
className,
onValueChange,
enableLegendSlider = false,
customTooltip,
rotateLabelX,
padding = !showXAxis && !showYAxis ? { left: 0, right: 0 } : { left: 20, right: 20 },
tickGap = 5,
xAxisLabel,
yAxisLabel,
...other
} = props;
const CustomTooltip = customTooltip;
const [legendHeight, setLegendHeight] = useState(60);
const [activeDot, setActiveDot] = useState<ActiveDot | undefined>(undefined);
const [activeLegend, setActiveLegend] = useState<string | undefined>(undefined);
const categoryColors = constructCategoryColors(categories, colors);
const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue);
const hasOnValueChange = !!onValueChange;
function onDotClick(itemData: any, event: React.MouseEvent) {
event.stopPropagation();
if (!hasOnValueChange) return;
if (
(itemData.index === activeDot?.index && itemData.dataKey === activeDot?.dataKey) ||
(hasOnlyOneValueForThisKey(data, itemData.dataKey) &&
activeLegend &&
activeLegend === itemData.dataKey)
) {
setActiveLegend(undefined);
setActiveDot(undefined);
onValueChange?.(null);
} else {
setActiveLegend(itemData.dataKey);
setActiveDot({
index: itemData.index,
dataKey: itemData.dataKey,
});
onValueChange?.({
eventType: "dot",
categoryClicked: itemData.dataKey,
...itemData.payload,
});
}
}
function onCategoryClick(dataKey: string) {
if (!hasOnValueChange) return;
if (
(dataKey === activeLegend && !activeDot) ||
(hasOnlyOneValueForThisKey(data, dataKey) && activeDot && activeDot.dataKey === dataKey)
) {
setActiveLegend(undefined);
onValueChange?.(null);
} else {
setActiveLegend(dataKey);
onValueChange?.({
eventType: "category",
categoryClicked: dataKey,
});
}
setActiveDot(undefined);
}
return (
<div ref={ref} className={tremorTwMerge("w-full h-80", className)} {...other}>
<ResponsiveContainer className="h-full w-full">
{data?.length ? (
<ReChartsLineChart
data={data}
onClick={
hasOnValueChange && (activeLegend || activeDot)
? () => {
setActiveDot(undefined);
setActiveLegend(undefined);
onValueChange?.(null);
}
: undefined
}
margin={{
bottom: xAxisLabel ? 30 : undefined,
left: yAxisLabel ? 20 : undefined,
right: yAxisLabel ? 5 : undefined,
top: 5,
}}
>
{showGridLines ? (
<CartesianGrid
className={tremorTwMerge(
// common
"stroke-1",
// light
"stroke-tremor-border",
// dark
"dark:stroke-dark-tremor-border",
)}
horizontal={true}
vertical={false}
/>
) : null}
<XAxis
padding={padding}
hide={!showXAxis}
dataKey={index}
interval={startEndOnly ? "preserveStartEnd" : intervalType}
tick={{ transform: "translate(0, 6)" }}
ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickLine={false}
axisLine={false}
minTickGap={tickGap}
angle={rotateLabelX?.angle}
dy={rotateLabelX?.verticalShift}
height={rotateLabelX?.xAxisHeight}
>
{xAxisLabel && (
<Label
position="insideBottom"
offset={-20}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{xAxisLabel}
</Label>
)}
</XAxis>
<YAxis
width={yAxisWidth}
hide={!showYAxis}
axisLine={false}
tickLine={false}
type="number"
domain={yAxisDomain as AxisDomain}
tick={{ transform: "translate(-3, 0)" }}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickFormatter={valueFormatter}
allowDecimals={allowDecimals}
>
{yAxisLabel && (
<Label
position="insideLeft"
style={{ textAnchor: "middle" }}
angle={-90}
offset={-15}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{yAxisLabel}
</Label>
)}
</YAxis>
<Tooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
content={
showTooltip ? (
({ active, payload, label }) =>
CustomTooltip ? (
<CustomTooltip
payload={payload?.map((payloadItem: any) => ({
...payloadItem,
color: categoryColors.get(payloadItem.dataKey) ?? BaseColors.Gray,
}))}
active={active}
label={label}
/>
) : (
<ChartTooltip
active={active}
payload={payload}
label={label}
valueFormatter={valueFormatter}
categoryColors={categoryColors}
/>
)
) : (
<></>
)
}
position={{ y: 0 }}
/>
{showLegend ? (
<Legend
verticalAlign="top"
height={legendHeight}
content={({ payload }) =>
ChartLegend(
{ payload },
categoryColors,
setLegendHeight,
activeLegend,
hasOnValueChange
? (clickedLegendItem: string) => onCategoryClick(clickedLegendItem)
: undefined,
enableLegendSlider,
)
}
/>
) : null}
{categories.map((category) => (
<Line
className={tremorTwMerge(
getColorClassNames(
categoryColors.get(category) ?? BaseColors.Gray,
colorPalette.text,
).strokeColor,
)}
strokeOpacity={activeDot || (activeLegend && activeLegend !== category) ? 0.3 : 1}
activeDot={(props: any) => {
const { cx, cy, stroke, strokeLinecap, strokeLinejoin, strokeWidth, dataKey } =
props;
return (
<Dot
className={tremorTwMerge(
"stroke-tremor-background dark:stroke-dark-tremor-background",
onValueChange ? "cursor-pointer" : "",
getColorClassNames(
categoryColors.get(dataKey) ?? BaseColors.Gray,
colorPalette.text,
).fillColor,
)}
cx={cx}
cy={cy}
r={5}
fill=""
stroke={stroke}
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
strokeWidth={strokeWidth}
onClick={(dotProps: any, event) => onDotClick(props, event)}
/>
);
}}
dot={(props: any) => {
const {
stroke,
strokeLinecap,
strokeLinejoin,
strokeWidth,
cx,
cy,
dataKey,
index,
} = props;
if (
(hasOnlyOneValueForThisKey(data, category) &&
!(activeDot || (activeLegend && activeLegend !== category))) ||
(activeDot?.index === index && activeDot?.dataKey === category)
) {
return (
<Dot
key={index}
cx={cx}
cy={cy}
r={5}
stroke={stroke}
fill=""
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
strokeWidth={strokeWidth}
className={tremorTwMerge(
"stroke-tremor-background dark:stroke-dark-tremor-background",
onValueChange ? "cursor-pointer" : "",
getColorClassNames(
categoryColors.get(dataKey) ?? BaseColors.Gray,
colorPalette.text,
).fillColor,
)}
/>
);
}
return <Fragment key={index}></Fragment>;
}}
key={category}
name={category}
type={curveType}
dataKey={category}
stroke=""
strokeWidth={2}
strokeLinejoin="round"
strokeLinecap="round"
isAnimationActive={showAnimation}
animationDuration={animationDuration}
connectNulls={connectNulls}
/>
))}
{onValueChange
? categories.map((category) => (
<Line
className={tremorTwMerge("cursor-pointer")}
strokeOpacity={0}
key={category}
name={category}
type={curveType}
dataKey={category}
stroke="transparent"
fill="transparent"
legendType="none"
tooltipType="none"
strokeWidth={12}
connectNulls={connectNulls}
onClick={(props: any, event) => {
event.stopPropagation();
const { name } = props;
onCategoryClick(name);
}}
/>
))
: null}
</ReChartsLineChart>
) : (
<NoData noDataText={noDataText} />
)}
</ResponsiveContainer>
</div>
);
});
LineChart.displayName = "LineChart";
export default LineChart;
================================================
FILE: src/components/chart-elements/LineChart/index.ts
================================================
export { default as LineChart } from "./LineChart";
export type { LineChartProps } from "./LineChart";
================================================
FILE: src/components/chart-elements/ScatterChart/ScatterChart.tsx
================================================
"use client";
import React, { useState } from "react";
import {
CartesianGrid,
Dot,
Legend,
ResponsiveContainer,
Scatter,
ScatterChart as ReChartsScatterChart,
Tooltip,
XAxis,
YAxis,
ZAxis,
Label,
} from "recharts";
import { AxisDomain } from "recharts/types/util/types";
import type { EventProps } from "components/chart-elements/common";
import ChartLegend from "components/chart-elements/common/ChartLegend";
import ScatterChartTooltip from "components/chart-elements/ScatterChart/ScatterChartTooltip";
import BaseAnimationTimingProps from "../common/BaseAnimationTimingProps";
import NoData from "../common/NoData";
import {
constructCategories,
constructCategoryColors,
deepEqual,
getYAxisDomain,
} from "../common/utils";
import { CustomTooltipProps } from "components/chart-elements/common/CustomTooltipProps";
import {
BaseColors,
colorPalette,
defaultValueFormatter,
getColorClassNames,
themeColorRange,
tremorTwMerge,
} from "lib";
import { Color, ValueFormatter, IntervalType } from "../../../lib/inputTypes";
export type ScatterChartValueFormatter = {
x?: ValueFormatter;
y?: ValueFormatter;
size?: ValueFormatter;
};
export interface ScatterChartProps
extends BaseAnimationTimingProps,
React.HTMLAttributes<HTMLDivElement> {
data: any[];
x: string;
y: string;
category: string;
size?: string;
valueFormatter?: ScatterChartValueFormatter;
sizeRange?: number[];
colors?: (Color | string)[];
showOpacity?: boolean;
startEndOnly?: boolean;
showXAxis?: boolean;
showYAxis?: boolean;
yAxisWidth?: number;
intervalType?: IntervalType;
showTooltip?: boolean;
showLegend?: boolean;
showGridLines?: boolean;
autoMinXValue?: boolean;
minXValue?: number;
maxXValue?: number;
autoMinYValue?: boolean;
minYValue?: number;
maxYValue?: number;
allowDecimals?: boolean;
noDataText?: string;
enableLegendSlider?: boolean;
onValueChange?: (value: EventProps) => void;
customTooltip?: React.ComponentType<CustomTooltipProps>;
rotateLabelX?: {
angle: number;
verticalShift: number;
xAxisHeight: number;
};
tickGap?: number;
xAxisLabel?: string;
yAxisLabel?: string;
}
const renderShape = (props: any, activeNode: any | undefined, activeLegend: string | undefined) => {
const { cx, cy, width, node, fillOpacity, name } = props;
return (
<Dot
cx={cx}
cy={cy}
r={width / 2}
opacity={
activeNode || (activeLegend && activeLegend !== name)
? deepEqual(activeNode, node)
? fillOpacity
: 0.3
: fillOpacity
}
/>
);
};
const ScatterChart = React.forwardRef<HTMLDivElement, ScatterChartProps>((props, ref) => {
const {
data = [],
x,
y,
size,
category,
colors = themeColorRange,
showOpacity = false,
sizeRange = [1, 1000],
valueFormatter = {
x: defaultValueFormatter,
y: defaultValueFormatter,
size: defaultValueFormatter,
},
startEndOnly = false,
showXAxis = true,
showYAxis = true,
yAxisWidth = 56,
intervalType = "equidistantPreserveStart",
animationDuration = 900,
showAnimation = false,
showTooltip = true,
showLegend = true,
showGridLines = true,
autoMinXValue = false,
minXValue,
maxXValue,
autoMinYValue = false,
minYValue,
maxYValue,
allowDecimals = true,
noDataText,
onValueChange,
customTooltip,
rotateLabelX,
className,
enableLegendSlider = false,
tickGap = 5,
xAxisLabel,
yAxisLabel,
...other
} = props;
const CustomTooltip = customTooltip;
const [legendHeight, setLegendHeight] = useState(60);
const [activeNode, setActiveNode] = React.useState<any | undefined>(undefined);
const [activeLegend, setActiveLegend] = useState<string | undefined>(undefined);
const hasOnValueChange = !!onValueChange;
function onNodeClick(data: any, index: number, event: React.MouseEvent) {
event.stopPropagation();
if (!hasOnValueChange) return;
if (deepEqual(activeNode, data.node)) {
setActiveLegend(undefined);
setActiveNode(undefined);
onValueChange?.(null);
} else {
setActiveNode(data.node);
setActiveLegend(data.payload[category]);
onValueChange?.({
eventType: "bubble",
categoryClicked: data.payload[category],
...data.payload,
});
}
}
function onCategoryClick(dataKey: string) {
if (!hasOnValueChange) return;
if (dataKey === activeLegend && !activeNode) {
setActiveLegend(undefined);
onValueChange?.(null);
} else {
setActiveLegend(dataKey);
onValueChange?.({
eventType: "category",
categoryClicked: dataKey,
});
}
setActiveNode(undefined);
}
const categories = constructCategories(data, category);
const categoryColors = constructCategoryColors(categories, colors);
//maybe rename getYAxisDomain to getAxisDomain
const xAxisDomain = getYAxisDomain(autoMinXValue, minXValue, maxXValue);
const yAxisDomain = getYAxisDomain(autoMinYValue, minYValue, maxYValue);
return (
<div ref={ref} className={tremorTwMerge("w-full h-80", className)} {...other}>
<ResponsiveContainer className="h-full w-full">
{data?.length ? (
<ReChartsScatterChart
onClick={
hasOnValueChange && (activeLegend || activeNode)
? () => {
setActiveNode(undefined);
setActiveLegend(undefined);
onValueChange?.(null);
}
: undefined
}
margin={{
bottom: xAxisLabel ? 20 : undefined,
left: 20,
right: 20,
top: 5,
}}
>
{showGridLines ? (
<CartesianGrid
className={tremorTwMerge(
// common
"stroke-1",
// light
"stroke-tremor-border",
// dark
"dark:stroke-dark-tremor-border",
)}
horizontal={true}
vertical={true}
/>
) : null}
{x ? (
<XAxis
hide={!showXAxis}
dataKey={x}
interval={startEndOnly ? "preserveStartEnd" : intervalType}
tick={{ transform: "translate(0, 6)" }}
ticks={startEndOnly ? [data[0][x], data[data.length - 1][x]] : undefined}
type="number"
name={x}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
tickLine={false}
tickFormatter={valueFormatter.x}
axisLine={false}
minTickGap={tickGap}
domain={xAxisDomain as AxisDomain}
allowDataOverflow={true}
angle={rotateLabelX?.angle}
dy={rotateLabelX?.verticalShift}
height={rotateLabelX?.xAxisHeight}
>
{xAxisLabel && (
<Label
position="insideBottom"
offset={-20}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{xAxisLabel}
</Label>
)}
</XAxis>
) : null}
{y ? (
<YAxis
width={yAxisWidth}
hide={!showYAxis}
axisLine={false}
tickLine={false}
dataKey={y}
type="number"
name={y}
domain={yAxisDomain as AxisDomain}
tick={{ transform: "translate(-3, 0)" }}
tickFormatter={valueFormatter.y}
fill=""
stroke=""
className={tremorTwMerge(
// common
"text-tremor-label",
// light
"fill-tremor-content",
// dark
"dark:fill-dark-tremor-content",
)}
allowDecimals={allowDecimals}
allowDataOverflow={true}
>
{yAxisLabel && (
<Label
position="insideLeft"
style={{ textAnchor: "middle" }}
angle={-90}
offset={-15}
className="fill-tremor-content-emphasis text-tremor-default font-medium dark:fill-dark-tremor-content-emphasis"
>
{yAxisLabel}
</Label>
)}
</YAxis>
) : null}
<Tooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
content={
showTooltip ? (
({ active, payload, label }) => {
const color = category ? payload?.[0]?.payload?.[category] : label;
return CustomTooltip ? (
<CustomTooltip
payload={payload?.map((payloadItem) => ({
...payloadItem,
color: categoryColors.get(color) ?? BaseColors.Gray,
}))}
active={active}
label={color}
/>
) : (
<ScatterChartTooltip
active={active}
payload={payload}
label={color}
valueFormatter={valueFormatter}
axis={{ x: x, y: y, size: size }}
category={category}
categoryColors={categoryColors}
/>
);
}
) : (
<></>
)
}
/>
{size ? <ZAxis dataKey={size} type="number" range={sizeRange} name={size} /> : null}
{categories.map((cat) => {
return (
<Scatter
className={tremorTwMerge(
getColorClassNames(
categoryColors.get(cat) ?? BaseColors.Gray,
colorPalette.text,
).fillColor,
showOpacity
? getColorClassNames(
categoryColors.get(cat) ?? BaseColors.Gray,
colorPalette.text,
).strokeColor
: "",
onValueChange ? "cursor-pointer" : "",
)}
fill={`url(#${categoryColors.get(cat)})`}
fillOpacity={showOpacity ? 0.7 : 1}
key={cat}
name={cat}
data={category ? data.filter((d) => d[category] === cat) : data}
isAnimationActive={showAnimation}
animationDuration={animationDuration}
shape={(props: any) => renderShape(props, activeNode, activeLegend)}
onClick={onNodeClick}
/>
);
})}
{showLegend ? (
<Legend
verticalAlign="top"
height={legendHeight}
content={({ payload }) =>
ChartLegend(
{ payload },
categoryColors,
setLegendHeight,
activeLegend,
hasOnValueChange
? (clickedLegendItem: string) => onCategoryClick(clickedLegendItem)
: undefined,
enableLegendSlider,
)
}
/>
) : null}
</ReChartsScatterChart>
) : (
<NoData noDataText={noDataText} />
)}
</ResponsiveContainer>
</div>
);
});
ScatterChart.displayName = "ScatterChart";
export default ScatterChart;
================================================
FILE: src/components/chart-elements/ScatterChart/ScatterChartTooltip.tsx
================================================
import React from "react";
import { ScatterChartValueFormatter } from "components/chart-elements/ScatterChart/ScatterChart";
import {
BaseColors,
getColorClassNames,
Color,
defaultValueFormatter,
tremorTwMerge,
colorPalette,
} from "lib";
export const ChartTooltipFrame = ({ children }: { children: React.ReactNode }) => (
<div
className={tremorTwMerge(
// common
"rounded-tremor-default text-tremor-default border",
// light
"bg-tremor-background shadow-tremor-dropdown border-tremor-border",
// dark
"dark:bg-dark-tremor-background dark:shadow-dark-tremor-dropdown dark:border-dark-tremor-border",
)}
>
{children}
</div>
);
export interface ChartTooltipRowProps {
value: string;
name: string;
}
export const ChartTooltipRow = ({ value, name }: ChartTooltipRowProps) => (
<div className="flex items-center justify-between space-x-8">
<div className="flex items-center space-x-2">
<p
className={tremorTwMerge(
// commmon
"text-right whitespace-nowrap",
// light
"text-tremor-content",
// dark
"dark:text-dark-tremor-content",
)}
>
{name}
</p>
</div>
<p
className={tremorTwMerge(
// common
"font-medium tabular-nums text-right whitespace-nowrap",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
>
{value}
</p>
</div>
);
export interface ScatterChartTooltipProps {
label: string;
categoryColors: Map<string, Color | string>;
active: boolean | undefined;
payload: any;
valueFormatter: ScatterChartValueFormatter;
axis: any;
category?: string;
}
const ScatterChartTooltip = ({
label,
active,
payload,
valueFormatter,
axis,
category,
categoryColors,
}: ScatterChartTooltipProps) => {
if (active && payload) {
return (
<ChartTooltipFrame>
<div
className={tremorTwMerge(
// common
"flex items-center space-x-2 border-b px-4 py-2",
// light
"border-tremor-border",
// dark
"dark:border-dark-tremor-border",
)}
>
<span
className={tremorTwMerge(
// common
"shrink-0 rounded-tremor-full border-2 h-3 w-3",
// light
"border-tremor-background shadow-tremor-card",
// dark
"dark:border-dark-tremor-background dark:shadow-dark-tremor-card",
getColorClassNames(
category
? (categoryColors.get(payload?.[0]?.payload[category]) ?? BaseColors.Blue)
: BaseColors.Blue,
colorPalette.background,
).bgColor,
)}
/>
<p
className={tremorTwMerge(
// common
"font-medium",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
>
{label}
</p>
</div>
<div className={tremorTwMerge("px-4 py-2 space-y-1")}>
{payload.map(({ value, name }: { value: number; name: string }, idx: number) => {
const valueFormatterKey = Object.keys(axis).find((key) => axis[key] === name) ?? "";
const valueFormatterFn =
valueFormatter[valueFormatterKey as keyof ScatterChartValueFormatter] ??
defaultValueFormatter;
return (
<ChartTooltipRow
key={`id-${idx}`}
value={valueFormatter && valueFormatterFn ? valueFormatterFn(value) : `${value}`}
name={name}
/>
);
})}
</div>
</ChartTooltipFrame>
);
}
return null;
};
export default ScatterChartTooltip;
================================================
FILE: src/components/chart-elements/ScatterChart/index.tsx
================================================
export { default as ScatterChart } from "./ScatterChart";
export type { ScatterChartProps } from "./ScatterChart";
================================================
FILE: src/components/chart-elements/common/BaseAnimationTimingProps.tsx
================================================
interface BaseAnimationTimingProps {
animationDuration?: number;
showAnimation?: boolean;
}
export default BaseAnimationTimingProps;
================================================
FILE: src/components/chart-elements/common/BaseChartProps.tsx
================================================
import { Color, ValueFormatter, IntervalType } from "../../../lib";
import type BaseAnimationTimingProps from "./BaseAnimationTimingProps";
import { CustomTooltipProps } from "./CustomTooltipProps";
type FixedProps = {
eventType: "dot" | "category" | "bar" | "slice" | "bubble";
categoryClicked: string;
};
type BaseEventProps = FixedProps & {
[key: string]: number | string;
};
export type EventProps = BaseEventProps | null | undefined;
interface BaseChartProps extends BaseAnimationTimingProps, React.HTMLAttributes<HTMLDivElement> {
data: any[];
categories: string[];
index: string;
colors?: (Color | string)[];
valueFormatter?: ValueFormatter;
startEndOnly?: boolean;
showXAxis?: boolean;
showYAxis?: boolean;
yAxisWidth?: number;
intervalType?: IntervalType;
showTooltip?: boolean;
showLegend?: boolean;
showGridLines?: boolean;
autoMinValue?: boolean;
minValue?: number;
maxValue?: number;
allowDecimals?: boolean;
noDataText?: string;
onValueChange?: (value: EventProps) => void;
enableLegendSlider?: boolean;
padding?: { left?: number; right?: number };
customTooltip?: React.ComponentType<CustomTooltipProps>;
rotateLabelX?: {
angle: number;
verticalShift?: number;
xAxisHeight?: number;
};
tickGap?: number;
xAxisLabel?: string;
yAxisLabel?: string;
}
export default BaseChartProps;
================================================
FILE: src/components/chart-elements/common/ChartLegend.tsx
================================================
import React, { useRef } from "react";
import { useOnWindowResize } from "hooks";
import { Legend } from "components/text-elements/Legend";
import { Color } from "../../../lib";
const ChartLegend = (
{ payload }: any,
categoryColors: Map<string, Color | string>,
setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
activeLegend: string | undefined,
onClick?: (category: string, color: Color | string) => void,
enableLegendSlider?: boolean,
) => {
const legendRef = useRef<HTMLDivElement>(null);
useOnWindowResize(() => {
const calculateHeight = (height: number | undefined) =>
height
? Number(height) + 20 // 20px extra padding
: 60; // default height
setLegendHeight(calculateHeight(legendRef.current?.clientHeight));
});
const filteredPayload = payload.filter((item: any) => item.type !== "none");
return (
<div ref={legendRef} className="flex items-center justify-end">
<Legend
categories={filteredPayload.map((entry: any) => entry.value)}
colors={filteredPayload.map((entry: any) => categoryColors.get(entry.value))}
onClickLegendItem={onClick}
activeLegend={activeLegend}
enableLegendSlider={enableLegendSlider}
/>
</div>
);
};
export default ChartLegend;
================================================
FILE: src/components/chart-elements/common/ChartTooltip.tsx
================================================
import React from "react";
import {
BaseColors,
getColorClassNames,
tremorTwMerge,
Color,
ValueFormatter,
colorPalette,
} from "lib";
export const ChartTooltipFrame = ({ children }: { children: React.ReactNode }) => (
<div
className={tremorTwMerge(
// common
"rounded-tremor-default text-tremor-default border",
// light
"bg-tremor-background shadow-tremor-dropdown border-tremor-border",
// dark
"dark:bg-dark-tremor-background dark:shadow-dark-tremor-dropdown dark:border-dark-tremor-border",
)}
>
{children}
</div>
);
export interface ChartTooltipRowProps {
value: string;
name: string;
color: Color | string;
}
export const ChartTooltipRow = ({ value, name, color }: ChartTooltipRowProps) => (
<div className="flex items-center justify-between space-x-8">
<div className="flex items-center space-x-2">
<span
className={tremorTwMerge(
// common
"shrink-0 rounded-tremor-full border-2 h-3 w-3",
// light
"border-tremor-background shadow-tremor-card",
// dark
"dark:border-dark-tremor-background dark:shadow-dark-tremor-card",
getColorClassNames(color, colorPalette.background).bgColor,
)}
/>
<p
className={tremorTwMerge(
// commmon
"text-right whitespace-nowrap",
// light
"text-tremor-content",
// dark
"dark:text-dark-tremor-content",
)}
>
{name}
</p>
</div>
<p
className={tremorTwMerge(
// common
"font-medium tabular-nums text-right whitespace-nowrap",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
>
{value}
</p>
</div>
);
export interface ChartTooltipProps {
active: boolean | undefined;
payload: any;
label: string;
categoryColors: Map<string, Color | string>;
valueFormatter: ValueFormatter;
}
const ChartTooltip = ({
active,
payload,
label,
categoryColors,
valueFormatter,
}: ChartTooltipProps) => {
if (active && payload) {
const filteredPayload = payload.filter((item: any) => item.type !== "none");
return (
<ChartTooltipFrame>
<div
className={tremorTwMerge(
// light
"border-tremor-border border-b px-4 py-2",
// dark
"dark:border-dark-tremor-border",
)}
>
<p
className={tremorTwMerge(
// common
"font-medium",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
>
{label}
</p>
</div>
<div className={tremorTwMerge("px-4 py-2 space-y-1")}>
{filteredPayload.map(({ value, name }: { value: number; name: string }, idx: number) => (
<ChartTooltipRow
key={`id-${idx}`}
value={valueFormatter(value)}
name={name}
color={categoryColors.get(name) ?? BaseColors.Blue}
/>
))}
</div>
</ChartTooltipFrame>
);
}
return null;
};
export default ChartTooltip;
================================================
FILE: src/components/chart-elements/common/CustomTooltipProps.tsx
================================================
import { NameType, Payload } from "recharts/types/component/DefaultTooltipContent";
export type CustomTooltipProps = {
payload: Payload<string | number | (string | number)[], string | number>[] | undefined;
active: boolean | undefined;
label: NameType | undefined;
};
================================================
FILE: src/components/chart-elements/common/NoData.tsx
================================================
import { tremorTwMerge } from "lib";
import React from "react";
interface NoDataProps {
noDataText?: string;
className?: string;
}
const NoData = ({ className, noDataText = "No data" }: NoDataProps) => {
return (
<div
className={tremorTwMerge(
// common
"flex items-center justify-center w-full h-full border border-dashed rounded-tremor-default",
// light
"border-tremor-border",
// dark
"dark:border-dark-tremor-border",
className,
)}
>
<p
className={tremorTwMerge(
// light
"text-tremor-content text-tremor-default",
// dark
"dark:text-dark-tremor-content",
)}
>
{noDataText}
</p>
</div>
);
};
export default NoData;
================================================
FILE: src/components/chart-elements/common/index.ts
================================================
export type { EventProps } from "./BaseChartProps";
export type { CustomTooltipProps } from "./CustomTooltipProps";
================================================
FILE: src/components/chart-elements/common/utils.ts
================================================
import { Color } from "../../../lib/inputTypes";
export const constructCategoryColors = (
categories: string[],
colors: (Color | string)[],
): Map<string, Color | string> => {
const categoryColors = new Map<string, Color | string>();
categories.forEach((category, idx) => {
categoryColors.set(category, colors[idx % colors.length]);
});
return categoryColors;
};
export const getYAxisDomain = (
autoMinValue: boolean,
minValue: number | undefined,
maxValue: number | undefined,
) => {
const minDomain = autoMinValue ? "auto" : (minValue ?? 0);
const maxDomain = maxValue ?? "auto";
return [minDomain, maxDomain];
};
export const constructCategories = (data: any[], color?: string): string[] => {
if (!color) {
return [];
}
const categories = new Set<string>();
data.forEach((datum) => {
categories.add(datum[color]);
});
return Array.from(categories);
};
export function deepEqual(obj1: any, obj2: any) {
if (obj1 === obj2) return true;
if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null)
return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) return false;
}
return true;
}
// export function deepEqual(obj1: unknown, obj2: unknown): boolean {
// if (obj1 === obj2) return true;
// if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null)
// return false;
// if (Object.prototype.toString.call(obj1) !== Object.prototype.toString.call(obj2)) return false;
// const keys1 = Object.keys(obj1);
// const keys2 = new Set(Object.keys(obj2));
// if (keys1.length !== keys2.size) return false;
// for (const key of keys1) {
// if (
// !keys2.has(key) ||
// !deepEqual((obj1 as Record<string, unknown>)[key], (obj2 as Record<string, unknown>)[key])
// )
// return false;
// }
// return true;
// }
export function hasOnlyOneValueForThisKey(array: any[], keyToCheck: string) {
const val = [];
for (const obj of array) {
if (Object.prototype.hasOwnProperty.call(obj, keyToCheck)) {
val.push(obj[keyToCheck]);
if (val.length > 1) {
return false;
}
}
}
return true;
}
================================================
FILE: src/components/chart-elements/index.ts
================================================
export * from "./AreaChart";
export * from "./BarChart";
export { EventProps } from "./common/BaseChartProps";
export { CustomTooltipProps } from "./common/CustomTooltipProps";
export * from "./DonutChart";
export * from "./LineChart";
export * from "./ScatterChart";
export * from "./FunnelChart";
================================================
FILE: src/components/icon-elements/Badge/Badge.tsx
================================================
"use client";
import React from "react";
import Tooltip, { useTooltip } from "components/util-elements/Tooltip/Tooltip";
import {
Color,
getColorClassNames,
makeClassName,
mergeRefs,
Size,
Sizes,
tremorTwMerge,
colorPalette,
} from "lib";
import { badgeProportions, iconSizes } from "./styles";
const makeBadgeClassName = makeClassName("Badge");
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
color?: Color;
size?: Size;
icon?: React.ElementType;
tooltip?: string;
}
const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
const { color, icon, size = Sizes.SM, tooltip, className, children, ...other } = props;
const Icon = icon ? icon : null;
const { tooltipProps, getReferenceProps } = useTooltip();
return (
<span
ref={mergeRefs([ref, tooltipProps.refs.setReference])}
className={tremorTwMerge(
makeBadgeClassName("root"),
// common
"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",
color
? tremorTwMerge(
getColorClassNames(color, colorPalette.background).bgColor,
getColorClassNames(color, colorPalette.iconText).textColor,
getColorClassNames(color, colorPalette.iconRing).ringColor,
// light
"bg-opacity-10 ring-opacity-20",
// dark
"dark:bg-opacity-5 dark:ring-opacity-60",
)
: tremorTwMerge(
// light
"bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20",
// dark
"dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20",
),
badgeProportions[size].paddingX,
badgeProportions[size].paddingY,
badgeProportions[size].fontSize,
className,
)}
{...getReferenceProps}
{...other}
>
<Tooltip text={tooltip} {...tooltipProps} />
{Icon ? (
<Icon
className={tremorTwMerge(
makeBadgeClassName("icon"),
"shrink-0 -ml-1 mr-1.5",
iconSizes[size].height,
iconSizes[size].width,
)}
/>
) : null}
<span className={tremorTwMerge(makeBadgeClassName("text"), "whitespace-nowrap")}>
{children}
</span>
</span>
);
});
Badge.displayName = "Badge";
export default Badge;
================================================
FILE: src/components/icon-elements/Badge/index.ts
================================================
export { default as Badge } from "./Badge";
export type { BadgeProps } from "./Badge";
================================================
FILE: src/components/icon-elements/Badge/styles.ts
================================================
export type BadgeProportionTypes = {
paddingX: string;
paddingY: string;
fontSize: string;
};
export const badgeProportions: { [char: string]: BadgeProportionTypes } = {
xs: {
paddingX: "px-2",
paddingY: "py-0.5",
fontSize: "text-xs",
},
sm: {
paddingX: "px-2.5",
paddingY: "py-0.5",
fontSize: "text-sm",
},
md: {
paddingX: "px-3",
paddingY: "py-0.5",
fontSize: "text-md",
},
lg: {
paddingX: "px-3.5",
paddingY: "py-0.5",
fontSize: "text-lg",
},
xl: {
paddingX: "px-4",
paddingY: "py-1",
fontSize: "text-xl",
},
};
export const iconSizes: {
[size: string]: {
height: string;
width: string;
};
} = {
xs: {
height: "h-4",
width: "w-4",
},
sm: {
height: "h-4",
width: "w-4",
},
md: {
height: "h-4",
width: "w-4",
},
lg: {
height: "h-5",
width: "w-5",
},
xl: {
height: "h-6",
width: "w-6",
},
};
================================================
FILE: src/components/icon-elements/BadgeDelta/BadgeDelta.tsx
================================================
"use client";
import React from "react";
import Tooltip, { useTooltip } from "components/util-elements/Tooltip/Tooltip";
import {
DeltaType,
DeltaTypes,
makeClassName,
mapInputsToDeltaType,
mergeRefs,
Size,
Sizes,
tremorTwMerge,
} from "lib";
import {
badgeProportionsIconOnly,
badgeProportionsWithText,
colors,
deltaIcons,
iconSizes,
} from "./styles";
const makeBadgeDeltaClassName = makeClassName("BadgeDelta");
export interface BadgeDeltaProps extends React.HTMLAttributes<HTMLSpanElement> {
deltaType?: DeltaType;
isIncreasePositive?: boolean;
size?: Size;
tooltip?: string;
}
const BadgeDelta = React.forwardRef<HTMLSpanElement, BadgeDeltaProps>((props, ref) => {
const {
deltaType = DeltaTypes.Increase,
isIncreasePositive = true,
size = Sizes.SM,
tooltip,
children,
className,
...other
} = props;
const Icon = deltaIcons[deltaType];
const mappedDeltaType = mapInputsToDeltaType(deltaType, isIncreasePositive);
const badgeProportions = children ? badgeProportionsWithText : badgeProportionsIconOnly;
const { tooltipProps, getReferenceProps } = useTooltip();
return (
<span
ref={mergeRefs([ref, tooltipProps.refs.setReference])}
className={tremorTwMerge(
makeBadgeDeltaClassName("root"),
// common
"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",
colors[mappedDeltaType].bgColor,
colors[mappedDeltaType].textColor,
colors[mappedDeltaType].ringColor,
badgeProportions[size].paddingX,
badgeProportions[size].paddingY,
badgeProportions[size].fontSize,
// light
"bg-opacity-10 ring-opacity-20",
// dark
"dark:bg-opacity-5 dark:ring-opacity-60",
className,
)}
{...getReferenceProps}
{...other}
>
<Tooltip text={tooltip} {...tooltipProps} />
<Icon
className={tremorTwMerge(
makeBadgeDeltaClassName("icon"),
"shrink-0",
children ? tremorTwMerge("-ml-1 mr-1.5") : iconSizes[size].height,
iconSizes[size].width,
)}
/>
{children ? (
<span className={tremorTwMerge(makeBadgeDeltaClassName("text"), "whitespace-nowrap")}>
{children}
</span>
) : null}
</span>
);
});
BadgeDelta.displayName = "BadgeDelta";
export default BadgeDelta;
================================================
FILE: src/components/icon-elements/BadgeDelta/index.ts
================================================
export { default as BadgeDelta } from "./BadgeDelta";
export type { BadgeDeltaProps } from "./BadgeDelta";
================================================
FILE: src/components/icon-elements/BadgeDelta/styles.ts
================================================
import { BaseColors, DeltaTypes, getColorClassNames, colorPalette } from "lib";
import {
ArrowDownIcon,
ArrowDownRightIcon,
ArrowRightIcon,
ArrowUpIcon,
ArrowUpRightIcon,
} from "assets";
export type BadgeProportionTypes = {
paddingX: string;
paddingY: string;
fontSize: string;
};
export const badgeProportionsIconOnly: {
[char: string]: BadgeProportionTypes;
} = {
xs: {
paddingX: "px-2",
paddingY: "py-0.5",
fontSize: "text-xs",
},
sm: {
paddingX: "px-2.5",
paddingY: "py-1",
fontSize: "text-sm",
},
md: {
paddingX: "px-3",
paddingY: "py-1.5",
fontSize: "text-md",
},
lg: {
paddingX: "px-3.5",
paddingY: "py-1.5",
fontSize: "text-lg",
},
xl: {
paddingX: "px-3.5",
paddingY: "py-1.5",
fontSize: "text-xl",
},
};
export const badgeProportionsWithText: {
[char: string]: BadgeProportionTypes;
} = {
xs: {
paddingX: "px-2",
paddingY: "py-0.5",
fontSize: "text-xs",
},
sm: {
paddingX: "px-2.5",
paddingY: "py-0.5",
fontSize: "text-sm",
},
md: {
paddingX: "px-3",
paddingY: "py-0.5",
fontSize: "text-md",
},
lg: {
paddingX: "px-3.5",
paddingY: "py-0.5",
fontSize: "text-lg",
},
xl: {
paddingX: "px-4",
paddingY: "py-1",
fontSize: "text-xl",
},
};
export const iconSizes: {
[size: string]: {
height: string;
width: string;
};
} = {
xs: {
height: "h-4",
width: "w-4",
},
sm: {
height: "h-4",
width: "w-4",
},
md: {
height: "h-4",
width: "w-4",
},
lg: {
height: "h-5",
width: "w-5",
},
xl: {
height: "h-6",
width: "w-6",
},
};
export type ColorTypes = {
bgColor: string;
textColor: string;
ringColor: string;
};
export const colors: { [key: string]: ColorTypes } = {
[DeltaTypes.Increase]: {
bgColor: getColorClassNames(BaseColors.Emerald, colorPalette.background).bgColor,
textColor: getColorClassNames(BaseColors.Emerald, colorPalette.iconText).textColor,
ringColor: getColorClassNames(BaseColors.Emerald, colorPalette.iconRing).ringColor,
},
[DeltaTypes.ModerateIncrease]: {
bgColor: getColorClassNames(BaseColors.Emerald, colorPalette.background).bgColor,
textColor: getColorClassNames(BaseColors.Emerald, colorPalette.iconText).textColor,
ringColor: getColorClassNames(BaseColors.Emerald, colorPalette.iconRing).ringColor,
},
[DeltaTypes.Decrease]: {
bgColor: getColorClassNames(BaseColors.Red, colorPalette.background).bgColor,
textColor: getColorClassNames(BaseColors.Red, colorPalette.iconText).textColor,
ringColor: getColorClassNames(BaseColors.Red, colorPalette.iconRing).ringColor,
},
[DeltaTypes.ModerateDecrease]: {
bgColor: getColorClassNames(BaseColors.Red, colorPalette.background).bgColor,
textColor: getColorClassNames(BaseColors.Red, colorPalette.iconText).textColor,
ringColor: getColorClassNames(BaseColors.Red, colorPalette.iconRing).ringColor,
},
[DeltaTypes.Unchanged]: {
bgColor: getColorClassNames(BaseColors.Orange, colorPalette.background).bgColor,
textColor: getColorClassNames(BaseColors.Orange, colorPalette.iconText).textColor,
ringColor: getColorClassNames(BaseColors.Orange, colorPalette.iconRing).ringColor,
},
};
export const deltaIcons: { [key: string]: React.ElementType } = {
[DeltaTypes.Increase]: ArrowUpIcon,
[DeltaTypes.ModerateIncrease]: ArrowUpRightIcon,
[DeltaTypes.Decrease]: ArrowDownIcon,
[DeltaTypes.ModerateDecrease]: ArrowDownRightIcon,
[DeltaTypes.Unchanged]: ArrowRightIcon,
};
================================================
FILE: src/components/icon-elements/Icon/Icon.tsx
================================================
"use client";
import React from "react";
import Tooltip, { useTooltip } from "components/util-elements/Tooltip/Tooltip";
import { makeClassName, mergeRefs, Sizes, tremorTwMerge, Color, IconVariant, Size } from "lib";
import { getIconColors, iconSizes, shape, wrapperProportions } from "./styles";
const makeIconClassName = makeClassName("Icon");
export const IconVariants: { [key: string]: IconVariant } = {
Simple: "simple",
Light: "light",
Shadow: "shadow",
Solid: "solid",
Outlined: "outlined",
};
export interface IconProps extends React.HTMLAttributes<HTMLSpanElement> {
icon: React.ElementType;
variant?: IconVariant;
tooltip?: string;
size?: Size;
color?: Color;
}
const Icon = React.forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
const {
icon,
variant = IconVariants.Simple,
tooltip,
size = Sizes.SM,
color,
className,
...other
} = props;
const Icon = icon;
const iconColorStyles = getIconColors(variant, color);
const { tooltipProps, getReferenceProps } = useTooltip();
return (
<span
ref={mergeRefs([ref, tooltipProps.refs.setReference])}
className={tremorTwMerge(
makeIconClassName("root"),
"inline-flex shrink-0 items-center justify-center",
iconColorStyles.bgColor,
iconColorStyles.textColor,
iconColorStyles.borderColor,
iconColorStyles.ringColor,
shape[variant].rounded,
shape[variant].border,
shape[variant].shadow,
shape[variant].ring,
wrapperProportions[size].paddingX,
wrapperProportions[size].paddingY,
className,
)}
{...getReferenceProps}
{...other}
>
<Tooltip text={tooltip} {...tooltipProps} />
<Icon
className={tremorTwMerge(
makeIconClassName("icon"),
"shrink-0",
iconSizes[size].height,
iconSizes[size].width,
)}
/>
</span>
);
});
Icon.displayName = "Icon";
export default Icon;
================================================
FILE: src/components/icon-elements/Icon/index.ts
================================================
export { default as Icon } from "./Icon";
export type { IconProps } from "./Icon";
================================================
FILE: src/components/icon-elements/Icon/styles.ts
================================================
import { getColorClassNames, tremorTwMerge, colorPalette, Color, IconVariant } from "lib";
export type WrapperProportionTypes = {
paddingX: string;
paddingY: string;
};
export const wrapperProportions: { [size: string]: WrapperProportionTypes } = {
xs: {
paddingX: "px-1.5",
paddingY: "py-1.5",
},
sm: {
paddingX: "px-1.5",
paddingY: "py-1.5",
},
md: {
paddingX: "px-2",
paddingY: "py-2",
},
lg: {
paddingX: "px-2",
paddingY: "py-2",
},
xl: {
paddingX: "px-2.5",
paddingY: "py-2.5",
},
};
export const iconSizes: {
[size: string]: {
height: string;
width: string;
};
} = {
xs: {
height: "h-3",
width: "w-3",
},
sm: {
height: "h-5",
width: "w-5",
},
md: {
height: "h-5",
width: "w-5",
},
lg: {
height: "h-7",
width: "w-7",
},
xl: {
height: "h-9",
width: "w-9",
},
};
export type ShapeTypes = {
rounded: string;
border: string;
ring: string;
shadow: string;
};
export const shape: { [style: string]: ShapeTypes } = {
simple: {
rounded: "",
border: "",
ring: "",
shadow: "",
},
light: {
rounded: "rounded-tremor-default",
border: "",
ring: "",
shadow: "",
},
shadow: {
rounded: "rounded-tremor-default",
border: "border",
ring: "",
shadow: "shadow-tremor-card dark:shadow-dark-tremor-card",
},
solid: {
rounded: "rounded-tremor-default",
border: "border-2",
ring: "ring-1",
shadow: "",
},
outlined: {
rounded: "rounded-tremor-default",
border: "border",
ring: "ring-2",
shadow: "",
},
};
export const getIconColors = (variant: IconVariant, color?: Color) => {
switch (variant) {
case "simple":
return {
textColor: color
? getColorClassNames(color, colorPalette.text).textColor
: "text-tremor-brand dark:text-dark-tremor-brand",
bgColor: "",
borderColor: "",
ringColor: "",
};
case "light":
return {
textColor: color
? getColorClassNames(color, colorPalette.text).textColor
: "text-tremor-brand dark:text-dark-tremor-brand",
bgColor: color
? tremorTwMerge(
getColorClassNames(color, colorPalette.background).bgColor,
"bg-opacity-20",
)
: "bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",
borderColor: "",
ringColor: "",
};
case "shadow":
return {
textColor: color
? getColorClassNames(color, colorPalette.text).textColor
: "text-tremor-brand dark:text-dark-tremor-brand",
bgColor: color
? tremorTwMerge(
getColorClassNames(color, colorPalette.background).bgColor,
"bg-opacity-20",
)
: "bg-tremor-background dark:bg-dark-tremor-background",
borderColor: "border-tremor-border dark:border-dark-tremor-border",
ringColor: "",
};
case "solid":
return {
textColor: color
? getColorClassNames(color, colorPalette.text).textColor
: "text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",
bgColor: color
? tremorTwMerge(
getColorClassNames(color, colorPalette.background).bgColor,
"bg-opacity-20",
)
: "bg-tremor-brand dark:bg-dark-tremor-brand",
borderColor: "border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",
ringColor: "ring-tremor-ring dark:ring-dark-tremor-ring",
};
case "outlined":
return {
textColor: color
? getColorClassNames(color, colorPalette.text).textColor
: "text-tremor-brand dark:text-dark-tremor-brand",
bgColor: color
? tremorTwMerge(
getColorClassNames(color, colorPalette.background).bgColor,
"bg-opacity-20",
)
: "bg-tremor-background dark:bg-dark-tremor-background",
borderColor: color
? getColorClassNames(color, colorPalette.ring).borderColor
: "border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",
ringColor: color
? tremorTwMerge(getColorClassNames(color, colorPalette.ring).ringColor, "ring-opacity-40")
: "ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted",
};
}
};
================================================
FILE: src/components/icon-elements/index.ts
================================================
export * from "./Badge";
export * from "./BadgeDelta";
export * from "./Icon";
================================================
FILE: src/components/index.ts
================================================
export * from "./chart-elements";
export * from "./icon-elements";
export * from "./input-elements";
export * from "./layout-elements";
export * from "./list-elements";
export * from "./spark-elements";
export * from "./text-elements";
export * from "./vis-elements";
================================================
FILE: src/components/input-elements/BaseInput.tsx
================================================
"use client";
import React, { ReactNode, useCallback, useRef, useState } from "react";
import { ExclamationFilledIcon, EyeIcon, EyeOffIcon } from "assets";
import { getSelectButtonColors, hasValue } from "components/input-elements/selectUtils";
import { mergeRefs, tremorTwMerge } from "lib";
export interface BaseInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
type?: "text" | "password" | "email" | "url" | "number" | "search" | "tel";
defaultValue?: string | number;
value?: string | number;
icon?: React.ElementType | React.JSXElementConstructor<any>;
error?: boolean;
errorMessage?: string;
disabled?: boolean;
stepper?: ReactNode;
onValueChange?: (value: any) => void;
makeInputClassName: (className: string) => string;
pattern?: string;
}
const BaseInput = React.forwardRef<HTMLInputElement, BaseInputProps>((props, ref) => {
const {
value,
defaultValue,
type,
placeholder = "Type...",
icon,
error = false,
errorMessage,
disabled = false,
stepper,
makeInputClassName,
className,
onChange,
onValueChange,
autoFocus,
pattern,
...other
} = props;
const [isFocused, setIsFocused] = useState(autoFocus || false);
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const toggleIsPasswordVisible = useCallback(
() => setIsPasswordVisible(!isPasswordVisible),
[isPasswordVisible, setIsPasswordVisible],
);
const Icon = icon;
const inputRef = useRef<HTMLInputElement>(null);
const hasSelection = hasValue(value || defaultValue);
React.useEffect(() => {
const handleFocus = () => setIsFocused(true);
const handleBlur = () => setIsFocused(false);
const node = inputRef.current;
if (node) {
node.addEventListener("focus", handleFocus);
node.addEventListener("blur", handleBlur);
// Autofocus logic
if (autoFocus) {
node.focus();
}
}
return () => {
if (node) {
node.removeEventListener("focus", handleFocus);
node.removeEventListener("blur", handleBlur);
}
};
}, [autoFocus]);
return (
<>
<div
className={tremorTwMerge(
makeInputClassName("root"),
// common
"relative w-full flex items-center min-w-[10rem] outline-none rounded-tremor-default transition duration-100 border",
// light
"shadow-tremor-input",
// dark
"dark:shadow-dark-tremor-input",
getSelectButtonColors(hasSelection, disabled, error),
isFocused &&
tremorTwMerge(
// common
"ring-2",
// light
"border-tremor-brand-subtle ring-tremor-brand-muted",
// light
"dark:border-dark-tremor-brand-subtle dark:ring-dark-tremor-brand-muted",
),
className,
)}
>
{Icon ? (
<Icon
className={tremorTwMerge(
makeInputClassName("icon"),
// common
"shrink-0 h-5 w-5 mx-2.5 absolute left-0 flex items-center",
// light
"text-tremor-content-subtle",
// light
"dark:text-dark-tremor-content-subtle",
)}
/>
) : null}
<input
ref={mergeRefs([inputRef, ref])}
defaultValue={defaultValue}
value={value}
type={isPasswordVisible ? "text" : type}
className={tremorTwMerge(
makeInputClassName("input"),
// common
"w-full bg-transparent focus:outline-none focus:ring-0 border-none text-tremor-default rounded-tremor-default transition duration-100 py-2",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
"[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
type === "password" ? (error ? "pr-16" : "pr-12") : error ? "pr-8" : "pr-3",
Icon ? "pl-10" : "pl-3",
disabled
? "placeholder:text-tremor-content-subtle dark:placeholder:text-dark-tremor-content-subtle"
: "placeholder:text-tremor-content dark:placeholder:text-dark-tremor-content",
)}
placeholder={placeholder}
disabled={disabled}
data-testid="base-input"
onChange={(e) => {
onChange?.(e);
onValueChange?.(e.target.value);
}}
pattern={pattern}
{...other}
/>
{type === "password" && !disabled ? (
<button
className={tremorTwMerge(
makeInputClassName("toggleButton"),
"absolute inset-y-0 right-0 flex items-center px-2.5 rounded-lg",
)}
type="button"
onClick={() => toggleIsPasswordVisible()}
aria-label={isPasswordVisible ? "Hide password" : "Show Password"}
>
{isPasswordVisible ? (
<EyeOffIcon
className={tremorTwMerge(
// common
"flex-none h-5 w-5 transition",
// light
"text-tremor-content-subtle hover:text-tremor-content",
// dark
"dark:text-dark-tremor-content-subtle hover:dark:text-dark-tremor-content",
)}
aria-hidden
/>
) : (
<EyeIcon
className={tremorTwMerge(
// common
"flex-none h-5 w-5 transition",
// light
"text-tremor-content-subtle hover:text-tremor-content",
// dark
"dark:text-dark-tremor-content-subtle hover:dark:text-dark-tremor-content",
)}
aria-hidden
/>
)}
</button>
) : null}
{error ? (
<ExclamationFilledIcon
className={tremorTwMerge(
makeInputClassName("errorIcon"),
"text-red-500 shrink-0 h-5 w-5 absolute right-0 flex items-center",
type === "password"
? "mr-10"
: type === "number"
? stepper
? "mr-20"
: "mr-3"
: "mx-2.5",
)}
/>
) : null}
{stepper ?? null}
</div>
{error && errorMessage ? (
<p
className={tremorTwMerge(makeInputClassName("errorMessage"), "text-sm text-red-500 mt-1")}
>
{errorMessage}
</p>
) : null}
</>
);
});
BaseInput.displayName = "BaseInput";
export default BaseInput;
================================================
FILE: src/components/input-elements/Button/Button.tsx
================================================
"use client";
import Tooltip, { useTooltip } from "components/util-elements/Tooltip/Tooltip";
import React, { useEffect } from "react";
import { useTransition, type TransitionStatus } from "react-transition-state";
import { HorizontalPositions, makeClassName, mergeRefs, Sizes, tremorTwMerge } from "lib";
import { LoadingSpinner } from "assets";
import { ButtonVariant, Color, HorizontalPosition, Size } from "../../../lib";
import { getButtonColors, getButtonProportions, iconSizes } from "./styles";
const makeButtonClassName = makeClassName("Button");
export interface ButtonIconOrSpinnerProps {
loading: boolean;
iconSize: string;
iconPosition: string;
Icon: React.ElementType | undefined;
needMargin: boolean;
transitionStatus: TransitionStatus;
}
export const ButtonIconOrSpinner = ({
loading,
iconSize,
iconPosition,
Icon,
needMargin,
transitionStatus,
}: ButtonIconOrSpinnerProps) => {
Icon = Icon!;
const margin = !needMargin
? ""
: iconPosition === HorizontalPositions.Left
? tremorTwMerge("-ml-1", "mr-1.5")
: tremorTwMerge("-mr-1", "ml-1.5");
const defaultSpinnerSize = tremorTwMerge("w-0 h-0");
const spinnerSize: { [key: string]: any } = {
default: defaultSpinnerSize,
entering: defaultSpinnerSize,
entered: iconSize,
exiting: iconSize,
exited: defaultSpinnerSize,
};
return loading ? (
<LoadingSpinner
className={tremorTwMerge(
makeButtonClassName("icon"),
"animate-spin shrink-0",
margin,
spinnerSize.default,
spinnerSize[transitionStatus],
)}
style={{ transition: `width 150ms` }}
/>
) : (
<Icon className={tremorTwMerge(makeButtonClassName("icon"), "shrink-0", iconSize, margin)} />
);
};
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon?: React.ElementType;
iconPosition?: HorizontalPosition;
size?: Size;
color?: Color;
variant?: ButtonVariant;
disabled?: boolean;
loading?: boolean;
loadingText?: string;
tooltip?: string;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
const {
icon,
iconPosition = HorizontalPositions.Left,
size = Sizes.SM,
color,
variant = "primary",
disabled,
loading = false,
loadingText,
children,
tooltip,
clas
gitextract_8mwyg1k6/ ├── .eslintignore ├── .eslintrc ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yaml │ │ ├── config.yaml │ │ └── feature-request.yaml │ ├── pull_request_template.md │ └── workflows/ │ ├── build.yaml │ ├── lint.yaml │ └── release.yaml ├── .gitignore ├── .prettierrc.json ├── .storybook/ │ ├── main.js │ ├── manager.js │ ├── preview.js │ └── tremorTheme.js ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── License ├── README.md ├── babel.config.js ├── jest.config.js ├── package.json ├── postcss.config.js ├── rollup.config.js ├── setupTests.js ├── src/ │ ├── assets/ │ │ ├── ArrowDownHeadIcon.tsx │ │ ├── ArrowDownIcon.tsx │ │ ├── ArrowDownRightIcon.tsx │ │ ├── ArrowLeftHeadIcon.tsx │ │ ├── ArrowRightHeadIcon.tsx │ │ ├── ArrowRightIcon.tsx │ │ ├── ArrowUpHeadIcon.tsx │ │ ├── ArrowUpIcon.tsx │ │ ├── ArrowUpRightIcon.tsx │ │ ├── CalendarIcon.tsx │ │ ├── ChevronLeftFill.tsx │ │ ├── ChevronRightFill.tsx │ │ ├── DoubleArrowLeftHeadIcon.tsx │ │ ├── DoubleArrowRightHeadIcon.tsx │ │ ├── ExclamationFilledIcon.tsx │ │ ├── EyeIcon.tsx │ │ ├── EyeOffIcon.tsx │ │ ├── LoadingSpinner.tsx │ │ ├── MinusIcon.tsx │ │ ├── PlusIcon.tsx │ │ ├── SearchIcon.tsx │ │ ├── XCircleIcon.tsx │ │ ├── XIcon.tsx │ │ └── index.ts │ ├── components/ │ │ ├── chart-elements/ │ │ │ ├── AreaChart/ │ │ │ │ ├── AreaChart.tsx │ │ │ │ └── index.ts │ │ │ ├── BarChart/ │ │ │ │ ├── BarChart.tsx │ │ │ │ └── index.ts │ │ │ ├── DonutChart/ │ │ │ │ ├── DonutChart.tsx │ │ │ │ ├── DonutChartTooltip.tsx │ │ │ │ ├── index.ts │ │ │ │ └── inputParser.ts │ │ │ ├── FunnelChart/ │ │ │ │ ├── FunnelChart.tsx │ │ │ │ └── index.ts │ │ │ ├── LineChart/ │ │ │ │ ├── LineChart.tsx │ │ │ │ └── index.ts │ │ │ ├── ScatterChart/ │ │ │ │ ├── ScatterChart.tsx │ │ │ │ ├── ScatterChartTooltip.tsx │ │ │ │ └── index.tsx │ │ │ ├── common/ │ │ │ │ ├── BaseAnimationTimingProps.tsx │ │ │ │ ├── BaseChartProps.tsx │ │ │ │ ├── ChartLegend.tsx │ │ │ │ ├── ChartTooltip.tsx │ │ │ │ ├── CustomTooltipProps.tsx │ │ │ │ ├── NoData.tsx │ │ │ │ ├── index.ts │ │ │ │ └── utils.ts │ │ │ └── index.ts │ │ ├── icon-elements/ │ │ │ ├── Badge/ │ │ │ │ ├── Badge.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styles.ts │ │ │ ├── BadgeDelta/ │ │ │ │ ├── BadgeDelta.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styles.ts │ │ │ ├── Icon/ │ │ │ │ ├── Icon.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styles.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── input-elements/ │ │ │ ├── BaseInput.tsx │ │ │ ├── Button/ │ │ │ │ ├── Button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styles.ts │ │ │ ├── Calendar/ │ │ │ │ ├── Calendar.tsx │ │ │ │ ├── NavButton.tsx │ │ │ │ └── index.ts │ │ │ ├── DatePicker/ │ │ │ │ ├── DatePicker.tsx │ │ │ │ ├── datePickerUtils.tsx │ │ │ │ └── index.ts │ │ │ ├── DateRangePicker/ │ │ │ │ ├── DateRangePicker.tsx │ │ │ │ ├── DateRangePickerItem.tsx │ │ │ │ ├── dateRangePickerUtils.tsx │ │ │ │ └── index.ts │ │ │ ├── MultiSelect/ │ │ │ │ ├── MultiSelect.tsx │ │ │ │ ├── MultiSelectItem.tsx │ │ │ │ └── index.ts │ │ │ ├── NumberInput/ │ │ │ │ ├── NumberInput.tsx │ │ │ │ └── index.ts │ │ │ ├── SearchSelect/ │ │ │ │ ├── SearchSelect.tsx │ │ │ │ ├── SearchSelectItem.tsx │ │ │ │ └── index.ts │ │ │ ├── Select/ │ │ │ │ ├── Select.tsx │ │ │ │ ├── SelectItem.tsx │ │ │ │ └── index.ts │ │ │ ├── Switch/ │ │ │ │ ├── Switch.tsx │ │ │ │ └── index.ts │ │ │ ├── Tabs/ │ │ │ │ ├── Tab.tsx │ │ │ │ ├── TabGroup.tsx │ │ │ │ ├── TabList.tsx │ │ │ │ ├── TabPanel.tsx │ │ │ │ ├── TabPanels.tsx │ │ │ │ └── index.ts │ │ │ ├── TextInput/ │ │ │ │ ├── TextInput.tsx │ │ │ │ └── index.ts │ │ │ ├── Textarea/ │ │ │ │ ├── Textarea.tsx │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── selectUtils.ts │ │ ├── layout-elements/ │ │ │ ├── Accordion/ │ │ │ │ ├── Accordion.tsx │ │ │ │ ├── AccordionBody.tsx │ │ │ │ ├── AccordionHeader.tsx │ │ │ │ ├── AccordionList.tsx │ │ │ │ └── index.ts │ │ │ ├── Card/ │ │ │ │ ├── Card.tsx │ │ │ │ └── index.ts │ │ │ ├── Dialog/ │ │ │ │ ├── Dialog.tsx │ │ │ │ ├── DialogPanel.tsx │ │ │ │ └── index.ts │ │ │ ├── Divider/ │ │ │ │ ├── Divider.tsx │ │ │ │ └── index.ts │ │ │ ├── Flex/ │ │ │ │ ├── Flex.tsx │ │ │ │ └── index.ts │ │ │ ├── Grid/ │ │ │ │ ├── Col.tsx │ │ │ │ ├── Grid.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styles.ts │ │ │ └── index.ts │ │ ├── list-elements/ │ │ │ ├── List/ │ │ │ │ ├── List.tsx │ │ │ │ ├── ListItem.tsx │ │ │ │ └── index.ts │ │ │ ├── Table/ │ │ │ │ ├── Table.tsx │ │ │ │ ├── TableBody.tsx │ │ │ │ ├── TableCell.tsx │ │ │ │ ├── TableFoot.tsx │ │ │ │ ├── TableFooterCell.tsx │ │ │ │ ├── TableHead.tsx │ │ │ │ ├── TableHeaderCell.tsx │ │ │ │ ├── TableRow.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── spark-elements/ │ │ │ ├── SparkAreaChart/ │ │ │ │ ├── SparkAreaChart.tsx │ │ │ │ └── index.ts │ │ │ ├── SparkBarChart/ │ │ │ │ ├── SparkBarChart.tsx │ │ │ │ └── index.ts │ │ │ ├── SparkLineChart/ │ │ │ │ ├── SparkLineChart.tsx │ │ │ │ └── index.ts │ │ │ ├── common/ │ │ │ │ └── BaseSparkChartProps.tsx │ │ │ └── index.ts │ │ ├── text-elements/ │ │ │ ├── Bold/ │ │ │ │ ├── Bold.tsx │ │ │ │ └── index.ts │ │ │ ├── Callout/ │ │ │ │ ├── Callout.tsx │ │ │ │ └── index.ts │ │ │ ├── Italic/ │ │ │ │ ├── Italic.tsx │ │ │ │ └── index.ts │ │ │ ├── Legend/ │ │ │ │ ├── Legend.tsx │ │ │ │ └── index.ts │ │ │ ├── Metric/ │ │ │ │ ├── Metric.tsx │ │ │ │ └── index.ts │ │ │ ├── Subtitle/ │ │ │ │ ├── Subtitle.tsx │ │ │ │ └── index.ts │ │ │ ├── Text/ │ │ │ │ ├── Text.tsx │ │ │ │ └── index.ts │ │ │ ├── Title/ │ │ │ │ ├── Title.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── util-elements/ │ │ │ ├── Tooltip/ │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ └── vis-elements/ │ │ ├── BarList/ │ │ │ ├── BarList.tsx │ │ │ └── index.ts │ │ ├── CategoryBar/ │ │ │ ├── CategoryBar.tsx │ │ │ └── index.ts │ │ ├── DeltaBar/ │ │ │ ├── DeltaBar.tsx │ │ │ ├── index.ts │ │ │ └── styles.ts │ │ ├── MarkerBar/ │ │ │ ├── MarkerBar.tsx │ │ │ └── index.ts │ │ ├── ProgressBar/ │ │ │ ├── ProgressBar.tsx │ │ │ └── index.ts │ │ ├── ProgressCircle/ │ │ │ ├── ProgressCircle.tsx │ │ │ └── index.ts │ │ ├── Tracker/ │ │ │ ├── Tracker.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── contexts/ │ │ ├── BaseColorContext.tsx │ │ ├── IndexContext.tsx │ │ ├── RootStylesContext.tsx │ │ ├── SelectedValueContext.tsx │ │ └── index.ts │ ├── hooks/ │ │ ├── index.ts │ │ ├── useInternalState.tsx │ │ └── useOnWindowResize.tsx │ ├── index.ts │ ├── lib/ │ │ ├── constants.ts │ │ ├── index.ts │ │ ├── inputTypes.ts │ │ ├── theme.ts │ │ ├── tremorTwMerge.ts │ │ └── utils.tsx │ ├── stories/ │ │ ├── chart-elements/ │ │ │ ├── AreaChart.stories.tsx │ │ │ ├── BarChart.stories.tsx │ │ │ ├── DonutChart.stories.tsx │ │ │ ├── FunnelChart.stories.tsx │ │ │ ├── LineChart.stories.tsx │ │ │ ├── ScatterChart.stories.tsx │ │ │ └── helpers/ │ │ │ ├── testData.tsx │ │ │ ├── testDataScatterChart.tsx │ │ │ └── utils.ts │ │ ├── icon-elements/ │ │ │ ├── Badge.stories.tsx │ │ │ ├── BadgeDelta.stories.tsx │ │ │ └── Icon.stories.tsx │ │ ├── input-elements/ │ │ │ ├── Button.stories.tsx │ │ │ ├── DatePicker.stories.tsx │ │ │ ├── DateRangePicker.stories.tsx │ │ │ ├── MultiSelect.stories.tsx │ │ │ ├── NumberInput.stories.tsx │ │ │ ├── SearchSelect.stories.tsx │ │ │ ├── Select.stories.tsx │ │ │ ├── Switch.stories.tsx │ │ │ ├── Tabs.stories.tsx │ │ │ ├── TextArea.stories.tsx │ │ │ ├── TextInput.stories.tsx │ │ │ └── helpers/ │ │ │ ├── SimpleMultiSelect.tsx │ │ │ ├── SimpleNumberInput.tsx │ │ │ ├── SimpleSearchSelect.tsx │ │ │ ├── SimpleSelect.tsx │ │ │ ├── SimpleSwitch.tsx │ │ │ ├── SimpleTextInput.tsx │ │ │ └── testData.ts │ │ ├── layout-elements/ │ │ │ ├── Accordion.stories.tsx │ │ │ ├── AccordionList.stories.tsx │ │ │ ├── Card.stories.tsx │ │ │ ├── Dialog.stories.tsx │ │ │ ├── Divider.stories.tsx │ │ │ ├── Flex.stories.tsx │ │ │ ├── Grid.stories.tsx │ │ │ └── helpers/ │ │ │ ├── SimpleAccordion.tsx │ │ │ ├── SimpleCard.tsx │ │ │ └── SimpleText.tsx │ │ ├── list-elements/ │ │ │ ├── List.stories.tsx │ │ │ └── Table.stories.tsx │ │ ├── spark-elements/ │ │ │ ├── SparkAreaChart.stories.tsx │ │ │ ├── SparkBarChart.stories.tsx │ │ │ ├── SparkLineChart.stories.tsx │ │ │ └── helpers/ │ │ │ ├── ExampleCard.tsx │ │ │ └── testData.ts │ │ ├── text-elements/ │ │ │ ├── Callout.stories.tsx │ │ │ ├── Legend.stories.tsx │ │ │ ├── Metric.stories.tsx │ │ │ ├── Subtitle.stories.tsx │ │ │ ├── Text.stories.tsx │ │ │ ├── TextElements.stories.tsx │ │ │ └── Title.stories.tsx │ │ └── vis-elements/ │ │ ├── BarList.stories.tsx │ │ ├── CategoryBar.stories.tsx │ │ ├── DeltaBar.stories.tsx │ │ ├── MarkerBar.stories.tsx │ │ ├── ProgressBar.stories.tsx │ │ ├── ProgressCircle.stories.tsx │ │ └── Tracker.stories.tsx │ ├── styles.css │ └── tests/ │ ├── chart-elements/ │ │ ├── AreaChart.test.tsx │ │ └── BarChart.test.tsx │ ├── icon-elements/ │ │ ├── Badge.test.tsx │ │ ├── BadgeDelta.test.tsx │ │ └── Icon.test.tsx │ ├── input-elements/ │ │ ├── Button.test.tsx │ │ ├── DatePicker.test.tsx │ │ ├── DateRangePicker.test.tsx │ │ ├── MultiSelect.test.tsx │ │ ├── NumberInput.test.tsx │ │ ├── SearchSelect.test.tsx │ │ ├── Select.test.tsx │ │ ├── Switch.text.tsx │ │ ├── Tabs.test.tsx │ │ ├── TextInput.test.tsx │ │ └── Textarea.test.tsx │ ├── layout-elements/ │ │ ├── Accordion.test.tsx │ │ ├── AccordionList.test.tsx │ │ ├── Card.test.tsx │ │ ├── Dialog.test.tsx │ │ ├── Divider.test.tsx │ │ ├── Flex.test.tsx │ │ └── Grid.test.tsx │ ├── list-elements/ │ │ ├── List.test.tsx │ │ └── Table.test.tsx │ ├── spark-elements/ │ │ └── SparkAreaChart.test.tsx │ ├── text-elements/ │ │ ├── Callout.test.tsx │ │ ├── Legend.test.tsx │ │ ├── Metric.test.tsx │ │ ├── Subtitle.test.tsx │ │ ├── Text.test.tsx │ │ └── Title.test.tsx │ └── vis-elements/ │ ├── BarList.test.tsx │ ├── CategoryBar.test.tsx │ ├── DeltaBar.test.tsx │ ├── MarkerBar.test.tsx │ ├── ProgressBar.test.tsx │ ├── ProgressCircle.text.tsx │ └── Tracker.test.tsx ├── tailwind.config.js └── tsconfig.json
SYMBOL INDEX (212 symbols across 122 files)
FILE: src/components/chart-elements/AreaChart/AreaChart.tsx
type AreaChartProps (line 38) | interface AreaChartProps extends BaseChartProps {
type ActiveDot (line 45) | interface ActiveDot {
function onDotClick (line 98) | function onDotClick(itemData: any, event: React.MouseEvent) {
function onCategoryClick (line 125) | function onCategoryClick(dataKey: string) {
FILE: src/components/chart-elements/BarChart/BarChart.tsx
type BarChartProps (line 60) | interface BarChartProps extends BaseChartProps {
function onBarClick (line 111) | function onBarClick(data: any, idx: number, event: React.MouseEvent) {
function onCategoryClick (line 132) | function onCategoryClick(dataKey: string) {
FILE: src/components/chart-elements/DonutChart/DonutChart.tsx
type DonutChartVariant (line 22) | type DonutChartVariant = "donut" | "pie";
type DonutChartProps (line 24) | interface DonutChartProps extends BaseAnimationTimingProps {
function onShapeClick (line 102) | function onShapeClick(data: any, index: number, event: React.MouseEvent) {
FILE: src/components/chart-elements/DonutChart/DonutChartTooltip.tsx
type DonutChartTooltipProps (line 6) | interface DonutChartTooltipProps {
FILE: src/components/chart-elements/FunnelChart/FunnelChart.tsx
type FormattedDataT (line 16) | type FormattedDataT = DataT & {
type CalculateFrom (line 27) | type CalculateFrom = "first" | "previous";
type Tooltip (line 29) | type Tooltip = {
type DataT (line 44) | type DataT = {
constant GLOBAL_PADDING (line 49) | const GLOBAL_PADDING = 10;
constant HALF_PADDING (line 50) | const HALF_PADDING = GLOBAL_PADDING / 2;
constant Y_AXIS_LABELS (line 51) | const Y_AXIS_LABELS = ["100%", "75%", "50%", "25%", "0%"];
type FunnelChartProps (line 53) | interface FunnelChartProps extends React.HTMLAttributes<HTMLDivElement> {
function onBarClick (line 122) | function onBarClick(data: any, idx: number, event: React.MouseEvent) {
FILE: src/components/chart-elements/LineChart/LineChart.tsx
type LineChartProps (line 37) | interface LineChartProps extends BaseChartProps {
type ActiveDot (line 42) | interface ActiveDot {
function onDotClick (line 91) | function onDotClick(itemData: any, event: React.MouseEvent) {
function onCategoryClick (line 118) | function onCategoryClick(dataKey: string) {
FILE: src/components/chart-elements/ScatterChart/ScatterChart.tsx
type ScatterChartValueFormatter (line 41) | type ScatterChartValueFormatter = {
type ScatterChartProps (line 47) | interface ScatterChartProps
function onNodeClick (line 156) | function onNodeClick(data: any, index: number, event: React.MouseEvent) {
function onCategoryClick (line 174) | function onCategoryClick(dataKey: string) {
FILE: src/components/chart-elements/ScatterChart/ScatterChartTooltip.tsx
type ChartTooltipRowProps (line 28) | interface ChartTooltipRowProps {
type ScatterChartTooltipProps (line 64) | interface ScatterChartTooltipProps {
FILE: src/components/chart-elements/common/BaseAnimationTimingProps.tsx
type BaseAnimationTimingProps (line 1) | interface BaseAnimationTimingProps {
FILE: src/components/chart-elements/common/BaseChartProps.tsx
type FixedProps (line 5) | type FixedProps = {
type BaseEventProps (line 10) | type BaseEventProps = FixedProps & {
type EventProps (line 14) | type EventProps = BaseEventProps | null | undefined;
type BaseChartProps (line 16) | interface BaseChartProps extends BaseAnimationTimingProps, React.HTMLAtt...
FILE: src/components/chart-elements/common/ChartTooltip.tsx
type ChartTooltipRowProps (line 26) | interface ChartTooltipRowProps {
type ChartTooltipProps (line 74) | interface ChartTooltipProps {
FILE: src/components/chart-elements/common/CustomTooltipProps.tsx
type CustomTooltipProps (line 3) | type CustomTooltipProps = {
FILE: src/components/chart-elements/common/NoData.tsx
type NoDataProps (line 4) | interface NoDataProps {
FILE: src/components/chart-elements/common/utils.ts
function deepEqual (line 36) | function deepEqual(obj1: any, obj2: any) {
function hasOnlyOneValueForThisKey (line 78) | function hasOnlyOneValueForThisKey(array: any[], keyToCheck: string) {
FILE: src/components/icon-elements/Badge/Badge.tsx
type BadgeProps (line 18) | interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
FILE: src/components/icon-elements/Badge/styles.ts
type BadgeProportionTypes (line 1) | type BadgeProportionTypes = {
FILE: src/components/icon-elements/BadgeDelta/BadgeDelta.tsx
type BadgeDeltaProps (line 24) | interface BadgeDeltaProps extends React.HTMLAttributes<HTMLSpanElement> {
FILE: src/components/icon-elements/BadgeDelta/styles.ts
type BadgeProportionTypes (line 11) | type BadgeProportionTypes = {
type ColorTypes (line 105) | type ColorTypes = {
FILE: src/components/icon-elements/Icon/Icon.tsx
type IconProps (line 18) | interface IconProps extends React.HTMLAttributes<HTMLSpanElement> {
FILE: src/components/icon-elements/Icon/styles.ts
type WrapperProportionTypes (line 3) | type WrapperProportionTypes = {
type ShapeTypes (line 59) | type ShapeTypes = {
FILE: src/components/input-elements/BaseInput.tsx
type BaseInputProps (line 7) | interface BaseInputProps extends React.InputHTMLAttributes<HTMLInputElem...
FILE: src/components/input-elements/Button/Button.tsx
type ButtonIconOrSpinnerProps (line 14) | interface ButtonIconOrSpinnerProps {
type ButtonProps (line 64) | interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonEleme...
FILE: src/components/input-elements/Calendar/Calendar.tsx
function Calendar (line 20) | function Calendar<T extends DayPickerSingleProps | DayPickerRangeProps>({
FILE: src/components/input-elements/Calendar/NavButton.tsx
type NavButtonProps (line 5) | interface NavButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
FILE: src/components/input-elements/DatePicker/DatePicker.tsx
constant TODAY (line 18) | const TODAY = startOfToday();
type Locale (line 20) | type Locale = typeof enUS;
type DatePickerValue (line 22) | type DatePickerValue = Date | undefined;
type DatePickerProps (line 24) | interface DatePickerProps
FILE: src/components/input-elements/DateRangePicker/DateRangePicker.tsx
constant TODAY (line 38) | const TODAY = startOfToday();
type Locale (line 40) | type Locale = typeof enUS;
type DateRangePickerValue (line 42) | type DateRangePickerValue = { from?: Date; to?: Date; selectValue?: stri...
type DateRangePickerProps (line 44) | interface DateRangePickerProps
FILE: src/components/input-elements/DateRangePicker/DateRangePickerItem.tsx
type DateRangePickerItemProps (line 6) | interface DateRangePickerItemProps extends React.HTMLAttributes<HTMLDivE...
FILE: src/components/input-elements/DateRangePicker/dateRangePickerUtils.tsx
type DateRangePickerOption (line 16) | type DateRangePickerOption = {
type DropdownValues (line 22) | type DropdownValues = Map<string, Omit<DateRangePickerOption, "value">>;
FILE: src/components/input-elements/MultiSelect/MultiSelect.tsx
type MultiSelectProps (line 13) | interface MultiSelectProps extends React.HTMLAttributes<HTMLInputElement> {
FILE: src/components/input-elements/MultiSelect/MultiSelectItem.tsx
type MultiSelectItemProps (line 9) | interface MultiSelectItemProps extends React.HTMLAttributes<HTMLDivEleme...
FILE: src/components/input-elements/NumberInput/NumberInput.tsx
type NumberInputProps (line 7) | interface NumberInputProps
FILE: src/components/input-elements/SearchSelect/SearchSelect.tsx
type SearchSelectProps (line 23) | interface SearchSelectProps extends React.HTMLAttributes<HTMLInputElemen...
FILE: src/components/input-elements/SearchSelect/SearchSelectItem.tsx
type SearchSelectItemProps (line 10) | interface SearchSelectItemProps extends React.HTMLAttributes<HTMLDivElem...
FILE: src/components/input-elements/Select/Select.tsx
type SelectProps (line 13) | interface SelectProps extends React.HTMLAttributes<HTMLInputElement> {
FILE: src/components/input-elements/Select/SelectItem.tsx
type SelectItemProps (line 8) | interface SelectItemProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/input-elements/Switch/Switch.tsx
type SwitchProps (line 29) | interface SwitchProps extends Omit<React.HTMLAttributes<HTMLDivElement>,...
FILE: src/components/input-elements/Tabs/Tab.tsx
function getVariantStyles (line 12) | function getVariantStyles(tabVariant: TabVariant, color?: Color) {
type TabProps (line 46) | interface TabProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
FILE: src/components/input-elements/Tabs/TabGroup.tsx
type TabGroupProps (line 8) | interface TabGroupProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/input-elements/Tabs/TabList.tsx
type TabVariant (line 11) | type TabVariant = "line" | "solid";
type TabListProps (line 34) | interface TabListProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/input-elements/TextInput/TextInput.tsx
type TextInputProps (line 6) | type TextInputProps = Omit<BaseInputProps, "stepper" | "makeInputClassNa...
FILE: src/components/input-elements/Textarea/Textarea.tsx
type TextareaProps (line 9) | interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAre...
FILE: src/components/input-elements/selectUtils.ts
type SelectItemProps (line 4) | interface SelectItemProps {
function constructValueToNameMapping (line 15) | function constructValueToNameMapping(children: React.ReactElement[] | Re...
function getFilteredOptions (line 23) | function getFilteredOptions(
function hasValue (line 55) | function hasValue<T>(value: T | null | undefined) {
FILE: src/components/layout-elements/Accordion/Accordion.tsx
type OpenContextValue (line 9) | interface OpenContextValue {
type AccordionProps (line 16) | interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/layout-elements/Accordion/AccordionList.tsx
type AccordionListProps (line 10) | interface AccordionListProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/layout-elements/Card/Card.tsx
type CardProps (line 31) | interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/layout-elements/Dialog/Dialog.tsx
type Without (line 7) | type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR (line 8) | type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U...
type DialogProps (line 10) | type DialogProps = React.HTMLAttributes<HTMLDivElement> & {
FILE: src/components/layout-elements/Dialog/DialogPanel.tsx
type DialogPanelProps (line 8) | type DialogPanelProps = React.HTMLAttributes<HTMLDivElement>;
FILE: src/components/layout-elements/Flex/Flex.tsx
type FlexProps (line 33) | interface FlexProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/layout-elements/Grid/Col.tsx
type ColProps (line 9) | interface ColProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/layout-elements/Grid/Grid.tsx
type GridProps (line 15) | interface GridProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/layout-elements/Grid/styles.ts
type GridClassesMapping (line 1) | type GridClassesMapping = {
FILE: src/components/spark-elements/SparkAreaChart/SparkAreaChart.tsx
type SparkAreaChartProps (line 12) | interface SparkAreaChartProps extends BaseSparkChartProps {
FILE: src/components/spark-elements/SparkBarChart/SparkBarChart.tsx
type SparkBarChartProps (line 13) | interface SparkBarChartProps extends BaseSparkChartProps {
FILE: src/components/spark-elements/SparkLineChart/SparkLineChart.tsx
type SparkLineChartProps (line 12) | interface SparkLineChartProps extends BaseSparkChartProps {
FILE: src/components/spark-elements/common/BaseSparkChartProps.tsx
type FixedProps (line 4) | type FixedProps = {
type BaseEventProps (line 9) | type BaseEventProps = FixedProps & {
type EventProps (line 13) | type EventProps = BaseEventProps | null | undefined;
type BaseSparkChartProps (line 15) | interface BaseSparkChartProps
FILE: src/components/text-elements/Callout/Callout.tsx
type CalloutProps (line 7) | interface CalloutProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/text-elements/Legend/Legend.tsx
type LegendItemProps (line 9) | interface LegendItemProps {
type ScrollButtonProps (line 67) | interface ScrollButtonProps {
type LegendProps (line 132) | interface LegendProps extends React.OlHTMLAttributes<HTMLOListElement> {
type HasScrollProps (line 140) | type HasScrollProps = {
FILE: src/components/text-elements/Metric/Metric.tsx
type MetricProps (line 8) | interface MetricProps extends React.HTMLAttributes<HTMLParagraphElement> {
FILE: src/components/text-elements/Subtitle/Subtitle.tsx
type SubtitleProps (line 8) | interface SubtitleProps extends React.HTMLAttributes<HTMLParagraphElemen...
FILE: src/components/text-elements/Text/Text.tsx
type TextProps (line 8) | interface TextProps extends React.HTMLAttributes<HTMLParagraphElement> {
FILE: src/components/text-elements/Title/Title.tsx
type TitleProps (line 8) | interface TitleProps extends React.HTMLAttributes<HTMLParagraphElement> {
FILE: src/components/util-elements/Tooltip/Tooltip.tsx
type TooltipProps (line 69) | interface TooltipProps {
FILE: src/components/vis-elements/BarList/BarList.tsx
type Bar (line 16) | type Bar<T> = T & {
type BarListProps (line 26) | interface BarListProps<T = any> extends React.HTMLAttributes<HTMLDivElem...
function BarListInner (line 35) | function BarListInner<T>(props: BarListProps<T>, ref: React.ForwardedRef...
FILE: src/components/vis-elements/CategoryBar/CategoryBar.tsx
type CategoryBarProps (line 88) | interface CategoryBarProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/vis-elements/DeltaBar/DeltaBar.tsx
type DeltaBarProps (line 11) | interface DeltaBarProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/vis-elements/DeltaBar/styles.ts
type ColorTypes (line 4) | type ColorTypes = {
FILE: src/components/vis-elements/MarkerBar/MarkerBar.tsx
type MarkerBarProps (line 11) | interface MarkerBarProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/vis-elements/ProgressBar/ProgressBar.tsx
type ProgressBarProps (line 9) | interface ProgressBarProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/components/vis-elements/ProgressCircle/ProgressCircle.tsx
type Size (line 7) | type Size = "xs" | "sm" | "md" | "lg" | "xl";
type ProgressCircleProps (line 9) | interface ProgressCircleProps extends React.HTMLAttributes<HTMLDivElemen...
function getLimitedValue (line 43) | function getLimitedValue(input: number | undefined) {
FILE: src/components/vis-elements/Tracker/Tracker.tsx
type TrackerBlockProps (line 11) | interface TrackerBlockProps {
type TrackerProps (line 40) | interface TrackerProps extends React.HTMLAttributes<HTMLDivElement> {
FILE: src/contexts/SelectedValueContext.tsx
type SelectedValueContextValue (line 3) | interface SelectedValueContextValue {
FILE: src/lib/inputTypes.ts
type ValueFormatter (line 1) | type ValueFormatter = {
type CurveType (line 5) | type CurveType = "linear" | "natural" | "monotone" | "step";
type Interval (line 7) | type Interval = "preserveStartEnd" | "equidistantPreserveStart";
type IntervalType (line 9) | type IntervalType = "preserveStartEnd" | Interval;
type IconVariant (line 14) | type IconVariant = (typeof iconVariantValues)[number];
type HorizontalPosition (line 16) | type HorizontalPosition = "left" | "right";
type VerticalPosition (line 18) | type VerticalPosition = "top" | "bottom";
type ButtonVariant (line 20) | type ButtonVariant = "primary" | "secondary" | "light";
type DeltaType (line 31) | type DeltaType = (typeof deltaTypeValues)[number];
type Size (line 36) | type Size = (typeof sizeValues)[number];
type Color (line 63) | type Color = (typeof colorValues)[number];
type CustomColor (line 64) | type CustomColor = Color | string;
type JustifyContent (line 69) | type JustifyContent = (typeof justifyContentValues)[number];
type AlignItems (line 73) | type AlignItems = (typeof alignItemsValues)[number];
type FlexDirection (line 75) | type FlexDirection = "row" | "col" | "row-reverse" | "col-reverse";
type FunnelVariantType (line 77) | type FunnelVariantType = "base" | "center";
FILE: src/lib/theme.ts
constant DEFAULT_COLOR (line 4) | const DEFAULT_COLOR: Color = "gray";
constant WHITE (line 5) | const WHITE = "white";
constant TRANSPARENT (line 6) | const TRANSPARENT = "transparent";
FILE: src/lib/utils.tsx
function mergeRefs (line 38) | function mergeRefs<T = any>(
function makeClassName (line 52) | function makeClassName(componentName: string) {
type ColorClassNames (line 58) | interface ColorClassNames {
function getColorClassNames (line 81) | function getColorClassNames(color: Color | string, shade?: number): Colo...
FILE: src/stories/chart-elements/AreaChart.stories.tsx
type Story (line 25) | type Story = StoryObj<typeof AreaChart>;
FILE: src/stories/chart-elements/BarChart.stories.tsx
type Story (line 23) | type Story = StoryObj<typeof BarChart>;
FILE: src/stories/chart-elements/DonutChart.stories.tsx
type Story (line 21) | type Story = StoryObj<typeof DonutChart>;
FILE: src/stories/chart-elements/FunnelChart.stories.tsx
type Story (line 26) | type Story = StoryObj<typeof FunnelChart>;
FILE: src/stories/chart-elements/LineChart.stories.tsx
type Story (line 25) | type Story = StoryObj<typeof LineChart>;
FILE: src/stories/chart-elements/ScatterChart.stories.tsx
type Story (line 20) | type Story = StoryObj<typeof ScatterChart>;
FILE: src/stories/icon-elements/Badge.stories.tsx
type Story (line 21) | type Story = StoryObj<typeof Badge>;
FILE: src/stories/icon-elements/BadgeDelta.stories.tsx
type Story (line 19) | type Story = StoryObj<typeof BadgeDelta>;
FILE: src/stories/icon-elements/Icon.stories.tsx
type Story (line 22) | type Story = StoryObj<typeof Icon>;
FILE: src/stories/input-elements/Button.stories.tsx
type Story (line 14) | type Story = StoryObj<typeof Button>;
function LoadingState (line 116) | function LoadingState({ ...args }) {
FILE: src/stories/input-elements/DatePicker.stories.tsx
type Story (line 14) | type Story = StoryObj<typeof DatePicker>;
function Uncontrolled (line 17) | function Uncontrolled({ ...args }) {
function Controlled (line 31) | function Controlled({ ...args }) {
FILE: src/stories/input-elements/DateRangePicker.stories.tsx
type Story (line 16) | type Story = StoryObj<typeof DateRangePicker>;
function Uncontrolled (line 19) | function Uncontrolled({ ...args }) {
function UncontrolledChildren (line 46) | function UncontrolledChildren({ ...args }) {
function Controlled (line 85) | function Controlled({ ...args }) {
FILE: src/stories/input-elements/MultiSelect.stories.tsx
type Story (line 19) | type Story = StoryObj<typeof MultiSelect>;
FILE: src/stories/input-elements/NumberInput.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof NumberInput>;
FILE: src/stories/input-elements/SearchSelect.stories.tsx
type Story (line 20) | type Story = StoryObj<typeof SearchSelect>;
FILE: src/stories/input-elements/Select.stories.tsx
type Story (line 19) | type Story = StoryObj<typeof Select>;
FILE: src/stories/input-elements/Switch.stories.tsx
type Story (line 11) | type Story = StoryObj<typeof Switch>;
FILE: src/stories/input-elements/Tabs.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof TabGroup>;
type MyTabProps (line 17) | interface MyTabProps {
function MyTab (line 26) | function MyTab(props: MyTabProps) {
function WithControlledStateTemplate (line 49) | function WithControlledStateTemplate({ ...args }) {
function TabSet (line 69) | function TabSet({ showText = true, ...args }) {
FILE: src/stories/input-elements/TextArea.stories.tsx
type Story (line 11) | type Story = StoryObj<typeof Textarea>;
function Controlled (line 13) | function Controlled({ ...args }) {
FILE: src/stories/input-elements/TextInput.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof TextInput>;
FILE: src/stories/input-elements/helpers/SimpleSearchSelect.tsx
function SimpleSearchSelectControlled (line 53) | function SimpleSearchSelectControlled() {
function SimpleSearchSelectServerSideRendering (line 101) | function SimpleSearchSelectServerSideRendering() {
FILE: src/stories/input-elements/helpers/SimpleSelect.tsx
function SimpleSelectControlled (line 32) | function SimpleSelectControlled() {
FILE: src/stories/layout-elements/Accordion.stories.tsx
type Story (line 12) | type Story = StoryObj<typeof Accordion>;
FILE: src/stories/layout-elements/AccordionList.stories.tsx
type Story (line 14) | type Story = StoryObj<typeof AccordionList>;
FILE: src/stories/layout-elements/Card.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof Card>;
FILE: src/stories/layout-elements/Dialog.stories.tsx
type Story (line 35) | type Story = StoryObj<typeof Dialog>;
FILE: src/stories/layout-elements/Divider.stories.tsx
type Story (line 14) | type Story = StoryObj<typeof Divider>;
FILE: src/stories/layout-elements/Flex.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof Flex>;
FILE: src/stories/layout-elements/Grid.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof Grid>;
FILE: src/stories/list-elements/List.stories.tsx
type Story (line 12) | type Story = StoryObj<typeof List>;
FILE: src/stories/list-elements/Table.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof Table>;
FILE: src/stories/spark-elements/SparkAreaChart.stories.tsx
type Story (line 27) | type Story = StoryObj<typeof SparkAreaChart>;
FILE: src/stories/spark-elements/SparkBarChart.stories.tsx
type Story (line 27) | type Story = StoryObj<typeof SparkBarChart>;
FILE: src/stories/spark-elements/SparkLineChart.stories.tsx
type Story (line 27) | type Story = StoryObj<typeof SparkLineChart>;
FILE: src/stories/text-elements/Callout.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof Callout>;
FILE: src/stories/text-elements/Legend.stories.tsx
type Story (line 20) | type Story = StoryObj<typeof Legend>;
function LegendComponent (line 22) | function LegendComponent(args: any) {
FILE: src/stories/text-elements/Metric.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof Metric>;
FILE: src/stories/text-elements/Subtitle.stories.tsx
type Story (line 11) | type Story = StoryObj<typeof Subtitle>;
FILE: src/stories/text-elements/Text.stories.tsx
type Story (line 15) | type Story = StoryObj<typeof Text>;
FILE: src/stories/text-elements/TextElements.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof Title>;
FILE: src/stories/text-elements/Title.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof Title>;
FILE: src/stories/vis-elements/BarList.stories.tsx
type Story (line 16) | type Story = StoryObj<typeof BarList>;
FILE: src/stories/vis-elements/CategoryBar.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof CategoryBar>;
FILE: src/stories/vis-elements/DeltaBar.stories.tsx
type Story (line 13) | type Story = StoryObj<typeof DeltaBar>;
FILE: src/stories/vis-elements/MarkerBar.stories.tsx
type Story (line 14) | type Story = StoryObj<typeof MarkerBar>;
FILE: src/stories/vis-elements/ProgressBar.stories.tsx
type Story (line 16) | type Story = StoryObj<typeof ProgressBar>;
FILE: src/stories/vis-elements/ProgressCircle.stories.tsx
type Story (line 14) | type Story = StoryObj<typeof ProgressCircle>;
function formatNumber (line 16) | function formatNumber(num: number) {
FILE: src/stories/vis-elements/Tracker.stories.tsx
type Story (line 110) | type Story = StoryObj<typeof Tracker>;
Condensed preview — 317 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (669K chars).
[
{
"path": ".eslintignore",
"chars": 23,
"preview": "node_modules\ndist\ntypes"
},
{
"path": ".eslintrc",
"chars": 934,
"preview": "{\n \"root\": true,\n \"extends\": [\n \"prettier\",\n \"plugin:prettier/recommended\",\n \"eslint:recommended\",\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report.yaml",
"chars": 2706,
"preview": "name: \"\\U0001F41E Bug report\"\ndescription: Create a report to help us improve\ntitle: \"[Bug]: \"\nlabels: [\"Triage\"]\nbody:\n"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yaml",
"chars": 224,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: Slack Community\n url: https://join.slack.com/t/tremor-community/s"
},
{
"path": ".github/ISSUE_TEMPLATE/feature-request.yaml",
"chars": 1674,
"preview": "name: \"\\U0001F680 New feature proposal\"\ndescription: Suggest an idea for this project\ntitle: \"[Feature]: \"\nlabels: [\"Tri"
},
{
"path": ".github/pull_request_template.md",
"chars": 2410,
"preview": "<!--\nPlease make sure to read the Contribution Guidelines:\nhttps://github.com/tremorlabs/tremor-npm/blob/main/CONTRIBUTI"
},
{
"path": ".github/workflows/build.yaml",
"chars": 685,
"preview": "name: \"Build\"\n\non:\n pull_request:\n types:\n - opened\n - edited\n - synchronize\n push:\n branches:\n "
},
{
"path": ".github/workflows/lint.yaml",
"chars": 410,
"preview": "name: \"Lint PR\"\n\non:\n pull_request:\n types:\n - opened\n - edited\n - synchronize\n pull_request_target:"
},
{
"path": ".github/workflows/release.yaml",
"chars": 694,
"preview": "name: \"Release\"\n\non:\n push:\n branches:\n - main\n - beta\n - beta-**\n\njobs:\n release:\n name: Release"
},
{
"path": ".gitignore",
"chars": 80,
"preview": "node_modules\ndist\n.DS_Store\nstorybook-static\npackage-lock.json\n.vscode\nyarn.lock"
},
{
"path": ".prettierrc.json",
"chars": 183,
"preview": "{\n \"bracketSpacing\": true,\n \"singleQuote\": false,\n \"trailingComma\": \"all\",\n \"tabWidth\": 2,\n \"semi\": true,\n \"printW"
},
{
"path": ".storybook/main.js",
"chars": 1416,
"preview": "var path = require(\"path\");\n\nmodule.exports = {\n stories: [\"../src/**/*.stories.@(js|jsx|ts|tsx)\"],\n\n addons: [\n \"@"
},
{
"path": ".storybook/manager.js",
"chars": 181,
"preview": "import { addons } from \"@storybook/manager-api\";\nimport { themes } from \"@storybook/theming\";\nimport tremorTheme from \"."
},
{
"path": ".storybook/preview.js",
"chars": 647,
"preview": "import \"../src/styles.css\";\nimport { withThemeByDataAttribute } from \"@storybook/addon-themes\";\n\nexport const parameters"
},
{
"path": ".storybook/tremorTheme.js",
"chars": 473,
"preview": "import { create } from \"@storybook/theming/create\";\n\nexport default create({\n base: \"light\",\n brandTitle: \"Tremor Stor"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5201,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 4176,
"preview": "## **Contributing to Tremor**\n\nThanks for your interest in contributing to Tremor. Please take a moment to review this d"
},
{
"path": "License",
"chars": 10173,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 3963,
"preview": "<br />\n<br />\n<p align=\"center\">\n <a href=\"https://npm.tremor.so\">\n <picture>\n <source media=\"(prefers-color-s"
},
{
"path": "babel.config.js",
"chars": 137,
"preview": "/* eslint-disable no-undef */\nmodule.exports = {\n presets: [\"@babel/preset-env\", \"@babel/preset-react\", \"@babel/preset-"
},
{
"path": "jest.config.js",
"chars": 389,
"preview": "/* eslint-disable no-undef */\nmodule.exports = {\n testEnvironment: \"jsdom\",\n moduleDirectories: [\"node_modules\", \"src\""
},
{
"path": "package.json",
"chars": 4530,
"preview": "{\n \"name\": \"@tremor/react\",\n \"version\": \"0.0.0-development\",\n \"description\": \"The React library to build dashboards f"
},
{
"path": "postcss.config.js",
"chars": 113,
"preview": "/* eslint-disable no-undef */\nmodule.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};\n"
},
{
"path": "rollup.config.js",
"chars": 1398,
"preview": "/* eslint-disable @typescript-eslint/no-var-requires */\n/* eslint-disable no-undef */\nimport commonjs from \"@rollup/plug"
},
{
"path": "setupTests.js",
"chars": 150,
"preview": "/* eslint-disable @typescript-eslint/no-require-imports */\n/* eslint-disable no-undef */\nglobal.ResizeObserver = require"
},
{
"path": "src/assets/ArrowDownHeadIcon.tsx",
"chars": 350,
"preview": "import React from \"react\";\n\nconst ArrowDownHeadIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" view"
},
{
"path": "src/assets/ArrowDownIcon.tsx",
"chars": 356,
"preview": "import React from \"react\";\n\nconst ArrowDownIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox="
},
{
"path": "src/assets/ArrowDownRightIcon.tsx",
"chars": 352,
"preview": "import React from \"react\";\n\nconst ArrowDownRightIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" vie"
},
{
"path": "src/assets/ArrowLeftHeadIcon.tsx",
"chars": 343,
"preview": "import React from \"react\";\n\nconst ArrowLeftHeadIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" view"
},
{
"path": "src/assets/ArrowRightHeadIcon.tsx",
"chars": 353,
"preview": "import React from \"react\";\n\nconst ArrowRightHeadIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" vie"
},
{
"path": "src/assets/ArrowRightIcon.tsx",
"chars": 357,
"preview": "import React from \"react\";\n\nconst ArrowRightIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox"
},
{
"path": "src/assets/ArrowUpHeadIcon.tsx",
"chars": 338,
"preview": "import React from \"react\";\n\nconst ArrowUpHeadIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBo"
},
{
"path": "src/assets/ArrowUpIcon.tsx",
"chars": 352,
"preview": "import React from \"react\";\n\nconst ArrowUpIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0"
},
{
"path": "src/assets/ArrowUpRightIcon.tsx",
"chars": 332,
"preview": "import React from \"react\";\n\nconst ArrowUpRightIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewB"
},
{
"path": "src/assets/CalendarIcon.tsx",
"chars": 433,
"preview": "import React from \"react\";\n\nconst CalendarIcon = ({ ...props }) => (\n <svg {...props} xmlns=\"http://www.w3.org/2000/svg"
},
{
"path": "src/assets/ChevronLeftFill.tsx",
"chars": 253,
"preview": "import React from \"react\";\n\nconst ChevronLeftFill = ({ ...props }) => (\n <svg {...props} xmlns=\"http://www.w3.org/2000/"
},
{
"path": "src/assets/ChevronRightFill.tsx",
"chars": 257,
"preview": "import React from \"react\";\n\nconst ChevronRightFill = ({ ...props }) => (\n <svg {...props} xmlns=\"http://www.w3.org/2000"
},
{
"path": "src/assets/DoubleArrowLeftHeadIcon.tsx",
"chars": 444,
"preview": "import React from \"react\";\n\nconst DoubleArrowLeftHeadIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg"
},
{
"path": "src/assets/DoubleArrowRightHeadIcon.tsx",
"chars": 448,
"preview": "import React from \"react\";\n\nconst DoubleArrowRightHeadIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/sv"
},
{
"path": "src/assets/ExclamationFilledIcon.tsx",
"chars": 395,
"preview": "import React from \"react\";\n\nconst ExclamationFilledIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" "
},
{
"path": "src/assets/EyeIcon.tsx",
"chars": 701,
"preview": "import React from \"react\";\n\nconst EyeIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 2"
},
{
"path": "src/assets/EyeOffIcon.tsx",
"chars": 1133,
"preview": "import React from \"react\";\n\nconst EyeOffIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 "
},
{
"path": "src/assets/LoadingSpinner.tsx",
"chars": 335,
"preview": "import React from \"react\";\n\nconst LoadingSpinner = ({ ...props }) => (\n <svg {...props} xmlns=\"http://www.w3.org/2000/s"
},
{
"path": "src/assets/MinusIcon.tsx",
"chars": 284,
"preview": "import React from \"react\";\n\nconst MinusIcon = ({ ...props }) => (\n <svg\n {...props}\n xmlns=\"http://www.w3.org/200"
},
{
"path": "src/assets/PlusIcon.tsx",
"chars": 288,
"preview": "import React from \"react\";\n\nconst PlusIcon = ({ ...props }) => (\n <svg\n {...props}\n xmlns=\"http://www.w3.org/2000"
},
{
"path": "src/assets/SearchIcon.tsx",
"chars": 609,
"preview": "import React from \"react\";\n\nconst SearchIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 "
},
{
"path": "src/assets/XCircleIcon.tsx",
"chars": 523,
"preview": "import React from \"react\";\n\nconst XCircleIcon = ({ ...props }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0"
},
{
"path": "src/assets/XIcon.tsx",
"chars": 478,
"preview": "import React from \"react\";\n\nconst XIcon = ({ ...props }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/sv"
},
{
"path": "src/assets/index.ts",
"chars": 1386,
"preview": "export { default as ArrowDownHeadIcon } from \"./ArrowDownHeadIcon\";\nexport { default as ArrowDownIcon } from \"./ArrowDow"
},
{
"path": "src/components/chart-elements/AreaChart/AreaChart.tsx",
"chars": 16082,
"preview": "\"use client\";\nimport React, { Fragment, useState } from \"react\";\nimport {\n Area,\n AreaChart as ReChartsAreaChart,\n Ca"
},
{
"path": "src/components/chart-elements/AreaChart/index.ts",
"chars": 103,
"preview": "export { default as AreaChart } from \"./AreaChart\";\nexport type { AreaChartProps } from \"./AreaChart\";\n"
},
{
"path": "src/components/chart-elements/BarChart/BarChart.tsx",
"chars": 13535,
"preview": "\"use client\";\nimport { colorPalette, getColorClassNames, tremorTwMerge } from \"lib\";\nimport React, { useState } from \"re"
},
{
"path": "src/components/chart-elements/BarChart/index.ts",
"chars": 99,
"preview": "export { default as BarChart } from \"./BarChart\";\nexport type { BarChartProps } from \"./BarChart\";\n"
},
{
"path": "src/components/chart-elements/DonutChart/DonutChart.tsx",
"chars": 6722,
"preview": "\"use client\";\nimport { BaseColors, defaultValueFormatter, themeColorRange, tremorTwMerge } from \"lib\";\nimport React, { u"
},
{
"path": "src/components/chart-elements/DonutChart/DonutChartTooltip.tsx",
"chars": 813,
"preview": "import React from \"react\";\nimport { tremorTwMerge, ValueFormatter } from \"lib\";\n\nimport { ChartTooltipFrame, ChartToolti"
},
{
"path": "src/components/chart-elements/DonutChart/index.ts",
"chars": 107,
"preview": "export { default as DonutChart } from \"./DonutChart\";\nexport type { DonutChartProps } from \"./DonutChart\";\n"
},
{
"path": "src/components/chart-elements/DonutChart/inputParser.ts",
"chars": 968,
"preview": "import { BaseColors, colorPalette, getColorClassNames, sumNumericArray } from \"lib\";\nimport { Color, ValueFormatter } fr"
},
{
"path": "src/components/chart-elements/FunnelChart/FunnelChart.tsx",
"chars": 29178,
"preview": "import React from \"react\";\nimport { ChartTooltipFrame, ChartTooltipRow } from \"../common/ChartTooltip\";\nimport {\n BaseC"
},
{
"path": "src/components/chart-elements/FunnelChart/index.ts",
"chars": 111,
"preview": "export { default as FunnelChart } from \"./FunnelChart\";\nexport type { FunnelChartProps } from \"./FunnelChart\";\n"
},
{
"path": "src/components/chart-elements/LineChart/LineChart.tsx",
"chars": 13543,
"preview": "\"use client\";\nimport React, { Fragment, useState } from \"react\";\nimport {\n CartesianGrid,\n Dot,\n Legend,\n Line,\n Li"
},
{
"path": "src/components/chart-elements/LineChart/index.ts",
"chars": 103,
"preview": "export { default as LineChart } from \"./LineChart\";\nexport type { LineChartProps } from \"./LineChart\";\n"
},
{
"path": "src/components/chart-elements/ScatterChart/ScatterChart.tsx",
"chars": 12569,
"preview": "\"use client\";\nimport React, { useState } from \"react\";\nimport {\n CartesianGrid,\n Dot,\n Legend,\n ResponsiveContainer,"
},
{
"path": "src/components/chart-elements/ScatterChart/ScatterChartTooltip.tsx",
"chars": 3985,
"preview": "import React from \"react\";\n\nimport { ScatterChartValueFormatter } from \"components/chart-elements/ScatterChart/ScatterCh"
},
{
"path": "src/components/chart-elements/ScatterChart/index.tsx",
"chars": 115,
"preview": "export { default as ScatterChart } from \"./ScatterChart\";\nexport type { ScatterChartProps } from \"./ScatterChart\";\n"
},
{
"path": "src/components/chart-elements/common/BaseAnimationTimingProps.tsx",
"chars": 138,
"preview": "interface BaseAnimationTimingProps {\n animationDuration?: number;\n showAnimation?: boolean;\n}\n\nexport default BaseAnim"
},
{
"path": "src/components/chart-elements/common/BaseChartProps.tsx",
"chars": 1371,
"preview": "import { Color, ValueFormatter, IntervalType } from \"../../../lib\";\nimport type BaseAnimationTimingProps from \"./BaseAni"
},
{
"path": "src/components/chart-elements/common/ChartLegend.tsx",
"chars": 1293,
"preview": "import React, { useRef } from \"react\";\n\nimport { useOnWindowResize } from \"hooks\";\n\nimport { Legend } from \"components/t"
},
{
"path": "src/components/chart-elements/common/ChartTooltip.tsx",
"chars": 3327,
"preview": "import React from \"react\";\nimport {\n BaseColors,\n getColorClassNames,\n tremorTwMerge,\n Color,\n ValueFormatter,\n co"
},
{
"path": "src/components/chart-elements/common/CustomTooltipProps.tsx",
"chars": 275,
"preview": "import { NameType, Payload } from \"recharts/types/component/DefaultTooltipContent\";\n\nexport type CustomTooltipProps = {\n"
},
{
"path": "src/components/chart-elements/common/NoData.tsx",
"chars": 792,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\ninterface NoDataProps {\n noDataText?: string;\n classN"
},
{
"path": "src/components/chart-elements/common/index.ts",
"chars": 116,
"preview": "export type { EventProps } from \"./BaseChartProps\";\nexport type { CustomTooltipProps } from \"./CustomTooltipProps\";\n"
},
{
"path": "src/components/chart-elements/common/utils.ts",
"chars": 2376,
"preview": "import { Color } from \"../../../lib/inputTypes\";\n\nexport const constructCategoryColors = (\n categories: string[],\n col"
},
{
"path": "src/components/chart-elements/index.ts",
"chars": 299,
"preview": "export * from \"./AreaChart\";\nexport * from \"./BarChart\";\nexport { EventProps } from \"./common/BaseChartProps\";\nexport { "
},
{
"path": "src/components/icon-elements/Badge/Badge.tsx",
"chars": 2491,
"preview": "\"use client\";\nimport React from \"react\";\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\";"
},
{
"path": "src/components/icon-elements/Badge/index.ts",
"chars": 87,
"preview": "export { default as Badge } from \"./Badge\";\nexport type { BadgeProps } from \"./Badge\";\n"
},
{
"path": "src/components/icon-elements/Badge/styles.ts",
"chars": 952,
"preview": "export type BadgeProportionTypes = {\n paddingX: string;\n paddingY: string;\n fontSize: string;\n};\n\nexport const badgeP"
},
{
"path": "src/components/icon-elements/BadgeDelta/BadgeDelta.tsx",
"chars": 2440,
"preview": "\"use client\";\nimport React from \"react\";\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\";"
},
{
"path": "src/components/icon-elements/BadgeDelta/index.ts",
"chars": 107,
"preview": "export { default as BadgeDelta } from \"./BadgeDelta\";\nexport type { BadgeDeltaProps } from \"./BadgeDelta\";\n"
},
{
"path": "src/components/icon-elements/BadgeDelta/styles.ts",
"chars": 3583,
"preview": "import { BaseColors, DeltaTypes, getColorClassNames, colorPalette } from \"lib\";\n\nimport {\n ArrowDownIcon,\n ArrowDownRi"
},
{
"path": "src/components/icon-elements/Icon/Icon.tsx",
"chars": 2006,
"preview": "\"use client\";\nimport React from \"react\";\n\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\""
},
{
"path": "src/components/icon-elements/Icon/index.ts",
"chars": 83,
"preview": "export { default as Icon } from \"./Icon\";\nexport type { IconProps } from \"./Icon\";\n"
},
{
"path": "src/components/icon-elements/Icon/styles.ts",
"chars": 4417,
"preview": "import { getColorClassNames, tremorTwMerge, colorPalette, Color, IconVariant } from \"lib\";\n\nexport type WrapperProportio"
},
{
"path": "src/components/icon-elements/index.ts",
"chars": 79,
"preview": "export * from \"./Badge\";\nexport * from \"./BadgeDelta\";\nexport * from \"./Icon\";\n"
},
{
"path": "src/components/index.ts",
"chars": 268,
"preview": "export * from \"./chart-elements\";\nexport * from \"./icon-elements\";\nexport * from \"./input-elements\";\nexport * from \"./la"
},
{
"path": "src/components/input-elements/BaseInput.tsx",
"chars": 6828,
"preview": "\"use client\";\nimport React, { ReactNode, useCallback, useRef, useState } from \"react\";\nimport { ExclamationFilledIcon, E"
},
{
"path": "src/components/input-elements/Button/Button.tsx",
"chars": 5492,
"preview": "\"use client\";\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\";\nimport React, { useEffect "
},
{
"path": "src/components/input-elements/Button/index.ts",
"chars": 91,
"preview": "export { default as Button } from \"./Button\";\nexport type { ButtonProps } from \"./Button\";\n"
},
{
"path": "src/components/input-elements/Button/styles.ts",
"chars": 4107,
"preview": "import { getColorClassNames, tremorTwMerge, ButtonVariant, Color, colorPalette } from \"lib\";\n\nexport const iconSizes: {\n"
},
{
"path": "src/components/input-elements/Calendar/Calendar.tsx",
"chars": 4837,
"preview": "\"use client\";\nimport React from \"react\";\nimport {\n DayPicker,\n DayPickerRangeProps,\n DayPickerSingleProps,\n useNavig"
},
{
"path": "src/components/input-elements/Calendar/NavButton.tsx",
"chars": 1126,
"preview": "import { Icon as IconComponent } from \"components/icon-elements\";\nimport { tremorTwMerge } from \"lib\";\nimport React from"
},
{
"path": "src/components/input-elements/Calendar/index.ts",
"chars": 50,
"preview": "export { default as Calendar } from \"./Calendar\";\n"
},
{
"path": "src/components/input-elements/DatePicker/DatePicker.tsx",
"chars": 6367,
"preview": "\"use client\";\nimport { tremorTwMerge } from \"lib\";\nimport React, { useMemo } from \"react\";\nimport { DayPickerSingleProps"
},
{
"path": "src/components/input-elements/DatePicker/datePickerUtils.tsx",
"chars": 106,
"preview": "import { makeClassName } from \"lib\";\n\nexport const makeDatePickerClassName = makeClassName(\"DatePicker\");\n"
},
{
"path": "src/components/input-elements/DatePicker/index.ts",
"chars": 124,
"preview": "export { default as DatePicker } from \"./DatePicker\";\nexport type { DatePickerProps, DatePickerValue } from \"./DatePicke"
},
{
"path": "src/components/input-elements/DateRangePicker/DateRangePicker.tsx",
"chars": 13184,
"preview": "\"use client\";\n\nimport { CalendarIcon, XCircleIcon } from \"assets\";\nimport { startOfMonth, startOfToday } from \"date-fns\""
},
{
"path": "src/components/input-elements/DateRangePicker/DateRangePickerItem.tsx",
"chars": 635,
"preview": "\"use client\";\nimport React from \"react\";\n\nimport { SelectItem } from \"../Select\";\n\nexport interface DateRangePickerItemP"
},
{
"path": "src/components/input-elements/DateRangePicker/dateRangePickerUtils.tsx",
"chars": 3651,
"preview": "import {\n format,\n isEqual,\n max,\n min,\n startOfDay,\n startOfMonth,\n startOfToday,\n startOfYear,\n sub,\n Locale"
},
{
"path": "src/components/input-elements/DateRangePicker/index.ts",
"chars": 292,
"preview": "export { default as DateRangePicker } from \"./DateRangePicker\";\nexport type { DateRangePickerProps, DateRangePickerValue"
},
{
"path": "src/components/input-elements/MultiSelect/MultiSelect.tsx",
"chars": 13342,
"preview": "\"use client\";\nimport React, { isValidElement, useMemo, useRef, useState } from \"react\";\nimport { SelectedValueContext } "
},
{
"path": "src/components/input-elements/MultiSelect/MultiSelectItem.tsx",
"chars": 2257,
"preview": "\"use client\";\nimport { SelectedValueContext } from \"contexts\";\nimport React, { useContext } from \"react\";\nimport { isVal"
},
{
"path": "src/components/input-elements/MultiSelect/index.ts",
"chars": 238,
"preview": "export { default as MultiSelect } from \"./MultiSelect\";\nexport type { MultiSelectProps } from \"./MultiSelect\";\nexport { "
},
{
"path": "src/components/input-elements/NumberInput/NumberInput.tsx",
"chars": 4697,
"preview": "\"use client\";\nimport { MinusIcon, PlusIcon } from \"assets\";\nimport { makeClassName, mergeRefs, tremorTwMerge } from \"lib"
},
{
"path": "src/components/input-elements/NumberInput/index.ts",
"chars": 111,
"preview": "export { default as NumberInput } from \"./NumberInput\";\nexport type { NumberInputProps } from \"./NumberInput\";\n"
},
{
"path": "src/components/input-elements/SearchSelect/SearchSelect.tsx",
"chars": 9210,
"preview": "\"use client\";\nimport { useInternalState } from \"hooks\";\nimport React, { isValidElement, useMemo, useRef } from \"react\";\n"
},
{
"path": "src/components/input-elements/SearchSelect/SearchSelectItem.tsx",
"chars": 1896,
"preview": "\"use client\";\nimport React from \"react\";\n\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nimport { ComboboxOption }"
},
{
"path": "src/components/input-elements/SearchSelect/index.ts",
"chars": 246,
"preview": "export { default as SearchSelect } from \"./SearchSelect\";\nexport type { SearchSelectProps } from \"./SearchSelect\";\nexpor"
},
{
"path": "src/components/input-elements/Select/Select.tsx",
"chars": 7910,
"preview": "\"use client\";\n\nimport { ArrowDownHeadIcon, XCircleIcon } from \"assets\";\nimport { makeClassName, tremorTwMerge } from \"li"
},
{
"path": "src/components/input-elements/Select/SelectItem.tsx",
"chars": 1842,
"preview": "\"use client\";\nimport React from \"react\";\nimport { ListboxOption } from \"@headlessui/react\";\nimport { makeClassName, trem"
},
{
"path": "src/components/input-elements/Select/index.ts",
"chars": 198,
"preview": "export { default as Select } from \"./Select\";\nexport type { SelectProps } from \"./Select\";\nexport { default as SelectIte"
},
{
"path": "src/components/input-elements/Switch/Switch.tsx",
"chars": 4642,
"preview": "\"use client\";\nimport { Switch as HeadlessSwitch } from \"@headlessui/react\";\nimport { useInternalState } from \"hooks\";\nim"
},
{
"path": "src/components/input-elements/Switch/index.ts",
"chars": 91,
"preview": "export { default as Switch } from \"./Switch\";\nexport type { SwitchProps } from \"./Switch\";\n"
},
{
"path": "src/components/input-elements/Tabs/Tab.tsx",
"chars": 3441,
"preview": "\"use client\";\nimport { Tab as HeadlessTab } from \"@headlessui/react\";\nimport { colorPalette, getColorClassNames, tremorT"
},
{
"path": "src/components/input-elements/Tabs/TabGroup.tsx",
"chars": 954,
"preview": "\"use client\";\nimport { Tab } from \"@headlessui/react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\nimport React "
},
{
"path": "src/components/input-elements/Tabs/TabList.tsx",
"chars": 1638,
"preview": "\"use client\";\nimport React, { createContext } from \"react\";\n\nimport { BaseColorContext } from \"contexts\";\n\nimport { Tab "
},
{
"path": "src/components/input-elements/Tabs/TabPanel.tsx",
"chars": 1070,
"preview": "\"use client\";\nimport { IndexContext, SelectedValueContext } from \"contexts\";\nimport { makeClassName, tremorTwMerge } fro"
},
{
"path": "src/components/input-elements/Tabs/TabPanels.tsx",
"chars": 1034,
"preview": "\"use client\";\nimport { Tab } from \"@headlessui/react\";\nimport { IndexContext, SelectedValueContext } from \"contexts\";\nim"
},
{
"path": "src/components/input-elements/Tabs/index.ts",
"chars": 375,
"preview": "export { default as Tab } from \"./Tab\";\nexport type { TabProps } from \"./Tab\";\nexport { default as TabGroup } from \"./Ta"
},
{
"path": "src/components/input-elements/TextInput/TextInput.tsx",
"chars": 806,
"preview": "\"use client\";\nimport React from \"react\";\nimport { makeClassName } from \"lib\";\nimport BaseInput, { BaseInputProps } from "
},
{
"path": "src/components/input-elements/TextInput/index.ts",
"chars": 103,
"preview": "export { default as TextInput } from \"./TextInput\";\nexport type { TextInputProps } from \"./TextInput\";\n"
},
{
"path": "src/components/input-elements/Textarea/Textarea.tsx",
"chars": 2932,
"preview": "\"use client\";\n\nimport { getSelectButtonColors, hasValue } from \"components/input-elements/selectUtils\";\nimport { useInte"
},
{
"path": "src/components/input-elements/Textarea/index.ts",
"chars": 99,
"preview": "export { default as Textarea } from \"./Textarea\";\nexport type { TextareaProps } from \"./Textarea\";\n"
},
{
"path": "src/components/input-elements/index.ts",
"chars": 318,
"preview": "export * from \"./Button\";\nexport * from \"./DatePicker\";\nexport * from \"./DateRangePicker\";\nexport * from \"./MultiSelect\""
},
{
"path": "src/components/input-elements/selectUtils.ts",
"chars": 2176,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nexport interface SelectItemProps {\n value: string;\n c"
},
{
"path": "src/components/layout-elements/Accordion/Accordion.tsx",
"chars": 1433,
"preview": "\"use client\";\nimport { Disclosure } from \"@headlessui/react\";\nimport { RootStylesContext } from \"contexts\";\nimport { mak"
},
{
"path": "src/components/layout-elements/Accordion/AccordionBody.tsx",
"chars": 891,
"preview": "\"use client\";\nimport React from \"react\";\nimport { Disclosure } from \"@headlessui/react\";\nimport { makeClassName, tremorT"
},
{
"path": "src/components/layout-elements/Accordion/AccordionHeader.tsx",
"chars": 1691,
"preview": "\"use client\";\nimport React, { useContext } from \"react\";\n\nimport { Disclosure } from \"@headlessui/react\";\nimport { Arrow"
},
{
"path": "src/components/layout-elements/Accordion/AccordionList.tsx",
"chars": 1787,
"preview": "\"use client\";\n\nimport React from \"react\";\n\nimport { RootStylesContext } from \"contexts\";\nimport { makeClassName, tremorT"
},
{
"path": "src/components/layout-elements/Accordion/index.ts",
"chars": 346,
"preview": "export { default as Accordion } from \"./Accordion\";\nexport type { AccordionProps } from \"./Accordion\";\nexport { default "
},
{
"path": "src/components/layout-elements/Card/Card.tsx",
"chars": 1831,
"preview": "import React from \"react\";\nimport {\n getColorClassNames,\n makeClassName,\n tremorTwMerge,\n Color,\n HorizontalPositio"
},
{
"path": "src/components/layout-elements/Card/index.ts",
"chars": 83,
"preview": "export { default as Card } from \"./Card\";\nexport type { CardProps } from \"./Card\";\n"
},
{
"path": "src/components/layout-elements/Dialog/Dialog.tsx",
"chars": 1490,
"preview": "import React from \"react\";\nimport { Dialog as HeadlessUiDialog, DialogBackdrop, Transition } from \"@headlessui/react\";\ni"
},
{
"path": "src/components/layout-elements/Dialog/DialogPanel.tsx",
"chars": 1539,
"preview": "import React from \"react\";\nimport { DialogPanel as HeadlessuiDialogPanel, TransitionChild } from \"@headlessui/react\";\nim"
},
{
"path": "src/components/layout-elements/Dialog/index.ts",
"chars": 143,
"preview": "export { default as Dialog, type DialogProps } from \"./Dialog\";\nexport { default as DialogPanel, type DialogPanelProps }"
},
{
"path": "src/components/layout-elements/Divider/Divider.tsx",
"chars": 1415,
"preview": "import { makeClassName, tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nconst makeDividerClassName = makeClassNa"
},
{
"path": "src/components/layout-elements/Divider/index.ts",
"chars": 48,
"preview": "export { default as Divider } from \"./Divider\";\n"
},
{
"path": "src/components/layout-elements/Flex/Flex.tsx",
"chars": 1679,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { makeClassName } from \"lib\";\nimport { AlignItem"
},
{
"path": "src/components/layout-elements/Flex/index.ts",
"chars": 83,
"preview": "export { default as Flex } from \"./Flex\";\nexport type { FlexProps } from \"./Flex\";\n"
},
{
"path": "src/components/layout-elements/Grid/Col.tsx",
"chars": 1461,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { makeClassName } from \"lib\";\nimport { colSpan, "
},
{
"path": "src/components/layout-elements/Grid/Grid.tsx",
"chars": 1387,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { makeClassName } from \"lib\";\nimport { GridClass"
},
{
"path": "src/components/layout-elements/Grid/index.ts",
"chars": 162,
"preview": "export { default as Col } from \"./Col\";\nexport type { ColProps } from \"./Col\";\nexport { default as Grid } from \"./Grid\";"
},
{
"path": "src/components/layout-elements/Grid/styles.ts",
"chars": 2801,
"preview": "export type GridClassesMapping = {\n [key: string]: string;\n};\n\nexport const gridCols: GridClassesMapping = {\n 0: \"grid"
},
{
"path": "src/components/layout-elements/index.ts",
"chars": 154,
"preview": "export * from \"./Accordion\";\nexport * from \"./Card\";\nexport * from \"./Divider\";\nexport * from \"./Flex\";\nexport * from \"."
},
{
"path": "src/components/list-elements/List/List.tsx",
"chars": 796,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { makeClassName } from \"lib\";\n\nconst makeListCla"
},
{
"path": "src/components/list-elements/List/ListItem.tsx",
"chars": 723,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeListItemClassName = makeClassN"
},
{
"path": "src/components/list-elements/List/index.ts",
"chars": 92,
"preview": "export { default as List } from \"./List\";\nexport { default as ListItem } from \"./ListItem\";\n"
},
{
"path": "src/components/list-elements/Table/Table.tsx",
"chars": 868,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableClassName = makeClassName"
},
{
"path": "src/components/list-elements/Table/TableBody.tsx",
"chars": 789,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableBodyClassName = makeClass"
},
{
"path": "src/components/list-elements/Table/TableCell.tsx",
"chars": 670,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableCellClassName = makeClass"
},
{
"path": "src/components/list-elements/Table/TableFoot.tsx",
"chars": 811,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableFootClassName = makeClass"
},
{
"path": "src/components/list-elements/Table/TableFooterCell.tsx",
"chars": 828,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableFooterCellClassName = mak"
},
{
"path": "src/components/list-elements/Table/TableHead.tsx",
"chars": 778,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableHeadClassName = makeClass"
},
{
"path": "src/components/list-elements/Table/TableHeaderCell.tsx",
"chars": 874,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeTableHeaderCellClassName = mak"
},
{
"path": "src/components/list-elements/Table/TableRow.tsx",
"chars": 545,
"preview": "import React from \"react\";\nimport { makeClassName, tremorTwMerge } from \"lib\";\n\nconst makeRowClassName = makeClassName(\""
},
{
"path": "src/components/list-elements/Table/index.ts",
"chars": 430,
"preview": "export { default as Table } from \"./Table\";\nexport { default as TableBody } from \"./TableBody\";\nexport { default as Tabl"
},
{
"path": "src/components/list-elements/index.ts",
"chars": 49,
"preview": "export * from \"./List\";\nexport * from \"./Table\";\n"
},
{
"path": "src/components/spark-elements/SparkAreaChart/SparkAreaChart.tsx",
"chars": 4360,
"preview": "\"use client\";\nimport React from \"react\";\nimport { Area, AreaChart as ReChartsAreaChart, ResponsiveContainer, XAxis, YAxi"
},
{
"path": "src/components/spark-elements/SparkAreaChart/index.ts",
"chars": 123,
"preview": "export { default as SparkAreaChart } from \"./SparkAreaChart\";\nexport type { SparkAreaChartProps } from \"./SparkAreaChart"
},
{
"path": "src/components/spark-elements/SparkBarChart/SparkBarChart.tsx",
"chars": 2533,
"preview": "\"use client\";\nimport { colorPalette, getColorClassNames, tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport "
},
{
"path": "src/components/spark-elements/SparkBarChart/index.ts",
"chars": 119,
"preview": "export { default as SparkBarChart } from \"./SparkBarChart\";\nexport type { SparkBarChartProps } from \"./SparkBarChart\";\n"
},
{
"path": "src/components/spark-elements/SparkLineChart/SparkLineChart.tsx",
"chars": 2654,
"preview": "\"use client\";\nimport React from \"react\";\nimport { Line, LineChart as ReChartsLineChart, ResponsiveContainer, XAxis, YAxi"
},
{
"path": "src/components/spark-elements/SparkLineChart/index.ts",
"chars": 123,
"preview": "export { default as SparkLineChart } from \"./SparkLineChart\";\nexport type { SparkLineChartProps } from \"./SparkLineChart"
},
{
"path": "src/components/spark-elements/common/BaseSparkChartProps.tsx",
"chars": 712,
"preview": "import BaseAnimationTimingProps from \"components/chart-elements/common/BaseAnimationTimingProps\";\nimport { Color } from "
},
{
"path": "src/components/spark-elements/index.ts",
"chars": 101,
"preview": "export * from \"./SparkBarChart\";\nexport * from \"./SparkLineChart\";\nexport * from \"./SparkAreaChart\";\n"
},
{
"path": "src/components/text-elements/Bold/Bold.tsx",
"chars": 400,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nconst Bold = React.forwardRef<HTMLElement, React.HTMLAt"
},
{
"path": "src/components/text-elements/Bold/index.ts",
"chars": 42,
"preview": "export { default as Bold } from \"./Bold\";\n"
},
{
"path": "src/components/text-elements/Callout/Callout.tsx",
"chars": 2065,
"preview": "import React from \"react\";\nimport { getColorClassNames, makeClassName, tremorTwMerge, Color } from \"lib\";\nimport { color"
},
{
"path": "src/components/text-elements/Callout/index.ts",
"chars": 95,
"preview": "export { default as Callout } from \"./Callout\";\nexport type { CalloutProps } from \"./Callout\";\n"
},
{
"path": "src/components/text-elements/Italic/Italic.tsx",
"chars": 405,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nconst Italic = React.forwardRef<HTMLElement, React.HTML"
},
{
"path": "src/components/text-elements/Italic/index.ts",
"chars": 46,
"preview": "export { default as Italic } from \"./Italic\";\n"
},
{
"path": "src/components/text-elements/Legend/Legend.tsx",
"chars": 9332,
"preview": "import React, { useEffect, useCallback } from \"react\";\n\nimport { getColorClassNames, makeClassName, themeColorRange, Col"
},
{
"path": "src/components/text-elements/Legend/index.ts",
"chars": 91,
"preview": "export { default as Legend } from \"./Legend\";\nexport type { LegendProps } from \"./Legend\";\n"
},
{
"path": "src/components/text-elements/Metric/Metric.tsx",
"chars": 849,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { getColorClassNames } from \"lib\";\nimport { colo"
},
{
"path": "src/components/text-elements/Metric/index.ts",
"chars": 91,
"preview": "export { default as Metric } from \"./Metric\";\nexport type { MetricProps } from \"./Metric\";\n"
},
{
"path": "src/components/text-elements/Subtitle/Subtitle.tsx",
"chars": 822,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { getColorClassNames } from \"lib\";\nimport { colo"
},
{
"path": "src/components/text-elements/Subtitle/index.ts",
"chars": 99,
"preview": "export { default as Subtitle } from \"./Subtitle\";\nexport type { SubtitleProps } from \"./Subtitle\";\n"
},
{
"path": "src/components/text-elements/Text/Text.tsx",
"chars": 914,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { getColorClassNames } from \"lib\";\nimport { colo"
},
{
"path": "src/components/text-elements/Text/index.ts",
"chars": 83,
"preview": "export { default as Text } from \"./Text\";\nexport type { TextProps } from \"./Text\";\n"
},
{
"path": "src/components/text-elements/Title/Title.tsx",
"chars": 858,
"preview": "import { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport { getColorClassNames } from \"lib\";\nimport { colo"
},
{
"path": "src/components/text-elements/Title/index.ts",
"chars": 87,
"preview": "export { default as Title } from \"./Title\";\nexport type { TitleProps } from \"./Title\";\n"
},
{
"path": "src/components/text-elements/index.ts",
"chars": 206,
"preview": "export * from \"./Bold\";\nexport * from \"./Callout\";\nexport * from \"./Italic\";\nexport * from \"./Legend\";\nexport * from \"./"
},
{
"path": "src/components/util-elements/Tooltip/Tooltip.tsx",
"chars": 2378,
"preview": "import {\n autoUpdate,\n ExtendedRefs,\n flip,\n offset,\n ReferenceType,\n shift,\n Strategy,\n useDismiss,\n useFloati"
},
{
"path": "src/components/util-elements/Tooltip/index.ts",
"chars": 95,
"preview": "export { default as Tooltip } from \"./Tooltip\";\nexport type { TooltipProps } from \"./Tooltip\";\n"
},
{
"path": "src/components/util-elements/index.ts",
"chars": 27,
"preview": "export * from \"./Tooltip\";\n"
},
{
"path": "src/components/vis-elements/BarList/BarList.tsx",
"chars": 6963,
"preview": "\"use client\";\n\nimport React from \"react\";\nimport {\n Color,\n defaultValueFormatter,\n getColorClassNames,\n makeClassNa"
},
{
"path": "src/components/vis-elements/BarList/index.ts",
"chars": 95,
"preview": "export { default as BarList } from \"./BarList\";\nexport type { BarListProps } from \"./BarList\";\n"
},
{
"path": "src/components/vis-elements/CategoryBar/CategoryBar.tsx",
"chars": 5829,
"preview": "\"use client\";\nimport React, { useMemo } from \"react\";\nimport Tooltip, { useTooltip } from \"components/util-elements/Tool"
},
{
"path": "src/components/vis-elements/CategoryBar/index.ts",
"chars": 111,
"preview": "export { default as CategoryBar } from \"./CategoryBar\";\nexport type { CategoryBarProps } from \"./CategoryBar\";\n"
},
{
"path": "src/components/vis-elements/DeltaBar/DeltaBar.tsx",
"chars": 3332,
"preview": "\"use client\";\nimport React from \"react\";\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\";"
},
{
"path": "src/components/vis-elements/DeltaBar/index.ts",
"chars": 99,
"preview": "export { default as DeltaBar } from \"./DeltaBar\";\nexport type { DeltaBarProps } from \"./DeltaBar\";\n"
},
{
"path": "src/components/vis-elements/DeltaBar/styles.ts",
"chars": 817,
"preview": "import { BaseColors, DeltaTypes, getColorClassNames } from \"lib\";\nimport { colorPalette } from \"lib/theme\";\n\nexport type"
},
{
"path": "src/components/vis-elements/MarkerBar/MarkerBar.tsx",
"chars": 3150,
"preview": "\"use client\";\nimport React from \"react\";\n\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\""
},
{
"path": "src/components/vis-elements/MarkerBar/index.ts",
"chars": 103,
"preview": "export { default as MarkerBar } from \"./MarkerBar\";\nexport type { MarkerBarProps } from \"./MarkerBar\";\n"
},
{
"path": "src/components/vis-elements/ProgressBar/ProgressBar.tsx",
"chars": 2706,
"preview": "import React from \"react\";\nimport Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\";\nimport { getC"
},
{
"path": "src/components/vis-elements/ProgressBar/index.ts",
"chars": 111,
"preview": "export { default as ProgressBar } from \"./ProgressBar\";\nexport type { ProgressBarProps } from \"./ProgressBar\";\n"
},
{
"path": "src/components/vis-elements/ProgressCircle/ProgressCircle.tsx",
"chars": 3989,
"preview": "import Tooltip, { useTooltip } from \"components/util-elements/Tooltip/Tooltip\";\nimport { Color, colorPalette, getColorCl"
},
{
"path": "src/components/vis-elements/ProgressCircle/index.ts",
"chars": 123,
"preview": "export { default as ProgressCircle } from \"./ProgressCircle\";\nexport type { ProgressCircleProps } from \"./ProgressCircle"
},
{
"path": "src/components/vis-elements/Tracker/Tracker.tsx",
"chars": 1817,
"preview": "\"use client\";\nimport { tremorTwMerge } from \"lib\";\nimport React from \"react\";\n\nimport Tooltip, { useTooltip } from \"comp"
},
{
"path": "src/components/vis-elements/Tracker/index.ts",
"chars": 95,
"preview": "export { default as Tracker } from \"./Tracker\";\nexport type { TrackerProps } from \"./Tracker\";\n"
},
{
"path": "src/components/vis-elements/index.ts",
"chars": 207,
"preview": "export * from \"./BarList\";\nexport * from \"./CategoryBar\";\nexport * from \"./DeltaBar\";\nexport * from \"./MarkerBar\";\nexpor"
},
{
"path": "src/contexts/BaseColorContext.tsx",
"chars": 228,
"preview": "import { createContext } from \"react\";\n\nimport { BaseColors } from \"lib\";\nimport { Color } from \"../lib/inputTypes\";\n\nco"
}
]
// ... and 117 more files (download for full content)
About this extraction
This page contains the full source code of the tremorlabs/tremor-npm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 317 files (599.3 KB), approximately 165.7k tokens, and a symbol index with 212 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.