diff --git a/playground-vue/src/app.vue b/playground-vue/src/app.vue index cc2e69d572..37b2171865 100644 --- a/playground-vue/src/app.vue +++ b/playground-vue/src/app.vue @@ -64,6 +64,7 @@ const components = [ 'textarea', 'timeline', 'toast', + 'toolbar', 'tooltip', 'tree' ] diff --git a/playground/app/app.vue b/playground/app/app.vue index 7348fbd70f..4993036c08 100644 --- a/playground/app/app.vue +++ b/playground/app/app.vue @@ -64,6 +64,7 @@ const components = [ 'textarea', 'timeline', 'toast', + 'toolbar', 'tooltip', 'tree' ] diff --git a/playground/app/pages/components/toolbar.vue b/playground/app/pages/components/toolbar.vue new file mode 100644 index 0000000000..bcc58541f7 --- /dev/null +++ b/playground/app/pages/components/toolbar.vue @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Variants: + + + + + + + + + + Sizes: + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/runtime/components/Toolbar.vue b/src/runtime/components/Toolbar.vue new file mode 100644 index 0000000000..6ce3cef347 --- /dev/null +++ b/src/runtime/components/Toolbar.vue @@ -0,0 +1,71 @@ + + + + + + + + + + + + {{ title }} + + + + + + + + + + + + + + + diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts index f413b71caa..6c02f85f95 100644 --- a/src/runtime/types/index.ts +++ b/src/runtime/types/index.ts @@ -50,6 +50,7 @@ export * from '../components/Textarea.vue' export * from '../components/Timeline.vue' export * from '../components/Toast.vue' export * from '../components/Toaster.vue' +export * from '../components/Toolbar.vue' export * from '../components/Tooltip.vue' export * from '../components/Tree.vue' export * from './form' diff --git a/src/theme/index.ts b/src/theme/index.ts index e03aecd3b5..d1419691f2 100644 --- a/src/theme/index.ts +++ b/src/theme/index.ts @@ -48,5 +48,6 @@ export { default as textarea } from './textarea' export { default as timeline } from './timeline' export { default as toast } from './toast' export { default as toaster } from './toaster' +export { default as toolbar } from './toolbar' export { default as tooltip } from './tooltip' export { default as tree } from './tree' diff --git a/src/theme/toolbar.ts b/src/theme/toolbar.ts new file mode 100644 index 0000000000..047723bea0 --- /dev/null +++ b/src/theme/toolbar.ts @@ -0,0 +1,60 @@ +export default { + slots: { + root: 'flex justify-between items-center shrink-0 no-wrap relative w-full px-3 overflow-x-auto', + title: 'text-pretty pr-3 truncate font-semibold text-highlighted', + left: 'flex items-center', + right: 'flex items-center', + center: 'flex items-center' + }, + variants: { + variant: { + solid: { + root: 'bg-inverted text-inverted', + title: 'text-inverted' + }, + outline: { + root: 'bg-default border border-default' + }, + soft: { + root: 'bg-elevated/50' + }, + subtle: { + root: 'bg-elevated/50 border border-default' + } + }, + size: { + sm: { + root: 'gap-1 min-h-[40px]', + title: 'text-sm', + left: 'gap-1', + center: 'gap-1', + right: 'gap-1' + }, + md: { + root: 'gap-1.5 min-h-[49px]', + title: 'text-base', + left: 'gap-1.5', + center: 'gap-1.5', + right: 'gap-1.5' + }, + lg: { + root: 'gap-2 min-h-14', + title: 'text-lg', + left: 'gap-2', + center: 'gap-2', + right: 'gap-2' + }, + xl: { + root: 'gap-3 min-h-16', + title: 'text-xl', + left: 'gap-3', + center: 'gap-3', + right: 'gap-3' + } + } + }, + defaultVariants: { + variant: 'outline', + size: 'md' + } +} diff --git a/test/components/Toolbar.spec.ts b/test/components/Toolbar.spec.ts new file mode 100644 index 0000000000..6b3fea905f --- /dev/null +++ b/test/components/Toolbar.spec.ts @@ -0,0 +1,26 @@ +import { describe, it, expect } from 'vitest' +import Toolbar, { type ToolbarProps, type ToolbarSlots } from '../../src/runtime/components/Toolbar.vue' +import ComponentRender from '../component-render' +import theme from '#build/ui/toolbar' + +describe('Toolbar', () => { + const variants = Object.keys(theme.variants.variant) as any + const sizes = Object.keys(theme.variants.size) as any + + it.each([ + // Props + ['with as', { props: { as: 'section' } }], + ...variants.map((variant: string) => [`with variant ${variant}`, { props: { variant } }]), + ...sizes.map((size: string) => [`with variant ${size}`, { props: { size } }]), + ['with class', { props: { class: 'border-0 border-b' } }], + ['with ui', { props: { ui: { root: 'border-x-0' } } }], + // Slots + ['with left slot', { slots: { left: () => 'Left slot' } }], + ['with title slot', { slots: { title: () => 'Title slot' } }], + ['with right slot', { slots: { right: () => 'Right slot' } }], + ['with center slot', { slots: { center: () => 'Center slot' } }] + ])('renders %s correctly', async (nameOrHtml: string, options: { props?: ToolbarProps, slots?: Partial }) => { + const html = await ComponentRender(nameOrHtml, options, Toolbar) + expect(html).toMatchSnapshot() + }) +}) diff --git a/test/components/__snapshots__/Toolbar-vue.spec.ts.snap b/test/components/__snapshots__/Toolbar-vue.spec.ts.snap new file mode 100644 index 0000000000..1ac0518966 --- /dev/null +++ b/test/components/__snapshots__/Toolbar-vue.spec.ts.snap @@ -0,0 +1,149 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Toolbar > renders with as correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with center slot correctly 1`] = ` +" + + + + Center slot + +" +`; + +exports[`Toolbar > renders with class correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with left slot correctly 1`] = ` +" + Left slot + + +" +`; + +exports[`Toolbar > renders with right slot correctly 1`] = ` +" + + + + + Right slot +" +`; + +exports[`Toolbar > renders with title slot correctly 1`] = ` +" + + Title slot + + + +" +`; + +exports[`Toolbar > renders with ui correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant lg correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant md correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant outline correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant sm correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant soft correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant solid correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant subtle correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant xl correctly 1`] = ` +" + + + + + +" +`; diff --git a/test/components/__snapshots__/Toolbar.spec.ts.snap b/test/components/__snapshots__/Toolbar.spec.ts.snap new file mode 100644 index 0000000000..1ac0518966 --- /dev/null +++ b/test/components/__snapshots__/Toolbar.spec.ts.snap @@ -0,0 +1,149 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Toolbar > renders with as correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with center slot correctly 1`] = ` +" + + + + Center slot + +" +`; + +exports[`Toolbar > renders with class correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with left slot correctly 1`] = ` +" + Left slot + + +" +`; + +exports[`Toolbar > renders with right slot correctly 1`] = ` +" + + + + + Right slot +" +`; + +exports[`Toolbar > renders with title slot correctly 1`] = ` +" + + Title slot + + + +" +`; + +exports[`Toolbar > renders with ui correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant lg correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant md correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant outline correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant sm correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant soft correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant solid correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant subtle correctly 1`] = ` +" + + + + + +" +`; + +exports[`Toolbar > renders with variant xl correctly 1`] = ` +" + + + + + +" +`;