UNPKG

vueless

Version:

Vue Styleless UI Component Library, powered by Tailwind CSS.

744 lines (706 loc) 21.2 kB
import type { Meta, StoryFn } from "@storybook/vue3-vite"; import { getArgTypes, getSlotNames, getSlotsFragment, getDocsDescription, } from "../../utils/storybook.ts"; import { getRandomId } from "../../utils/helper.ts"; import UTable from "../UTable.vue"; import UButton from "../../ui.button/UButton.vue"; import ULink from "../../ui.button-link/ULink.vue"; import UNumber from "../../ui.text-number/UNumber.vue"; import UBadge from "../../ui.text-badge/UBadge.vue"; import URow from "../../ui.container-row/URow.vue"; import UIcon from "../../ui.image-icon/UIcon.vue"; import ULoader from "../../ui.loader/ULoader.vue"; import type { Row, Props } from "../types.ts"; interface UTableArgs extends Props { slotTemplate?: string; enum: "size"; numberOfRows: number; row: Row | typeof getRow; } export default { id: "7010", title: "Data / Table", component: UTable, argTypes: { ...getArgTypes(UTable.__name), row: { table: { disable: true, }, }, }, args: { columns: [ { key: "orderId", label: "Order Id", thClass: "w-2/5" }, { key: "customerName", label: "Customer Name" }, { key: "status", label: "Status" }, { key: "totalPrice", label: "Total Price" }, ], rows: [ { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: "row-3", orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, ], }, parameters: { docs: { ...getDocsDescription(UTable.__name), }, }, } as Meta; function getDateDividerRow(rowAmount: number) { return Array(rowAmount) .fill({}) .map((_, index) => { let rowDate = new Date().toString(); if (index > 1) { const date = new Date(); date.setFullYear(date.getFullYear()); rowDate = date.toDateString(); } return { id: getRandomId(), rowDate, orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }; }); } function getRow(numberOfRows: number) { return Array.from({ length: numberOfRows }, () => ({ id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, })); } const DefaultTemplate: StoryFn<UTableArgs> = (args: UTableArgs) => ({ components: { UTable, UButton, ULink, UNumber, UBadge, URow, UIcon, ULoader }, setup: () => ({ args, slots: getSlotNames(UTable.__name) }), template: ` <UTable v-bind="args"> ${args.slotTemplate || getSlotsFragment("")} </UTable> `, }); export const Default = DefaultTemplate.bind({}); Default.args = {}; export const Loading: StoryFn<UTableArgs> = (args: UTableArgs) => ({ components: { UTable, UButton }, setup: () => ({ args }), template: ` <UButton label="Toggle loading" size="sm" class="mb-4" @click="args.loading = !args.loading" /> <UTable :columns="[ { key: 'orderId', label: 'Order Id', thClass: 'w-2/5' }, { key: 'customerName', label: 'Customer Name' }, { key: 'status', label: 'Status' }, { key: 'totalPrice', label: 'Total Price' }, ]" :rows="[]" :loading="args.loading" /> `, }); Loading.parameters = { docs: { description: { story: "Set table loader state.", }, }, }; export const EmptyCellLabel = DefaultTemplate.bind({}); EmptyCellLabel.args = { emptyCellLabel: "---", rows: [ { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, ], }; EmptyCellLabel.parameters = { docs: { description: { story: "Label to display for empty cell values.", }, }, }; export const Nesting = DefaultTemplate.bind({}); Nesting.args = { rows: [ { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Processing", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, row: [ { id: getRandomId(), orderId: "Suborder-1", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, row: { id: getRandomId(), orderId: "Extra Services", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, }, { id: getRandomId(), orderId: "Suborder-2", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, row: { id: getRandomId(), orderId: "Extra Services", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, }, ], }, ], }; Nesting.parameters = { docs: { description: { story: "If you need to have nested row(s) in the table, you can use the `row` key inside a row object.", }, }, }; export const CellClasses = DefaultTemplate.bind({}); CellClasses.args = { rows: [ { id: getRandomId(), orderId: { value: `ORD-${Math.floor(Math.random() * 10000)}`, class: "bg-error/25" }, customerName: "John Doe", status: "Cancelled", totalPrice: "$18.92", }, { id: getRandomId(), class: "!bg-success/25", orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: "Bob Smith", status: "Delivered", totalPrice: "$173.11", }, { id: getRandomId(), orderId: { value: `ORD-${Math.floor(Math.random() * 10000)}`, contentClass: "text-green-300 line-through", }, customerName: "Helen Williams", status: "Delivered", totalPrice: "$314.26", }, ], }; CellClasses.parameters = { docs: { description: { story: // eslint-disable-next-line vue/max-len "To apply classes to a table content, you may use different approaches: <br/> 1. Pass a string with classes to the `class` key in a row object (classes are applied to the whole row). <br/> 2. Pass a string with classes to the `class` key in a cell object (classes are applied to the table cell, while the value is passed via a `value` key). <br/> 3. Pass a string with classes to the `contentClass` key in a cell object (classes are applied to the cell content, while the value is passed via a `value` key).", }, }, }; export const Empty = DefaultTemplate.bind({}); Empty.args = { rows: [] }; export const Selectable = DefaultTemplate.bind({}); Selectable.args = { selectable: true }; export const StickyHeader = DefaultTemplate.bind({}); StickyHeader.args = { rows: getRow(10), selectable: true, stickyHeader: true, }; StickyHeader.parameters = { docs: { story: { inline: false, iframeHeight: 450, }, source: { code: ` <UTable sticky-header selectable :columns="[ {'key':'orderId','label':'Order Id','thClass':'w-2/5'}, {'key':'customerName','label':'Customer Name'}, {'key':'status','label':'Status'}, {'key':'totalPrice','label':'Total Price'} ]" :rows="[ { 'id': 'xsJCpznyamFLstB', 'orderId': 'ORD-2339', 'customerName': 'James Wilson', 'status': 'Pending', 'totalPrice': '$240.15' }, { 'id': 'RMHWrRRYAfpmPtR', 'orderId': 'ORD-2927', 'customerName': 'James Wilson', 'status': 'Cancelled', 'totalPrice': '$350.40' }, { 'id': 'HqCFkWgubiNvVhd', 'orderId': 'ORD-5975', 'customerName': 'Alice Johnson', 'status': 'Shipped', 'totalPrice': '$180.41' }, { 'id': 'nQyrwynyqIRUTPM', 'orderId': 'ORD-8643', 'customerName': 'Michael Smith', 'status': 'Pending', 'totalPrice': '$318.30' } ]" /> `, }, }, }; export const StickyFooter = DefaultTemplate.bind({}); StickyFooter.args = { rows: getRow(10), selectable: true, stickyFooter: true, slotTemplate: ` <template #footer> <td colspan="4" class="p-4"> <p class="font-semibold text-accented"> 📊 Summary: 50 transactions processed | Total Revenue: <strong>$12,345.67</strong> </p> </td> </template> `, }; StickyFooter.parameters = { docs: { story: { inline: false, iframeHeight: 450, }, source: { code: ` <UTable sticky-footer selectable :columns="[ {'key':'orderId','label':'Order Id','thClass':'w-2/5'}, {'key':'customerName','label':'Customer Name'}, {'key':'status','label':'Status'}, {'key':'totalPrice','label':'Total Price'} ]" :rows="[ { 'id': 'xsJCpznyamFLstB', 'orderId': 'ORD-2339', 'customerName': 'James Wilson', 'status': 'Pending', 'totalPrice': '$240.15' }, { 'id': 'RMHWrRRYAfpmPtR', 'orderId': 'ORD-2927', 'customerName': 'James Wilson', 'status': 'Cancelled', 'totalPrice': '$350.40' }, { 'id': 'HqCFkWgubiNvVhd', 'orderId': 'ORD-5975', 'customerName': 'Alice Johnson', 'status': 'Shipped', 'totalPrice': '$180.41' }, { 'id': 'nQyrwynyqIRUTPM', 'orderId': 'ORD-8643', 'customerName': 'Michael Smith', 'status': 'Pending', 'totalPrice': '$318.30' } ]" /> `, }, }, }; export const Compact = DefaultTemplate.bind({}); Compact.args = { compact: true }; Compact.parameters = { docs: { description: { story: "`compact` prop makes the table compact (fewer spacings).", }, }, }; export const DateDivider = DefaultTemplate.bind({}); DateDivider.args = { dateDivider: true, rows: getDateDividerRow(4) }; DateDivider.parameters = { docs: { description: { story: "Show date divider line between dates.", }, }, }; export const DateDividerCustomLabel = DefaultTemplate.bind({}); DateDividerCustomLabel.args = { rows: getDateDividerRow(4), dateDivider: [ { date: getDateDividerRow(4).at(2)!.rowDate, label: new Date().toLocaleDateString(undefined, { weekday: "long", day: "2-digit", month: "long", year: "numeric", }), config: { label: "!text-orange-400", divider: "!border-orange-300" }, }, ], }; DateDividerCustomLabel.parameters = { docs: { description: { story: "You can customize date divider by passing necessary data in `date`, `label` and `config` object keys.", }, }, }; export const HeaderKeySlot = DefaultTemplate.bind({}); HeaderKeySlot.args = { slotTemplate: ` <template #header-status="{ column }"> <UBadge :label="column?.label" /> </template> `, }; export const HeaderActionsSlot = DefaultTemplate.bind({}); HeaderActionsSlot.args = { selectable: true, selectedRows: [{ id: "row-3" }], slotTemplate: ` <template #header-actions> <URow gap="2xs"> <UButton label="Edit" variant="ghost" color="primary" size="sm" /> <UButton label="Delete" variant="ghost" color="primary" size="sm" /> </URow> </template> `, }; export const BeforeHeaderSlot = DefaultTemplate.bind({}); BeforeHeaderSlot.args = { slotTemplate: ` <template #before-header="{ colsCount, classes }"> <th :colspan="colsCount" :class="classes">📊 Latest orders report.</th> </template> `, }; export const BeforeFirstRowSlot = DefaultTemplate.bind({}); BeforeFirstRowSlot.args = { slotTemplate: ` <template #before-first-row> <UButton label="Load planned" size="xs" class="my-3" /> </template> `, }; export const CellSlots = DefaultTemplate.bind({}); CellSlots.args = { slotTemplate: ` <template #cell-orderId="{ value }"> <ULink :label="value" color="success" /> </template> <template #cell-status="{ value }"> <UBadge :label="value" variant="soft" :color=" value === 'Delivered' ? 'success' : value === 'Cancelled' ? 'error' : value === 'Pending' ? 'notice' : value === 'Shipped' ? 'info' : '' " /> </template> <template #cell-totalPrice="{ value }"> <UNumber :value="value.slice(1)" currency="€" currency-align="left" /> </template> `, }; export const ExpandSlot = DefaultTemplate.bind({}); ExpandSlot.args = { rows: [ { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Pending", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Processing", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, row: [ { id: getRandomId(), orderId: "Suborder-1", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, row: { id: getRandomId(), orderId: "Extra Services", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, }, { id: getRandomId(), orderId: "Suborder-2", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, row: { id: getRandomId(), orderId: "Extra Services", customerName: "", status: "", totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, }, ], }, ], slotTemplate: ` <template #expand="{ expanded }"> <UButton :icon="expanded ? 'remove' : 'add'" variant="ghost" size="xs" square /> </template> `, }; export const NestedRowSlot = DefaultTemplate.bind({}); NestedRowSlot.args = { columns: [ { key: "orderId", label: "Order Id" }, { key: "customerName", label: "Customer Name" }, { key: "status", label: "Status" }, ], rows: [ { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Processing", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: "John Doe", status: "Processing", row: { id: getRandomId(), rows: [ { id: getRandomId(), category: "Gadgets", itemName: "Ergonomic Mouse", quantity: 2, }, { id: getRandomId(), category: "Gadgets", itemName: "Wireless Keyboard", quantity: 1, }, { id: getRandomId(), category: "Electronics", itemName: "USB-C Hub", quantity: 3, }, ], }, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Processing", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, { id: getRandomId(), orderId: `ORD-${Math.floor(Math.random() * 10000)}`, customerName: ["Alice Johnson", "Michael Smith", "Emma Brown", "James Wilson"][ Math.floor(Math.random() * 4) ], status: ["Processing", "Shipped", "Delivered", "Cancelled"][Math.floor(Math.random() * 4)], totalPrice: `$${(Math.random() * 500).toFixed(2)}`, }, ], slotTemplate: ` <template #nested-row="{ row }"> <div class="p-4 bg-lifted"> <UTable :columns="[ { key: 'category', label: 'Category' }, { key: 'itemName', label: 'Product' }, { key: 'quantity', label: 'Quantity' }, ]" :rows="row.rows" compact /> </div> </template> `, }; export const AfterLastRowSlot = DefaultTemplate.bind({}); AfterLastRowSlot.args = { slotTemplate: ` <template #after-last-row="{ colsCount, classes }"> <td :colspan="colsCount" :class="classes"> <URow block justify="end"> Totals: <UNumber color="success" :value="${Math.random() * 500}" currency="$" /> </URow> </td> </template> `, }; export const EmptyStateSlot = DefaultTemplate.bind({}); EmptyStateSlot.args = { rows: [], config: { i18n: { noData: "Fetching data..." }, bodyEmptyStateCell: "py-10", }, slotTemplate: ` <template #empty-state> <ULoader loading size="lg" :config="{ loader: 'mx-auto mb-4' }" /> <p class="text-center">Fetching latest data, please wait...</p> </template> `, }; export const FooterSlot = DefaultTemplate.bind({}); FooterSlot.args = { slotTemplate: ` <template #footer> <td colspan="100%" class="px-2"> 🔍 For more detailed insights, please visit our data analysis page or reach out to support for assistance. </td> </template> `, };