Files
sigpro-ui/docs/components_old/table.md
2026-04-06 03:19:15 +02:00

22 KiB

Table

Data table component with sorting, pagination, zebra stripes, pin rows, and custom cell rendering.

Tag

Table

Props

Prop Type Default Description
items Array | Signal<Array> [] Data array to display
columns Array<{label: string, key?: string, render?: function, class?: string, footer?: string}> [] Column definitions
keyFn function (item, idx) => idx Unique key function for rows
zebra boolean | Signal<boolean> false Enable zebra striping
pinRows boolean | Signal<boolean> false Pin header rows on scroll
empty string | VNode 'No data' Content to show when no data
class string '' Additional CSS classes (DaisyUI + Tailwind)

Live Examples

Basic Table

Live Demo

const BasicDemo = () => {
  const users = [
    { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
  ];
  
  return Table({
    items: users,
    columns: [
      { label: 'ID', key: 'id' },
      { label: 'Name', key: 'name' },
      { label: 'Email', key: 'email' },
      { label: 'Role', key: 'role' }
    ]
  });
};
Mount(BasicDemo, '#demo-basic');

With Zebra Stripes

Live Demo

const ZebraDemo = () => {
  const products = [
    { id: 1, name: 'Laptop', price: '$999', stock: 15 },
    { id: 2, name: 'Mouse', price: '$29', stock: 42 },
    { id: 3, name: 'Keyboard', price: '$79', stock: 28 },
    { id: 4, name: 'Monitor', price: '$299', stock: 12 }
  ];
  
  return Table({
    items: products,
    columns: [
      { label: 'Product', key: 'name' },
      { label: 'Price', key: 'price' },
      { label: 'Stock', key: 'stock' }
    ],
    zebra: true
  });
};
Mount(ZebraDemo, '#demo-zebra');

With Custom Cell Rendering

Live Demo

const CustomDemo = () => {
  const orders = [
    { id: 101, customer: 'Alice', amount: 250, status: 'completed' },
    { id: 102, customer: 'Bob', amount: 89, status: 'pending' },
    { id: 103, customer: 'Charlie', amount: 450, status: 'shipped' }
  ];
  
  return Table({
    items: orders,
    columns: [
      { label: 'Order ID', key: 'id' },
      { label: 'Customer', key: 'customer' },
      { 
        label: 'Amount', 
        key: 'amount',
        render: (item) => `$${item.amount}`
      },
      { 
        label: 'Status', 
        key: 'status',
        render: (item) => {
          const statusClass = {
            completed: 'badge badge-success',
            pending: 'badge badge-warning',
            shipped: 'badge badge-info'
          };
          return Span({ class: statusClass[item.status] }, item.status);
        }
      }
    ],
    zebra: true
  });
};
Mount(CustomDemo, '#demo-custom');

With Footers

Live Demo

const FooterDemo = () => {
  const sales = [
    { month: 'January', revenue: 12500, expenses: 8900 },
    { month: 'February', revenue: 14200, expenses: 9200 },
    { month: 'March', revenue: 16800, expenses: 10100 }
  ];
  
  const totalRevenue = sales.reduce((sum, item) => sum + item.revenue, 0);
  const totalExpenses = sales.reduce((sum, item) => sum + item.expenses, 0);
  
  return Table({
    items: sales,
    columns: [
      { label: 'Month', key: 'month' },
      { 
        label: 'Revenue', 
        key: 'revenue',
        render: (item) => `$${item.revenue.toLocaleString()}`,
        footer: `Total: $${totalRevenue.toLocaleString()}`
      },
      { 
        label: 'Expenses', 
        key: 'expenses',
        render: (item) => `$${item.expenses.toLocaleString()}`,
        footer: `Total: $${totalExpenses.toLocaleString()}`
      },
      { 
        label: 'Profit', 
        render: (item) => `$${(item.revenue - item.expenses).toLocaleString()}`,
        footer: `$${(totalRevenue - totalExpenses).toLocaleString()}`
      }
    ],
    zebra: true
  });
};
Mount(FooterDemo, '#demo-footer');

Empty State

Live Demo

const EmptyDemo = () => {
  const emptyList = [];
  
  return Table({
    items: emptyList,
    columns: [
      { label: 'ID', key: 'id' },
      { label: 'Name', key: 'name' }
    ],
    empty: Div({ class: 'flex flex-col items-center gap-2' }, [
      Icons.iconInfo,
      Span({}, 'No records found')
    ])
  });
};
Mount(EmptyDemo, '#demo-empty');

Reactive Data

Live Demo

const ReactiveDemo = () => {
  const filter = $('all');
  const tasks = $([
    { id: 1, title: 'Complete documentation', completed: true },
    { id: 2, title: 'Review pull requests', completed: false },
    { id: 3, title: 'Deploy to production', completed: false },
    { id: 4, title: 'Update dependencies', completed: true }
  ]);
  
  const filteredTasks = () => {
    if (filter() === 'completed') {
      return tasks().filter(t => t.completed);
    } else if (filter() === 'pending') {
      return tasks().filter(t => !t.completed);
    }
    return tasks();
  };
  
  const addTask = () => {
    const newId = Math.max(...tasks().map(t => t.id), 0) + 1;
    tasks([...tasks(), { id: newId, title: `Task ${newId}`, completed: false }]);
  };
  
  return Div({ class: 'flex flex-col gap-4' }, [
    Div({ class: 'flex gap-2' }, [
      Button({ 
        class: 'btn btn-sm',
        onclick: () => filter('all')
      }, 'All'),
      Button({ 
        class: 'btn btn-sm',
        onclick: () => filter('completed')
      }, 'Completed'),
      Button({ 
        class: 'btn btn-sm',
        onclick: () => filter('pending')
      }, 'Pending'),
      Button({ 
        class: 'btn btn-sm btn-primary',
        onclick: addTask
      }, 'Add Task')
    ]),
    Table({
      items: filteredTasks,
      columns: [
        { label: 'ID', key: 'id' },
        { label: 'Title', key: 'title' },
        {
          label: 'Status',
          render: (item) => item.completed 
            ? Span({ class: 'badge badge-success' }, '✓ Done')
            : Span({ class: 'badge badge-warning' }, '○ Pending')
        }
      ],
      zebra: true
    })
  ]);
};
Mount(ReactiveDemo, '#demo-reactive');

With Actions

Live Demo

const ActionsDemo = () => {
  const users = $([
    { id: 1, name: 'John Doe', email: 'john@example.com', active: true },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false },
    { id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true }
  ]);
  
  const deleteUser = (id) => {
    users(users().filter(u => u.id !== id));
    Toast('User deleted', 'alert-info', 2000);
  };
  
  const toggleActive = (id) => {
    users(users().map(u => 
      u.id === id ? { ...u, active: !u.active } : u
    ));
  };
  
  return Table({
    items: users,
    columns: [
      { label: 'ID', key: 'id' },
      { label: 'Name', key: 'name' },
      { label: 'Email', key: 'email' },
      {
        label: 'Status',
        render: (item) => item.active
          ? Span({ class: 'badge badge-success' }, 'Active')
          : Span({ class: 'badge badge-ghost' }, 'Inactive')
      },
      {
        label: 'Actions',
        render: (item) => Div({ class: 'flex gap-1' }, [
          Button({
            class: 'btn btn-xs btn-ghost',
            onclick: () => toggleActive(item.id)
          }, item.active ? 'Deactivate' : 'Activate'),
          Button({
            class: 'btn btn-xs btn-error',
            onclick: () => deleteUser(item.id)
          }, 'Delete')
        ])
      }
    ],
    zebra: true
  });
};
Mount(ActionsDemo, '#demo-actions');

