(memoryState)
+
+ React.useEffect(() => {
+ listeners.push(setState)
+ return () => {
+ const index = listeners.indexOf(setState)
+ if (index > -1) {
+ listeners.splice(index, 1)
+ }
+ }
+ }, [state])
+
+ return {
+ ...state,
+ toast,
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
+ }
+}
+
+export { useToast, toast }
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index d6e396e2b..512dbdba8 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -14,6 +14,9 @@ import './renderers';
// Export utils
export * from './lib/utils';
+// Export hooks
+export * from './hooks/use-toast';
+
// Export raw Shadcn UI components
export * from './ui';
diff --git a/packages/components/src/renderers/basic/button-group.tsx b/packages/components/src/renderers/basic/button-group.tsx
new file mode 100644
index 000000000..d49602b38
--- /dev/null
+++ b/packages/components/src/renderers/basic/button-group.tsx
@@ -0,0 +1,78 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { ButtonGroupSchema } from '@object-ui/types';
+import { Button } from '../../ui';
+import { cn } from '../../lib/utils';
+
+ComponentRegistry.register('button-group',
+ ({ schema, ...props }: { schema: ButtonGroupSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...buttonGroupProps
+ } = props;
+
+ return (
+
+ {schema.buttons?.map((button, idx) => (
+
+ ))}
+
+ );
+ },
+ {
+ label: 'Button Group',
+ inputs: [
+ {
+ name: 'variant',
+ type: 'enum',
+ enum: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],
+ defaultValue: 'default',
+ label: 'Variant'
+ },
+ {
+ name: 'size',
+ type: 'enum',
+ enum: ['default', 'sm', 'lg', 'icon'],
+ defaultValue: 'default',
+ label: 'Size'
+ },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ variant: 'default',
+ size: 'default',
+ buttons: [
+ { label: 'Left' },
+ { label: 'Middle' },
+ { label: 'Right' }
+ ]
+ }
+ }
+);
diff --git a/packages/components/src/renderers/basic/index.ts b/packages/components/src/renderers/basic/index.ts
index f24577171..62b8b3582 100644
--- a/packages/components/src/renderers/basic/index.ts
+++ b/packages/components/src/renderers/basic/index.ts
@@ -13,3 +13,6 @@ import './separator';
import './image';
import './icon';
import './html';
+import './button-group';
+import './pagination';
+import './navigation-menu';
diff --git a/packages/components/src/renderers/basic/navigation-menu.tsx b/packages/components/src/renderers/basic/navigation-menu.tsx
new file mode 100644
index 000000000..eb76a97ed
--- /dev/null
+++ b/packages/components/src/renderers/basic/navigation-menu.tsx
@@ -0,0 +1,80 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { NavigationMenuSchema } from '@object-ui/types';
+import { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink } from '../../ui/navigation-menu';
+import { cn } from '../../lib/utils';
+
+ComponentRegistry.register('navigation-menu',
+ ({ schema, ...props }: { schema: NavigationMenuSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...navigationMenuProps
+ } = props;
+
+ return (
+
+
+ {schema.items?.map((item, idx) => (
+
+ {item.children ? (
+ <>
+ {item.label}
+
+
+
+ >
+ ) : (
+ {item.label}
+ )}
+
+ ))}
+
+
+ );
+ },
+ {
+ label: 'Navigation Menu',
+ inputs: [
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ items: [
+ { label: 'Home', href: '/' },
+ { label: 'About', href: '/about' }
+ ]
+ }
+ }
+);
diff --git a/packages/components/src/renderers/basic/pagination.tsx b/packages/components/src/renderers/basic/pagination.tsx
new file mode 100644
index 000000000..511055472
--- /dev/null
+++ b/packages/components/src/renderers/basic/pagination.tsx
@@ -0,0 +1,82 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { PaginationSchema } from '@object-ui/types';
+import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from '../../ui/pagination';
+
+ComponentRegistry.register('pagination',
+ ({ schema, ...props }: { schema: PaginationSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...paginationProps
+ } = props;
+
+ const currentPage = schema.currentPage || schema.page || 1;
+ const totalPages = schema.totalPages || 1;
+ const showEllipsis = totalPages > 7;
+
+ const getPageNumbers = () => {
+ if (totalPages <= 7) {
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
+ }
+
+ if (currentPage <= 3) {
+ return [1, 2, 3, 4, 5, -1, totalPages];
+ }
+
+ if (currentPage >= totalPages - 2) {
+ return [1, -1, totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages];
+ }
+
+ return [1, -1, currentPage - 1, currentPage, currentPage + 1, -1, totalPages];
+ };
+
+ return (
+
+
+
+
+
+ {getPageNumbers().map((page, idx) => (
+
+ {page === -1 ? (
+
+ ) : (
+
+ {page}
+
+ )}
+
+ ))}
+
+
+
+
+
+ );
+ },
+ {
+ label: 'Pagination',
+ inputs: [
+ { name: 'currentPage', type: 'number', label: 'Current Page', defaultValue: 1 },
+ { name: 'totalPages', type: 'number', label: 'Total Pages', defaultValue: 10 },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ currentPage: 1,
+ totalPages: 10
+ }
+ }
+);
diff --git a/packages/components/src/renderers/data-display/breadcrumb.tsx b/packages/components/src/renderers/data-display/breadcrumb.tsx
new file mode 100644
index 000000000..111545717
--- /dev/null
+++ b/packages/components/src/renderers/data-display/breadcrumb.tsx
@@ -0,0 +1,59 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { BreadcrumbSchema } from '@object-ui/types';
+import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator } from '../../ui/breadcrumb';
+import { renderChildren } from '../../lib/utils';
+
+ComponentRegistry.register('breadcrumb',
+ ({ schema, ...props }: { schema: BreadcrumbSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...breadcrumbProps
+ } = props;
+
+ return (
+
+
+ {schema.items?.map((item, idx) => (
+
+
+ {idx === (schema.items?.length || 0) - 1 ? (
+ {item.label}
+ ) : (
+ {item.label}
+ )}
+
+ {idx < (schema.items?.length || 0) - 1 && }
+
+ ))}
+
+
+ );
+ },
+ {
+ label: 'Breadcrumb',
+ inputs: [
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ items: [
+ { label: 'Home', href: '/' },
+ { label: 'Products', href: '/products' },
+ { label: 'Product' }
+ ]
+ }
+ }
+);
diff --git a/packages/components/src/renderers/data-display/index.ts b/packages/components/src/renderers/data-display/index.ts
index e8516f6f8..1bb019675 100644
--- a/packages/components/src/renderers/data-display/index.ts
+++ b/packages/components/src/renderers/data-display/index.ts
@@ -12,3 +12,5 @@ import './alert';
import './list';
import './tree-view';
import './statistic';
+import './breadcrumb';
+import './kbd';
diff --git a/packages/components/src/renderers/data-display/kbd.tsx b/packages/components/src/renderers/data-display/kbd.tsx
new file mode 100644
index 000000000..81c6d19ad
--- /dev/null
+++ b/packages/components/src/renderers/data-display/kbd.tsx
@@ -0,0 +1,49 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { KbdSchema } from '@object-ui/types';
+import { cn } from '../../lib/utils';
+
+ComponentRegistry.register('kbd',
+ ({ schema, ...props }: { schema: KbdSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...kbdProps
+ } = props;
+
+ const keys = Array.isArray(schema.keys) ? schema.keys : [schema.keys || schema.label || 'K'];
+
+ return (
+
+ {keys.map((key, idx) => (
+
+ {key}
+
+ ))}
+
+ );
+ },
+ {
+ label: 'Keyboard Key',
+ inputs: [
+ { name: 'label', type: 'string', label: 'Label' },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ label: 'K'
+ }
+ }
+);
diff --git a/packages/components/src/renderers/disclosure/index.ts b/packages/components/src/renderers/disclosure/index.ts
index 3e7cad4fe..b8e55ae64 100644
--- a/packages/components/src/renderers/disclosure/index.ts
+++ b/packages/components/src/renderers/disclosure/index.ts
@@ -8,3 +8,4 @@
import './accordion';
import './collapsible';
+import './toggle-group';
diff --git a/packages/components/src/renderers/disclosure/toggle-group.tsx b/packages/components/src/renderers/disclosure/toggle-group.tsx
new file mode 100644
index 000000000..4e846db86
--- /dev/null
+++ b/packages/components/src/renderers/disclosure/toggle-group.tsx
@@ -0,0 +1,77 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { ToggleGroupSchema } from '@object-ui/types';
+import { ToggleGroup, ToggleGroupItem } from '../../ui/toggle-group';
+
+ComponentRegistry.register('toggle-group',
+ ({ schema, ...props }: { schema: ToggleGroupSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...toggleGroupProps
+ } = props;
+
+ return (
+
+ {schema.items?.map((item, idx) => (
+
+ {item.label}
+
+ ))}
+
+ );
+ },
+ {
+ label: 'Toggle Group',
+ inputs: [
+ {
+ name: 'selectionType',
+ type: 'enum',
+ enum: ['single', 'multiple'],
+ defaultValue: 'single',
+ label: 'Selection Type'
+ },
+ {
+ name: 'variant',
+ type: 'enum',
+ enum: ['default', 'outline'],
+ defaultValue: 'default',
+ label: 'Variant'
+ },
+ {
+ name: 'size',
+ type: 'enum',
+ enum: ['default', 'sm', 'lg'],
+ defaultValue: 'default',
+ label: 'Size'
+ },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ selectionType: 'single',
+ variant: 'default',
+ size: 'default',
+ items: [
+ { value: 'bold', label: 'Bold' },
+ { value: 'italic', label: 'Italic' },
+ { value: 'underline', label: 'Underline' }
+ ]
+ }
+ }
+);
diff --git a/packages/components/src/renderers/feedback/empty.tsx b/packages/components/src/renderers/feedback/empty.tsx
new file mode 100644
index 000000000..f59bb6b84
--- /dev/null
+++ b/packages/components/src/renderers/feedback/empty.tsx
@@ -0,0 +1,48 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { EmptySchema } from '@object-ui/types';
+import { InboxIcon } from 'lucide-react';
+import { cn } from '../../lib/utils';
+
+ComponentRegistry.register('empty',
+ ({ schema, ...props }: { schema: EmptySchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...emptyProps
+ } = props;
+
+ return (
+
+
+
{schema.title || 'No data'}
+ {schema.description && (
+
{schema.description}
+ )}
+
+ );
+ },
+ {
+ label: 'Empty',
+ inputs: [
+ { name: 'title', type: 'string', label: 'Title', defaultValue: 'No data' },
+ { name: 'description', type: 'string', label: 'Description' },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ title: 'No data'
+ }
+ }
+);
diff --git a/packages/components/src/renderers/feedback/index.ts b/packages/components/src/renderers/feedback/index.ts
index 90239104b..791b9f479 100644
--- a/packages/components/src/renderers/feedback/index.ts
+++ b/packages/components/src/renderers/feedback/index.ts
@@ -10,3 +10,7 @@ import './progress';
import './skeleton';
import './toaster';
import './loading';
+import './toast';
+import './spinner';
+import './empty';
+import './sonner';
diff --git a/packages/components/src/renderers/feedback/sonner.tsx b/packages/components/src/renderers/feedback/sonner.tsx
new file mode 100644
index 000000000..c5af9fbf1
--- /dev/null
+++ b/packages/components/src/renderers/feedback/sonner.tsx
@@ -0,0 +1,55 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { SonnerSchema } from '@object-ui/types';
+import { toast } from 'sonner';
+import { Button } from '../../ui';
+
+ComponentRegistry.register('sonner',
+ ({ schema, ...props }: { schema: SonnerSchema; [key: string]: any }) => {
+ const showToast = () => {
+ const toastFn = schema.variant === 'success' ? toast.success :
+ schema.variant === 'error' ? toast.error :
+ schema.variant === 'warning' ? toast.warning :
+ schema.variant === 'info' ? toast.info :
+ toast;
+
+ toastFn(schema.message || schema.title || 'Notification', {
+ description: schema.description,
+ });
+ };
+
+ return (
+
+ );
+ },
+ {
+ label: 'Sonner Toast',
+ inputs: [
+ { name: 'message', type: 'string', label: 'Message' },
+ { name: 'description', type: 'string', label: 'Description' },
+ {
+ name: 'variant',
+ type: 'enum',
+ enum: ['default', 'success', 'error', 'warning', 'info'],
+ defaultValue: 'default',
+ label: 'Variant'
+ },
+ { name: 'buttonLabel', type: 'string', label: 'Button Label' },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ message: 'Notification',
+ buttonLabel: 'Show Toast',
+ variant: 'default'
+ }
+ }
+);
diff --git a/packages/components/src/renderers/feedback/spinner.tsx b/packages/components/src/renderers/feedback/spinner.tsx
new file mode 100644
index 000000000..c2e6ac84a
--- /dev/null
+++ b/packages/components/src/renderers/feedback/spinner.tsx
@@ -0,0 +1,54 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { SpinnerSchema } from '@object-ui/types';
+import { Loader2 } from 'lucide-react';
+import { cn } from '../../lib/utils';
+
+ComponentRegistry.register('spinner',
+ ({ schema, ...props }: { schema: SpinnerSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...spinnerProps
+ } = props;
+
+ const sizeClasses = {
+ sm: 'h-4 w-4',
+ md: 'h-6 w-6',
+ lg: 'h-8 w-8',
+ xl: 'h-12 w-12'
+ };
+
+ return (
+
+ );
+ },
+ {
+ label: 'Spinner',
+ inputs: [
+ {
+ name: 'size',
+ type: 'enum',
+ enum: ['sm', 'md', 'lg', 'xl'],
+ defaultValue: 'md',
+ label: 'Size'
+ },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ size: 'md'
+ }
+ }
+);
diff --git a/packages/components/src/renderers/feedback/toast.tsx b/packages/components/src/renderers/feedback/toast.tsx
new file mode 100644
index 000000000..58f45cc8f
--- /dev/null
+++ b/packages/components/src/renderers/feedback/toast.tsx
@@ -0,0 +1,53 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { ToastSchema } from '@object-ui/types';
+import { useToast } from '../../hooks/use-toast';
+import { Button } from '../../ui';
+
+ComponentRegistry.register('toast',
+ ({ schema, ...props }: { schema: ToastSchema; [key: string]: any }) => {
+ const { toast } = useToast();
+
+ const showToast = () => {
+ toast({
+ title: schema.title,
+ description: schema.description,
+ variant: schema.variant as any,
+ });
+ };
+
+ return (
+
+ );
+ },
+ {
+ label: 'Toast',
+ inputs: [
+ { name: 'title', type: 'string', label: 'Title' },
+ { name: 'description', type: 'string', label: 'Description' },
+ {
+ name: 'variant',
+ type: 'enum',
+ enum: ['default', 'destructive'],
+ defaultValue: 'default',
+ label: 'Variant'
+ },
+ { name: 'buttonLabel', type: 'string', label: 'Button Label' },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ title: 'Notification',
+ buttonLabel: 'Show Toast',
+ variant: 'default'
+ }
+ }
+);
diff --git a/packages/components/src/renderers/feedback/toaster.tsx b/packages/components/src/renderers/feedback/toaster.tsx
index 711630a9b..28fb9cfb0 100644
--- a/packages/components/src/renderers/feedback/toaster.tsx
+++ b/packages/components/src/renderers/feedback/toaster.tsx
@@ -8,8 +8,8 @@
import { ComponentRegistry } from '@object-ui/core';
import type { ToasterSchema } from '@object-ui/types';
-import { Toaster as SonnerToaster } from '../../ui';
-import { Toaster as DefaultToaster } from '../../ui';
+import { Toaster as SonnerToaster } from '../../ui/sonner';
+import { ToastNotifier as DefaultToaster } from '../../ui';
// Note: In shadcn/ui typical setup, Toaster is exported from 'components/ui/toaster' and 'components/ui/sonner'.
// But in @object-ui/ui index.tsx, we need to check if they are exported.
// Assuming they are exported as Toaster and Sonner (or similar).
diff --git a/packages/components/src/renderers/form/combobox.tsx b/packages/components/src/renderers/form/combobox.tsx
new file mode 100644
index 000000000..aabd4ca47
--- /dev/null
+++ b/packages/components/src/renderers/form/combobox.tsx
@@ -0,0 +1,47 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { ComboboxSchema } from '@object-ui/types';
+import { Combobox } from '../../ui';
+
+ComponentRegistry.register('combobox',
+ ({ schema, ...props }: { schema: ComboboxSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...comboboxProps
+ } = props;
+
+ return (
+
+ );
+ },
+ {
+ label: 'Combobox',
+ inputs: [
+ { name: 'placeholder', type: 'string', label: 'Placeholder' },
+ { name: 'value', type: 'string', label: 'Value' },
+ { name: 'disabled', type: 'boolean', label: 'Disabled', defaultValue: false },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ placeholder: 'Select option...',
+ options: []
+ }
+ }
+);
diff --git a/packages/components/src/renderers/form/command.tsx b/packages/components/src/renderers/form/command.tsx
new file mode 100644
index 000000000..18baf02c1
--- /dev/null
+++ b/packages/components/src/renderers/form/command.tsx
@@ -0,0 +1,57 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { CommandSchema } from '@object-ui/types';
+import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../../ui/command';
+
+ComponentRegistry.register('command',
+ ({ schema, ...props }: { schema: CommandSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...commandProps
+ } = props;
+
+ return (
+
+
+
+ {schema.emptyText || 'No results found.'}
+ {schema.groups?.map((group, idx) => (
+
+ {group.items?.map((item, itemIdx) => (
+
+ {item.label}
+
+ ))}
+
+ ))}
+
+
+ );
+ },
+ {
+ label: 'Command',
+ inputs: [
+ { name: 'placeholder', type: 'string', label: 'Placeholder' },
+ { name: 'emptyText', type: 'string', label: 'Empty Text' },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ placeholder: 'Type a command or search...',
+ emptyText: 'No results found.',
+ groups: []
+ }
+ }
+);
diff --git a/packages/components/src/renderers/form/index.ts b/packages/components/src/renderers/form/index.ts
index 54cb00a70..1230512d1 100644
--- a/packages/components/src/renderers/form/index.ts
+++ b/packages/components/src/renderers/form/index.ts
@@ -22,3 +22,5 @@ import './input-otp';
import './calendar';
import './date-picker';
import './file-upload';
+import './combobox';
+import './command';
diff --git a/packages/components/src/renderers/layout/aspect-ratio.tsx b/packages/components/src/renderers/layout/aspect-ratio.tsx
new file mode 100644
index 000000000..d0b73953c
--- /dev/null
+++ b/packages/components/src/renderers/layout/aspect-ratio.tsx
@@ -0,0 +1,50 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { AspectRatioSchema } from '@object-ui/types';
+import { AspectRatio } from '../../ui/aspect-ratio';
+import { renderChildren } from '../../lib/utils';
+
+ComponentRegistry.register('aspect-ratio',
+ ({ schema, ...props }: { schema: AspectRatioSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...aspectRatioProps
+ } = props;
+
+ return (
+
+ {schema.image ? (
+
+ ) : (
+ renderChildren(schema.body)
+ )}
+
+ );
+ },
+ {
+ label: 'Aspect Ratio',
+ inputs: [
+ { name: 'ratio', type: 'number', label: 'Ratio', defaultValue: 16/9 },
+ { name: 'image', type: 'string', label: 'Image URL' },
+ { name: 'alt', type: 'string', label: 'Alt Text' },
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ ratio: 16 / 9
+ }
+ }
+);
diff --git a/packages/components/src/renderers/layout/index.ts b/packages/components/src/renderers/layout/index.ts
index 1f36ba837..f778c720e 100644
--- a/packages/components/src/renderers/layout/index.ts
+++ b/packages/components/src/renderers/layout/index.ts
@@ -14,5 +14,6 @@ import './stack';
import './container';
import './page';
import './semantic';
+import './aspect-ratio';
diff --git a/packages/components/src/renderers/overlay/index.ts b/packages/components/src/renderers/overlay/index.ts
index a5f328dab..f4cdc4396 100644
--- a/packages/components/src/renderers/overlay/index.ts
+++ b/packages/components/src/renderers/overlay/index.ts
@@ -15,3 +15,4 @@ import './drawer';
import './hover-card';
import './dropdown-menu';
import './context-menu';
+import './menubar';
diff --git a/packages/components/src/renderers/overlay/menubar.tsx b/packages/components/src/renderers/overlay/menubar.tsx
new file mode 100644
index 000000000..db6f211d0
--- /dev/null
+++ b/packages/components/src/renderers/overlay/menubar.tsx
@@ -0,0 +1,75 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ComponentRegistry } from '@object-ui/core';
+import type { MenubarSchema } from '@object-ui/types';
+import { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarSeparator, MenubarSub, MenubarSubTrigger, MenubarSubContent } from '../../ui/menubar';
+
+ComponentRegistry.register('menubar',
+ ({ schema, ...props }: { schema: MenubarSchema; [key: string]: any }) => {
+ const {
+ 'data-obj-id': dataObjId,
+ 'data-obj-type': dataObjType,
+ style,
+ ...menubarProps
+ } = props;
+
+ return (
+
+ {schema.menus?.map((menu, idx) => (
+
+ {menu.label}
+
+ {menu.items?.map((item, itemIdx) => (
+ item.separator ? (
+
+ ) : item.children ? (
+
+ {item.label}
+
+ {item.children.map((child, childIdx) => (
+ {child.label}
+ ))}
+
+
+ ) : (
+
+ {item.label}
+
+ )
+ ))}
+
+
+ ))}
+
+ );
+ },
+ {
+ label: 'Menubar',
+ inputs: [
+ { name: 'className', type: 'string', label: 'CSS Class' }
+ ],
+ defaultProps: {
+ menus: [
+ {
+ label: 'File',
+ items: [
+ { label: 'New' },
+ { label: 'Open' },
+ { separator: true },
+ { label: 'Exit' }
+ ]
+ }
+ ]
+ }
+ }
+);
diff --git a/packages/components/src/ui/combobox.tsx b/packages/components/src/ui/combobox.tsx
new file mode 100644
index 000000000..35ec3e2b4
--- /dev/null
+++ b/packages/components/src/ui/combobox.tsx
@@ -0,0 +1,104 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+"use client"
+
+import * as React from "react"
+import { Check, ChevronsUpDown } from "lucide-react"
+
+import { cn } from "../lib/utils"
+import { Button } from "./button"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "./command"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "./popover"
+
+export interface ComboboxOption {
+ value: string
+ label: string
+}
+
+export interface ComboboxProps {
+ options: ComboboxOption[]
+ value?: string
+ onValueChange?: (value: string) => void
+ placeholder?: string
+ searchPlaceholder?: string
+ emptyText?: string
+ className?: string
+ disabled?: boolean
+}
+
+export function Combobox({
+ options,
+ value,
+ onValueChange,
+ placeholder = "Select option...",
+ searchPlaceholder = "Search...",
+ emptyText = "No option found.",
+ className,
+ disabled,
+}: ComboboxProps) {
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+
+
+
+
+
+
+ {emptyText}
+
+ {options.map((option) => (
+ {
+ onValueChange?.(currentValue === value ? "" : currentValue)
+ setOpen(false)
+ }}
+ >
+
+ {option.label}
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/packages/components/src/ui/date-picker.tsx b/packages/components/src/ui/date-picker.tsx
new file mode 100644
index 000000000..ac47175ea
--- /dev/null
+++ b/packages/components/src/ui/date-picker.tsx
@@ -0,0 +1,61 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+"use client"
+
+import * as React from "react"
+import { CalendarIcon } from "lucide-react"
+import { format } from "date-fns"
+
+import { cn } from "../lib/utils"
+import { Button } from "./button"
+import { Calendar } from "./calendar"
+import { Popover, PopoverContent, PopoverTrigger } from "./popover"
+
+export interface DatePickerProps {
+ date?: Date
+ onDateChange?: (date: Date | undefined) => void
+ placeholder?: string
+ className?: string
+ disabled?: boolean
+}
+
+export function DatePicker({
+ date,
+ onDateChange,
+ placeholder = "Pick a date",
+ className,
+ disabled,
+}: DatePickerProps) {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/packages/components/src/ui/index.ts b/packages/components/src/ui/index.ts
index 66e3d7b60..7ae8593ae 100644
--- a/packages/components/src/ui/index.ts
+++ b/packages/components/src/ui/index.ts
@@ -22,8 +22,10 @@ export * from './carousel';
export * from './chatbot';
export * from './checkbox';
export * from './collapsible';
+export * from './combobox';
export * from './command';
export * from './context-menu';
+export * from './date-picker';
export * from './dialog';
export * from './drawer';
export * from './dropdown-menu';
@@ -59,6 +61,8 @@ export * from './table';
export * from './tabs';
export * from './textarea';
export * from './timeline';
+export * from './toast';
+export { Toaster as ToastNotifier } from './toaster';
export * from './toggle-group';
export * from './toggle';
export * from './tooltip';
diff --git a/packages/components/src/ui/toast.tsx b/packages/components/src/ui/toast.tsx
new file mode 100644
index 000000000..5e01d452e
--- /dev/null
+++ b/packages/components/src/ui/toast.tsx
@@ -0,0 +1,135 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import * as React from "react"
+import * as ToastPrimitives from "@radix-ui/react-toast"
+import { cva, type VariantProps } from "class-variance-authority"
+import { X } from "lucide-react"
+
+import { cn } from "../lib/utils"
+
+const ToastProvider = ToastPrimitives.Provider
+
+const ToastViewport = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastViewport.displayName = ToastPrimitives.Viewport.displayName
+
+const toastVariants = cva(
+ "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
+ {
+ variants: {
+ variant: {
+ default: "border bg-background text-foreground",
+ destructive:
+ "destructive group border-destructive bg-destructive text-destructive-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+const Toast = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, ...props }, ref) => {
+ return (
+
+ )
+})
+Toast.displayName = ToastPrimitives.Root.displayName
+
+const ToastAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastAction.displayName = ToastPrimitives.Action.displayName
+
+const ToastClose = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+ToastClose.displayName = ToastPrimitives.Close.displayName
+
+const ToastTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastTitle.displayName = ToastPrimitives.Title.displayName
+
+const ToastDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastDescription.displayName = ToastPrimitives.Description.displayName
+
+type ToastProps = React.ComponentPropsWithoutRef
+
+type ToastActionElement = React.ReactElement
+
+export {
+ type ToastProps,
+ type ToastActionElement,
+ ToastProvider,
+ ToastViewport,
+ Toast,
+ ToastTitle,
+ ToastDescription,
+ ToastClose,
+ ToastAction,
+}
diff --git a/packages/components/src/ui/toaster.tsx b/packages/components/src/ui/toaster.tsx
new file mode 100644
index 000000000..af8a212ed
--- /dev/null
+++ b/packages/components/src/ui/toaster.tsx
@@ -0,0 +1,43 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+"use client"
+
+import {
+ Toast,
+ ToastClose,
+ ToastDescription,
+ ToastProvider,
+ ToastTitle,
+ ToastViewport,
+} from "./toast"
+import { useToast } from "../hooks/use-toast"
+
+export function Toaster() {
+ const { toasts } = useToast()
+
+ return (
+
+ {toasts.map(function ({ id, title, description, action, ...props }) {
+ return (
+
+
+ {title && {title}}
+ {description && (
+ {description}
+ )}
+
+ {action}
+
+
+ )
+ })}
+
+
+ )
+}
diff --git a/packages/types/src/data-display.ts b/packages/types/src/data-display.ts
index 61a0010a9..83474da52 100644
--- a/packages/types/src/data-display.ts
+++ b/packages/types/src/data-display.ts
@@ -609,6 +609,51 @@ export interface TimelineSchema extends BaseSchema {
position?: 'left' | 'right' | 'alternate';
}
+/**
+ * Breadcrumb item
+ */
+export interface BreadcrumbItem {
+ /**
+ * Item label
+ */
+ label: string;
+ /**
+ * Item href/link
+ */
+ href?: string;
+}
+
+/**
+ * Breadcrumb component
+ */
+export interface BreadcrumbSchema extends BaseSchema {
+ type: 'breadcrumb';
+ /**
+ * Breadcrumb items
+ */
+ items: BreadcrumbItem[];
+ /**
+ * Separator character
+ * @default '/'
+ */
+ separator?: string;
+}
+
+/**
+ * Keyboard key component
+ */
+export interface KbdSchema extends BaseSchema {
+ type: 'kbd';
+ /**
+ * Key label (single key)
+ */
+ label?: string;
+ /**
+ * Key labels (multiple keys)
+ */
+ keys?: string | string[];
+}
+
/**
* Union type of all data display schemas
*/
@@ -624,7 +669,9 @@ export type DataDisplaySchema =
| ChartSchema
| TimelineSchema
| HtmlSchema
- | StatisticSchema;
+ | StatisticSchema
+ | BreadcrumbSchema
+ | KbdSchema;
/**
* Raw HTML component
diff --git a/packages/types/src/disclosure.ts b/packages/types/src/disclosure.ts
index 1f0d9855b..7915bd9bf 100644
--- a/packages/types/src/disclosure.ts
+++ b/packages/types/src/disclosure.ts
@@ -113,9 +113,74 @@ export interface CollapsibleSchema extends BaseSchema {
onOpenChange?: (open: boolean) => void;
}
+/**
+ * Toggle group item
+ */
+export interface ToggleGroupItem {
+ /**
+ * Item value
+ */
+ value: string;
+ /**
+ * Item label
+ */
+ label: string;
+ /**
+ * Item icon
+ */
+ icon?: string;
+ /**
+ * Whether item is disabled
+ */
+ disabled?: boolean;
+}
+
+/**
+ * Toggle group component
+ */
+export interface ToggleGroupSchema extends BaseSchema {
+ type: 'toggle-group';
+ /**
+ * Toggle group selection mode
+ * @default 'single'
+ */
+ selectionType?: 'single' | 'multiple';
+ /**
+ * Toggle group variant
+ * @default 'default'
+ */
+ variant?: 'default' | 'outline';
+ /**
+ * Toggle group size
+ * @default 'default'
+ */
+ size?: 'default' | 'sm' | 'lg';
+ /**
+ * Toggle group items
+ */
+ items?: ToggleGroupItem[];
+ /**
+ * Default selected value(s)
+ */
+ defaultValue?: string | string[];
+ /**
+ * Controlled selected value(s)
+ */
+ value?: string | string[];
+ /**
+ * Whether toggle group is disabled
+ */
+ disabled?: boolean;
+ /**
+ * Change handler
+ */
+ onValueChange?: (value: string | string[]) => void;
+}
+
/**
* Union type of all disclosure schemas
*/
export type DisclosureSchema =
| AccordionSchema
- | CollapsibleSchema;
+ | CollapsibleSchema
+ | ToggleGroupSchema;
diff --git a/packages/types/src/feedback.ts b/packages/types/src/feedback.ts
index 098a8bf72..720c53ae8 100644
--- a/packages/types/src/feedback.ts
+++ b/packages/types/src/feedback.ts
@@ -167,6 +167,69 @@ export interface ToasterSchema extends BaseSchema {
limit?: number;
}
+/**
+ * Spinner component
+ */
+export interface SpinnerSchema extends BaseSchema {
+ type: 'spinner';
+ /**
+ * Spinner size
+ * @default 'md'
+ */
+ size?: 'sm' | 'md' | 'lg' | 'xl';
+}
+
+/**
+ * Empty state component
+ */
+export interface EmptySchema extends BaseSchema {
+ type: 'empty';
+ /**
+ * Empty state title
+ */
+ title?: string;
+ /**
+ * Empty state description
+ */
+ description?: string;
+ /**
+ * Icon to display
+ */
+ icon?: string;
+}
+
+/**
+ * Sonner toast component (using sonner library)
+ */
+export interface SonnerSchema extends BaseSchema {
+ type: 'sonner';
+ /**
+ * Toast message/title
+ */
+ message?: string;
+ /**
+ * Toast title (alias for message)
+ */
+ title?: string;
+ /**
+ * Toast description
+ */
+ description?: string;
+ /**
+ * Toast variant
+ * @default 'default'
+ */
+ variant?: 'default' | 'success' | 'error' | 'warning' | 'info';
+ /**
+ * Button label to trigger toast
+ */
+ buttonLabel?: string;
+ /**
+ * Button variant
+ */
+ buttonVariant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link';
+}
+
/**
* Union type of all feedback schemas
*/
@@ -175,4 +238,7 @@ export type FeedbackSchema =
| ProgressSchema
| SkeletonSchema
| ToastSchema
- | ToasterSchema;
+ | ToasterSchema
+ | SpinnerSchema
+ | EmptySchema
+ | SonnerSchema;
diff --git a/packages/types/src/form.ts b/packages/types/src/form.ts
index 43eba573f..0883aff05 100644
--- a/packages/types/src/form.ts
+++ b/packages/types/src/form.ts
@@ -940,6 +940,126 @@ export interface LabelSchema extends BaseSchema {
htmlFor?: string;
}
+/**
+ * Combobox option
+ */
+export interface ComboboxOption {
+ /**
+ * Option value
+ */
+ value: string;
+ /**
+ * Option label (displayed to user)
+ */
+ label: string;
+ /**
+ * Whether option is disabled
+ */
+ disabled?: boolean;
+}
+
+/**
+ * Combobox component (searchable select)
+ */
+export interface ComboboxSchema extends BaseSchema {
+ type: 'combobox';
+ /**
+ * Field name for form submission
+ */
+ name?: string;
+ /**
+ * Combobox label
+ */
+ label?: string;
+ /**
+ * Placeholder text
+ */
+ placeholder?: string;
+ /**
+ * Combobox options
+ */
+ options?: ComboboxOption[];
+ /**
+ * Default selected value
+ */
+ defaultValue?: string;
+ /**
+ * Controlled value
+ */
+ value?: string;
+ /**
+ * Whether field is disabled
+ */
+ disabled?: boolean;
+ /**
+ * Help text or description
+ */
+ description?: string;
+ /**
+ * Error message
+ */
+ error?: string;
+ /**
+ * Change handler
+ */
+ onChange?: (value: string) => void;
+}
+
+/**
+ * Command menu item
+ */
+export interface CommandItem {
+ /**
+ * Item value
+ */
+ value: string;
+ /**
+ * Item label (displayed to user)
+ */
+ label: string;
+ /**
+ * Item icon
+ */
+ icon?: string;
+}
+
+/**
+ * Command menu group
+ */
+export interface CommandGroup {
+ /**
+ * Group heading
+ */
+ heading?: string;
+ /**
+ * Group items
+ */
+ items: CommandItem[];
+}
+
+/**
+ * Command component (command palette)
+ */
+export interface CommandSchema extends BaseSchema {
+ type: 'command';
+ /**
+ * Placeholder text
+ */
+ placeholder?: string;
+ /**
+ * Empty state text
+ */
+ emptyText?: string;
+ /**
+ * Command groups
+ */
+ groups?: CommandGroup[];
+ /**
+ * Change handler
+ */
+ onChange?: (value: string) => void;
+}
+
/**
* Union type of all form schemas
*/
@@ -958,5 +1078,7 @@ export type FormComponentSchema =
| CalendarSchema
| InputOTPSchema
| FormSchema
- | LabelSchema;
+ | LabelSchema
+ | ComboboxSchema
+ | CommandSchema;
diff --git a/packages/types/src/layout.ts b/packages/types/src/layout.ts
index 5ac68395a..b89656555 100644
--- a/packages/types/src/layout.ts
+++ b/packages/types/src/layout.ts
@@ -396,6 +396,34 @@ export interface ResizablePanel {
content: SchemaNode | SchemaNode[];
}
+/**
+ * Aspect ratio component
+ */
+export interface AspectRatioSchema extends BaseSchema {
+ type: 'aspect-ratio';
+ /**
+ * Aspect ratio (width / height)
+ * @default 16/9
+ */
+ ratio?: number;
+ /**
+ * Image URL to display
+ */
+ image?: string;
+ /**
+ * Image alt text
+ */
+ alt?: string;
+ /**
+ * Child components (alternative to image)
+ */
+ body?: SchemaNode | SchemaNode[];
+ /**
+ * Child components (alternative syntax)
+ */
+ children?: SchemaNode | SchemaNode[];
+}
+
/**
* Page layout component
* Top-level container for a page route
@@ -442,19 +470,6 @@ export type LayoutSchema =
| TabsSchema
| ScrollAreaSchema
| ResizableSchema
+ | AspectRatioSchema
| PageSchema;
-/**
- * Page container component
- */
-export interface PageSchema extends BaseSchema {
- type: 'page';
- /**
- * Page title
- */
- title?: string;
- /**
- * Child components
- */
- children?: SchemaNode | SchemaNode[];
-}
diff --git a/packages/types/src/navigation.ts b/packages/types/src/navigation.ts
index 6bbb3e496..5afe3db2a 100644
--- a/packages/types/src/navigation.ts
+++ b/packages/types/src/navigation.ts
@@ -207,7 +207,11 @@ export interface PaginationSchema extends BaseSchema {
/**
* Current page (1-indexed)
*/
- page: number;
+ currentPage?: number;
+ /**
+ * Legacy page property
+ */
+ page?: number;
/**
* Total number of pages
*/
@@ -233,6 +237,99 @@ export interface PaginationSchema extends BaseSchema {
onPageChange?: (page: number) => void;
}
+/**
+ * Navigation menu item
+ */
+export interface NavigationMenuItem {
+ /**
+ * Item label
+ */
+ label: string;
+ /**
+ * Item href/link
+ */
+ href?: string;
+ /**
+ * Item description
+ */
+ description?: string;
+ /**
+ * Item icon
+ */
+ icon?: string;
+ /**
+ * Child items
+ */
+ children?: NavigationMenuItem[];
+}
+
+/**
+ * Navigation menu component
+ */
+export interface NavigationMenuSchema extends BaseSchema {
+ type: 'navigation-menu';
+ /**
+ * Navigation menu items
+ */
+ items?: NavigationMenuItem[];
+ /**
+ * Navigation menu orientation
+ * @default 'horizontal'
+ */
+ orientation?: 'horizontal' | 'vertical';
+}
+
+/**
+ * Button group button
+ */
+export interface ButtonGroupButton {
+ /**
+ * Button label
+ */
+ label: string;
+ /**
+ * Button variant
+ */
+ variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link';
+ /**
+ * Button size
+ */
+ size?: 'default' | 'sm' | 'lg' | 'icon';
+ /**
+ * Whether button is disabled
+ */
+ disabled?: boolean;
+ /**
+ * Click handler
+ */
+ onClick?: () => void;
+ /**
+ * Button CSS class
+ */
+ className?: string;
+}
+
+/**
+ * Button group component
+ */
+export interface ButtonGroupSchema extends BaseSchema {
+ type: 'button-group';
+ /**
+ * Button group buttons
+ */
+ buttons?: ButtonGroupButton[];
+ /**
+ * Default button variant
+ * @default 'default'
+ */
+ variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link';
+ /**
+ * Default button size
+ * @default 'default'
+ */
+ size?: 'default' | 'sm' | 'lg' | 'icon';
+}
+
/**
* Union type of all navigation schemas
*/
@@ -240,4 +337,6 @@ export type NavigationSchema =
| HeaderBarSchema
| SidebarSchema
| BreadcrumbSchema
- | PaginationSchema;
+ | PaginationSchema
+ | NavigationMenuSchema
+ | ButtonGroupSchema;
diff --git a/packages/types/src/overlay.ts b/packages/types/src/overlay.ts
index 411c0f296..76167428b 100644
--- a/packages/types/src/overlay.ts
+++ b/packages/types/src/overlay.ts
@@ -411,6 +411,31 @@ export interface ContextMenuSchema extends BaseSchema {
children: SchemaNode | SchemaNode[];
}
+/**
+ * Menubar menu
+ */
+export interface MenubarMenu {
+ /**
+ * Menu label
+ */
+ label: string;
+ /**
+ * Menu items
+ */
+ items: MenuItem[];
+}
+
+/**
+ * Menubar component
+ */
+export interface MenubarSchema extends BaseSchema {
+ type: 'menubar';
+ /**
+ * Menubar menus
+ */
+ menus?: MenubarMenu[];
+}
+
/**
* Union type of all overlay schemas
*/
@@ -423,4 +448,5 @@ export type OverlaySchema =
| TooltipSchema
| HoverCardSchema
| DropdownMenuSchema
- | ContextMenuSchema;
+ | ContextMenuSchema
+ | MenubarSchema;