import type { ElementField, IconType, Option, Path } from '@bagelink/vue' import { Dropdown, ListItem } from '@bagelink/vue' import { markRaw } from 'vue' export type DefaultPathsOptions = Record export type PathsOptions = Record export type BaseElementField = ElementField export interface BtnElementOptions< T = any, PO extends PathsOptions = DefaultPathsOptions > extends Partial> { text?: string variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger' size?: 'sm' | 'md' | 'lg' disabled?: boolean loading?: boolean icon?: IconType iconPosition?: 'left' | 'right' href?: string target?: '_blank' | '_self' | '_parent' | '_top' } export interface PillElementOptions< T = any, PO extends PathsOptions = DefaultPathsOptions > extends Partial> { color?: string icon?: IconType } export interface TxtElementOptions< T = any, PO extends PathsOptions = DefaultPathsOptions > extends Partial> { text?: string tag?: 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' weight?: 'light' | 'normal' | 'medium' | 'semibold' | 'bold' color?: string align?: 'left' | 'center' | 'right' | 'justify' } export interface ImgElementOptions< T = any, PO extends PathsOptions = DefaultPathsOptions > extends Partial> { src?: string alt?: string width?: number | string height?: number | string loading?: 'lazy' | 'eager' fit?: 'cover' | 'contain' | 'fill' | 'scale-down' | 'none' rounded?: boolean | 'sm' | 'md' | 'lg' | 'full' fallback?: string } export interface DropdownElementOptions< T = any, PO extends PathsOptions = DefaultPathsOptions > extends Partial> { options?: Option[] | (() => Option[]) placeholder?: string searchable?: boolean multiselect?: boolean clearable?: boolean onSelect?: (value: any) => void onSearch?: (search: string) => any } export interface ListItemElementOptions< T = any, PO extends PathsOptions = DefaultPathsOptions > extends Partial> { id?: Path title?: string subtitle?: string icon?: IconType avatar?: string badge?: string | number actions?: Array<{ label: string, onClick: () => void, icon?: IconType }> clickable?: boolean divider?: boolean } // Magical button overloads with schema support export function btn< T = any, PO extends PathsOptions = DefaultPathsOptions >(options: BtnElementOptions): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, text: string): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, icon: IconType): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( id: P, text: string, options: BtnElementOptions ): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( id: P, icon: IconType, options: BtnElementOptions ): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( id: P, textOrIcon: string | IconType, options: BtnElementOptions ): BaseElementField export function btn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( idOrOptions?: P | BtnElementOptions, textOrIcon?: string | IconType, options?: BtnElementOptions, ): BaseElementField { // Handle different overload patterns let id: Path | undefined let text: string = '' let icon: IconType | undefined let finalOptions: BtnElementOptions = {} if (typeof idOrOptions === 'object' && idOrOptions !== null) { // btn(options) finalOptions = idOrOptions const { id: optionId, text: optionText, icon: optionIcon } = finalOptions id = optionId as Path | undefined text = optionText ?? '' icon = optionIcon } else { // btn(id, ...) patterns id = idOrOptions if (textOrIcon != null) { // Check if textOrIcon is likely an icon vs text // Icons are typically: lowercase, contain hyphens/underscores, no spaces, shorter length const isLikelyIcon = typeof textOrIcon === 'string' && textOrIcon.length <= 30 && !textOrIcon.includes(' ') && (textOrIcon.includes('-') || textOrIcon.includes('_') || textOrIcon.length <= 12) && textOrIcon === textOrIcon.toLowerCase() if (isLikelyIcon) { icon = textOrIcon as IconType if (options) { finalOptions = options } } else { text = textOrIcon if (options) { finalOptions = options } } } } return { $el: 'btn', id, class: finalOptions.class, vIf: finalOptions.vIf, style: finalOptions.style, onClick: finalOptions.onClick, attrs: { value: text || (finalOptions.text ?? '') || '', variant: finalOptions.variant, size: finalOptions.size, disabled: finalOptions.disabled, loading: finalOptions.loading, icon: icon ?? finalOptions.icon, iconPosition: finalOptions.iconPosition, href: finalOptions.href, target: finalOptions.target, }, } } export function getBaseElementField< T = any, PO extends PathsOptions = DefaultPathsOptions >( elementType: string, id?: Path, options: Partial> = {} ): BaseElementField { return { $el: elementType, id, class: options.class, vIf: options.vIf, style: options.style, attrs: options.attrs || {}, onClick: options.onClick, } } // Icon button shorthand export function iconBtn< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( id?: P, icon?: IconType, options?: BtnElementOptions, ): BaseElementField { if (icon != null && id != null) { return btn(id, icon, options || {}) } return btn({ id, icon, ...options } as BtnElementOptions) } // Magical text overloads with schema support export function txt< T = any, PO extends PathsOptions = DefaultPathsOptions >(options: TxtElementOptions): BaseElementField export function txt< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P): BaseElementField export function txt< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, text: string): BaseElementField export function txt< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( id: P, text: string, tag: 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' ): BaseElementField export function txt< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, text: string, options: TxtElementOptions): BaseElementField export function txt< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( idOrOptions?: P | TxtElementOptions, text?: string, tagOrOptions?: string | TxtElementOptions, ): BaseElementField { // Handle different overload patterns let id: Path | undefined let finalOptions: TxtElementOptions = {} if (typeof idOrOptions === 'object' && idOrOptions !== null) { // txt(options) finalOptions = idOrOptions const { id: optionId } = finalOptions id = optionId as Path | undefined } else { // txt(id, ...) patterns id = idOrOptions if (text != null) { if (typeof tagOrOptions === 'string') { // txt(id, text, tag) finalOptions = { tag: tagOrOptions as any, text } } else if (tagOrOptions) { // txt(id, text, options) finalOptions = { ...tagOrOptions, text } } else { finalOptions = { text } } } } return { $el: finalOptions.tag || 'p', id, class: finalOptions.class, vIf: finalOptions.vIf, style: finalOptions.style, attrs: { text: finalOptions.text ?? '', size: finalOptions.size, weight: finalOptions.weight, color: finalOptions.color, align: finalOptions.align, }, transform: finalOptions.transform, } } // Magical image overloads with schema support export function img< T = any, PO extends PathsOptions = DefaultPathsOptions >(options: ImgElementOptions): BaseElementField export function img< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P): BaseElementField export function img< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, src: string): BaseElementField export function img< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, src: string, alt: string): BaseElementField export function img< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >(id: P, src: string, options: ImgElementOptions): BaseElementField export function img< T = any, P extends Path = any, PO extends PathsOptions = DefaultPathsOptions >( idOrOptions?: P | ImgElementOptions, src?: string, altOrOptions?: string | ImgElementOptions, ): BaseElementField { // Handle different overload patterns let id: Path | undefined let finalOptions: ImgElementOptions = {} if (typeof idOrOptions === 'object' && idOrOptions !== null) { // img(options) finalOptions = idOrOptions const { id: optionId } = finalOptions id = optionId as Path | undefined } else { // img(id, ...) patterns id = idOrOptions if (src != null) { if (typeof altOrOptions === 'string') { // img(id, src, alt) finalOptions = { alt: altOrOptions } } else if (altOrOptions) { // img(id, src, options) finalOptions = altOrOptions } } } return { $el: 'img', id, class: finalOptions.class, vIf: finalOptions.vIf, style: finalOptions.style, transform: finalOptions.transform, attrs: { alt: finalOptions.alt, width: finalOptions.width, height: finalOptions.height, loading: finalOptions.loading, fit: finalOptions.fit, rounded: finalOptions.rounded, fallback: finalOptions.fallback, }, } } export function pill< T = any, PO extends PathsOptions = DefaultPathsOptions >(options: PillElementOptions): BaseElementField { return { $el: 'pill', id: options?.id, class: options?.class, vIf: options?.vIf, style: options?.style, attrs: options?.attrs || {}, } } export function dropdown< T = any, PO extends PathsOptions = DefaultPathsOptions >( ...args: Array | BaseElementField> ): BaseElementField { let opts: DropdownElementOptions | undefined let children: BaseElementField[] = [] if (args.length > 0) { const firstArg = args[0] as DropdownElementOptions | BaseElementField if ( typeof firstArg === 'object' && firstArg !== null && ('$el' in (firstArg as Record)) ) { children = args as BaseElementField[] } else { opts = firstArg as DropdownElementOptions children = args.slice(1) as BaseElementField[] } } const attrs: Record = {} if (opts?.attrs) { Object.assign(attrs, opts.attrs) } if (opts) { if (opts.options != null) { attrs.options = opts.options } if (opts.placeholder != null) { attrs.placeholder = opts.placeholder } if (opts.searchable != null) { attrs.searchable = opts.searchable } if (opts.multiselect != null) { attrs.multiselect = opts.multiselect } if (opts.clearable != null) { attrs.clearable = opts.clearable } if (opts.onSelect != null) { attrs.onSelect = opts.onSelect } if (opts.onSearch != null) { attrs.onSearch = opts.onSearch } } return { $el: markRaw(Dropdown), class: opts?.class, vIf: opts?.vIf, style: opts?.style, attrs, children, } } // ListItem element function following BagelForm pattern export function listItem< T = any, PO extends PathsOptions = DefaultPathsOptions >( title?: string, options?: ListItemElementOptions, ): BaseElementField { return { $el: markRaw(ListItem), id: options?.id, class: options?.class, vIf: options?.vIf, style: options?.style, onClick: options?.onClick, attrs: { thin: true, title: title ?? options?.title ?? '', subtitle: options?.subtitle, icon: options?.icon, avatar: options?.avatar, badge: options?.badge, actions: options?.actions, clickable: options?.clickable, divider: options?.divider, }, } } // Container element function following BagelForm pattern export function container< T = any, PO extends PathsOptions = DefaultPathsOptions >( id?: Path, children?: BaseElementField[], options?: Partial>, ): BaseElementField { return { $el: 'div', id, class: options?.class ?? 'flex gap-2', vIf: options?.vIf, style: options?.style, attrs: options?.attrs || {}, children: children as any, } } export function findElementById( id: string, elements: BaseElementField[] ): BaseElementField | undefined { for (const element of elements) { if (element.id === id) { return element } if (element.children && element.children.length > 0) { const child = findElementById(id, element.children) if (child) { return child } } } return undefined } export function column = any, PO extends PathsOptions = DefaultPathsOptions>( id?: P, labelOrOptions?: string | Partial>, options?: Partial>, ): BaseElementField { let label: string | undefined if (typeof labelOrOptions === 'string') { label = labelOrOptions } else { options = labelOrOptions } return { $el: 'div', id, class: options?.class ?? 'column-class ellipsis-1 line-height-15', vIf: options?.vIf, style: options?.style, transform: options?.transform, attrs: { ...options?.attrs, label: label ?? options?.attrs?.label ?? '', }, children: options?.children as any, } } export const col = column export function useElements() { return { btn, iconBtn, txt, img, dropdown, listItem, container, findElementById, getBaseElementField, col, column, } }