All Variants

Live Demo

const VariantsDemo = () => {
  const data = [
    { id: 1, name: 'Item 1', value: 100 },
    { id: 2, name: 'Item 2', value: 200 },
    { id: 3, name: 'Item 3', value: 300 }
  ];
  
  return Div({ class: 'flex flex-col gap-6' }, [
    Div({ class: 'text-sm font-bold' }, 'Default Table'),
    Table({
      items: data,
      columns: [
        { label: 'ID', key: 'id' },
        { label: 'Name', key: 'name' },
        { label: 'Value', key: 'value' }
      ]
    }),
    
    Div({ class: 'text-sm font-bold mt-4' }, 'Zebra Stripes'),
    Table({
      items: data,
      columns: [
        { label: 'ID', key: 'id' },
        { label: 'Name', key: 'name' },
        { label: 'Value', key: 'value' }
      ],
      zebra: true
    }),
    
    Div({ class: 'text-sm font-bold mt-4' }, 'Compact Table'),
    Table({
      items: data,
      columns: [
        { label: 'ID', key: 'id' },
        { label: 'Name', key: 'name' },
        { label: 'Value', key: 'value' }
      ],
      class: 'table-compact'
    })
  ]);
};
Mount(VariantsDemo, '#demo-variants');
<script> (function() { const initTableExamples = () => { // 1. Basic Table const basicTarget = document.querySelector('#demo-basic'); if (basicTarget && !basicTarget.hasChildNodes()) { const BasicDemo = () => { const users = [ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' }, { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' }, { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' } ]; return Table({ items: users, columns: [ { label: 'ID', key: 'id' }, { label: 'Name', key: 'name' }, { label: 'Email', key: 'email' }, { label: 'Role', key: 'role' } ] }); }; Mount(BasicDemo, basicTarget); } // 2. With Zebra Stripes const zebraTarget = document.querySelector('#demo-zebra'); if (zebraTarget && !zebraTarget.hasChildNodes()) { const ZebraDemo = () => { const products = [ { id: 1, name: 'Laptop', price: '$999', stock: 15 }, { id: 2, name: 'Mouse', price: '$29', stock: 42 }, { id: 3, name: 'Keyboard', price: '$79', stock: 28 }, { id: 4, name: 'Monitor', price: '$299', stock: 12 } ]; return Table({ items: products, columns: [ { label: 'Product', key: 'name' }, { label: 'Price', key: 'price' }, { label: 'Stock', key: 'stock' } ], zebra: true }); }; Mount(ZebraDemo, zebraTarget); } // 3. With Custom Cell Rendering const customTarget = document.querySelector('#demo-custom'); if (customTarget && !customTarget.hasChildNodes()) { const CustomDemo = () => { const orders = [ { id: 101, customer: 'Alice', amount: 250, status: 'completed' }, { id: 102, customer: 'Bob', amount: 89, status: 'pending' }, { id: 103, customer: 'Charlie', amount: 450, status: 'shipped' } ]; return Table({ items: orders, columns: [ { label: 'Order ID', key: 'id' }, { label: 'Customer', key: 'customer' }, { label: 'Amount', key: 'amount', render: (item) => `$${item.amount}` }, { label: 'Status', key: 'status', render: (item) => { const statusClass = { completed: 'badge badge-success', pending: 'badge badge-warning', shipped: 'badge badge-info' }; return Span({ class: statusClass[item.status] }, item.status); } } ], zebra: true }); }; Mount(CustomDemo, customTarget); } // 4. With Footers const footerTarget = document.querySelector('#demo-footer'); if (footerTarget && !footerTarget.hasChildNodes()) { const FooterDemo = () => { const sales = [ { month: 'January', revenue: 12500, expenses: 8900 }, { month: 'February', revenue: 14200, expenses: 9200 }, { month: 'March', revenue: 16800, expenses: 10100 } ]; const totalRevenue = sales.reduce((sum, item) => sum + item.revenue, 0); const totalExpenses = sales.reduce((sum, item) => sum + item.expenses, 0); return Table({ items: sales, columns: [ { label: 'Month', key: 'month' }, { label: 'Revenue', key: 'revenue', render: (item) => `$${item.revenue.toLocaleString()}`, footer: `Total: $${totalRevenue.toLocaleString()}` }, { label: 'Expenses', key: 'expenses', render: (item) => `$${item.expenses.toLocaleString()}`, footer: `Total: $${totalExpenses.toLocaleString()}` }, { label: 'Profit', render: (item) => `$${(item.revenue - item.expenses).toLocaleString()}`, footer: `$${(totalRevenue - totalExpenses).toLocaleString()}` } ], zebra: true }); }; Mount(FooterDemo, footerTarget); } // 5. Empty State const emptyTarget = document.querySelector('#demo-empty'); if (emptyTarget && !emptyTarget.hasChildNodes()) { const EmptyDemo = () => { const emptyList = []; return Table({ items: emptyList, columns: [ { label: 'ID', key: 'id' }, { label: 'Name', key: 'name' } ], empty: Div({ class: 'flex flex-col items-center gap-2' }, [ Icons.iconInfo, Span({}, 'No records found') ]) }); }; Mount(EmptyDemo, emptyTarget); } // 6. Reactive Data const reactiveTarget = document.querySelector('#demo-reactive'); if (reactiveTarget && !reactiveTarget.hasChildNodes()) { const ReactiveDemo = () => { const filter = $('all'); const tasks = $([ { id: 1, title: 'Complete documentation', completed: true }, { id: 2, title: 'Review pull requests', completed: false }, { id: 3, title: 'Deploy to production', completed: false }, { id: 4, title: 'Update dependencies', completed: true } ]); const filteredTasks = () => { if (filter() === 'completed') { return tasks().filter(t => t.completed); } else if (filter() === 'pending') { return tasks().filter(t => !t.completed); } return tasks(); }; const addTask = () => { const newId = Math.max(...tasks().map(t => t.id), 0) + 1; tasks([...tasks(), { id: newId, title: `Task ${newId}`, completed: false }]); }; return Div({ class: 'flex flex-col gap-4' }, [ Div({ class: 'flex gap-2' }, [ Button({ class: 'btn btn-sm', onclick: () => filter('all') }, 'All'), Button({ class: 'btn btn-sm', onclick: () => filter('completed') }, 'Completed'), Button({ class: 'btn btn-sm', onclick: () => filter('pending') }, 'Pending'), Button({ class: 'btn btn-sm btn-primary', onclick: addTask }, 'Add Task') ]), Table({ items: filteredTasks, columns: [ { label: 'ID', key: 'id' }, { label: 'Title', key: 'title' }, { label: 'Status', render: (item) => item.completed ? Span({ class: 'badge badge-success' }, '✓ Done') : Span({ class: 'badge badge-warning' }, '○ Pending') } ], zebra: true }) ]); }; Mount(ReactiveDemo, reactiveTarget); } // 7. With Actions const actionsTarget = document.querySelector('#demo-actions'); if (actionsTarget && !actionsTarget.hasChildNodes()) { const ActionsDemo = () => { const users = $([ { id: 1, name: 'John Doe', email: 'john@example.com', active: true }, { id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false }, { id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true } ]); const deleteUser = (id) => { users(users().filter(u => u.id !== id)); Toast('User deleted', 'alert-info', 2000); }; const toggleActive = (id) => { users(users().map(u => u.id === id ? { ...u, active: !u.active } : u )); }; return Table({ items: users, columns: [ { label: 'ID', key: 'id' }, { label: 'Name', key: 'name' }, { label: 'Email', key: 'email' }, { label: 'Status', render: (item) => item.active ? Span({ class: 'badge badge-success' }, 'Active') : Span({ class: 'badge badge-ghost' }, 'Inactive') }, { label: 'Actions', render: (item) => Div({ class: 'flex gap-1' }, [ Button({ class: 'btn btn-xs btn-ghost', onclick: () => toggleActive(item.id) }, item.active ? 'Deactivate' : 'Activate'), Button({ class: 'btn btn-xs btn-error', onclick: () => deleteUser(item.id) }, 'Delete') ]) } ], zebra: true }); }; Mount(ActionsDemo, actionsTarget); } // 8. All Variants const variantsTarget = document.querySelector('#demo-variants'); if (variantsTarget && !variantsTarget.hasChildNodes()) { const VariantsDemo = () => { const data = [ { id: 1, name: 'Item 1', value: 100 }, { id: 2, name: 'Item 2', value: 200 }, { id: 3, name: 'Item 3', value: 300 } ]; return Div({ class: 'flex flex-col gap-6' }, [ Div({ class: 'text-sm font-bold' }, 'Default Table'), Table({ items: data, columns: [ { label: 'ID', key: 'id' }, { label: 'Name', key: 'name' }, { label: 'Value', key: 'value' } ] }), Div({ class: 'text-sm font-bold mt-4' }, 'Zebra Stripes'), Table({ items: data, columns: [ { label: 'ID', key: 'id' }, { label: 'Name', key: 'name' }, { label: 'Value', key: 'value' } ], zebra: true }), Div({ class: 'text-sm font-bold mt-4' }, 'Compact Table'), Table({ items: data, columns: [ { label: 'ID', key: 'id' }, { label: 'Name', key: 'name' }, { label: 'Value', key: 'value' } ], class: 'table-compact' }) ]); }; Mount(VariantsDemo, variantsTarget); } }; initTableExamples(); if (window.$docsify) { window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => { hook.doneEach(initTableExamples); }); } })(); </script>