feat: Charts (#166)
* chore: update unovis deps * chore: update color to use the themePrimary * docs: use gradient for overview component * docs: add themePopover to MainLayout * docs: enable global theme on every page * feat: introduce area, line, bar, donut chart * feat: add more props * fix: revert old pipeline * fix: patch @unovis/vue deps * fix: patch @unovis/vue deps again * chore: revert unovis/ts to 1.2.1 * chore: wip * docs: add alpha tag, fix tooltipo styling * docs: add charts installations step * feat: use generic, add better color * chore: build registry * feat: improve generic props * chore: build registry * docs: add alpha label * fix: collapsible not open correctly * docs: add badge to mobile nav * chore: better types * chore: run registry * chore: wip * fix: crosshair issue * chore: fix type, import missing error * chore: build registry * chore: arrange interface, expose margin, slot * chore: build registry * docs: guide page feat: add prop to barchart * chore: fix pnpm-lock * chore: add feature * chore: run build registry * refactor: change color var * feat: codegen * chore: add meta tables * feat: add line, area example * feat: bar and donut examples * docs: codege * chore: build registry * docs: improve chart doc * chore: fix missing icon package
This commit is contained in:
parent
32d7b9ca4a
commit
77c6a16040
26
apps/www/.vitepress/theme/components/APITable.vue
Normal file
26
apps/www/.vitepress/theme/components/APITable.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<script setup lang="ts">
|
||||
import { capitalize } from 'vue'
|
||||
|
||||
defineProps<{
|
||||
type: 'prop' | 'emit' | 'slot' | 'method'
|
||||
data: { name: string, description: string, type: string, required: boolean }[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>{{ capitalize(type) }}</h3>
|
||||
|
||||
<div v-for="(item, index) in data" :key="index" class="py-4 border-b text-sm">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<h5 class="text-sm">
|
||||
<code>{{ item.name }}</code>
|
||||
</h5>
|
||||
<code>{{ item.type }}</code>
|
||||
<span v-if="item.required" class="font-normal text-red-500 text-xs">Required*</span>
|
||||
</div>
|
||||
|
||||
<div class="[&_p]:!my-2 ml-1" v-html="item.description" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -5,6 +5,7 @@ import Logo from './Logo.vue'
|
|||
import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet'
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
|
||||
import { Badge } from '@/lib/registry/new-york/ui/badge'
|
||||
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
|
@ -63,17 +64,26 @@ const open = ref(false)
|
|||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div v-for="(items, index) in docsConfig.sidebarNav" :key="index" class="flex flex-col space-y-3 pt-6">
|
||||
<h4 class="font-medium">
|
||||
{{ items.title }}
|
||||
</h4>
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-medium">
|
||||
{{ items.title }}
|
||||
</h4>
|
||||
<Badge v-if="items.label" class="ml-2">
|
||||
{{ items.label }}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<a
|
||||
v-for="item in items.items" :key="item.href"
|
||||
:href="item.href"
|
||||
class="text-muted-foreground"
|
||||
class="text-muted-foreground inline-flex items-center"
|
||||
@click="open = false"
|
||||
>
|
||||
{{ item.title }}
|
||||
|
||||
<Badge v-if="item.label" class="ml-2">
|
||||
{{ item.label }}
|
||||
</Badge>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
47
apps/www/.vitepress/theme/components/ThemePopover.vue
Normal file
47
apps/www/.vitepress/theme/components/ThemePopover.vue
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<script setup lang="ts">
|
||||
import { Paintbrush } from 'lucide-vue-next'
|
||||
import { onMounted, watch } from 'vue'
|
||||
import { allColors } from './theming/utils/data'
|
||||
import ThemeCustomizer from './ThemeCustomizer.vue'
|
||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
|
||||
const { theme, radius } = useConfigStore()
|
||||
|
||||
// Whenever the component is mounted, update the document class list
|
||||
onMounted(() => {
|
||||
document.documentElement.style.setProperty('--radius', `${radius.value}rem`)
|
||||
document.documentElement.classList.add(`theme-${theme.value}`)
|
||||
})
|
||||
|
||||
// Whenever the theme value changes, update the document class list
|
||||
watch(theme, (theme) => {
|
||||
document.documentElement.classList.remove(
|
||||
...allColors.map(color => `theme-${color}`),
|
||||
)
|
||||
document.documentElement.classList.add(`theme-${theme}`)
|
||||
})
|
||||
|
||||
// Whenever the radius value changes, update the document style
|
||||
watch(radius, (radius) => {
|
||||
document.documentElement.style.setProperty('--radius', `${radius}rem`)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
class="w-9 h-9"
|
||||
:variant="'ghost'"
|
||||
:size="'icon'"
|
||||
>
|
||||
<Paintbrush class="w-4 h-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent :side-offset="8" align="end" class="w-96">
|
||||
<ThemeCustomizer :all-colors="allColors" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
export { default as CodeWrapper } from './CodeWrapper'
|
||||
export { default as ComponentPreview } from './ComponentPreview.vue'
|
||||
export { default as APITable } from './APITable.vue'
|
||||
export { default as TabPreview } from './TabPreview.vue'
|
||||
export { default as TabMarkdown } from './TabMarkdown.vue'
|
||||
export { default as TabsMarkdown } from './TabsMarkdown.vue'
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@ import Metric from '@/lib/registry/new-york/example/Cards/Metric.vue'
|
|||
import DataTable from '@/lib/registry/new-york/example/Cards/DataTable.vue'
|
||||
import CardStats from '@/lib/registry/new-york/example/CardStats.vue'
|
||||
|
||||
import {
|
||||
Card,
|
||||
} from '@/lib/registry/new-york/ui/card'
|
||||
import { Card } from '@/lib/registry/new-york/ui/card'
|
||||
import { RangeCalendar } from '@/lib/registry/new-york/ui/range-calendar'
|
||||
|
||||
const now = today(getLocalTimeZone())
|
||||
|
|
|
|||
|
|
@ -2,6 +2,36 @@ import { CreditCard } from 'lucide-vue-next'
|
|||
import RiAppleFill from '~icons/ri/apple-fill'
|
||||
import RiPaypalFill from '~icons/ri/paypal-fill'
|
||||
|
||||
type Color =
|
||||
| 'zinc'
|
||||
| 'slate'
|
||||
| 'stone'
|
||||
| 'gray'
|
||||
| 'neutral'
|
||||
| 'red'
|
||||
| 'rose'
|
||||
| 'orange'
|
||||
| 'green'
|
||||
| 'blue'
|
||||
| 'yellow'
|
||||
| 'violet'
|
||||
|
||||
// Create an array of color values
|
||||
export const allColors: Color[] = [
|
||||
'zinc',
|
||||
'rose',
|
||||
'blue',
|
||||
'green',
|
||||
'orange',
|
||||
'red',
|
||||
'slate',
|
||||
'stone',
|
||||
'gray',
|
||||
'neutral',
|
||||
'yellow',
|
||||
'violet',
|
||||
]
|
||||
|
||||
interface Payment {
|
||||
status: string
|
||||
email: string
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export type SidebarNavItem = NavItem & {
|
|||
}
|
||||
|
||||
export type NavItemWithChildren = NavItem & {
|
||||
items: NavItemWithChildren[]
|
||||
items?: NavItemWithChildren[]
|
||||
}
|
||||
|
||||
interface DocsConfig {
|
||||
|
|
@ -55,22 +55,18 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs/introduction',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Installation',
|
||||
href: '/docs/installation',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'components.json',
|
||||
href: '/docs/components-json',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Theming',
|
||||
href: '/docs/theming',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Dark Mode',
|
||||
|
|
@ -80,27 +76,22 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'CLI',
|
||||
href: '/docs/cli',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Typography',
|
||||
href: '/docs/typography',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Figma',
|
||||
href: '/docs/figma',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Changelog',
|
||||
href: '/docs/changelog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'About',
|
||||
href: '/docs/about',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Contribution',
|
||||
|
|
@ -115,22 +106,18 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Vite',
|
||||
href: '/docs/installation/vite',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Nuxt',
|
||||
href: '/docs/installation/nuxt',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Astro',
|
||||
href: '/docs/installation/astro',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Laravel',
|
||||
href: '/docs/installation/laravel',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -142,6 +129,12 @@ export const docsConfig: DocsConfig = {
|
|||
href: '/docs/components/auto-form',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Charts',
|
||||
href: '/docs/charts',
|
||||
label: 'Alpha',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -150,32 +143,26 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Accordion',
|
||||
href: '/docs/components/accordion',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Alert',
|
||||
href: '/docs/components/alert',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Alert Dialog',
|
||||
href: '/docs/components/alert-dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Aspect Ratio',
|
||||
href: '/docs/components/aspect-ratio',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Avatar',
|
||||
href: '/docs/components/avatar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Badge',
|
||||
href: '/docs/components/badge',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Breadcrumb',
|
||||
|
|
@ -185,7 +172,6 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Button',
|
||||
href: '/docs/components/button',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Calendar',
|
||||
|
|
@ -196,7 +182,6 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Card',
|
||||
href: '/docs/components/card',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Carousel',
|
||||
|
|
@ -206,32 +191,26 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Checkbox',
|
||||
href: '/docs/components/checkbox',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Collapsible',
|
||||
href: '/docs/components/collapsible',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Combobox',
|
||||
href: '/docs/components/combobox',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Command',
|
||||
href: '/docs/components/command',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Context Menu',
|
||||
href: '/docs/components/context-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Data Table',
|
||||
href: '/docs/components/data-table',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Date Picker',
|
||||
|
|
@ -242,7 +221,6 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Dialog',
|
||||
href: '/docs/components/dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Drawer',
|
||||
|
|
@ -252,42 +230,34 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Dropdown Menu',
|
||||
href: '/docs/components/dropdown-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Form',
|
||||
href: '/docs/components/form',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Hover Card',
|
||||
href: '/docs/components/hover-card',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Input',
|
||||
href: '/docs/components/input',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Label',
|
||||
href: '/docs/components/label',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Menubar',
|
||||
href: '/docs/components/menubar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Navigation Menu',
|
||||
href: '/docs/components/navigation-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Pagination',
|
||||
href: '/docs/components/pagination',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Pin Input',
|
||||
|
|
@ -297,17 +267,14 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Popover',
|
||||
href: '/docs/components/popover',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Progress',
|
||||
href: '/docs/components/progress',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Radio Group',
|
||||
href: '/docs/components/radio-group',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Range Calendar',
|
||||
|
|
@ -323,32 +290,26 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Scroll Area',
|
||||
href: '/docs/components/scroll-area',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Select',
|
||||
href: '/docs/components/select',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Separator',
|
||||
href: '/docs/components/separator',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Sheet',
|
||||
href: '/docs/components/sheet',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Skeleton',
|
||||
href: '/docs/components/skeleton',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Slider',
|
||||
href: '/docs/components/slider',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Sonner',
|
||||
|
|
@ -358,17 +319,14 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Switch',
|
||||
href: '/docs/components/switch',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Table',
|
||||
href: '/docs/components/table',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tabs',
|
||||
href: '/docs/components/tabs',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tags Input',
|
||||
|
|
@ -378,27 +336,22 @@ export const docsConfig: DocsConfig = {
|
|||
{
|
||||
title: 'Textarea',
|
||||
href: '/docs/components/textarea',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Toast',
|
||||
href: '/docs/components/toast',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Toggle',
|
||||
href: '/docs/components/toggle',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Toggle Group',
|
||||
href: '/docs/components/toggle-group',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
href: '/docs/components/tooltip',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
|
|||
class="mb-1 rounded-md px-2 py-1 text-sm font-semibold"
|
||||
>
|
||||
{{ docsGroup.title }}
|
||||
|
||||
<span v-if="docsGroup.label" class="ml-2 font-normal rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||
{{ docsGroup.label }}
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
<div
|
||||
|
|
@ -72,9 +76,14 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<h1 class="scroll-m-20 text-4xl font-bold tracking-tight">
|
||||
{{ frontmatter.title }}
|
||||
</h1>
|
||||
<div class="flex items-center space-x-4">
|
||||
<h1 class="scroll-m-20 text-4xl font-bold tracking-tight">
|
||||
{{ frontmatter.title }}
|
||||
</h1>
|
||||
<span v-if="frontmatter.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||
{{ frontmatter.label }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-lg text-muted-foreground">
|
||||
{{ frontmatter.description }}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import MobileNav from '../components/MobileNav.vue'
|
|||
import CodeConfigCustomizer from '../components/CodeConfigCustomizer.vue'
|
||||
|
||||
import Kbd from '../components/Kbd.vue'
|
||||
import ThemePopover from '../components/ThemePopover.vue'
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/default/ui/command'
|
||||
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
|
|
@ -133,6 +134,8 @@ watch(() => $route.path, (n) => {
|
|||
</div>
|
||||
|
||||
<nav class="flex items-center">
|
||||
<ThemePopover />
|
||||
|
||||
<CodeConfigCustomizer />
|
||||
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -27,6 +27,21 @@
|
|||
--input: 240 5.9% 90%;
|
||||
--ring: 240 5% 64.9%;
|
||||
--radius: 0.5rem;
|
||||
|
||||
--vis-primary-color: var(--primary);
|
||||
--vis-secondary-color: 160 81% 40%;
|
||||
--vis-text-color: var(--muted-foreground);
|
||||
|
||||
|
||||
--vis-font-family: inherit !important;
|
||||
--vis-area-stroke-width: 2px !important;
|
||||
--vis-donut-central-label-text-color: hsl(var(--muted-foreground)) !important;
|
||||
--vis-tooltip-background-color: none !important;
|
||||
--vis-tooltip-border-color: none !important;
|
||||
--vis-tooltip-text-color: none !important;
|
||||
--vis-tooltip-shadow-color: none !important;
|
||||
--vis-tooltip-backdrop-filter: none !important;
|
||||
--vis-tooltip-padding: none !important;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
|
@ -142,7 +157,7 @@
|
|||
}
|
||||
|
||||
div[class^="language-"] {
|
||||
@apply mb-4 mt-6 max-h-[650px] overflow-x-auto md:rounded-lg border !bg-zinc-950 dark:!bg-zinc-900
|
||||
@apply mb-4 mt-6 max-h-[650px] overflow-x-auto md:rounded-lg border !bg-secondary-foreground dark:!bg-secondary
|
||||
}
|
||||
pre {
|
||||
@apply py-4;
|
||||
|
|
|
|||
|
|
@ -345,14 +345,14 @@
|
|||
}
|
||||
|
||||
.vp-doc [class*='language-'] code .highlighted {
|
||||
background-color: hsl(240 3.7% 15.9%);
|
||||
transition: background-color 0.5s;
|
||||
/* margin: 0 -24px;
|
||||
padding: 0 24px; */
|
||||
width: calc(100% + 2 * 24px);
|
||||
display: inline-block;
|
||||
@apply bg-[hsl(var(--foreground))] dark:bg-[hsl(var(--background)_/_50%)]
|
||||
}
|
||||
|
||||
hsl(var(--foreground) / 50%)
|
||||
.vp-doc [class*='language-'] code .highlighted.error {
|
||||
background-color: var(--vp-code-line-error-color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,27 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/default/example/AlertDialogDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/AlertDialogDemo.vue"],
|
||||
},
|
||||
"AreaChartCustomTooltip": {
|
||||
name: "AreaChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-area"],
|
||||
component: () => import("../src/lib/registry/default/example/AreaChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/AreaChartCustomTooltip.vue"],
|
||||
},
|
||||
"AreaChartDemo": {
|
||||
name: "AreaChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-area"],
|
||||
component: () => import("../src/lib/registry/default/example/AreaChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/AreaChartDemo.vue"],
|
||||
},
|
||||
"AreaChartSparkline": {
|
||||
name: "AreaChartSparkline",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-area"],
|
||||
component: () => import("../src/lib/registry/default/example/AreaChartSparkline.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/AreaChartSparkline.vue"],
|
||||
},
|
||||
"AspectRatioDemo": {
|
||||
name: "AspectRatioDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -129,6 +150,34 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/default/example/BadgeSecondaryDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/BadgeSecondaryDemo.vue"],
|
||||
},
|
||||
"BarChartCustomTooltip": {
|
||||
name: "BarChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/default/example/BarChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/BarChartCustomTooltip.vue"],
|
||||
},
|
||||
"BarChartDemo": {
|
||||
name: "BarChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/default/example/BarChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/BarChartDemo.vue"],
|
||||
},
|
||||
"BarChartRounded": {
|
||||
name: "BarChartRounded",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/default/example/BarChartRounded.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/BarChartRounded.vue"],
|
||||
},
|
||||
"BarChartStacked": {
|
||||
name: "BarChartStacked",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/default/example/BarChartStacked.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/BarChartStacked.vue"],
|
||||
},
|
||||
"BreadcrumbDemo": {
|
||||
name: "BreadcrumbDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -444,6 +493,13 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/default/example/ContextMenuDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/ContextMenuDemo.vue"],
|
||||
},
|
||||
"CustomChartTooltip": {
|
||||
name: "CustomChartTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["card"],
|
||||
component: () => import("../src/lib/registry/default/example/CustomChartTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/CustomChartTooltip.vue"],
|
||||
},
|
||||
"DataTableColumnPinningDemo": {
|
||||
name: "DataTableColumnPinningDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -528,6 +584,34 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/default/example/DialogScrollOverlayDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/DialogScrollOverlayDemo.vue"],
|
||||
},
|
||||
"DonutChartColor": {
|
||||
name: "DonutChartColor",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/default/example/DonutChartColor.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/DonutChartColor.vue"],
|
||||
},
|
||||
"DonutChartCustomTooltip": {
|
||||
name: "DonutChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/default/example/DonutChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/DonutChartCustomTooltip.vue"],
|
||||
},
|
||||
"DonutChartDemo": {
|
||||
name: "DonutChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/default/example/DonutChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/DonutChartDemo.vue"],
|
||||
},
|
||||
"DonutChartPie": {
|
||||
name: "DonutChartPie",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/default/example/DonutChartPie.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/DonutChartPie.vue"],
|
||||
},
|
||||
"DrawerDemo": {
|
||||
name: "DrawerDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -633,6 +717,27 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/default/example/LabelDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/LabelDemo.vue"],
|
||||
},
|
||||
"LineChartCustomTooltip": {
|
||||
name: "LineChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-line"],
|
||||
component: () => import("../src/lib/registry/default/example/LineChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/LineChartCustomTooltip.vue"],
|
||||
},
|
||||
"LineChartDemo": {
|
||||
name: "LineChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-line"],
|
||||
component: () => import("../src/lib/registry/default/example/LineChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/LineChartDemo.vue"],
|
||||
},
|
||||
"LineChartSparkline": {
|
||||
name: "LineChartSparkline",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-line"],
|
||||
component: () => import("../src/lib/registry/default/example/LineChartSparkline.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/LineChartSparkline.vue"],
|
||||
},
|
||||
"MenubarDemo": {
|
||||
name: "MenubarDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -1327,6 +1432,27 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/new-york/example/AlertDialogDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/AlertDialogDemo.vue"],
|
||||
},
|
||||
"AreaChartCustomTooltip": {
|
||||
name: "AreaChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-area"],
|
||||
component: () => import("../src/lib/registry/new-york/example/AreaChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/AreaChartCustomTooltip.vue"],
|
||||
},
|
||||
"AreaChartDemo": {
|
||||
name: "AreaChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-area"],
|
||||
component: () => import("../src/lib/registry/new-york/example/AreaChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/AreaChartDemo.vue"],
|
||||
},
|
||||
"AreaChartSparkline": {
|
||||
name: "AreaChartSparkline",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-area"],
|
||||
component: () => import("../src/lib/registry/new-york/example/AreaChartSparkline.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/AreaChartSparkline.vue"],
|
||||
},
|
||||
"AspectRatioDemo": {
|
||||
name: "AspectRatioDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -1425,6 +1551,34 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue"],
|
||||
},
|
||||
"BarChartCustomTooltip": {
|
||||
name: "BarChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/new-york/example/BarChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/BarChartCustomTooltip.vue"],
|
||||
},
|
||||
"BarChartDemo": {
|
||||
name: "BarChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/new-york/example/BarChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/BarChartDemo.vue"],
|
||||
},
|
||||
"BarChartRounded": {
|
||||
name: "BarChartRounded",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/new-york/example/BarChartRounded.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/BarChartRounded.vue"],
|
||||
},
|
||||
"BarChartStacked": {
|
||||
name: "BarChartStacked",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-bar"],
|
||||
component: () => import("../src/lib/registry/new-york/example/BarChartStacked.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/BarChartStacked.vue"],
|
||||
},
|
||||
"BreadcrumbDemo": {
|
||||
name: "BreadcrumbDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -1740,6 +1894,13 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/new-york/example/ContextMenuDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/ContextMenuDemo.vue"],
|
||||
},
|
||||
"CustomChartTooltip": {
|
||||
name: "CustomChartTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["card"],
|
||||
component: () => import("../src/lib/registry/new-york/example/CustomChartTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/CustomChartTooltip.vue"],
|
||||
},
|
||||
"DataTableColumnPinningDemo": {
|
||||
name: "DataTableColumnPinningDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -1824,6 +1985,34 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/new-york/example/DialogScrollOverlayDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/DialogScrollOverlayDemo.vue"],
|
||||
},
|
||||
"DonutChartColor": {
|
||||
name: "DonutChartColor",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/new-york/example/DonutChartColor.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/DonutChartColor.vue"],
|
||||
},
|
||||
"DonutChartCustomTooltip": {
|
||||
name: "DonutChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/new-york/example/DonutChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/DonutChartCustomTooltip.vue"],
|
||||
},
|
||||
"DonutChartDemo": {
|
||||
name: "DonutChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/new-york/example/DonutChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/DonutChartDemo.vue"],
|
||||
},
|
||||
"DonutChartPie": {
|
||||
name: "DonutChartPie",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-donut"],
|
||||
component: () => import("../src/lib/registry/new-york/example/DonutChartPie.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/DonutChartPie.vue"],
|
||||
},
|
||||
"DrawerDemo": {
|
||||
name: "DrawerDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -1929,6 +2118,27 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/new-york/example/LabelDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/LabelDemo.vue"],
|
||||
},
|
||||
"LineChartCustomTooltip": {
|
||||
name: "LineChartCustomTooltip",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-line"],
|
||||
component: () => import("../src/lib/registry/new-york/example/LineChartCustomTooltip.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/LineChartCustomTooltip.vue"],
|
||||
},
|
||||
"LineChartDemo": {
|
||||
name: "LineChartDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-line"],
|
||||
component: () => import("../src/lib/registry/new-york/example/LineChartDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/LineChartDemo.vue"],
|
||||
},
|
||||
"LineChartSparkline": {
|
||||
name: "LineChartSparkline",
|
||||
type: "components:example",
|
||||
registryDependencies: ["chart-line"],
|
||||
component: () => import("../src/lib/registry/new-york/example/LineChartSparkline.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/LineChartSparkline.vue"],
|
||||
},
|
||||
"MenubarDemo": {
|
||||
name: "MenubarDemo",
|
||||
type: "components:example",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
"typecheck": "vue-tsc",
|
||||
"typecheck:registry": "vue-tsc -p tsconfig.registry.json",
|
||||
"build:registry": "tsx ./scripts/build-registry.ts",
|
||||
"build:registry-strict": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts"
|
||||
"build:registry-strict": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts",
|
||||
"docs:gen": "tsx ./scripts/autogen.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
|
|
@ -44,9 +45,12 @@
|
|||
"zod": "^3.23.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/traverse": "^7.24.1",
|
||||
"@iconify-json/gravity-ui": "^1.1.2",
|
||||
"@iconify-json/lucide": "^1.1.180",
|
||||
"@iconify-json/ph": "^1.1.12",
|
||||
"@iconify-json/radix-icons": "^1.1.14",
|
||||
"@iconify-json/ri": "^1.1.18",
|
||||
"@iconify-json/simple-icons": "^1.1.94",
|
||||
"@iconify-json/tabler": "^1.1.106",
|
||||
"@iconify/vue": "^4.1.2",
|
||||
|
|
@ -60,7 +64,9 @@
|
|||
"@vue/compiler-dom": "^3.4.24",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"fast-glob": "^3.3.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
"pathe": "^1.1.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"shiki": "^1.3.0",
|
||||
|
|
@ -70,6 +76,7 @@
|
|||
"typescript": "^5.4.5",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"vitepress": "^1.1.3",
|
||||
"vue-component-meta": "^2.0.13",
|
||||
"vue-tsc": "^2.0.14"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
150
apps/www/scripts/autogen.ts
Normal file
150
apps/www/scripts/autogen.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { join, parse, resolve } from 'node:path'
|
||||
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import fg from 'fast-glob'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import type { ComponentMeta, MetaCheckerOptions, PropertyMeta, PropertyMetaSchema } from 'vue-component-meta'
|
||||
import { createComponentMetaChecker } from 'vue-component-meta'
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
const md = new MarkdownIt()
|
||||
|
||||
const ROOTPATH = '../'
|
||||
const OUTPUTPATH = '../src/content/meta'
|
||||
|
||||
const checkerOptions: MetaCheckerOptions = {
|
||||
forceUseTs: true,
|
||||
printer: { newLine: 1 },
|
||||
}
|
||||
|
||||
const tsconfigChecker = createComponentMetaChecker(
|
||||
resolve(__dirname, ROOTPATH, 'tsconfig.registry.json'),
|
||||
checkerOptions,
|
||||
)
|
||||
|
||||
const components = fg.sync(['chart/**/*.vue', 'chart*/**/*.vue'], {
|
||||
cwd: resolve(__dirname, ROOTPATH, 'src/lib/registry/default/ui/'),
|
||||
absolute: true,
|
||||
})
|
||||
|
||||
components.forEach((componentPath) => {
|
||||
try {
|
||||
const componentName = parse(componentPath).name
|
||||
const meta = parseMeta(tsconfigChecker.getComponentMeta(componentPath))
|
||||
|
||||
const metaDirPath = resolve(__dirname, OUTPUTPATH)
|
||||
// if meta dir doesn't exist create
|
||||
if (!existsSync(metaDirPath))
|
||||
mkdirSync(metaDirPath)
|
||||
|
||||
const metaMdFilePath = join(metaDirPath, `${componentName}.md`)
|
||||
|
||||
let parsedString = '<!-- This file was automatic generated. Do not edit it manually -->\n\n'
|
||||
if (meta.props.length)
|
||||
parsedString += `<APITable :type="'prop'" :data="${JSON.stringify(meta.props, null, 2).replace(/"/g, '\'')}" />\n`
|
||||
|
||||
if (meta.events.length)
|
||||
parsedString += `\n<APITable :type="'emit'" :data="${JSON.stringify(meta.events, null, 2).replace(/"/g, '\'')}" />\n`
|
||||
|
||||
if (meta.slots.length)
|
||||
parsedString += `\n<APITable :type="'slot'" :data="${JSON.stringify(meta.slots, null, 2).replace(/"/g, '\'')}" />\n`
|
||||
|
||||
if (meta.methods.length)
|
||||
parsedString += `\n<APITable :type="'method'" :data="${JSON.stringify(meta.methods, null, 2).replace(/"/g, '\'')}" />\n`
|
||||
|
||||
writeFileSync(metaMdFilePath, parsedString)
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
|
||||
function parseTypeFromSchema(schema: PropertyMetaSchema): string {
|
||||
if (typeof schema === 'object' && (schema.kind === 'enum' || schema.kind === 'array')) {
|
||||
const isFlatEnum = schema.schema?.every(val => typeof val === 'string')
|
||||
const enumValue = schema?.schema?.filter(i => i !== 'undefined') ?? []
|
||||
|
||||
if (isFlatEnum && /^[A-Z]/.test(schema.type))
|
||||
return enumValue.join(' | ')
|
||||
else if (typeof schema.schema?.[0] === 'object' && schema.schema?.[0].kind === 'enum')
|
||||
return schema.schema.map((s: PropertyMetaSchema) => parseTypeFromSchema(s)).join(' | ')
|
||||
else
|
||||
return schema.type
|
||||
}
|
||||
else if (typeof schema === 'object' && schema.kind === 'object') {
|
||||
return schema.type
|
||||
}
|
||||
else if (typeof schema === 'string') {
|
||||
return schema
|
||||
}
|
||||
else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities
|
||||
function parseMeta(meta: ComponentMeta) {
|
||||
const props = meta.props
|
||||
// Exclude global props
|
||||
.filter(prop => !prop.global)
|
||||
.map((prop) => {
|
||||
let defaultValue = prop.default
|
||||
const { name, description, required } = prop
|
||||
|
||||
if (name === 'as')
|
||||
defaultValue = defaultValue ?? '"div"'
|
||||
|
||||
if (defaultValue === 'undefined')
|
||||
defaultValue = undefined
|
||||
|
||||
return ({
|
||||
name,
|
||||
description: md.render(description),
|
||||
type: prop.type.replace(/\s*\|\s*undefined/g, ''),
|
||||
required,
|
||||
default: defaultValue ?? undefined,
|
||||
})
|
||||
})
|
||||
|
||||
const events = meta.events
|
||||
.map((event) => {
|
||||
const { name, type } = event
|
||||
return ({
|
||||
name,
|
||||
type: type.replace(/\s*\|\s*undefined/g, ''),
|
||||
})
|
||||
})
|
||||
|
||||
const defaultSlot = meta.slots?.[0]
|
||||
const slots: { name: string, description: string, type: string }[] = []
|
||||
|
||||
if (defaultSlot && defaultSlot.type !== '{}') {
|
||||
const schema = defaultSlot.schema
|
||||
if (typeof schema === 'object' && schema.schema) {
|
||||
Object.values(schema.schema).forEach((childMeta: PropertyMeta) => {
|
||||
slots.push({
|
||||
name: childMeta.name,
|
||||
description: md.render(childMeta.description),
|
||||
type: parseTypeFromSchema(childMeta.schema),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// exposed method
|
||||
const methods = meta.exposed
|
||||
.filter(expose => typeof expose.schema === 'object' && expose.schema.kind === 'event')
|
||||
.map(expose => ({
|
||||
name: expose.name,
|
||||
description: md.render(expose.description),
|
||||
type: expose.type,
|
||||
}))
|
||||
|
||||
return {
|
||||
props,
|
||||
events,
|
||||
slots,
|
||||
methods,
|
||||
}
|
||||
}
|
||||
107
apps/www/src/content/docs/charts.md
Normal file
107
apps/www/src/content/docs/charts.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
title: Charts
|
||||
description: Versatile visualization tool, allowing users to represent data using various types of charts for effective analysis.
|
||||
label: Alpha
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import Area from '~icons/gravity-ui/chart-area-stacked'
|
||||
import Bar from '~icons/gravity-ui/chart-column'
|
||||
import Line from '~icons/gravity-ui/chart-line'
|
||||
import Pie from '~icons/gravity-ui/chart-pie'
|
||||
</script>
|
||||
|
||||
<Callout>
|
||||
Only works with Vue >3.3
|
||||
</Callout>
|
||||
|
||||
`Charts` components were built on top of [Unovis](https://unovis.dev/) (a modular data visualization framework), and inspired by [tremor](https://www.tremor.so).
|
||||
|
||||
## Chart type
|
||||
|
||||
<div class="grid gap-4 mt-8 sm:grid-cols-2 sm:gap-6 not-docs">
|
||||
<LinkedCard href="/docs/charts/area">
|
||||
<Area class="text-foreground/80 w-11 h-11" />
|
||||
<p class="mt-2 font-medium">Area</p>
|
||||
</LinkedCard>
|
||||
|
||||
<LinkedCard href="/docs/charts/line">
|
||||
<Line class="text-foreground/80 w-11 h-11" />
|
||||
<p class="mt-2 font-medium">Line</p>
|
||||
</LinkedCard>
|
||||
|
||||
<LinkedCard href="/docs/charts/bar">
|
||||
<Bar class="text-foreground/80 w-11 h-11" />
|
||||
<p class="mt-2 font-medium">Bar</p>
|
||||
</LinkedCard>
|
||||
|
||||
<LinkedCard href="/docs/charts/donut">
|
||||
<Pie class="text-foreground/80 w-11 h-11" />
|
||||
<p class="mt-2 font-medium">Donut</p>
|
||||
</LinkedCard>
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `css`
|
||||
|
||||
Add the following tooltip styling to your `tailwind.css` file:
|
||||
|
||||
```css
|
||||
@layer base {
|
||||
:root {
|
||||
/* ... */
|
||||
--vis-tooltip-background-color: none !important;
|
||||
--vis-tooltip-border-color: none !important;
|
||||
--vis-tooltip-text-color: none !important;
|
||||
--vis-tooltip-shadow-color: none !important;
|
||||
--vis-tooltip-backdrop-filter: none !important;
|
||||
--vis-tooltip-padding: none !important;
|
||||
|
||||
--vis-primary-color: var(--primary);
|
||||
/* change to any hsl value you want */
|
||||
--vis-secondary-color: 160 81% 40%;
|
||||
--vis-text-color: var(--muted-foreground);
|
||||
}
|
||||
```
|
||||
|
||||
If you are not using `css-variables` for your component, you need to update the `--vis-primary-color` and `--vis-text-color` to your desired hsl value.
|
||||
|
||||
You may use [this tool](https://redpixelthemes.com/blog/tailwindcss-colors-different-formats/) to help you find the hsl value for your primary color and text color. Be sure to provide `dark` mode styling as well.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Colors
|
||||
|
||||
By default, we construct the primary theme color, and secondary (`--vis-secondary-color`) color with different opacity for the graph.
|
||||
|
||||
However, you can always pass in the desired `color` into each chart.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<AreaChart
|
||||
:data="data"
|
||||
:colors="['blue', 'pink', 'orange', 'red']"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Custom tooltip
|
||||
|
||||
If you want to customize the `Tooltip` for the chart, you can pass `customTooltip` prop with a custom Vue component.
|
||||
The custom component would receive `title` and `data` props, check out [ChartTooltip.vue component](https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/lib/registry/default/ui/chart/ChartTooltip.vue) for example.
|
||||
|
||||
The expected prop definition would be:
|
||||
|
||||
```ts
|
||||
defineProps<{
|
||||
title?: string
|
||||
data: {
|
||||
name: string
|
||||
color: string
|
||||
value: any
|
||||
}[]
|
||||
}>()
|
||||
```
|
||||
46
apps/www/src/content/docs/charts/area.md
Normal file
46
apps/www/src/content/docs/charts/area.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
title: Area
|
||||
description: An area chart visually represents data over time, displaying trends and patterns through filled-in areas under a line graph.
|
||||
source: apps/www/src/lib/registry/default/ui/chart-area
|
||||
label: Alpha
|
||||
---
|
||||
|
||||
<ComponentPreview name="AreaChartDemo" />
|
||||
|
||||
## Installation
|
||||
|
||||
<Callout>
|
||||
Only works with Vue >3.3
|
||||
</Callout>
|
||||
|
||||
<Steps>
|
||||
|
||||
### Run the following command
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add chart-area
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
Follow the [guide](/docs/charts.html#installation) to complete the setup.
|
||||
|
||||
</Steps>
|
||||
|
||||
## API
|
||||
|
||||
<!-- @include: @/content/meta/AreaChart.md -->
|
||||
|
||||
## Example
|
||||
|
||||
### Sparkline
|
||||
|
||||
We can turn the chart into sparkline chart by hiding axis, gridline and legends.
|
||||
|
||||
<ComponentPreview name="AreaChartSparkline" />
|
||||
|
||||
### Custom Tooltip
|
||||
|
||||
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
|
||||
|
||||
<ComponentPreview name="AreaChartCustomTooltip" />
|
||||
50
apps/www/src/content/docs/charts/bar.md
Normal file
50
apps/www/src/content/docs/charts/bar.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: Bar
|
||||
description: An line chart visually represents data using rectangular bars of varying lengths to compare quantities across different categories or groups.
|
||||
source: apps/www/src/lib/registry/default/ui/chart-bar
|
||||
label: Alpha
|
||||
---
|
||||
|
||||
<ComponentPreview name="BarChartDemo" />
|
||||
|
||||
## Installation
|
||||
|
||||
<Callout>
|
||||
Only works with Vue >3.3
|
||||
</Callout>
|
||||
|
||||
<Steps>
|
||||
|
||||
### Run the following command
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add chart-bar
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
Follow the [guide](/docs/charts.html#installation) to complete the setup.
|
||||
|
||||
</Steps>
|
||||
|
||||
## API
|
||||
|
||||
<!-- @include: @/content/meta/BarChart.md -->
|
||||
|
||||
## Example
|
||||
|
||||
### Stacked
|
||||
|
||||
You can stack the bar chart by settings prop `type` to `stacked`.
|
||||
|
||||
<ComponentPreview name="BarChartStacked" />
|
||||
|
||||
### Rounded
|
||||
|
||||
<ComponentPreview name="BarChartRounded" />
|
||||
|
||||
### Custom Tooltip
|
||||
|
||||
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
|
||||
|
||||
<ComponentPreview name="BarChartCustomTooltip" />
|
||||
52
apps/www/src/content/docs/charts/donut.md
Normal file
52
apps/www/src/content/docs/charts/donut.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Donut
|
||||
description: An line chart visually represents data in a circular form, similar to a pie chart but with a central void, emphasizing proportions within categories.
|
||||
source: apps/www/src/lib/registry/default/ui/chart-donut
|
||||
label: Alpha
|
||||
---
|
||||
|
||||
<ComponentPreview name="DonutChartDemo" />
|
||||
|
||||
## Installation
|
||||
|
||||
<Callout>
|
||||
Only works with Vue >3.3
|
||||
</Callout>
|
||||
|
||||
<Steps>
|
||||
|
||||
### Run the following command
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add chart-donut
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
Follow the [guide](/docs/charts.html#installation) to complete the setup.
|
||||
|
||||
</Steps>
|
||||
|
||||
## API
|
||||
|
||||
<!-- @include: @/content/meta/DonutChart.md -->
|
||||
|
||||
## Example
|
||||
|
||||
### Pie Chart
|
||||
|
||||
If you want to render pie chart instead, pass `type` as `pie`.
|
||||
|
||||
<ComponentPreview name="DonutChartPie" />
|
||||
|
||||
### Color
|
||||
|
||||
We generate colors automatically based on the primary and secondary color and assigned them accordingly. Feel free to pass in your own array of colors.
|
||||
|
||||
<ComponentPreview name="DonutChartColor" />
|
||||
|
||||
### Custom Tooltip
|
||||
|
||||
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
|
||||
|
||||
<ComponentPreview name="DonutChartCustomTooltip" />
|
||||
46
apps/www/src/content/docs/charts/line.md
Normal file
46
apps/www/src/content/docs/charts/line.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
title: Line
|
||||
description: An line chart visually displays data points connected by straight lines, illustrating trends or relationships over a continuous axis.
|
||||
source: apps/www/src/lib/registry/default/ui/chart-line
|
||||
label: Alpha
|
||||
---
|
||||
|
||||
<ComponentPreview name="LineChartDemo" />
|
||||
|
||||
## Installation
|
||||
|
||||
<Callout>
|
||||
Only works with Vue >3.3
|
||||
</Callout>
|
||||
|
||||
<Steps>
|
||||
|
||||
### Run the following command
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add chart-line
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
Follow the [guide](/docs/charts.html#installation) to complete the setup.
|
||||
|
||||
</Steps>
|
||||
|
||||
## API
|
||||
|
||||
<!-- @include: @/content/meta/LineChart.md -->
|
||||
|
||||
## Example
|
||||
|
||||
### Sparkline
|
||||
|
||||
We can turn the chart into sparkline chart by hiding axis, gridline and legends.
|
||||
|
||||
<ComponentPreview name="LineChartSparkline" />
|
||||
|
||||
### Custom Tooltip
|
||||
|
||||
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
|
||||
|
||||
<ComponentPreview name="LineChartCustomTooltip" />
|
||||
|
|
@ -45,7 +45,7 @@ import { useToast } from '@/components/ui/toast/use-toast'
|
|||
<script setup lang="ts">
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useToast } from '@/components/ui/toast/use-toast'
|
||||
import { Toaster } from "@/components/ui/toast"
|
||||
import { Toaster } from '@/components/ui/toast'
|
||||
|
||||
const { toast } = useToast()
|
||||
</script>
|
||||
|
|
|
|||
116
apps/www/src/content/meta/AreaChart.md
Normal file
116
apps/www/src/content/meta/AreaChart.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'data',
|
||||
'description': '<p>The source data, in which each entry is a dictionary.</p>\n',
|
||||
'type': 'Record<string, any>[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'categories',
|
||||
'description': '<p>Select the categories from your data. Used to populate the legend and toolip.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'index',
|
||||
'description': '<p>Sets the key to map the data to the axis.</p>\n',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'colors',
|
||||
'description': '<p>Change the default colors.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'margin',
|
||||
'description': '<p>Margin of each the container</p>\n',
|
||||
'type': 'Spacing',
|
||||
'required': false,
|
||||
'default': '{ top: 0, bottom: 0, left: 0, right: 0 }'
|
||||
},
|
||||
{
|
||||
'name': 'filterOpacity',
|
||||
'description': '<p>Change the opacity of the non-selected field</p>\n',
|
||||
'type': 'number',
|
||||
'required': false,
|
||||
'default': '0.2'
|
||||
},
|
||||
{
|
||||
'name': 'xFormatter',
|
||||
'description': '<p>Function to format X label</p>\n',
|
||||
'type': '((tick: number | Date, i: number, ticks: number[] | Date[]) => string)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'yFormatter',
|
||||
'description': '<p>Function to format Y label</p>\n',
|
||||
'type': '((tick: number | Date, i: number, ticks: number[] | Date[]) => string)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'showXAxis',
|
||||
'description': '<p>Controls the visibility of the X axis.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showYAxis',
|
||||
'description': '<p>Controls the visibility of the Y axis.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showTooltip',
|
||||
'description': '<p>Controls the visibility of tooltip.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showLegend',
|
||||
'description': '<p>Controls the visibility of legend.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showGridLine',
|
||||
'description': '<p>Controls the visibility of gridline.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'customTooltip',
|
||||
'description': '<p>Render custom tooltip component.</p>\n',
|
||||
'type': 'Component',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'curveType',
|
||||
'description': '<p>Type of curve</p>\n',
|
||||
'type': 'CurveType',
|
||||
'required': false,
|
||||
'default': 'CurveType.MonotoneX'
|
||||
},
|
||||
{
|
||||
'name': 'showGradiant',
|
||||
'description': '<p>Controls the visibility of gradient.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
}
|
||||
]" />
|
||||
|
||||
<APITable :type="'emit'" :data="[
|
||||
{
|
||||
'name': 'legendItemClick',
|
||||
'type': '[d: BulletLegendItemInterface, i: number]'
|
||||
}
|
||||
]" />
|
||||
116
apps/www/src/content/meta/BarChart.md
Normal file
116
apps/www/src/content/meta/BarChart.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'data',
|
||||
'description': '<p>The source data, in which each entry is a dictionary.</p>\n',
|
||||
'type': 'Record<string, any>[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'categories',
|
||||
'description': '<p>Select the categories from your data. Used to populate the legend and toolip.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'index',
|
||||
'description': '<p>Sets the key to map the data to the axis.</p>\n',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'colors',
|
||||
'description': '<p>Change the default colors.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'margin',
|
||||
'description': '<p>Margin of each the container</p>\n',
|
||||
'type': 'Spacing',
|
||||
'required': false,
|
||||
'default': '{ top: 0, bottom: 0, left: 0, right: 0 }'
|
||||
},
|
||||
{
|
||||
'name': 'filterOpacity',
|
||||
'description': '<p>Change the opacity of the non-selected field</p>\n',
|
||||
'type': 'number',
|
||||
'required': false,
|
||||
'default': '0.2'
|
||||
},
|
||||
{
|
||||
'name': 'xFormatter',
|
||||
'description': '<p>Function to format X label</p>\n',
|
||||
'type': '((tick: number | Date, i: number, ticks: number[] | Date[]) => string)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'yFormatter',
|
||||
'description': '<p>Function to format Y label</p>\n',
|
||||
'type': '((tick: number | Date, i: number, ticks: number[] | Date[]) => string)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'showXAxis',
|
||||
'description': '<p>Controls the visibility of the X axis.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showYAxis',
|
||||
'description': '<p>Controls the visibility of the Y axis.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showTooltip',
|
||||
'description': '<p>Controls the visibility of tooltip.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showLegend',
|
||||
'description': '<p>Controls the visibility of legend.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showGridLine',
|
||||
'description': '<p>Controls the visibility of gridline.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'customTooltip',
|
||||
'description': '<p>Render custom tooltip component.</p>\n',
|
||||
'type': 'Component',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'type',
|
||||
'description': '<p>Change the type of the chart</p>\n',
|
||||
'type': '\'stacked\' | \'grouped\'',
|
||||
'required': false,
|
||||
'default': '\'grouped\''
|
||||
},
|
||||
{
|
||||
'name': 'roundedCorners',
|
||||
'description': '<p>Rounded bar corners</p>\n',
|
||||
'type': 'number',
|
||||
'required': false,
|
||||
'default': '0'
|
||||
}
|
||||
]" />
|
||||
|
||||
<APITable :type="'emit'" :data="[
|
||||
{
|
||||
'name': 'legendItemClick',
|
||||
'type': '[d: BulletLegendItemInterface, i: number]'
|
||||
}
|
||||
]" />
|
||||
29
apps/www/src/content/meta/ChartCrosshair.md
Normal file
29
apps/www/src/content/meta/ChartCrosshair.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'colors',
|
||||
'description': '',
|
||||
'type': 'string[]',
|
||||
'required': false,
|
||||
'default': '[]'
|
||||
},
|
||||
{
|
||||
'name': 'index',
|
||||
'description': '',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'items',
|
||||
'description': '',
|
||||
'type': 'BulletLegendItemInterface[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'customTooltip',
|
||||
'description': '',
|
||||
'type': 'Component',
|
||||
'required': false
|
||||
}
|
||||
]" />
|
||||
22
apps/www/src/content/meta/ChartLegend.md
Normal file
22
apps/www/src/content/meta/ChartLegend.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'items',
|
||||
'description': '',
|
||||
'type': 'BulletLegendItemInterface[]',
|
||||
'required': false,
|
||||
'default': '[]'
|
||||
}
|
||||
]" />
|
||||
|
||||
<APITable :type="'emit'" :data="[
|
||||
{
|
||||
'name': 'legendItemClick',
|
||||
'type': '[d: BulletLegendItemInterface, i: number]'
|
||||
},
|
||||
{
|
||||
'name': 'update:items',
|
||||
'type': '[payload: BulletLegendItemInterface[]]'
|
||||
}
|
||||
]" />
|
||||
43
apps/www/src/content/meta/ChartSingleTooltip.md
Normal file
43
apps/www/src/content/meta/ChartSingleTooltip.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'valueFormatter',
|
||||
'description': '',
|
||||
'type': '((tick: number, i?: number, ticks?: number[]) => string)',
|
||||
'required': false,
|
||||
'default': '`${tick}`'
|
||||
},
|
||||
{
|
||||
'name': 'index',
|
||||
'description': '',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'items',
|
||||
'description': '',
|
||||
'type': 'BulletLegendItemInterface[]',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'customTooltip',
|
||||
'description': '',
|
||||
'type': 'Component',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'selector',
|
||||
'description': '',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
}
|
||||
]" />
|
||||
|
||||
<APITable :type="'method'" :data="[
|
||||
{
|
||||
'name': 'valueFormatter',
|
||||
'description': '',
|
||||
'type': '(tick: number, i?: number | undefined, ticks?: number[] | undefined) => string'
|
||||
}
|
||||
]" />
|
||||
16
apps/www/src/content/meta/ChartTooltip.md
Normal file
16
apps/www/src/content/meta/ChartTooltip.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'title',
|
||||
'description': '',
|
||||
'type': 'string',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'data',
|
||||
'description': '',
|
||||
'type': '{ name: string; color: string; value: any; }[]',
|
||||
'required': true
|
||||
}
|
||||
]" />
|
||||
82
apps/www/src/content/meta/DonutChart.md
Normal file
82
apps/www/src/content/meta/DonutChart.md
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'colors',
|
||||
'description': '<p>Change the default colors.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'index',
|
||||
'description': '<p>Sets the key to map the data to the axis.</p>\n',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'data',
|
||||
'description': '<p>The source data, in which each entry is a dictionary.</p>\n',
|
||||
'type': 'Record<string, any>[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'margin',
|
||||
'description': '<p>Margin of each the container</p>\n',
|
||||
'type': 'Spacing',
|
||||
'required': false,
|
||||
'default': '{ top: 0, bottom: 0, left: 0, right: 0 }'
|
||||
},
|
||||
{
|
||||
'name': 'filterOpacity',
|
||||
'description': '<p>Change the opacity of the non-selected field</p>\n',
|
||||
'type': 'number',
|
||||
'required': false,
|
||||
'default': '0.2'
|
||||
},
|
||||
{
|
||||
'name': 'showTooltip',
|
||||
'description': '<p>Controls the visibility of tooltip.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showLegend',
|
||||
'description': '<p>Controls the visibility of legend.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'category',
|
||||
'description': '<p>Sets the name of the key containing the quantitative chart values.</p>\n',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'type',
|
||||
'description': '<p>Change the type of the chart</p>\n',
|
||||
'type': '\'donut\' | \'pie\'',
|
||||
'required': false,
|
||||
'default': '\'donut\''
|
||||
},
|
||||
{
|
||||
'name': 'sortFunction',
|
||||
'description': '<p>Function to sort the segment</p>\n',
|
||||
'type': '((a: any, b: any) => number)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'valueFormatter',
|
||||
'description': '<p>Controls the formatting for the label.</p>\n',
|
||||
'type': '((tick: number, i?: number, ticks?: number[]) => string)',
|
||||
'required': false,
|
||||
'default': '`${tick}`'
|
||||
},
|
||||
{
|
||||
'name': 'customTooltip',
|
||||
'description': '<p>Render custom tooltip component.</p>\n',
|
||||
'type': 'Component',
|
||||
'required': false
|
||||
}
|
||||
]" />
|
||||
109
apps/www/src/content/meta/LineChart.md
Normal file
109
apps/www/src/content/meta/LineChart.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<!-- This file was automatic generated. Do not edit it manually -->
|
||||
|
||||
<APITable :type="'prop'" :data="[
|
||||
{
|
||||
'name': 'data',
|
||||
'description': '<p>The source data, in which each entry is a dictionary.</p>\n',
|
||||
'type': 'Record<string, any>[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'categories',
|
||||
'description': '<p>Select the categories from your data. Used to populate the legend and toolip.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'index',
|
||||
'description': '<p>Sets the key to map the data to the axis.</p>\n',
|
||||
'type': 'string',
|
||||
'required': true
|
||||
},
|
||||
{
|
||||
'name': 'colors',
|
||||
'description': '<p>Change the default colors.</p>\n',
|
||||
'type': 'string[]',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'margin',
|
||||
'description': '<p>Margin of each the container</p>\n',
|
||||
'type': 'Spacing',
|
||||
'required': false,
|
||||
'default': '{ top: 0, bottom: 0, left: 0, right: 0 }'
|
||||
},
|
||||
{
|
||||
'name': 'filterOpacity',
|
||||
'description': '<p>Change the opacity of the non-selected field</p>\n',
|
||||
'type': 'number',
|
||||
'required': false,
|
||||
'default': '0.2'
|
||||
},
|
||||
{
|
||||
'name': 'xFormatter',
|
||||
'description': '<p>Function to format X label</p>\n',
|
||||
'type': '((tick: number | Date, i: number, ticks: number[] | Date[]) => string)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'yFormatter',
|
||||
'description': '<p>Function to format Y label</p>\n',
|
||||
'type': '((tick: number | Date, i: number, ticks: number[] | Date[]) => string)',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'showXAxis',
|
||||
'description': '<p>Controls the visibility of the X axis.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showYAxis',
|
||||
'description': '<p>Controls the visibility of the Y axis.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showTooltip',
|
||||
'description': '<p>Controls the visibility of tooltip.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showLegend',
|
||||
'description': '<p>Controls the visibility of legend.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'showGridLine',
|
||||
'description': '<p>Controls the visibility of gridline.</p>\n',
|
||||
'type': 'boolean',
|
||||
'required': false,
|
||||
'default': 'true'
|
||||
},
|
||||
{
|
||||
'name': 'customTooltip',
|
||||
'description': '<p>Render custom tooltip component.</p>\n',
|
||||
'type': 'Component',
|
||||
'required': false
|
||||
},
|
||||
{
|
||||
'name': 'curveType',
|
||||
'description': '<p>Type of curve</p>\n',
|
||||
'type': 'CurveType',
|
||||
'required': false,
|
||||
'default': 'CurveType.MonotoneX'
|
||||
}
|
||||
]" />
|
||||
|
||||
<APITable :type="'emit'" :data="[
|
||||
{
|
||||
'name': 'legendItemClick',
|
||||
'type': '[d: BulletLegendItemInterface, i: number]'
|
||||
}
|
||||
]" />
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { VisAxis, VisStackedBar, VisXYContainer } from '@unovis/vue'
|
||||
import { BarChart } from '@/lib/registry/new-york/ui/chart-bar'
|
||||
|
||||
type Data = typeof data[number]
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 5000) + 1000 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 5000) + 1000 },
|
||||
|
|
@ -19,28 +18,5 @@ const data = [
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<VisXYContainer height="350px" :margin="{ left: 20, right: 20 }" :data="data">
|
||||
<VisStackedBar
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d.total"
|
||||
color="#41b883"
|
||||
:rounded-corners="4"
|
||||
:bar-padding="0.15"
|
||||
/>
|
||||
<VisAxis
|
||||
type="x"
|
||||
:num-ticks="data.length"
|
||||
:tick-format="(index: number) => data[index]?.name"
|
||||
:grid-line="false"
|
||||
:tick-line="false" color="#888888"
|
||||
/>
|
||||
<VisAxis
|
||||
type="y"
|
||||
:num-ticks="data.length"
|
||||
:tick-format="(index: number) => data[index]?.name"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
:domain-line="false" color="#888888"
|
||||
/>
|
||||
</VisXYContainer>
|
||||
<BarChart :data="data" :categories="['total']" :index="'name'" :rounded-corners="4" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { AreaChart } from '@/lib/registry/default/ui/chart-area'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AreaChart
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total', 'predicted']"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
17
apps/www/src/lib/registry/default/example/AreaChartDemo.vue
Normal file
17
apps/www/src/lib/registry/default/example/AreaChartDemo.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import { AreaChart } from '@/lib/registry/default/ui/chart-area'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AreaChart :data="data" index="name" :categories="['total', 'predicted']" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<script setup lang="ts">
|
||||
import { CurveType } from '@unovis/ts'
|
||||
import { AreaChart } from '@/lib/registry/default/ui/chart-area'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Aug', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Sep', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Oct', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Nov', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Dec', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AreaChart
|
||||
class="h-[100px] w-[400px]"
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total']"
|
||||
:show-grid-line="false"
|
||||
:show-legend="false"
|
||||
:show-x-axis="false"
|
||||
:show-y-axis="false"
|
||||
:curve-type="CurveType.Linear"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { BarChart } from '@/lib/registry/default/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
:data="data"
|
||||
index="name"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
26
apps/www/src/lib/registry/default/example/BarChartDemo.vue
Normal file
26
apps/www/src/lib/registry/default/example/BarChartDemo.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<script setup lang="ts">
|
||||
import { BarChart } from '@/lib/registry/default/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
:data="data"
|
||||
index="name"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { BarChart } from '@/lib/registry/default/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:rounded-corners="4"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { BarChart } from '@/lib/registry/default/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:type="'stacked'"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -52,14 +52,9 @@ const lineY = (d: Data) => d.revenue
|
|||
left: 10,
|
||||
bottom: 0,
|
||||
}"
|
||||
:style="{
|
||||
'--theme-primary': `hsl(${
|
||||
theme?.cssVars[isDark ? 'dark' : 'light'].primary
|
||||
})`,
|
||||
}"
|
||||
>
|
||||
<VisLine :x="lineX" :y="lineY" color="var(--theme-primary)" />
|
||||
<VisScatter :x="lineX" :y="lineY" :size="6" stroke-color="var(--theme-primary)" :stroke-width="2" color="white" />
|
||||
<VisLine :x="lineX" :y="lineY" color="hsl(var(--primary))" />
|
||||
<VisScatter :x="lineX" :y="lineY" :size="6" stroke-color="hsl(var(--primary))" :stroke-width="2" color="white" />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
@ -91,7 +86,7 @@ const lineY = (d: Data) => d.revenue
|
|||
:x="lineX"
|
||||
:y="(d: Data) => d.subscription"
|
||||
:bar-padding="0.1"
|
||||
:rounded-corners="0" color="var(--theme-primary)"
|
||||
:rounded-corners="0" color="hsl(var(--primary))"
|
||||
/>
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ import {
|
|||
import { themes } from '@/lib/registry/themes'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
|
||||
const { isDark } = useData()
|
||||
const cfg = useConfigStore()
|
||||
const theme = computed(() => themes.find(theme => theme.name === cfg.config.value.theme))
|
||||
|
||||
const goal = ref(350)
|
||||
|
||||
type Data = typeof data[number]
|
||||
|
|
@ -84,16 +80,13 @@ const data = [
|
|||
:data="data"
|
||||
height="60px"
|
||||
:style="{
|
||||
'opacity': 0.2,
|
||||
'--theme-primary': `hsl(${
|
||||
theme?.cssVars[isDark ? 'dark' : 'light'].primary
|
||||
})`,
|
||||
opacity: 0.2,
|
||||
}"
|
||||
>
|
||||
<VisStackedBar
|
||||
:x="(d: Data, i :number) => i"
|
||||
:y="(d: Data) => d.goal"
|
||||
color="var(--theme-primary)"
|
||||
color="hsl(var(--primary))"
|
||||
:bar-padding="0.1"
|
||||
:rounded-corners="0"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ import {
|
|||
} from '@/lib/registry/default/ui/card'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
|
||||
const { themePrimary } = useConfigStore()
|
||||
|
||||
type Data = typeof data[number]
|
||||
const data = [
|
||||
{ average: 400, today: 240 },
|
||||
|
|
@ -74,15 +72,14 @@ function computeLineOpacity(val: any, index: number) {
|
|||
bottom: 0,
|
||||
}"
|
||||
:style="{
|
||||
'--theme-primary': themePrimary,
|
||||
'--vis-tooltip-padding': '0px',
|
||||
'--vis-tooltip-background-color': 'transparent',
|
||||
'--vis-tooltip-border-color': 'transparent',
|
||||
}"
|
||||
>
|
||||
<VisTooltip />
|
||||
<VisLine :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :stroke-width="2" color="var(--theme-primary)" :attributes="{ [Line.selectors.linePath]: { opacity: computeLineOpacity } }" />
|
||||
<VisScatter :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :size="6" :stroke-width="2" stroke-color="var(--theme-primary)" color="white" />
|
||||
<VisLine :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :stroke-width="2" color="hsl(var(--primary))" :attributes="{ [Line.selectors.linePath]: { opacity: computeLineOpacity } }" />
|
||||
<VisScatter :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :size="6" :stroke-width="2" stroke-color="hsl(var(--primary))" color="white" />
|
||||
<VisCrosshair :template="template" />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<script setup lang="ts">
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
|
||||
defineProps<{
|
||||
title?: string
|
||||
data: {
|
||||
name: string
|
||||
color: string
|
||||
value: any
|
||||
}[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="text-sm">
|
||||
<CardContent class="p-3 min-w-[180px] flex flex-col gap-2">
|
||||
<div v-for="(item, key) in data" :key="key" class="flex justify-between items-center">
|
||||
<div class="flex items-center">
|
||||
<span class="w-1 h-7 mr-4 rounded-full" :style="{ background: item.color }" />
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
<span class="font-semibold ml-4">{{ item.value }}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { DonutChart } from '@/lib/registry/new-york/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
|
||||
const valueFormatter = (tick: number | Date) => typeof tick === 'number' ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}` : ''
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:value-formatter="valueFormatter"
|
||||
:colors="['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'purple']"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { DonutChart } from '@/lib/registry/default/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
20
apps/www/src/lib/registry/default/example/DonutChartDemo.vue
Normal file
20
apps/www/src/lib/registry/default/example/DonutChartDemo.vue
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { DonutChart } from '@/lib/registry/default/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
/>
|
||||
</template>
|
||||
21
apps/www/src/lib/registry/default/example/DonutChartPie.vue
Normal file
21
apps/www/src/lib/registry/default/example/DonutChartPie.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import { DonutChart } from '@/lib/registry/default/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:type="'pie'"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { LineChart } from '@/lib/registry/default/ui/chart-line'
|
||||
|
||||
const data = [
|
||||
{
|
||||
'year': 1970,
|
||||
'Export Growth Rate': 2.04,
|
||||
'Import Growth Rate': 1.53,
|
||||
},
|
||||
{
|
||||
'year': 1971,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.58,
|
||||
},
|
||||
{
|
||||
'year': 1972,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1973,
|
||||
'Export Growth Rate': 1.93,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1974,
|
||||
'Export Growth Rate': 1.88,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1975,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.64,
|
||||
},
|
||||
{
|
||||
'year': 1976,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.62,
|
||||
},
|
||||
{
|
||||
'year': 1977,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.69,
|
||||
},
|
||||
{
|
||||
'year': 1978,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1979,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1980,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1981,
|
||||
'Export Growth Rate': 1.81,
|
||||
'Import Growth Rate': 1.72,
|
||||
},
|
||||
{
|
||||
'year': 1982,
|
||||
'Export Growth Rate': 1.84,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1983,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1984,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.78,
|
||||
},
|
||||
{
|
||||
'year': 1985,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.81,
|
||||
},
|
||||
{
|
||||
'year': 1986,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.89,
|
||||
},
|
||||
{
|
||||
'year': 1987,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.91,
|
||||
},
|
||||
{
|
||||
'year': 1988,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1989,
|
||||
'Export Growth Rate': 1.76,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1990,
|
||||
'Export Growth Rate': 1.75,
|
||||
'Import Growth Rate': 1.97,
|
||||
},
|
||||
{
|
||||
'year': 1991,
|
||||
'Export Growth Rate': 1.62,
|
||||
'Import Growth Rate': 1.99,
|
||||
},
|
||||
{
|
||||
'year': 1992,
|
||||
'Export Growth Rate': 1.56,
|
||||
'Import Growth Rate': 2.12,
|
||||
},
|
||||
{
|
||||
'year': 1993,
|
||||
'Export Growth Rate': 1.5,
|
||||
'Import Growth Rate': 2.13,
|
||||
},
|
||||
{
|
||||
'year': 1994,
|
||||
'Export Growth Rate': 1.46,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1995,
|
||||
'Export Growth Rate': 1.43,
|
||||
'Import Growth Rate': 2.17,
|
||||
},
|
||||
{
|
||||
'year': 1996,
|
||||
'Export Growth Rate': 1.4,
|
||||
'Import Growth Rate': 2.2,
|
||||
},
|
||||
{
|
||||
'year': 1997,
|
||||
'Export Growth Rate': 1.37,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1998,
|
||||
'Export Growth Rate': 1.34,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 1999,
|
||||
'Export Growth Rate': 1.32,
|
||||
'Import Growth Rate': 2.05,
|
||||
},
|
||||
{
|
||||
'year': 2000,
|
||||
'Export Growth Rate': 1.33,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 2001,
|
||||
'Export Growth Rate': 1.31,
|
||||
'Import Growth Rate': 2.08,
|
||||
},
|
||||
{
|
||||
'year': 2002,
|
||||
'Export Growth Rate': 1.29,
|
||||
'Import Growth Rate': 2.1,
|
||||
},
|
||||
{
|
||||
'year': 2003,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 2004,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.21,
|
||||
},
|
||||
{
|
||||
'year': 2005,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.23,
|
||||
},
|
||||
{
|
||||
'year': 2006,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.29,
|
||||
},
|
||||
{
|
||||
'year': 2007,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2008,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2009,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2010,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2011,
|
||||
'Export Growth Rate': 1.24,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2012,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2013,
|
||||
'Export Growth Rate': 1.22,
|
||||
'Import Growth Rate': 2.3,
|
||||
},
|
||||
{
|
||||
'year': 2014,
|
||||
'Export Growth Rate': 1.2,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2015,
|
||||
'Export Growth Rate': 1.17,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2016,
|
||||
'Export Growth Rate': 1.16,
|
||||
'Import Growth Rate': 2.41,
|
||||
},
|
||||
{
|
||||
'year': 2017,
|
||||
'Export Growth Rate': 1.13,
|
||||
'Import Growth Rate': 2.44,
|
||||
},
|
||||
{
|
||||
'year': 2018,
|
||||
'Export Growth Rate': 1.07,
|
||||
'Import Growth Rate': 2.45,
|
||||
},
|
||||
{
|
||||
'year': 2019,
|
||||
'Export Growth Rate': 1.03,
|
||||
'Import Growth Rate': 2.47,
|
||||
},
|
||||
{
|
||||
'year': 2020,
|
||||
'Export Growth Rate': 0.92,
|
||||
'Import Growth Rate': 2.48,
|
||||
},
|
||||
{
|
||||
'year': 2021,
|
||||
'Export Growth Rate': 0.82,
|
||||
'Import Growth Rate': 2.51,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LineChart
|
||||
:data="data"
|
||||
index="year"
|
||||
:categories="['Export Growth Rate', 'Import Growth Rate']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
279
apps/www/src/lib/registry/default/example/LineChartDemo.vue
Normal file
279
apps/www/src/lib/registry/default/example/LineChartDemo.vue
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<script setup lang="ts">
|
||||
import { LineChart } from '@/lib/registry/default/ui/chart-line'
|
||||
|
||||
const data = [
|
||||
{
|
||||
'year': 1970,
|
||||
'Export Growth Rate': 2.04,
|
||||
'Import Growth Rate': 1.53,
|
||||
},
|
||||
{
|
||||
'year': 1971,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.58,
|
||||
},
|
||||
{
|
||||
'year': 1972,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1973,
|
||||
'Export Growth Rate': 1.93,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1974,
|
||||
'Export Growth Rate': 1.88,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1975,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.64,
|
||||
},
|
||||
{
|
||||
'year': 1976,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.62,
|
||||
},
|
||||
{
|
||||
'year': 1977,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.69,
|
||||
},
|
||||
{
|
||||
'year': 1978,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1979,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1980,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1981,
|
||||
'Export Growth Rate': 1.81,
|
||||
'Import Growth Rate': 1.72,
|
||||
},
|
||||
{
|
||||
'year': 1982,
|
||||
'Export Growth Rate': 1.84,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1983,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1984,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.78,
|
||||
},
|
||||
{
|
||||
'year': 1985,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.81,
|
||||
},
|
||||
{
|
||||
'year': 1986,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.89,
|
||||
},
|
||||
{
|
||||
'year': 1987,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.91,
|
||||
},
|
||||
{
|
||||
'year': 1988,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1989,
|
||||
'Export Growth Rate': 1.76,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1990,
|
||||
'Export Growth Rate': 1.75,
|
||||
'Import Growth Rate': 1.97,
|
||||
},
|
||||
{
|
||||
'year': 1991,
|
||||
'Export Growth Rate': 1.62,
|
||||
'Import Growth Rate': 1.99,
|
||||
},
|
||||
{
|
||||
'year': 1992,
|
||||
'Export Growth Rate': 1.56,
|
||||
'Import Growth Rate': 2.12,
|
||||
},
|
||||
{
|
||||
'year': 1993,
|
||||
'Export Growth Rate': 1.5,
|
||||
'Import Growth Rate': 2.13,
|
||||
},
|
||||
{
|
||||
'year': 1994,
|
||||
'Export Growth Rate': 1.46,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1995,
|
||||
'Export Growth Rate': 1.43,
|
||||
'Import Growth Rate': 2.17,
|
||||
},
|
||||
{
|
||||
'year': 1996,
|
||||
'Export Growth Rate': 1.4,
|
||||
'Import Growth Rate': 2.2,
|
||||
},
|
||||
{
|
||||
'year': 1997,
|
||||
'Export Growth Rate': 1.37,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1998,
|
||||
'Export Growth Rate': 1.34,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 1999,
|
||||
'Export Growth Rate': 1.32,
|
||||
'Import Growth Rate': 2.05,
|
||||
},
|
||||
{
|
||||
'year': 2000,
|
||||
'Export Growth Rate': 1.33,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 2001,
|
||||
'Export Growth Rate': 1.31,
|
||||
'Import Growth Rate': 2.08,
|
||||
},
|
||||
{
|
||||
'year': 2002,
|
||||
'Export Growth Rate': 1.29,
|
||||
'Import Growth Rate': 2.1,
|
||||
},
|
||||
{
|
||||
'year': 2003,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 2004,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.21,
|
||||
},
|
||||
{
|
||||
'year': 2005,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.23,
|
||||
},
|
||||
{
|
||||
'year': 2006,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.29,
|
||||
},
|
||||
{
|
||||
'year': 2007,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2008,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2009,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2010,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2011,
|
||||
'Export Growth Rate': 1.24,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2012,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2013,
|
||||
'Export Growth Rate': 1.22,
|
||||
'Import Growth Rate': 2.3,
|
||||
},
|
||||
{
|
||||
'year': 2014,
|
||||
'Export Growth Rate': 1.2,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2015,
|
||||
'Export Growth Rate': 1.17,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2016,
|
||||
'Export Growth Rate': 1.16,
|
||||
'Import Growth Rate': 2.41,
|
||||
},
|
||||
{
|
||||
'year': 2017,
|
||||
'Export Growth Rate': 1.13,
|
||||
'Import Growth Rate': 2.44,
|
||||
},
|
||||
{
|
||||
'year': 2018,
|
||||
'Export Growth Rate': 1.07,
|
||||
'Import Growth Rate': 2.45,
|
||||
},
|
||||
{
|
||||
'year': 2019,
|
||||
'Export Growth Rate': 1.03,
|
||||
'Import Growth Rate': 2.47,
|
||||
},
|
||||
{
|
||||
'year': 2020,
|
||||
'Export Growth Rate': 0.92,
|
||||
'Import Growth Rate': 2.48,
|
||||
},
|
||||
{
|
||||
'year': 2021,
|
||||
'Export Growth Rate': 0.82,
|
||||
'Import Growth Rate': 2.51,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LineChart
|
||||
:data="data"
|
||||
index="year"
|
||||
:categories="['Export Growth Rate', 'Import Growth Rate']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
285
apps/www/src/lib/registry/default/example/LineChartSparkline.vue
Normal file
285
apps/www/src/lib/registry/default/example/LineChartSparkline.vue
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
<script setup lang="ts">
|
||||
import { LineChart } from '@/lib/registry/default/ui/chart-line'
|
||||
|
||||
const data = [
|
||||
{
|
||||
'year': 1970,
|
||||
'Export Growth Rate': 2.04,
|
||||
'Import Growth Rate': 1.53,
|
||||
},
|
||||
{
|
||||
'year': 1971,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.58,
|
||||
},
|
||||
{
|
||||
'year': 1972,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1973,
|
||||
'Export Growth Rate': 1.93,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1974,
|
||||
'Export Growth Rate': 1.88,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1975,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.64,
|
||||
},
|
||||
{
|
||||
'year': 1976,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.62,
|
||||
},
|
||||
{
|
||||
'year': 1977,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.69,
|
||||
},
|
||||
{
|
||||
'year': 1978,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1979,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1980,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1981,
|
||||
'Export Growth Rate': 1.81,
|
||||
'Import Growth Rate': 1.72,
|
||||
},
|
||||
{
|
||||
'year': 1982,
|
||||
'Export Growth Rate': 1.84,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1983,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1984,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.78,
|
||||
},
|
||||
{
|
||||
'year': 1985,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.81,
|
||||
},
|
||||
{
|
||||
'year': 1986,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.89,
|
||||
},
|
||||
{
|
||||
'year': 1987,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.91,
|
||||
},
|
||||
{
|
||||
'year': 1988,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1989,
|
||||
'Export Growth Rate': 1.76,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1990,
|
||||
'Export Growth Rate': 1.75,
|
||||
'Import Growth Rate': 1.97,
|
||||
},
|
||||
{
|
||||
'year': 1991,
|
||||
'Export Growth Rate': 1.62,
|
||||
'Import Growth Rate': 1.99,
|
||||
},
|
||||
{
|
||||
'year': 1992,
|
||||
'Export Growth Rate': 1.56,
|
||||
'Import Growth Rate': 2.12,
|
||||
},
|
||||
{
|
||||
'year': 1993,
|
||||
'Export Growth Rate': 1.5,
|
||||
'Import Growth Rate': 2.13,
|
||||
},
|
||||
{
|
||||
'year': 1994,
|
||||
'Export Growth Rate': 1.46,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1995,
|
||||
'Export Growth Rate': 1.43,
|
||||
'Import Growth Rate': 2.17,
|
||||
},
|
||||
{
|
||||
'year': 1996,
|
||||
'Export Growth Rate': 1.4,
|
||||
'Import Growth Rate': 2.2,
|
||||
},
|
||||
{
|
||||
'year': 1997,
|
||||
'Export Growth Rate': 1.37,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1998,
|
||||
'Export Growth Rate': 1.34,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 1999,
|
||||
'Export Growth Rate': 1.32,
|
||||
'Import Growth Rate': 2.05,
|
||||
},
|
||||
{
|
||||
'year': 2000,
|
||||
'Export Growth Rate': 1.33,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 2001,
|
||||
'Export Growth Rate': 1.31,
|
||||
'Import Growth Rate': 2.08,
|
||||
},
|
||||
{
|
||||
'year': 2002,
|
||||
'Export Growth Rate': 1.29,
|
||||
'Import Growth Rate': 2.1,
|
||||
},
|
||||
{
|
||||
'year': 2003,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 2004,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.21,
|
||||
},
|
||||
{
|
||||
'year': 2005,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.23,
|
||||
},
|
||||
{
|
||||
'year': 2006,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.29,
|
||||
},
|
||||
{
|
||||
'year': 2007,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2008,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2009,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2010,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2011,
|
||||
'Export Growth Rate': 1.24,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2012,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2013,
|
||||
'Export Growth Rate': 1.22,
|
||||
'Import Growth Rate': 2.3,
|
||||
},
|
||||
{
|
||||
'year': 2014,
|
||||
'Export Growth Rate': 1.2,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2015,
|
||||
'Export Growth Rate': 1.17,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2016,
|
||||
'Export Growth Rate': 1.16,
|
||||
'Import Growth Rate': 2.41,
|
||||
},
|
||||
{
|
||||
'year': 2017,
|
||||
'Export Growth Rate': 1.13,
|
||||
'Import Growth Rate': 2.44,
|
||||
},
|
||||
{
|
||||
'year': 2018,
|
||||
'Export Growth Rate': 1.07,
|
||||
'Import Growth Rate': 2.45,
|
||||
},
|
||||
{
|
||||
'year': 2019,
|
||||
'Export Growth Rate': 1.03,
|
||||
'Import Growth Rate': 2.47,
|
||||
},
|
||||
{
|
||||
'year': 2020,
|
||||
'Export Growth Rate': 0.92,
|
||||
'Import Growth Rate': 2.48,
|
||||
},
|
||||
{
|
||||
'year': 2021,
|
||||
'Export Growth Rate': 0.82,
|
||||
'Import Growth Rate': 2.51,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LineChart
|
||||
index="year"
|
||||
class="h-[100px] w-[400px]"
|
||||
:data="data"
|
||||
:categories="['Export Growth Rate']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:show-tooltip="false"
|
||||
:show-grid-line="false"
|
||||
:show-legend="false"
|
||||
:show-x-axis="false"
|
||||
:show-y-axis="false"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -19,6 +19,7 @@ export const buttonVariants = cva(
|
|||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
xs: 'h-7 rounded px-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10',
|
||||
|
|
|
|||
135
apps/www/src/lib/registry/default/ui/chart-area/AreaChart.vue
Normal file
135
apps/www/src/lib/registry/default/ui/chart-area/AreaChart.vue
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
|
||||
import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
|
||||
import { Area, Axis, Line } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
/**
|
||||
* Type of curve
|
||||
*/
|
||||
curveType?: CurveType
|
||||
/**
|
||||
* Controls the visibility of gradient.
|
||||
* @default true
|
||||
*/
|
||||
showGradiant?: boolean
|
||||
}>(), {
|
||||
curveType: CurveType.MonotoneX,
|
||||
filterOpacity: 0.2,
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
showGridLine: true,
|
||||
showGradiant: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
}>()
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
||||
|
||||
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
||||
name: category,
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const isMounted = useMounted()
|
||||
|
||||
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
||||
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
||||
|
||||
<VisXYContainer :style="{ height: isMounted ? '100%' : 'auto' }" :margin="{ left: 20, right: 20 }" :data="data">
|
||||
<svg width="0" height="0">
|
||||
<defs>
|
||||
<linearGradient v-for="(color, i) in colors" :id="`color-${i}`" :key="i" x1="0" y1="0" x2="0" y2="1">
|
||||
<template v-if="showGradiant">
|
||||
<stop offset="5%" :stop-color="color" stop-opacity="0.4" />
|
||||
<stop offset="95%" :stop-color="color" stop-opacity="0" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<stop offset="0%" :stop-color="color" />
|
||||
</template>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
|
||||
|
||||
<template v-for="(category, i) in categories" :key="category">
|
||||
<VisArea
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d[category]"
|
||||
color="auto"
|
||||
:curve-type="curveType"
|
||||
:attributes="{
|
||||
[Area.selectors.area]: {
|
||||
fill: `url(#color-${i})`,
|
||||
},
|
||||
}"
|
||||
:opacity="legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-for="(category, i) in categories" :key="category">
|
||||
<VisLine
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d[category]"
|
||||
:color="colors[i]"
|
||||
:curve-type="curveType"
|
||||
:attributes="{
|
||||
[Line.selectors.line]: {
|
||||
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VisAxis
|
||||
v-if="showXAxis"
|
||||
type="x"
|
||||
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
<VisAxis
|
||||
v-if="showYAxis"
|
||||
type="y"
|
||||
:tick-line="false"
|
||||
:tick-format="yFormatter"
|
||||
:domain-line="false"
|
||||
:grid-line="showGridLine"
|
||||
:attributes="{
|
||||
[Axis.selectors.grid]: {
|
||||
class: 'text-muted',
|
||||
},
|
||||
}"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</template>
|
||||
1
apps/www/src/lib/registry/default/ui/chart-area/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/chart-area/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as AreaChart } from './AreaChart.vue'
|
||||
114
apps/www/src/lib/registry/default/ui/chart-bar/BarChart.vue
Normal file
114
apps/www/src/lib/registry/default/ui/chart-bar/BarChart.vue
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import type { BulletLegendItemInterface, Spacing } from '@unovis/ts'
|
||||
import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue'
|
||||
import { Axis, GroupedBar, StackedBar } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
/**
|
||||
* Change the type of the chart
|
||||
* @default "grouped"
|
||||
*/
|
||||
type?: 'stacked' | 'grouped'
|
||||
/**
|
||||
* Rounded bar corners
|
||||
* @default 0
|
||||
*/
|
||||
roundedCorners?: number
|
||||
}>(), {
|
||||
type: 'grouped',
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
filterOpacity: 0.2,
|
||||
roundedCorners: 0,
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
showGridLine: true,
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
}>()
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
||||
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
||||
name: category,
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const isMounted = useMounted()
|
||||
|
||||
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
}
|
||||
|
||||
const VisBarComponent = computed(() => props.type === 'grouped' ? VisGroupedBar : VisStackedBar)
|
||||
const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.selectors.bar : StackedBar.selectors.bar)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
||||
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
||||
|
||||
<VisXYContainer
|
||||
:data="data"
|
||||
:style="{ height: isMounted ? '100%' : 'auto' }"
|
||||
:margin="margin"
|
||||
>
|
||||
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :custom-tooltip="customTooltip" :index="index" />
|
||||
|
||||
<VisBarComponent
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="categories.map(category => (d: Data) => d[category]) "
|
||||
:color="colors"
|
||||
:rounded-corners="roundedCorners"
|
||||
:bar-padding="0.05"
|
||||
:attributes="{
|
||||
[selectorsBar]: {
|
||||
opacity: (d: Data, i:number) => {
|
||||
const pos = i % categories.length
|
||||
return legendItems[pos]?.inactive ? filterOpacity : 1
|
||||
},
|
||||
},
|
||||
}"
|
||||
/>
|
||||
|
||||
<VisAxis
|
||||
v-if="showXAxis"
|
||||
type="x"
|
||||
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
<VisAxis
|
||||
v-if="showYAxis"
|
||||
type="y"
|
||||
:tick-line="false"
|
||||
:tick-format="yFormatter"
|
||||
:domain-line="false"
|
||||
:grid-line="showGridLine"
|
||||
:attributes="{
|
||||
[Axis.selectors.grid]: {
|
||||
class: 'text-muted',
|
||||
},
|
||||
}"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</template>
|
||||
1
apps/www/src/lib/registry/default/ui/chart-bar/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/chart-bar/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as BarChart } from './BarChart.vue'
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import { VisDonut, VisSingleContainer } from '@unovis/vue'
|
||||
import { Donut } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartSingleTooltip, defaultColors } from '@/lib/registry/default/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<Pick<BaseChartProps<T>, 'data' | 'colors' | 'index' | 'margin' | 'showLegend' | 'showTooltip' | 'filterOpacity'> & {
|
||||
/**
|
||||
* Sets the name of the key containing the quantitative chart values.
|
||||
*/
|
||||
category: KeyOfT
|
||||
/**
|
||||
* Change the type of the chart
|
||||
* @default "donut"
|
||||
*/
|
||||
type?: 'donut' | 'pie'
|
||||
/**
|
||||
* Function to sort the segment
|
||||
*/
|
||||
sortFunction?: (a: any, b: any) => number | undefined
|
||||
/**
|
||||
* Controls the formatting for the label.
|
||||
*/
|
||||
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
}>(), {
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
sortFunction: () => undefined,
|
||||
valueFormatter: (tick: number) => `${tick}`,
|
||||
type: 'donut',
|
||||
filterOpacity: 0.2,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
})
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const category = computed(() => props.category as KeyOfT)
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
|
||||
const isMounted = useMounted()
|
||||
const activeSegmentKey = ref<string>()
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.data.filter(d => d[props.category]).filter(Boolean).length))
|
||||
const legendItems = computed(() => props.data.map((item, i) => ({
|
||||
name: item[props.index],
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const totalValue = computed(() => props.data.reduce((prev, curr) => {
|
||||
return prev + curr[props.category]
|
||||
}, 0))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-48 flex flex-col items-end', $attrs.class ?? '')">
|
||||
<VisSingleContainer :style="{ height: isMounted ? '100%' : 'auto' }" :margin="{ left: 20, right: 20 }" :data="data">
|
||||
<ChartSingleTooltip
|
||||
:selector="Donut.selectors.segment"
|
||||
:index="category"
|
||||
:items="legendItems"
|
||||
:value-formatter="valueFormatter"
|
||||
:custom-tooltip="customTooltip"
|
||||
/>
|
||||
|
||||
<VisDonut
|
||||
:value="(d: Data) => d[category]"
|
||||
:sort-function="sortFunction"
|
||||
:color="colors"
|
||||
:arc-width="type === 'donut' ? 20 : 0"
|
||||
:show-background="false"
|
||||
:central-label="type === 'donut' ? valueFormatter(totalValue) : ''"
|
||||
:events="{
|
||||
[Donut.selectors.segment]: {
|
||||
click: (d: Data, ev: PointerEvent, i: number, elements: HTMLElement[]) => {
|
||||
if (d?.data?.[index] === activeSegmentKey) {
|
||||
activeSegmentKey = undefined
|
||||
elements.forEach(el => el.style.opacity = '1')
|
||||
}
|
||||
else {
|
||||
activeSegmentKey = d?.data?.[index]
|
||||
elements.forEach(el => el.style.opacity = `${filterOpacity}`)
|
||||
elements[i].style.opacity = '1'
|
||||
}
|
||||
},
|
||||
},
|
||||
}"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisSingleContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DonutChart } from './DonutChart.vue'
|
||||
104
apps/www/src/lib/registry/default/ui/chart-line/LineChart.vue
Normal file
104
apps/www/src/lib/registry/default/ui/chart-line/LineChart.vue
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
|
||||
import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
|
||||
import { Axis, Line } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
/**
|
||||
* Type of curve
|
||||
*/
|
||||
curveType?: CurveType
|
||||
}>(), {
|
||||
curveType: CurveType.MonotoneX,
|
||||
filterOpacity: 0.2,
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
showGridLine: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
}>()
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
||||
|
||||
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
||||
name: category,
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const isMounted = useMounted()
|
||||
|
||||
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
||||
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
||||
|
||||
<VisXYContainer
|
||||
:margin="{ left: 20, right: 20 }"
|
||||
:data="data"
|
||||
:style="{ height: isMounted ? '100%' : 'auto' }"
|
||||
>
|
||||
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
|
||||
|
||||
<template v-for="(category, i) in categories" :key="category">
|
||||
<VisLine
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d[category]"
|
||||
:curve-type="curveType"
|
||||
:color="colors[i]"
|
||||
:attributes="{
|
||||
[Line.selectors.line]: {
|
||||
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VisAxis
|
||||
v-if="showXAxis"
|
||||
type="x"
|
||||
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
<VisAxis
|
||||
v-if="showYAxis"
|
||||
type="y"
|
||||
:tick-line="false"
|
||||
:tick-format="yFormatter"
|
||||
:domain-line="false"
|
||||
:grid-line="showGridLine"
|
||||
:attributes="{
|
||||
[Axis.selectors.grid]: {
|
||||
class: 'text-muted',
|
||||
},
|
||||
}"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</template>
|
||||
1
apps/www/src/lib/registry/default/ui/chart-line/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/chart-line/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as LineChart } from './LineChart.vue'
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
import { VisCrosshair, VisTooltip } from '@unovis/vue'
|
||||
import type { BulletLegendItemInterface } from '@unovis/ts'
|
||||
import { omit } from '@unovis/ts'
|
||||
import { type Component, createApp } from 'vue'
|
||||
import { ChartTooltip } from '@/lib/registry/default/ui/chart'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
colors: string[]
|
||||
index: string
|
||||
items: BulletLegendItemInterface[]
|
||||
customTooltip?: Component
|
||||
}>(), {
|
||||
colors: () => [],
|
||||
})
|
||||
|
||||
// Use weakmap to store reference to each datapoint for Tooltip
|
||||
const wm = new WeakMap()
|
||||
function template(d: any) {
|
||||
if (wm.has(d)) {
|
||||
return wm.get(d)
|
||||
}
|
||||
else {
|
||||
const componentDiv = document.createElement('div')
|
||||
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
|
||||
const legendReference = props.items.find(i => i.name === key)
|
||||
return { ...legendReference, value }
|
||||
})
|
||||
const TooltipComponent = props.customTooltip ?? ChartTooltip
|
||||
createApp(TooltipComponent, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv)
|
||||
wm.set(d, componentDiv.innerHTML)
|
||||
return componentDiv.innerHTML
|
||||
}
|
||||
}
|
||||
|
||||
function color(d: unknown, i: number) {
|
||||
return props.colors[i] ?? 'transparent'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VisTooltip :horizontal-shift="20" :vertical-shift="20" />
|
||||
<VisCrosshair :template="template" :color="color" />
|
||||
</template>
|
||||
50
apps/www/src/lib/registry/default/ui/chart/ChartLegend.vue
Normal file
50
apps/www/src/lib/registry/default/ui/chart/ChartLegend.vue
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<script setup lang="ts">
|
||||
import { VisBulletLegend } from '@unovis/vue'
|
||||
import type { BulletLegendItemInterface } from '@unovis/ts'
|
||||
import { BulletLegend } from '@unovis/ts'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { buttonVariants } from '@/lib/registry/default/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<{ items: BulletLegendItemInterface[] }>(), {
|
||||
items: () => [],
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
'legendItemClick': [d: BulletLegendItemInterface, i: number]
|
||||
'update:items': [payload: BulletLegendItemInterface[]]
|
||||
}>()
|
||||
|
||||
const elRef = ref<HTMLElement>()
|
||||
|
||||
onMounted(() => {
|
||||
const selector = `.${BulletLegend.selectors.item}`
|
||||
nextTick(() => {
|
||||
const elements = elRef.value?.querySelectorAll(selector)
|
||||
const classes = buttonVariants({ variant: 'ghost', size: 'xs' }).split(' ')
|
||||
elements?.forEach(el => el.classList.add(...classes, '!inline-flex', '!mr-2'))
|
||||
})
|
||||
})
|
||||
|
||||
function onLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
const isBulletActive = !props.items[i].inactive
|
||||
const isFilterApplied = props.items.some(i => i.inactive)
|
||||
if (isFilterApplied && isBulletActive) {
|
||||
// reset filter
|
||||
emits('update:items', props.items.map(item => ({ ...item, inactive: false })))
|
||||
}
|
||||
else {
|
||||
// apply selection, set other item as inactive
|
||||
emits('update:items', props.items.map(item => item.name === d.name ? ({ ...d, inactive: false }) : { ...item, inactive: true }))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="elRef" class="w-max">
|
||||
<VisBulletLegend
|
||||
:items="items"
|
||||
:on-legend-item-click="onLegendItemClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import { VisTooltip } from '@unovis/vue'
|
||||
import type { BulletLegendItemInterface } from '@unovis/ts'
|
||||
import { omit } from '@unovis/ts'
|
||||
import { type Component, createApp } from 'vue'
|
||||
import { ChartTooltip } from '@/lib/registry/default/ui/chart'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
selector: string
|
||||
index: string
|
||||
items?: BulletLegendItemInterface[]
|
||||
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
|
||||
customTooltip?: Component
|
||||
}>(), {
|
||||
valueFormatter: (tick: number) => `${tick}`,
|
||||
})
|
||||
|
||||
// Use weakmap to store reference to each datapoint for Tooltip
|
||||
const wm = new WeakMap()
|
||||
function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
|
||||
if (props.index in d) {
|
||||
if (wm.has(d)) {
|
||||
return wm.get(d)
|
||||
}
|
||||
else {
|
||||
const componentDiv = document.createElement('div')
|
||||
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
|
||||
const legendReference = props.items?.find(i => i.name === key)
|
||||
return { ...legendReference, value: props.valueFormatter(value) }
|
||||
})
|
||||
const TooltipComponent = props.customTooltip ?? ChartTooltip
|
||||
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
|
||||
wm.set(d, componentDiv.innerHTML)
|
||||
return componentDiv.innerHTML
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
const data = d.data
|
||||
|
||||
if (wm.has(data)) {
|
||||
return wm.get(data)
|
||||
}
|
||||
else {
|
||||
const style = getComputedStyle(elements[i])
|
||||
const omittedData = [{ name: data.name, value: props.valueFormatter(data[props.index]), color: style.fill }]
|
||||
const componentDiv = document.createElement('div')
|
||||
const TooltipComponent = props.customTooltip ?? ChartTooltip
|
||||
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
|
||||
wm.set(d, componentDiv.innerHTML)
|
||||
return componentDiv.innerHTML
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VisTooltip
|
||||
:horizontal-shift="20" :vertical-shift="20" :triggers="{
|
||||
[selector]: template,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
40
apps/www/src/lib/registry/default/ui/chart/ChartTooltip.vue
Normal file
40
apps/www/src/lib/registry/default/ui/chart/ChartTooltip.vue
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/lib/registry/default/ui/card'
|
||||
|
||||
defineProps<{
|
||||
title?: string
|
||||
data: {
|
||||
name: string
|
||||
color: string
|
||||
value: any
|
||||
}[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="text-sm">
|
||||
<CardHeader v-if="title" class="p-3 border-b">
|
||||
<CardTitle>
|
||||
{{ title }}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="p-3 min-w-[180px] flex flex-col gap-1">
|
||||
<div v-for="(item, key) in data" :key="key" class="flex justify-between">
|
||||
<div class="flex items-center">
|
||||
<span class="w-2.5 h-2.5 mr-2">
|
||||
<svg width="100%" height="100%" viewBox="0 0 30 30">
|
||||
<path
|
||||
d=" M 15 15 m -14, 0 a 14,14 0 1,1 28,0 a 14,14 0 1,1 -28,0"
|
||||
:stroke="item.color"
|
||||
:fill="item.color"
|
||||
stroke-width="1"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
<span class="font-semibold ml-4">{{ item.value }}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
18
apps/www/src/lib/registry/default/ui/chart/index.ts
Normal file
18
apps/www/src/lib/registry/default/ui/chart/index.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export { default as ChartTooltip } from './ChartTooltip.vue'
|
||||
export { default as ChartSingleTooltip } from './ChartSingleTooltip.vue'
|
||||
export { default as ChartLegend } from './ChartLegend.vue'
|
||||
export { default as ChartCrosshair } from './ChartCrosshair.vue'
|
||||
|
||||
export function defaultColors(count: number = 3) {
|
||||
const quotient = Math.floor(count / 2)
|
||||
const remainder = count % 2
|
||||
|
||||
const primaryCount = quotient + remainder
|
||||
const secondaryCount = quotient
|
||||
return [
|
||||
...Array.from(Array(primaryCount).keys()).map(i => `hsl(var(--vis-primary-color) / ${1 - (1 / primaryCount) * i})`),
|
||||
...Array.from(Array(secondaryCount).keys()).map(i => `hsl(var(--vis-secondary-color) / ${1 - (1 / secondaryCount) * i})`),
|
||||
]
|
||||
}
|
||||
|
||||
export * from './interface'
|
||||
64
apps/www/src/lib/registry/default/ui/chart/interface.ts
Normal file
64
apps/www/src/lib/registry/default/ui/chart/interface.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import type { Spacing } from '@unovis/ts'
|
||||
|
||||
type KeyOf<T extends Record<string, any>> = Extract<keyof T, string>
|
||||
|
||||
export interface BaseChartProps<T extends Record<string, any>> {
|
||||
/**
|
||||
* The source data, in which each entry is a dictionary.
|
||||
*/
|
||||
data: T[]
|
||||
/**
|
||||
* Select the categories from your data. Used to populate the legend and toolip.
|
||||
*/
|
||||
categories: KeyOf<T>[]
|
||||
/**
|
||||
* Sets the key to map the data to the axis.
|
||||
*/
|
||||
index: KeyOf<T>
|
||||
/**
|
||||
* Change the default colors.
|
||||
*/
|
||||
colors?: string[]
|
||||
/**
|
||||
* Margin of each the container
|
||||
*/
|
||||
margin?: Spacing
|
||||
/**
|
||||
* Change the opacity of the non-selected field
|
||||
* @default 0.2
|
||||
*/
|
||||
filterOpacity?: number
|
||||
/**
|
||||
* Function to format X label
|
||||
*/
|
||||
xFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
|
||||
/**
|
||||
* Function to format Y label
|
||||
*/
|
||||
yFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
|
||||
/**
|
||||
* Controls the visibility of the X axis.
|
||||
* @default true
|
||||
*/
|
||||
showXAxis?: boolean
|
||||
/**
|
||||
* Controls the visibility of the Y axis.
|
||||
* @default true
|
||||
*/
|
||||
showYAxis?: boolean
|
||||
/**
|
||||
* Controls the visibility of tooltip.
|
||||
* @default true
|
||||
*/
|
||||
showTooltip?: boolean
|
||||
/**
|
||||
* Controls the visibility of legend.
|
||||
* @default true
|
||||
*/
|
||||
showLegend?: boolean
|
||||
/**
|
||||
* Controls the visibility of gridline.
|
||||
* @default true
|
||||
*/
|
||||
showGridLine?: boolean
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { AreaChart } from '@/lib/registry/new-york/ui/chart-area'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AreaChart
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total', 'predicted']"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
17
apps/www/src/lib/registry/new-york/example/AreaChartDemo.vue
Normal file
17
apps/www/src/lib/registry/new-york/example/AreaChartDemo.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import { AreaChart } from '@/lib/registry/new-york/ui/chart-area'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AreaChart :data="data" index="name" :categories="['total', 'predicted']" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<script setup lang="ts">
|
||||
import { CurveType } from '@unovis/ts'
|
||||
import { AreaChart } from '@/lib/registry/new-york/ui/chart-area'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Aug', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Sep', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Oct', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Nov', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
{ name: 'Dec', total: Math.floor(Math.random() * 2000) + 1000 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AreaChart
|
||||
class="h-[100px] w-[400px]"
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total']"
|
||||
:show-grid-line="false"
|
||||
:show-legend="false"
|
||||
:show-x-axis="false"
|
||||
:show-y-axis="false"
|
||||
:curve-type="CurveType.Linear"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { BarChart } from '@/lib/registry/new-york/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
:data="data"
|
||||
index="name"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
26
apps/www/src/lib/registry/new-york/example/BarChartDemo.vue
Normal file
26
apps/www/src/lib/registry/new-york/example/BarChartDemo.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<script setup lang="ts">
|
||||
import { BarChart } from '@/lib/registry/new-york/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
:data="data"
|
||||
index="name"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { BarChart } from '@/lib/registry/new-york/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:rounded-corners="4"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { BarChart } from '@/lib/registry/new-york/ui/chart-bar'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BarChart
|
||||
index="name"
|
||||
:data="data"
|
||||
:categories="['total', 'predicted']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:type="'stacked'"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { VisLine, VisScatter, VisStackedBar, VisXYContainer } from '@unovis/vue'
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import { VisScatter, VisStackedBar, VisXYContainer } from '@unovis/vue'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/lib/registry/new-york/ui/card'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
import { themes } from '@/lib/registry/themes'
|
||||
import { LineChart } from '@/lib/registry/new-york/ui/chart-line'
|
||||
import { BarChart } from '@/lib/registry/new-york/ui/chart-bar'
|
||||
|
||||
type Data = typeof data[number]
|
||||
const data = [
|
||||
|
|
@ -17,14 +15,6 @@ const data = [
|
|||
{ revenue: 11244, subscription: 278 },
|
||||
{ revenue: 26475, subscription: 189 },
|
||||
]
|
||||
|
||||
const cfg = useConfigStore()
|
||||
|
||||
const { isDark } = useData()
|
||||
const theme = computed(() => themes.find(theme => theme.name === cfg.config.value.theme))
|
||||
|
||||
const lineX = (d: Data, i: number) => i
|
||||
const lineY = (d: Data) => d.revenue
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -44,23 +34,27 @@ const lineY = (d: Data) => d.revenue
|
|||
</p>
|
||||
|
||||
<div class="h-20">
|
||||
<VisXYContainer
|
||||
height="80px"
|
||||
:data="data" :margin="{
|
||||
top: 5,
|
||||
right: 10,
|
||||
left: 10,
|
||||
bottom: 0,
|
||||
}"
|
||||
:style="{
|
||||
'--theme-primary': `hsl(${
|
||||
theme?.cssVars[isDark ? 'dark' : 'light'].primary
|
||||
})`,
|
||||
}"
|
||||
<LineChart
|
||||
class="h-[80px]"
|
||||
:data="data"
|
||||
:margin="{ top: 5, right: 10, left: 10, bottom: 0 }"
|
||||
:categories="['revenue']"
|
||||
:index="'revenue'"
|
||||
:show-grid-line="false"
|
||||
:show-legend="false"
|
||||
:show-tooltip="false"
|
||||
:show-x-axis="false"
|
||||
:show-y-axis="false"
|
||||
>
|
||||
<VisLine :x="lineX" :y="lineY" color="var(--theme-primary)" />
|
||||
<VisScatter :x="lineX" :y="lineY" :size="6" stroke-color="var(--theme-primary)" :stroke-width="2" color="white" />
|
||||
</VisXYContainer>
|
||||
<VisScatter
|
||||
color="white"
|
||||
stroke-color="hsl(var(--primary))"
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d.revenue"
|
||||
:size="6"
|
||||
:stroke-width="2"
|
||||
/>
|
||||
</LineChart>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -80,20 +74,27 @@ const lineY = (d: Data) => d.revenue
|
|||
</p>
|
||||
|
||||
<div class="mt-4 h-20">
|
||||
<VisXYContainer
|
||||
height="80px" :data="data" :style="{
|
||||
'--theme-primary': `hsl(${
|
||||
theme?.cssVars[isDark ? 'dark' : 'light'].primary
|
||||
})`,
|
||||
}"
|
||||
<BarChart
|
||||
class="h-[80px]"
|
||||
:data="data"
|
||||
:categories="['subscription']"
|
||||
:index="'subscription'"
|
||||
:show-grid-line="false"
|
||||
:show-legend="false"
|
||||
:show-x-axis="false"
|
||||
:show-y-axis="false"
|
||||
:show-tooltip="false"
|
||||
/>
|
||||
<!-- <VisXYContainer
|
||||
height="80px" :data="data"
|
||||
>
|
||||
<VisStackedBar
|
||||
:x="lineX"
|
||||
:x="(d: Data, i:number) => i"
|
||||
:y="(d: Data) => d.subscription"
|
||||
:bar-padding="0.1"
|
||||
:rounded-corners="0" color="var(--theme-primary)"
|
||||
:rounded-corners="0" color="hsl(var(--primary))"
|
||||
/>
|
||||
</VisXYContainer>
|
||||
</VisXYContainer> -->
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/lib/registry/new-york/ui/card'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
|
||||
const { themePrimary } = useConfigStore()
|
||||
|
||||
const goal = ref(350)
|
||||
|
||||
|
|
@ -80,14 +77,13 @@ const data = [
|
|||
:data="data"
|
||||
height="60px"
|
||||
:style="{
|
||||
'opacity': 0.2,
|
||||
'--theme-primary': themePrimary,
|
||||
opacity: 0.2,
|
||||
}"
|
||||
>
|
||||
<VisStackedBar
|
||||
:x="(d: Data, i :number) => i"
|
||||
:y="(d: Data) => d.goal"
|
||||
color="var(--theme-primary)"
|
||||
color="hsl(var(--primary))"
|
||||
:bar-padding="0.1"
|
||||
:rounded-corners="0"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ import {
|
|||
} from '@/lib/registry/new-york/ui/card'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
|
||||
const { themePrimary } = useConfigStore()
|
||||
|
||||
type Data = typeof data[number]
|
||||
const data = [
|
||||
{ average: 400, today: 240 },
|
||||
|
|
@ -74,15 +72,14 @@ function computeLineOpacity(val: any, index: number) {
|
|||
bottom: 0,
|
||||
}"
|
||||
:style="{
|
||||
'--theme-primary': themePrimary,
|
||||
'--vis-tooltip-padding': '0px',
|
||||
'--vis-tooltip-background-color': 'transparent',
|
||||
'--vis-tooltip-border-color': 'transparent',
|
||||
}"
|
||||
>
|
||||
<VisTooltip />
|
||||
<VisLine :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :stroke-width="2" color="var(--theme-primary)" :attributes="{ [Line.selectors.linePath]: { opacity: computeLineOpacity } }" />
|
||||
<VisScatter :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :size="6" :stroke-width="2" stroke-color="var(--theme-primary)" color="white" />
|
||||
<VisLine :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :stroke-width="2" color="hsl(var(--primary))" :attributes="{ [Line.selectors.linePath]: { opacity: computeLineOpacity } }" />
|
||||
<VisScatter :x="x" :y="[(d: Data) => d.average, (d: Data) => d.today]" :size="6" :stroke-width="2" stroke-color="hsl(var(--primary))" color="white" />
|
||||
<VisCrosshair :template="template" />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<script setup lang="ts">
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
|
||||
defineProps<{
|
||||
title?: string
|
||||
data: {
|
||||
name: string
|
||||
color: string
|
||||
value: any
|
||||
}[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="text-sm">
|
||||
<CardContent class="p-3 min-w-[180px] flex flex-col gap-2">
|
||||
<div v-for="(item, key) in data" :key="key" class="flex justify-between items-center">
|
||||
<div class="flex items-center">
|
||||
<span class="w-1 h-7 mr-4 rounded-full" :style="{ background: item.color }" />
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
<span class="font-semibold ml-4">{{ item.value }}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { DonutChart } from '@/lib/registry/new-york/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
|
||||
const valueFormatter = (tick: number | Date) => typeof tick === 'number' ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}` : ''
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:value-formatter="valueFormatter"
|
||||
:colors="['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'purple']"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { DonutChart } from '@/lib/registry/new-york/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { DonutChart } from '@/lib/registry/new-york/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
|
||||
const valueFormatter = (tick: number | Date) => typeof tick === 'number' ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}` : ''
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:value-formatter="valueFormatter"
|
||||
/>
|
||||
</template>
|
||||
21
apps/www/src/lib/registry/new-york/example/DonutChartPie.vue
Normal file
21
apps/www/src/lib/registry/new-york/example/DonutChartPie.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import { DonutChart } from '@/lib/registry/new-york/ui/chart-donut'
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
{ name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DonutChart
|
||||
index="name"
|
||||
:category="'total'"
|
||||
:data="data"
|
||||
:type="'pie'"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
<script setup lang="ts">
|
||||
import CustomChartTooltip from './CustomChartTooltip.vue'
|
||||
import { LineChart } from '@/lib/registry/new-york/ui/chart-line'
|
||||
|
||||
const data = [
|
||||
{
|
||||
'year': 1970,
|
||||
'Export Growth Rate': 2.04,
|
||||
'Import Growth Rate': 1.53,
|
||||
},
|
||||
{
|
||||
'year': 1971,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.58,
|
||||
},
|
||||
{
|
||||
'year': 1972,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1973,
|
||||
'Export Growth Rate': 1.93,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1974,
|
||||
'Export Growth Rate': 1.88,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1975,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.64,
|
||||
},
|
||||
{
|
||||
'year': 1976,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.62,
|
||||
},
|
||||
{
|
||||
'year': 1977,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.69,
|
||||
},
|
||||
{
|
||||
'year': 1978,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1979,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1980,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1981,
|
||||
'Export Growth Rate': 1.81,
|
||||
'Import Growth Rate': 1.72,
|
||||
},
|
||||
{
|
||||
'year': 1982,
|
||||
'Export Growth Rate': 1.84,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1983,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1984,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.78,
|
||||
},
|
||||
{
|
||||
'year': 1985,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.81,
|
||||
},
|
||||
{
|
||||
'year': 1986,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.89,
|
||||
},
|
||||
{
|
||||
'year': 1987,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.91,
|
||||
},
|
||||
{
|
||||
'year': 1988,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1989,
|
||||
'Export Growth Rate': 1.76,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1990,
|
||||
'Export Growth Rate': 1.75,
|
||||
'Import Growth Rate': 1.97,
|
||||
},
|
||||
{
|
||||
'year': 1991,
|
||||
'Export Growth Rate': 1.62,
|
||||
'Import Growth Rate': 1.99,
|
||||
},
|
||||
{
|
||||
'year': 1992,
|
||||
'Export Growth Rate': 1.56,
|
||||
'Import Growth Rate': 2.12,
|
||||
},
|
||||
{
|
||||
'year': 1993,
|
||||
'Export Growth Rate': 1.5,
|
||||
'Import Growth Rate': 2.13,
|
||||
},
|
||||
{
|
||||
'year': 1994,
|
||||
'Export Growth Rate': 1.46,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1995,
|
||||
'Export Growth Rate': 1.43,
|
||||
'Import Growth Rate': 2.17,
|
||||
},
|
||||
{
|
||||
'year': 1996,
|
||||
'Export Growth Rate': 1.4,
|
||||
'Import Growth Rate': 2.2,
|
||||
},
|
||||
{
|
||||
'year': 1997,
|
||||
'Export Growth Rate': 1.37,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1998,
|
||||
'Export Growth Rate': 1.34,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 1999,
|
||||
'Export Growth Rate': 1.32,
|
||||
'Import Growth Rate': 2.05,
|
||||
},
|
||||
{
|
||||
'year': 2000,
|
||||
'Export Growth Rate': 1.33,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 2001,
|
||||
'Export Growth Rate': 1.31,
|
||||
'Import Growth Rate': 2.08,
|
||||
},
|
||||
{
|
||||
'year': 2002,
|
||||
'Export Growth Rate': 1.29,
|
||||
'Import Growth Rate': 2.1,
|
||||
},
|
||||
{
|
||||
'year': 2003,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 2004,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.21,
|
||||
},
|
||||
{
|
||||
'year': 2005,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.23,
|
||||
},
|
||||
{
|
||||
'year': 2006,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.29,
|
||||
},
|
||||
{
|
||||
'year': 2007,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2008,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2009,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2010,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2011,
|
||||
'Export Growth Rate': 1.24,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2012,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2013,
|
||||
'Export Growth Rate': 1.22,
|
||||
'Import Growth Rate': 2.3,
|
||||
},
|
||||
{
|
||||
'year': 2014,
|
||||
'Export Growth Rate': 1.2,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2015,
|
||||
'Export Growth Rate': 1.17,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2016,
|
||||
'Export Growth Rate': 1.16,
|
||||
'Import Growth Rate': 2.41,
|
||||
},
|
||||
{
|
||||
'year': 2017,
|
||||
'Export Growth Rate': 1.13,
|
||||
'Import Growth Rate': 2.44,
|
||||
},
|
||||
{
|
||||
'year': 2018,
|
||||
'Export Growth Rate': 1.07,
|
||||
'Import Growth Rate': 2.45,
|
||||
},
|
||||
{
|
||||
'year': 2019,
|
||||
'Export Growth Rate': 1.03,
|
||||
'Import Growth Rate': 2.47,
|
||||
},
|
||||
{
|
||||
'year': 2020,
|
||||
'Export Growth Rate': 0.92,
|
||||
'Import Growth Rate': 2.48,
|
||||
},
|
||||
{
|
||||
'year': 2021,
|
||||
'Export Growth Rate': 0.82,
|
||||
'Import Growth Rate': 2.51,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LineChart
|
||||
:data="data"
|
||||
index="year"
|
||||
:categories="['Export Growth Rate', 'Import Growth Rate']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:custom-tooltip="CustomChartTooltip"
|
||||
/>
|
||||
</template>
|
||||
279
apps/www/src/lib/registry/new-york/example/LineChartDemo.vue
Normal file
279
apps/www/src/lib/registry/new-york/example/LineChartDemo.vue
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<script setup lang="ts">
|
||||
import { LineChart } from '@/lib/registry/new-york/ui/chart-line'
|
||||
|
||||
const data = [
|
||||
{
|
||||
'year': 1970,
|
||||
'Export Growth Rate': 2.04,
|
||||
'Import Growth Rate': 1.53,
|
||||
},
|
||||
{
|
||||
'year': 1971,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.58,
|
||||
},
|
||||
{
|
||||
'year': 1972,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1973,
|
||||
'Export Growth Rate': 1.93,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1974,
|
||||
'Export Growth Rate': 1.88,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1975,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.64,
|
||||
},
|
||||
{
|
||||
'year': 1976,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.62,
|
||||
},
|
||||
{
|
||||
'year': 1977,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.69,
|
||||
},
|
||||
{
|
||||
'year': 1978,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1979,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1980,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1981,
|
||||
'Export Growth Rate': 1.81,
|
||||
'Import Growth Rate': 1.72,
|
||||
},
|
||||
{
|
||||
'year': 1982,
|
||||
'Export Growth Rate': 1.84,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1983,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1984,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.78,
|
||||
},
|
||||
{
|
||||
'year': 1985,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.81,
|
||||
},
|
||||
{
|
||||
'year': 1986,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.89,
|
||||
},
|
||||
{
|
||||
'year': 1987,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.91,
|
||||
},
|
||||
{
|
||||
'year': 1988,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1989,
|
||||
'Export Growth Rate': 1.76,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1990,
|
||||
'Export Growth Rate': 1.75,
|
||||
'Import Growth Rate': 1.97,
|
||||
},
|
||||
{
|
||||
'year': 1991,
|
||||
'Export Growth Rate': 1.62,
|
||||
'Import Growth Rate': 1.99,
|
||||
},
|
||||
{
|
||||
'year': 1992,
|
||||
'Export Growth Rate': 1.56,
|
||||
'Import Growth Rate': 2.12,
|
||||
},
|
||||
{
|
||||
'year': 1993,
|
||||
'Export Growth Rate': 1.5,
|
||||
'Import Growth Rate': 2.13,
|
||||
},
|
||||
{
|
||||
'year': 1994,
|
||||
'Export Growth Rate': 1.46,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1995,
|
||||
'Export Growth Rate': 1.43,
|
||||
'Import Growth Rate': 2.17,
|
||||
},
|
||||
{
|
||||
'year': 1996,
|
||||
'Export Growth Rate': 1.4,
|
||||
'Import Growth Rate': 2.2,
|
||||
},
|
||||
{
|
||||
'year': 1997,
|
||||
'Export Growth Rate': 1.37,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1998,
|
||||
'Export Growth Rate': 1.34,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 1999,
|
||||
'Export Growth Rate': 1.32,
|
||||
'Import Growth Rate': 2.05,
|
||||
},
|
||||
{
|
||||
'year': 2000,
|
||||
'Export Growth Rate': 1.33,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 2001,
|
||||
'Export Growth Rate': 1.31,
|
||||
'Import Growth Rate': 2.08,
|
||||
},
|
||||
{
|
||||
'year': 2002,
|
||||
'Export Growth Rate': 1.29,
|
||||
'Import Growth Rate': 2.1,
|
||||
},
|
||||
{
|
||||
'year': 2003,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 2004,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.21,
|
||||
},
|
||||
{
|
||||
'year': 2005,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.23,
|
||||
},
|
||||
{
|
||||
'year': 2006,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.29,
|
||||
},
|
||||
{
|
||||
'year': 2007,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2008,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2009,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2010,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2011,
|
||||
'Export Growth Rate': 1.24,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2012,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2013,
|
||||
'Export Growth Rate': 1.22,
|
||||
'Import Growth Rate': 2.3,
|
||||
},
|
||||
{
|
||||
'year': 2014,
|
||||
'Export Growth Rate': 1.2,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2015,
|
||||
'Export Growth Rate': 1.17,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2016,
|
||||
'Export Growth Rate': 1.16,
|
||||
'Import Growth Rate': 2.41,
|
||||
},
|
||||
{
|
||||
'year': 2017,
|
||||
'Export Growth Rate': 1.13,
|
||||
'Import Growth Rate': 2.44,
|
||||
},
|
||||
{
|
||||
'year': 2018,
|
||||
'Export Growth Rate': 1.07,
|
||||
'Import Growth Rate': 2.45,
|
||||
},
|
||||
{
|
||||
'year': 2019,
|
||||
'Export Growth Rate': 1.03,
|
||||
'Import Growth Rate': 2.47,
|
||||
},
|
||||
{
|
||||
'year': 2020,
|
||||
'Export Growth Rate': 0.92,
|
||||
'Import Growth Rate': 2.48,
|
||||
},
|
||||
{
|
||||
'year': 2021,
|
||||
'Export Growth Rate': 0.82,
|
||||
'Import Growth Rate': 2.51,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LineChart
|
||||
:data="data"
|
||||
index="year"
|
||||
:categories="['Export Growth Rate', 'Import Growth Rate']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
<script setup lang="ts">
|
||||
import { LineChart } from '@/lib/registry/new-york/ui/chart-line'
|
||||
|
||||
const data = [
|
||||
{
|
||||
'year': 1970,
|
||||
'Export Growth Rate': 2.04,
|
||||
'Import Growth Rate': 1.53,
|
||||
},
|
||||
{
|
||||
'year': 1971,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.58,
|
||||
},
|
||||
{
|
||||
'year': 1972,
|
||||
'Export Growth Rate': 1.96,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1973,
|
||||
'Export Growth Rate': 1.93,
|
||||
'Import Growth Rate': 1.61,
|
||||
},
|
||||
{
|
||||
'year': 1974,
|
||||
'Export Growth Rate': 1.88,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1975,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.64,
|
||||
},
|
||||
{
|
||||
'year': 1976,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.62,
|
||||
},
|
||||
{
|
||||
'year': 1977,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.69,
|
||||
},
|
||||
{
|
||||
'year': 1978,
|
||||
'Export Growth Rate': 1.74,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1979,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.67,
|
||||
},
|
||||
{
|
||||
'year': 1980,
|
||||
'Export Growth Rate': 1.79,
|
||||
'Import Growth Rate': 1.7,
|
||||
},
|
||||
{
|
||||
'year': 1981,
|
||||
'Export Growth Rate': 1.81,
|
||||
'Import Growth Rate': 1.72,
|
||||
},
|
||||
{
|
||||
'year': 1982,
|
||||
'Export Growth Rate': 1.84,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1983,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.73,
|
||||
},
|
||||
{
|
||||
'year': 1984,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.78,
|
||||
},
|
||||
{
|
||||
'year': 1985,
|
||||
'Export Growth Rate': 1.78,
|
||||
'Import Growth Rate': 1.81,
|
||||
},
|
||||
{
|
||||
'year': 1986,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.89,
|
||||
},
|
||||
{
|
||||
'year': 1987,
|
||||
'Export Growth Rate': 1.82,
|
||||
'Import Growth Rate': 1.91,
|
||||
},
|
||||
{
|
||||
'year': 1988,
|
||||
'Export Growth Rate': 1.77,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1989,
|
||||
'Export Growth Rate': 1.76,
|
||||
'Import Growth Rate': 1.94,
|
||||
},
|
||||
{
|
||||
'year': 1990,
|
||||
'Export Growth Rate': 1.75,
|
||||
'Import Growth Rate': 1.97,
|
||||
},
|
||||
{
|
||||
'year': 1991,
|
||||
'Export Growth Rate': 1.62,
|
||||
'Import Growth Rate': 1.99,
|
||||
},
|
||||
{
|
||||
'year': 1992,
|
||||
'Export Growth Rate': 1.56,
|
||||
'Import Growth Rate': 2.12,
|
||||
},
|
||||
{
|
||||
'year': 1993,
|
||||
'Export Growth Rate': 1.5,
|
||||
'Import Growth Rate': 2.13,
|
||||
},
|
||||
{
|
||||
'year': 1994,
|
||||
'Export Growth Rate': 1.46,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1995,
|
||||
'Export Growth Rate': 1.43,
|
||||
'Import Growth Rate': 2.17,
|
||||
},
|
||||
{
|
||||
'year': 1996,
|
||||
'Export Growth Rate': 1.4,
|
||||
'Import Growth Rate': 2.2,
|
||||
},
|
||||
{
|
||||
'year': 1997,
|
||||
'Export Growth Rate': 1.37,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 1998,
|
||||
'Export Growth Rate': 1.34,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 1999,
|
||||
'Export Growth Rate': 1.32,
|
||||
'Import Growth Rate': 2.05,
|
||||
},
|
||||
{
|
||||
'year': 2000,
|
||||
'Export Growth Rate': 1.33,
|
||||
'Import Growth Rate': 2.07,
|
||||
},
|
||||
{
|
||||
'year': 2001,
|
||||
'Export Growth Rate': 1.31,
|
||||
'Import Growth Rate': 2.08,
|
||||
},
|
||||
{
|
||||
'year': 2002,
|
||||
'Export Growth Rate': 1.29,
|
||||
'Import Growth Rate': 2.1,
|
||||
},
|
||||
{
|
||||
'year': 2003,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.15,
|
||||
},
|
||||
{
|
||||
'year': 2004,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.21,
|
||||
},
|
||||
{
|
||||
'year': 2005,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.23,
|
||||
},
|
||||
{
|
||||
'year': 2006,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.29,
|
||||
},
|
||||
{
|
||||
'year': 2007,
|
||||
'Export Growth Rate': 1.27,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2008,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2009,
|
||||
'Export Growth Rate': 1.26,
|
||||
'Import Growth Rate': 2.36,
|
||||
},
|
||||
{
|
||||
'year': 2010,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2011,
|
||||
'Export Growth Rate': 1.24,
|
||||
'Import Growth Rate': 2.34,
|
||||
},
|
||||
{
|
||||
'year': 2012,
|
||||
'Export Growth Rate': 1.25,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2013,
|
||||
'Export Growth Rate': 1.22,
|
||||
'Import Growth Rate': 2.3,
|
||||
},
|
||||
{
|
||||
'year': 2014,
|
||||
'Export Growth Rate': 1.2,
|
||||
'Import Growth Rate': 2.35,
|
||||
},
|
||||
{
|
||||
'year': 2015,
|
||||
'Export Growth Rate': 1.17,
|
||||
'Import Growth Rate': 2.39,
|
||||
},
|
||||
{
|
||||
'year': 2016,
|
||||
'Export Growth Rate': 1.16,
|
||||
'Import Growth Rate': 2.41,
|
||||
},
|
||||
{
|
||||
'year': 2017,
|
||||
'Export Growth Rate': 1.13,
|
||||
'Import Growth Rate': 2.44,
|
||||
},
|
||||
{
|
||||
'year': 2018,
|
||||
'Export Growth Rate': 1.07,
|
||||
'Import Growth Rate': 2.45,
|
||||
},
|
||||
{
|
||||
'year': 2019,
|
||||
'Export Growth Rate': 1.03,
|
||||
'Import Growth Rate': 2.47,
|
||||
},
|
||||
{
|
||||
'year': 2020,
|
||||
'Export Growth Rate': 0.92,
|
||||
'Import Growth Rate': 2.48,
|
||||
},
|
||||
{
|
||||
'year': 2021,
|
||||
'Export Growth Rate': 0.82,
|
||||
'Import Growth Rate': 2.51,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LineChart
|
||||
index="year"
|
||||
class="h-[100px] w-[400px]"
|
||||
:data="data"
|
||||
:categories="['Export Growth Rate']"
|
||||
:y-formatter="(tick, i) => {
|
||||
return typeof tick === 'number'
|
||||
? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`
|
||||
: ''
|
||||
}"
|
||||
:show-tooltip="false"
|
||||
:show-grid-line="false"
|
||||
:show-legend="false"
|
||||
:show-x-axis="false"
|
||||
:show-y-axis="false"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -19,6 +19,7 @@ export const buttonVariants = cva(
|
|||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2',
|
||||
xs: 'h-7 rounded px-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9',
|
||||
|
|
|
|||
135
apps/www/src/lib/registry/new-york/ui/chart-area/AreaChart.vue
Normal file
135
apps/www/src/lib/registry/new-york/ui/chart-area/AreaChart.vue
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
|
||||
import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
|
||||
import { Area, Axis, Line } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
/**
|
||||
* Type of curve
|
||||
*/
|
||||
curveType?: CurveType
|
||||
/**
|
||||
* Controls the visibility of gradient.
|
||||
* @default true
|
||||
*/
|
||||
showGradiant?: boolean
|
||||
}>(), {
|
||||
curveType: CurveType.MonotoneX,
|
||||
filterOpacity: 0.2,
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
showGridLine: true,
|
||||
showGradiant: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
}>()
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
||||
|
||||
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
||||
name: category,
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const isMounted = useMounted()
|
||||
|
||||
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
||||
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
||||
|
||||
<VisXYContainer :style="{ height: isMounted ? '100%' : 'auto' }" :margin="{ left: 20, right: 20 }" :data="data">
|
||||
<svg width="0" height="0">
|
||||
<defs>
|
||||
<linearGradient v-for="(color, i) in colors" :id="`color-${i}`" :key="i" x1="0" y1="0" x2="0" y2="1">
|
||||
<template v-if="showGradiant">
|
||||
<stop offset="5%" :stop-color="color" stop-opacity="0.4" />
|
||||
<stop offset="95%" :stop-color="color" stop-opacity="0" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<stop offset="0%" :stop-color="color" />
|
||||
</template>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
|
||||
|
||||
<template v-for="(category, i) in categories" :key="category">
|
||||
<VisArea
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d[category]"
|
||||
color="auto"
|
||||
:curve-type="curveType"
|
||||
:attributes="{
|
||||
[Area.selectors.area]: {
|
||||
fill: `url(#color-${i})`,
|
||||
},
|
||||
}"
|
||||
:opacity="legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-for="(category, i) in categories" :key="category">
|
||||
<VisLine
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d[category]"
|
||||
:color="colors[i]"
|
||||
:curve-type="curveType"
|
||||
:attributes="{
|
||||
[Line.selectors.line]: {
|
||||
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VisAxis
|
||||
v-if="showXAxis"
|
||||
type="x"
|
||||
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
<VisAxis
|
||||
v-if="showYAxis"
|
||||
type="y"
|
||||
:tick-line="false"
|
||||
:tick-format="yFormatter"
|
||||
:domain-line="false"
|
||||
:grid-line="showGridLine"
|
||||
:attributes="{
|
||||
[Axis.selectors.grid]: {
|
||||
class: 'text-muted',
|
||||
},
|
||||
}"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as AreaChart } from './AreaChart.vue'
|
||||
115
apps/www/src/lib/registry/new-york/ui/chart-bar/BarChart.vue
Normal file
115
apps/www/src/lib/registry/new-york/ui/chart-bar/BarChart.vue
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import type { BulletLegendItemInterface, Spacing } from '@unovis/ts'
|
||||
import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue'
|
||||
import { Axis, GroupedBar, StackedBar } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
/**
|
||||
* Change the type of the chart
|
||||
* @default "grouped"
|
||||
*/
|
||||
type?: 'stacked' | 'grouped'
|
||||
/**
|
||||
* Rounded bar corners
|
||||
* @default 0
|
||||
*/
|
||||
roundedCorners?: number
|
||||
}>(), {
|
||||
type: 'grouped',
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
filterOpacity: 0.2,
|
||||
roundedCorners: 0,
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
showGridLine: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
}>()
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
||||
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
||||
name: category,
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const isMounted = useMounted()
|
||||
|
||||
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
}
|
||||
|
||||
const VisBarComponent = computed(() => props.type === 'grouped' ? VisGroupedBar : VisStackedBar)
|
||||
const selectorsBar = computed(() => props.type === 'grouped' ? GroupedBar.selectors.bar : StackedBar.selectors.bar)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
||||
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
||||
|
||||
<VisXYContainer
|
||||
:data="data"
|
||||
:style="{ height: isMounted ? '100%' : 'auto' }"
|
||||
:margin="margin"
|
||||
>
|
||||
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :custom-tooltip="customTooltip" :index="index" />
|
||||
|
||||
<VisBarComponent
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="categories.map(category => (d: Data) => d[category]) "
|
||||
:color="colors"
|
||||
:rounded-corners="roundedCorners"
|
||||
:bar-padding="0.05"
|
||||
:attributes="{
|
||||
[selectorsBar]: {
|
||||
opacity: (d: Data, i:number) => {
|
||||
const pos = i % categories.length
|
||||
return legendItems[pos]?.inactive ? filterOpacity : 1
|
||||
},
|
||||
},
|
||||
}"
|
||||
/>
|
||||
|
||||
<VisAxis
|
||||
v-if="showXAxis"
|
||||
type="x"
|
||||
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
<VisAxis
|
||||
v-if="showYAxis"
|
||||
type="y"
|
||||
:tick-line="false"
|
||||
:tick-format="yFormatter"
|
||||
:domain-line="false"
|
||||
:grid-line="showGridLine"
|
||||
:attributes="{
|
||||
[Axis.selectors.grid]: {
|
||||
class: 'text-muted',
|
||||
},
|
||||
}"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</template>
|
||||
1
apps/www/src/lib/registry/new-york/ui/chart-bar/index.ts
Normal file
1
apps/www/src/lib/registry/new-york/ui/chart-bar/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as BarChart } from './BarChart.vue'
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import { VisDonut, VisSingleContainer } from '@unovis/vue'
|
||||
import { Donut } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartSingleTooltip, defaultColors } from '@/lib/registry/new-york/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<Pick<BaseChartProps<T>, 'data' | 'colors' | 'index' | 'margin' | 'showLegend' | 'showTooltip' | 'filterOpacity'> & {
|
||||
/**
|
||||
* Sets the name of the key containing the quantitative chart values.
|
||||
*/
|
||||
category: KeyOfT
|
||||
/**
|
||||
* Change the type of the chart
|
||||
* @default "donut"
|
||||
*/
|
||||
type?: 'donut' | 'pie'
|
||||
/**
|
||||
* Function to sort the segment
|
||||
*/
|
||||
sortFunction?: (a: any, b: any) => number | undefined
|
||||
/**
|
||||
* Controls the formatting for the label.
|
||||
*/
|
||||
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
}>(), {
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
sortFunction: () => undefined,
|
||||
valueFormatter: (tick: number) => `${tick}`,
|
||||
type: 'donut',
|
||||
filterOpacity: 0.2,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
})
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const category = computed(() => props.category as KeyOfT)
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
|
||||
const isMounted = useMounted()
|
||||
const activeSegmentKey = ref<string>()
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.data.filter(d => d[props.category]).filter(Boolean).length))
|
||||
const legendItems = computed(() => props.data.map((item, i) => ({
|
||||
name: item[props.index],
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const totalValue = computed(() => props.data.reduce((prev, curr) => {
|
||||
return prev + curr[props.category]
|
||||
}, 0))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-48 flex flex-col items-end', $attrs.class ?? '')">
|
||||
<VisSingleContainer :style="{ height: isMounted ? '100%' : 'auto' }" :margin="{ left: 20, right: 20 }" :data="data">
|
||||
<ChartSingleTooltip
|
||||
:selector="Donut.selectors.segment"
|
||||
:index="category"
|
||||
:items="legendItems"
|
||||
:value-formatter="valueFormatter"
|
||||
:custom-tooltip="customTooltip"
|
||||
/>
|
||||
|
||||
<VisDonut
|
||||
:value="(d: Data) => d[category]"
|
||||
:sort-function="sortFunction"
|
||||
:color="colors"
|
||||
:arc-width="type === 'donut' ? 20 : 0"
|
||||
:show-background="false"
|
||||
:central-label="type === 'donut' ? valueFormatter(totalValue) : ''"
|
||||
:events="{
|
||||
[Donut.selectors.segment]: {
|
||||
click: (d: Data, ev: PointerEvent, i: number, elements: HTMLElement[]) => {
|
||||
if (d?.data?.[index] === activeSegmentKey) {
|
||||
activeSegmentKey = undefined
|
||||
elements.forEach(el => el.style.opacity = '1')
|
||||
}
|
||||
else {
|
||||
activeSegmentKey = d?.data?.[index]
|
||||
elements.forEach(el => el.style.opacity = `${filterOpacity}`)
|
||||
elements[i].style.opacity = '1'
|
||||
}
|
||||
},
|
||||
},
|
||||
}"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisSingleContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DonutChart } from './DonutChart.vue'
|
||||
104
apps/www/src/lib/registry/new-york/ui/chart-line/LineChart.vue
Normal file
104
apps/www/src/lib/registry/new-york/ui/chart-line/LineChart.vue
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<script setup lang="ts" generic="T extends Record<string, any>">
|
||||
import { type BulletLegendItemInterface, CurveType } from '@unovis/ts'
|
||||
import { VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
|
||||
import { Axis, Line } from '@unovis/ts'
|
||||
import { type Component, computed, ref } from 'vue'
|
||||
import { useMounted } from '@vueuse/core'
|
||||
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/new-york/ui/chart'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<BaseChartProps<T> & {
|
||||
/**
|
||||
* Render custom tooltip component.
|
||||
*/
|
||||
customTooltip?: Component
|
||||
/**
|
||||
* Type of curve
|
||||
*/
|
||||
curveType?: CurveType
|
||||
}>(), {
|
||||
curveType: CurveType.MonotoneX,
|
||||
filterOpacity: 0.2,
|
||||
margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
showTooltip: true,
|
||||
showLegend: true,
|
||||
showGridLine: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
}>()
|
||||
|
||||
type KeyOfT = Extract<keyof T, string>
|
||||
type Data = typeof props.data[number]
|
||||
|
||||
const index = computed(() => props.index as KeyOfT)
|
||||
const colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))
|
||||
|
||||
const legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({
|
||||
name: category,
|
||||
color: colors.value[i],
|
||||
inactive: false,
|
||||
})))
|
||||
|
||||
const isMounted = useMounted()
|
||||
|
||||
function handleLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')">
|
||||
<ChartLegend v-if="showLegend" v-model:items="legendItems" @legend-item-click="handleLegendItemClick" />
|
||||
|
||||
<VisXYContainer
|
||||
:margin="{ left: 20, right: 20 }"
|
||||
:data="data"
|
||||
:style="{ height: isMounted ? '100%' : 'auto' }"
|
||||
>
|
||||
<ChartCrosshair v-if="showTooltip" :colors="colors" :items="legendItems" :index="index" :custom-tooltip="customTooltip" />
|
||||
|
||||
<template v-for="(category, i) in categories" :key="category">
|
||||
<VisLine
|
||||
:x="(d: Data, i: number) => i"
|
||||
:y="(d: Data) => d[category]"
|
||||
:curve-type="curveType"
|
||||
:color="colors[i]"
|
||||
:attributes="{
|
||||
[Line.selectors.line]: {
|
||||
opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VisAxis
|
||||
v-if="showXAxis"
|
||||
type="x"
|
||||
:tick-format="xFormatter ?? ((v: number) => data[v]?.[index])"
|
||||
:grid-line="false"
|
||||
:tick-line="false"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
<VisAxis
|
||||
v-if="showYAxis"
|
||||
type="y"
|
||||
:tick-line="false"
|
||||
:tick-format="yFormatter"
|
||||
:domain-line="false"
|
||||
:grid-line="showGridLine"
|
||||
:attributes="{
|
||||
[Axis.selectors.grid]: {
|
||||
class: 'text-muted',
|
||||
},
|
||||
}"
|
||||
tick-text-color="hsl(var(--vis-text-color))"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
</VisXYContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as LineChart } from './LineChart.vue'
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
import { VisCrosshair, VisTooltip } from '@unovis/vue'
|
||||
import type { BulletLegendItemInterface } from '@unovis/ts'
|
||||
import { omit } from '@unovis/ts'
|
||||
import { type Component, createApp } from 'vue'
|
||||
import { ChartTooltip } from '@/lib/registry/new-york/ui/chart'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
colors: string[]
|
||||
index: string
|
||||
items: BulletLegendItemInterface[]
|
||||
customTooltip?: Component
|
||||
}>(), {
|
||||
colors: () => [],
|
||||
})
|
||||
|
||||
// Use weakmap to store reference to each datapoint for Tooltip
|
||||
const wm = new WeakMap()
|
||||
function template(d: any) {
|
||||
if (wm.has(d)) {
|
||||
return wm.get(d)
|
||||
}
|
||||
else {
|
||||
const componentDiv = document.createElement('div')
|
||||
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
|
||||
const legendReference = props.items.find(i => i.name === key)
|
||||
return { ...legendReference, value }
|
||||
})
|
||||
const TooltipComponent = props.customTooltip ?? ChartTooltip
|
||||
createApp(TooltipComponent, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv)
|
||||
wm.set(d, componentDiv.innerHTML)
|
||||
return componentDiv.innerHTML
|
||||
}
|
||||
}
|
||||
|
||||
function color(d: unknown, i: number) {
|
||||
return props.colors[i] ?? 'transparent'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VisTooltip :horizontal-shift="20" :vertical-shift="20" />
|
||||
<VisCrosshair :template="template" :color="color" />
|
||||
</template>
|
||||
51
apps/www/src/lib/registry/new-york/ui/chart/ChartLegend.vue
Normal file
51
apps/www/src/lib/registry/new-york/ui/chart/ChartLegend.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<script setup lang="ts">
|
||||
import { VisBulletLegend } from '@unovis/vue'
|
||||
import type { BulletLegendItemInterface } from '@unovis/ts'
|
||||
import { BulletLegend } from '@unovis/ts'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { buttonVariants } from '@/lib/registry/new-york/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<{ items: BulletLegendItemInterface[] }>(), {
|
||||
items: () => [],
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
legendItemClick: [d: BulletLegendItemInterface, i: number]
|
||||
'update:items': [payload: BulletLegendItemInterface[]]
|
||||
}>()
|
||||
|
||||
const elRef = ref<HTMLElement>()
|
||||
|
||||
onMounted(() => {
|
||||
const selector = `.${BulletLegend.selectors.item}`
|
||||
nextTick(() => {
|
||||
const elements = elRef.value?.querySelectorAll(selector)
|
||||
const classes = buttonVariants({ variant: 'ghost', size: 'xs' }).split(' ')
|
||||
|
||||
elements?.forEach(el => el.classList.add(...classes, '!inline-flex', '!mr-2'))
|
||||
})
|
||||
})
|
||||
|
||||
function onLegendItemClick(d: BulletLegendItemInterface, i: number) {
|
||||
emits('legendItemClick', d, i)
|
||||
const isBulletActive = !props.items[i].inactive
|
||||
const isFilterApplied = props.items.some(i => i.inactive)
|
||||
if (isFilterApplied && isBulletActive) {
|
||||
// reset filter
|
||||
emits('update:items', props.items.map(item => ({ ...item, inactive: false })))
|
||||
}
|
||||
else {
|
||||
// apply selection, set other item as inactive
|
||||
emits('update:items', props.items.map(item => item.name === d.name ? ({ ...d, inactive: false }) : { ...item, inactive: true }))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="elRef" class="w-max">
|
||||
<VisBulletLegend
|
||||
:items="items"
|
||||
:on-legend-item-click="onLegendItemClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import { VisTooltip } from '@unovis/vue'
|
||||
import type { BulletLegendItemInterface } from '@unovis/ts'
|
||||
import { omit } from '@unovis/ts'
|
||||
import { type Component, createApp } from 'vue'
|
||||
import { ChartTooltip } from '@/lib/registry/new-york/ui/chart'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
selector: string
|
||||
index: string
|
||||
items?: BulletLegendItemInterface[]
|
||||
valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string
|
||||
customTooltip?: Component
|
||||
}>(), {
|
||||
valueFormatter: (tick: number) => `${tick}`,
|
||||
})
|
||||
|
||||
// Use weakmap to store reference to each datapoint for Tooltip
|
||||
const wm = new WeakMap()
|
||||
function template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {
|
||||
if (props.index in d) {
|
||||
if (wm.has(d)) {
|
||||
return wm.get(d)
|
||||
}
|
||||
else {
|
||||
const componentDiv = document.createElement('div')
|
||||
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
|
||||
const legendReference = props.items?.find(i => i.name === key)
|
||||
return { ...legendReference, value: props.valueFormatter(value) }
|
||||
})
|
||||
const TooltipComponent = props.customTooltip ?? ChartTooltip
|
||||
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
|
||||
wm.set(d, componentDiv.innerHTML)
|
||||
return componentDiv.innerHTML
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
const data = d.data
|
||||
|
||||
if (wm.has(data)) {
|
||||
return wm.get(data)
|
||||
}
|
||||
else {
|
||||
const style = getComputedStyle(elements[i])
|
||||
const omittedData = [{ name: data.name, value: props.valueFormatter(data[props.index]), color: style.fill }]
|
||||
const componentDiv = document.createElement('div')
|
||||
const TooltipComponent = props.customTooltip ?? ChartTooltip
|
||||
createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)
|
||||
wm.set(d, componentDiv.innerHTML)
|
||||
return componentDiv.innerHTML
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VisTooltip
|
||||
:horizontal-shift="20" :vertical-shift="20" :triggers="{
|
||||
[selector]: template,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
40
apps/www/src/lib/registry/new-york/ui/chart/ChartTooltip.vue
Normal file
40
apps/www/src/lib/registry/new-york/ui/chart/ChartTooltip.vue
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/lib/registry/new-york/ui/card'
|
||||
|
||||
defineProps<{
|
||||
title?: string
|
||||
data: {
|
||||
name: string
|
||||
color: string
|
||||
value: any
|
||||
}[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="text-sm">
|
||||
<CardHeader v-if="title" class="p-3 border-b">
|
||||
<CardTitle>
|
||||
{{ title }}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="p-3 min-w-[180px] flex flex-col gap-1">
|
||||
<div v-for="(item, key) in data" :key="key" class="flex justify-between">
|
||||
<div class="flex items-center">
|
||||
<span class="w-2.5 h-2.5 mr-2">
|
||||
<svg width="100%" height="100%" viewBox="0 0 30 30">
|
||||
<path
|
||||
d=" M 15 15 m -14, 0 a 14,14 0 1,1 28,0 a 14,14 0 1,1 -28,0"
|
||||
:stroke="item.color"
|
||||
:fill="item.color"
|
||||
stroke-width="1"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
<span class="font-semibold ml-4">{{ item.value }}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
18
apps/www/src/lib/registry/new-york/ui/chart/index.ts
Normal file
18
apps/www/src/lib/registry/new-york/ui/chart/index.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export { default as ChartTooltip } from './ChartTooltip.vue'
|
||||
export { default as ChartSingleTooltip } from './ChartSingleTooltip.vue'
|
||||
export { default as ChartLegend } from './ChartLegend.vue'
|
||||
export { default as ChartCrosshair } from './ChartCrosshair.vue'
|
||||
|
||||
export function defaultColors(count: number = 3) {
|
||||
const quotient = Math.floor(count / 2)
|
||||
const remainder = count % 2
|
||||
|
||||
const primaryCount = quotient + remainder
|
||||
const secondaryCount = quotient
|
||||
return [
|
||||
...Array.from(Array(primaryCount).keys()).map(i => `hsl(var(--vis-primary-color) / ${1 - (1 / primaryCount) * i})`),
|
||||
...Array.from(Array(secondaryCount).keys()).map(i => `hsl(var(--vis-secondary-color) / ${1 - (1 / secondaryCount) * i})`),
|
||||
]
|
||||
}
|
||||
|
||||
export * from './interface'
|
||||
64
apps/www/src/lib/registry/new-york/ui/chart/interface.ts
Normal file
64
apps/www/src/lib/registry/new-york/ui/chart/interface.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import type { Spacing } from '@unovis/ts'
|
||||
|
||||
type KeyOf<T extends Record<string, any>> = Extract<keyof T, string>
|
||||
|
||||
export interface BaseChartProps<T extends Record<string, any>> {
|
||||
/**
|
||||
* The source data, in which each entry is a dictionary.
|
||||
*/
|
||||
data: T[]
|
||||
/**
|
||||
* Select the categories from your data. Used to populate the legend and toolip.
|
||||
*/
|
||||
categories: KeyOf<T>[]
|
||||
/**
|
||||
* Sets the key to map the data to the axis.
|
||||
*/
|
||||
index: KeyOf<T>
|
||||
/**
|
||||
* Change the default colors.
|
||||
*/
|
||||
colors?: string[]
|
||||
/**
|
||||
* Margin of each the container
|
||||
*/
|
||||
margin?: Spacing
|
||||
/**
|
||||
* Change the opacity of the non-selected field
|
||||
* @default 0.2
|
||||
*/
|
||||
filterOpacity?: number
|
||||
/**
|
||||
* Function to format X label
|
||||
*/
|
||||
xFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
|
||||
/**
|
||||
* Function to format Y label
|
||||
*/
|
||||
yFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
|
||||
/**
|
||||
* Controls the visibility of the X axis.
|
||||
* @default true
|
||||
*/
|
||||
showXAxis?: boolean
|
||||
/**
|
||||
* Controls the visibility of the Y axis.
|
||||
* @default true
|
||||
*/
|
||||
showYAxis?: boolean
|
||||
/**
|
||||
* Controls the visibility of tooltip.
|
||||
* @default true
|
||||
*/
|
||||
showTooltip?: boolean
|
||||
/**
|
||||
* Controls the visibility of legend.
|
||||
* @default true
|
||||
*/
|
||||
showLegend?: boolean
|
||||
/**
|
||||
* Controls the visibility of gridline.
|
||||
* @default true
|
||||
*/
|
||||
showGridLine?: boolean
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ const DEPENDENCIES = new Map<string, string[]>([
|
|||
['vaul-vue', []],
|
||||
['v-calendar', []],
|
||||
['@tanstack/vue-table', []],
|
||||
['@unovis/vue', ['@unovis/ts']],
|
||||
['embla-carousel-vue', ['embla-carousel']],
|
||||
['vee-validate', ['@vee-validate/zod', 'zod']],
|
||||
])
|
||||
|
|
|
|||
|
|
@ -222,6 +222,95 @@
|
|||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "chart",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"chart",
|
||||
"button",
|
||||
"card"
|
||||
],
|
||||
"files": [
|
||||
"ui/chart/ChartCrosshair.vue",
|
||||
"ui/chart/ChartLegend.vue",
|
||||
"ui/chart/ChartSingleTooltip.vue",
|
||||
"ui/chart/ChartTooltip.vue",
|
||||
"ui/chart/index.ts",
|
||||
"ui/chart/interface.ts"
|
||||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "chart-area",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts",
|
||||
"@vueuse/core"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"chart",
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
"ui/chart-area/AreaChart.vue",
|
||||
"ui/chart-area/index.ts"
|
||||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "chart-bar",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts",
|
||||
"@vueuse/core"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"chart",
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
"ui/chart-bar/BarChart.vue",
|
||||
"ui/chart-bar/index.ts"
|
||||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "chart-donut",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts",
|
||||
"@vueuse/core"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"chart",
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
"ui/chart-donut/DonutChart.vue",
|
||||
"ui/chart-donut/index.ts"
|
||||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "chart-line",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts",
|
||||
"@vueuse/core"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"chart",
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
"ui/chart-line/LineChart.vue",
|
||||
"ui/chart-line/index.ts"
|
||||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "checkbox",
|
||||
"dependencies": [],
|
||||
|
|
|
|||
19
apps/www/src/public/registry/styles/default/area-chart.json
Normal file
19
apps/www/src/public/registry/styles/default/area-chart.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "area-chart",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts"
|
||||
],
|
||||
"registryDependencies": [],
|
||||
"files": [
|
||||
{
|
||||
"name": "AreaChart.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'\nimport { Area } from '@unovis/ts'\n\ntype Data = typeof data[number]\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Feb', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Mar', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Apr', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'May', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Jun', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Jul', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Aug', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Sep', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Oct', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Nov', total: Math.floor(Math.random() * 5000) + 1000 },\n { name: 'Dec', total: Math.floor(Math.random() * 5000) + 1000 },\n]\n</script>\n\n<template>\n <VisXYContainer height=\"350px\" :margin=\"{ left: 20, right: 20 }\" :data=\"data\">\n <svg width=\"0\" height=\"0\">\n <defs>\n <linearGradient id=\"colorUv\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"5%\" stop-color=\"hsl(var(--primary))\" stop-opacity=\"0.6\" />\n <stop offset=\"95%\" stop-color=\"hsl(var(--primary))\" stop-opacity=\"0\" />\n </linearGradient>\n </defs>\n </svg>\n\n <VisArea\n :x=\"(d: Data, i: number) => i\"\n :y=\"(d: Data) => d.total\"\n color=\"auto\"\n :attributes=\"{\n [Area.selectors.area]: {\n fill: 'url(#colorUv)',\n },\n }\"\n :rounded-corners=\"4\"\n :bar-padding=\"0.15\"\n />\n <VisLine\n :x=\"(d: Data, i: number) => i\"\n :y=\"(d: Data) => d.total\"\n color=\"hsl(var(--primary))\"\n />\n <VisAxis\n type=\"x\"\n :num-ticks=\"data.length\"\n :tick-format=\"(index: number) => data[index]?.name\"\n :grid-line=\"false\"\n :tick-line=\"false\"\n tick-text-color=\"hsl(var(--muted-foreground))\"\n />\n <VisAxis\n type=\"y\"\n :num-ticks=\"data.length\"\n :tick-format=\"(index: number) => data[index]?.name\"\n :grid-line=\"false\"\n :tick-line=\"false\"\n :domain-line=\"false\"\n tick-text-color=\"hsl(var(--muted-foreground))\"\n />\n </VisXYContainer>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "index.ts",
|
||||
"content": "export { default as AreaChart } from './AreaChart.vue'\n"
|
||||
}
|
||||
],
|
||||
"type": "components:ui"
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
{
|
||||
"name": "index.ts",
|
||||
"content": "import { type VariantProps, cva } from 'class-variance-authority'\n\nexport { default as Button } from './Button.vue'\n\nexport const buttonVariants = cva(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n destructive:\n 'bg-destructive text-destructive-foreground hover:bg-destructive/90',\n outline:\n 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',\n secondary:\n 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n ghost: 'hover:bg-accent hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 hover:underline',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n)\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>\n"
|
||||
"content": "import { type VariantProps, cva } from 'class-variance-authority'\n\nexport { default as Button } from './Button.vue'\n\nexport const buttonVariants = cva(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n destructive:\n 'bg-destructive text-destructive-foreground hover:bg-destructive/90',\n outline:\n 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',\n secondary:\n 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n ghost: 'hover:bg-accent hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 hover:underline',\n },\n size: {\n default: 'h-10 px-4 py-2',\n xs: 'h-7 rounded px-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n)\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>\n"
|
||||
}
|
||||
],
|
||||
"type": "components:ui"
|
||||
|
|
|
|||
23
apps/www/src/public/registry/styles/default/chart-area.json
Normal file
23
apps/www/src/public/registry/styles/default/chart-area.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "chart-area",
|
||||
"dependencies": [
|
||||
"@unovis/vue",
|
||||
"@unovis/ts",
|
||||
"@vueuse/core"
|
||||
],
|
||||
"registryDependencies": [
|
||||
"chart",
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"name": "AreaChart.vue",
|
||||
"content": "<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { type BulletLegendItemInterface, CurveType } from '@unovis/ts'\nimport { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'\nimport { Area, Axis, Line } from '@unovis/ts'\nimport { type Component, computed, ref } from 'vue'\nimport { useMounted } from '@vueuse/core'\nimport { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(defineProps<BaseChartProps<T> & {\n /**\n * Render custom tooltip component.\n */\n customTooltip?: Component\n /**\n * Type of curve\n */\n curveType?: CurveType\n /**\n * Controls the visibility of gradient.\n * @default true\n */\n showGradiant?: boolean\n}>(), {\n curveType: CurveType.MonotoneX,\n filterOpacity: 0.2,\n margin: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),\n showXAxis: true,\n showYAxis: true,\n showTooltip: true,\n showLegend: true,\n showGridLine: true,\n showGradiant: true,\n})\n\nconst emits = defineEmits<{\n legendItemClick: [d: BulletLegendItemInterface, i: number]\n}>()\n\ntype KeyOfT = Extract<keyof T, string>\ntype Data = typeof props.data[number]\n\nconst index = computed(() => props.index as KeyOfT)\nconst colors = computed(() => props.colors?.length ? props.colors : defaultColors(props.categories.length))\n\nconst legendItems = ref<BulletLegendItemInterface[]>(props.categories.map((category, i) => ({\n name: category,\n color: colors.value[i],\n inactive: false,\n})))\n\nconst isMounted = useMounted()\n\nfunction handleLegendItemClick(d: BulletLegendItemInterface, i: number) {\n emits('legendItemClick', d, i)\n}\n</script>\n\n<template>\n <div :class=\"cn('w-full h-[400px] flex flex-col items-end', $attrs.class ?? '')\">\n <ChartLegend v-if=\"showLegend\" v-model:items=\"legendItems\" @legend-item-click=\"handleLegendItemClick\" />\n\n <VisXYContainer :style=\"{ height: isMounted ? '100%' : 'auto' }\" :margin=\"{ left: 20, right: 20 }\" :data=\"data\">\n <svg width=\"0\" height=\"0\">\n <defs>\n <linearGradient v-for=\"(color, i) in colors\" :id=\"`color-${i}`\" :key=\"i\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <template v-if=\"showGradiant\">\n <stop offset=\"5%\" :stop-color=\"color\" stop-opacity=\"0.4\" />\n <stop offset=\"95%\" :stop-color=\"color\" stop-opacity=\"0\" />\n </template>\n <template v-else>\n <stop offset=\"0%\" :stop-color=\"color\" />\n </template>\n </linearGradient>\n </defs>\n </svg>\n\n <ChartCrosshair v-if=\"showTooltip\" :colors=\"colors\" :items=\"legendItems\" :index=\"index\" :custom-tooltip=\"customTooltip\" />\n\n <template v-for=\"(category, i) in categories\" :key=\"category\">\n <VisArea\n :x=\"(d: Data, i: number) => i\"\n :y=\"(d: Data) => d[category]\"\n color=\"auto\"\n :curve-type=\"curveType\"\n :attributes=\"{\n [Area.selectors.area]: {\n fill: `url(#color-${i})`,\n },\n }\"\n :opacity=\"legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1\"\n />\n </template>\n\n <template v-for=\"(category, i) in categories\" :key=\"category\">\n <VisLine\n :x=\"(d: Data, i: number) => i\"\n :y=\"(d: Data) => d[category]\"\n :color=\"colors[i]\"\n :curve-type=\"curveType\"\n :attributes=\"{\n [Line.selectors.line]: {\n opacity: legendItems.find(item => item.name === category)?.inactive ? filterOpacity : 1,\n },\n }\"\n />\n </template>\n\n <VisAxis\n v-if=\"showXAxis\"\n type=\"x\"\n :tick-format=\"xFormatter ?? ((v: number) => data[v]?.[index])\"\n :grid-line=\"false\"\n :tick-line=\"false\"\n tick-text-color=\"hsl(var(--vis-text-color))\"\n />\n <VisAxis\n v-if=\"showYAxis\"\n type=\"y\"\n :tick-line=\"false\"\n :tick-format=\"yFormatter\"\n :domain-line=\"false\"\n :grid-line=\"showGridLine\"\n :attributes=\"{\n [Axis.selectors.grid]: {\n class: 'text-muted',\n },\n }\"\n tick-text-color=\"hsl(var(--vis-text-color))\"\n />\n\n <slot />\n </VisXYContainer>\n </div>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "index.ts",
|
||||
"content": "export { default as AreaChart } from './AreaChart.vue'\n"
|
||||
}
|
||||
],
|
||||
"type": "components:ui"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user