From 5e0aaf9bdf9384c9cd0b0b97b1eb22eefea45a07 Mon Sep 17 00:00:00 2001 From: hywax Date: Mon, 25 Nov 2024 19:19:02 +0500 Subject: [PATCH] feat(Tree): implement component --- docs/content/3.components/tree.md | 32 ++++++++ playground/app/app.vue | 3 +- playground/app/pages/components/tree.vue | 42 ++++++++++ src/runtime/components/Tree.vue | 99 ++++++++++++++++++++++++ src/runtime/types/index.ts | 1 + src/theme/index.ts | 1 + src/theme/tree.ts | 56 ++++++++++++++ test/components/Tree.spec.ts | 17 ++++ 8 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 docs/content/3.components/tree.md create mode 100644 playground/app/pages/components/tree.vue create mode 100644 src/runtime/components/Tree.vue create mode 100644 src/theme/tree.ts create mode 100644 test/components/Tree.spec.ts diff --git a/docs/content/3.components/tree.md b/docs/content/3.components/tree.md new file mode 100644 index 0000000000..d7268940a2 --- /dev/null +++ b/docs/content/3.components/tree.md @@ -0,0 +1,32 @@ +--- +description: +links: + - label: Tree + icon: i-custom-radix-vue + to: https://www.radix-vue.com/components/tree.html + - label: GitHub + icon: i-simple-icons-github + to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tree.vue +--- + +## Usage + +## Examples + +## API + +### Props + +:component-props + +### Slots + +:component-slots + +### Emits + +:component-emits + +## Theme + +:component-theme diff --git a/playground/app/app.vue b/playground/app/app.vue index 8a3a4b7cd4..1394c5d55e 100644 --- a/playground/app/app.vue +++ b/playground/app/app.vue @@ -60,7 +60,8 @@ const components = [ 'table', 'textarea', 'toast', - 'tooltip' + 'tooltip', + 'tree' ] const items = components.map(component => ({ label: upperName(component), to: `/components/${component}` })) diff --git a/playground/app/pages/components/tree.vue b/playground/app/pages/components/tree.vue new file mode 100644 index 0000000000..d048330342 --- /dev/null +++ b/playground/app/pages/components/tree.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/runtime/components/Tree.vue b/src/runtime/components/Tree.vue new file mode 100644 index 0000000000..a39d293b60 --- /dev/null +++ b/src/runtime/components/Tree.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts index 072e3e8db8..709c22731d 100644 --- a/src/runtime/types/index.ts +++ b/src/runtime/types/index.ts @@ -43,5 +43,6 @@ export * from '../components/Textarea.vue' export * from '../components/Toast.vue' export * from '../components/Toaster.vue' export * from '../components/Tooltip.vue' +export * from '../components/Tree.vue' export * from './form' export * from './locale' diff --git a/src/theme/index.ts b/src/theme/index.ts index 90d2af7156..229727826c 100644 --- a/src/theme/index.ts +++ b/src/theme/index.ts @@ -43,3 +43,4 @@ export { default as textarea } from './textarea' export { default as toast } from './toast' export { default as toaster } from './toaster' export { default as tooltip } from './tooltip' +export { default as tree } from './tree' diff --git a/src/theme/tree.ts b/src/theme/tree.ts new file mode 100644 index 0000000000..201caeac38 --- /dev/null +++ b/src/theme/tree.ts @@ -0,0 +1,56 @@ +import type { ModuleOptions } from '../module' + +export default (options: Required) => ({ + slots: { + root: '', + item: ['group relative w-full flex items-center gap-2 px-2 py-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-[var(--ui-text)] data-[selected]:text-[var(--ui-text-highlighted)] data-[selected]:before:bg-[var(--ui-bg-elevated)]/50', options.theme.transitions && 'transition-colors before:transition-colors'], + itemLeadingIcon: 'shrink-0', + itemLeadingAvatar: 'shrink-0', + itemLeadingAvatarSize: '', + itemTrailing: 'ms-auto inline-flex', + itemTrailingIcon: 'shrink-0', + itemLabel: 'truncate' + }, + variants: { + size: { + xs: { + label: 'p-1 text-xs gap-1', + item: 'p-1 text-xs gap-1', + itemLeadingIcon: 'size-4', + itemLeadingAvatarSize: '3xs', + itemTrailingIcon: 'size-4' + }, + sm: { + label: 'p-1.5 text-xs gap-1.5', + item: 'p-1.5 text-xs gap-1.5', + itemLeadingIcon: 'size-4', + itemLeadingAvatarSize: '3xs', + itemTrailingIcon: 'size-4' + }, + md: { + label: 'p-1.5 text-sm gap-1.5', + item: 'p-1.5 text-sm gap-1.5', + itemLeadingIcon: 'size-5', + itemLeadingAvatarSize: '2xs', + itemTrailingIcon: 'size-5' + }, + lg: { + label: 'p-2 text-sm gap-2', + item: 'p-2 text-sm gap-2', + itemLeadingIcon: 'size-5', + itemLeadingAvatarSize: '2xs', + itemTrailingIcon: 'size-5' + }, + xl: { + item: 'p-2 text-base gap-2', + itemLeadingIcon: 'size-6', + itemLeadingAvatarSize: 'xs', + itemTrailingIcon: 'size-6' + } + } + }, + compoundVariants: [], + defaultVariants: { + size: 'md' + } +}) diff --git a/test/components/Tree.spec.ts b/test/components/Tree.spec.ts new file mode 100644 index 0000000000..db6e90a108 --- /dev/null +++ b/test/components/Tree.spec.ts @@ -0,0 +1,17 @@ +import { describe, it, expect } from 'vitest' +import Tree, { type TreeProps, type TreeSlots } from '../../src/runtime/components/Tree.vue' +import ComponentRender from '../component-render' + +describe('Tree', () => { + it.each([ + // Props + ['with as', { props: { as: 'div' } }], + ['with class', { props: { class: '' } }], + ['with ui', { props: { ui: {} } }], + // Slots + ['with default slot', { slots: { default: () => 'Default slot' } }] + ])('renders %s correctly', async (nameOrHtml: string, options: { props?: TreeProps, slots?: Partial }) => { + const html = await ComponentRender(nameOrHtml, options, Tree) + expect(html).toMatchSnapshot() + }) +})