zebflow 0.6.1

Reactive web automation platform — pipelines, templates, and real-time collaboration in a single binary.
Documentation
import { cx } from "zeb";
import { useState } from "zeb";

interface TabsProps {
  defaultValue?: string;
  value?: string;
  onValueChange?: (value: string) => void;
  className?: string;
  children?: any;
  [key: string]: any;
}

interface TabsListProps {
  className?: string;
  children?: any;
  [key: string]: any;
}

interface TabsTriggerProps {
  value: string;
  disabled?: boolean;
  className?: string;
  children?: any;
  // injected by Tabs
  _activeTab?: string;
  _onTabChange?: (v: string) => void;
  [key: string]: any;
}

interface TabsContentProps {
  value: string;
  className?: string;
  children?: any;
  // injected by Tabs
  _activeTab?: string;
  [key: string]: any;
}

export function Tabs({ defaultValue, value, onValueChange, className, children, ...rest }: TabsProps) {
  const [internal, setInternal] = useState(defaultValue ?? "");
  const controlled = value !== undefined;
  const active = controlled ? value : internal;

  const handleChange = (v: string) => {
    if (!controlled) setInternal(v);
    onValueChange?.(v);
  };

  const inject = (child: any): any => {
    if (!child || typeof child !== "object") return child;
    const props = child.props ?? {};
    const newProps: any = { ...props, _activeTab: active, _onTabChange: handleChange };
    if (Array.isArray(props.children)) {
      newProps.children = props.children.map(inject);
    } else if (props.children && typeof props.children === "object") {
      newProps.children = inject(props.children);
    }
    return { ...child, props: newProps };
  };

  const items = Array.isArray(children) ? children : [children];
  return (
    <div className={cx("", className)} {...rest}>
      {items.map(inject)}
    </div>
  );
}

export function TabsList({ className, children, _activeTab, _onTabChange, ...rest }: TabsListProps & { _activeTab?: string; _onTabChange?: (v: string) => void }) {
  const items = Array.isArray(children) ? children : [children];
  const enhanced = items.map((child: any) => {
    if (!child || typeof child !== "object") return child;
    return { ...child, props: { ...child.props, _activeTab, _onTabChange } };
  });
  return (
    <div
      role="tablist"
      className={cx(
        "inline-flex h-9 items-center justify-center rounded-lg bg-gray-100 p-1 text-gray-500",
        className
      )}
      {...rest}
    >
      {enhanced}
    </div>
  );
}

export function TabsTrigger({ value, disabled, className, children, _activeTab, _onTabChange, ...rest }: TabsTriggerProps) {
  const active = _activeTab === value;
  return (
    <button
      type="button"
      role="tab"
      aria-selected={active}
      disabled={disabled}
      onClick={() => !disabled && _onTabChange?.(value)}
      className={cx(
        "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-white transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
        active ? "bg-white text-gray-900 shadow" : "text-gray-500 hover:text-gray-900",
        className
      )}
      {...rest}
    >
      {children}
    </button>
  );
}

export function TabsContent({ value, className, children, _activeTab, ...rest }: TabsContentProps) {
  if (_activeTab !== value) return null;
  return (
    <div
      role="tabpanel"
      className={cx(
        "mt-2 ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2",
        className
      )}
      {...rest}
    >
      {children}
    </div>
  );
}

export default Tabs;