import * as React from 'react'
import type { StoryFn, Meta } from '@storybook/react-webpack5'
import { SpreadsheetGrid } from '.'
import type {
Column,
GridProps,
GridActionsMenuFullProps,
GridConfirmPayload,
} from '../../types'
import { GridCellDefault } from '../../components'
import {
ListItem,
ListItemDivider,
ListItemInfo,
Menu,
} from '@planview/pv-uikit'
import { Copy, Cut, Trash } from '@planview/pv-icons'
import { GridEditorInputNumeric } from '../../editors'
export default {
title: 'pv-grid/Components/SpreadsheetGrid (Experimental)',
id: 'pv-grid/Components/SpreadsheetGrid',
component: SpreadsheetGrid,
parameters: {
badges: ['intl'],
},
args: {
rowHeight: 'medium',
filterMode: 'default',
sortMode: 'internal',
},
argTypes: {
onRangeCopy: {
table: {
category: 'Bulk editing',
},
},
onBulkCellChange: {
table: {
category: 'Bulk editing',
},
},
loading: {
control: { type: 'radio' },
options: [false, true, 3, 6],
table: {
category: 'Status',
},
},
emptyContent: {
table: {
category: 'Status',
},
},
columns: {
control: false,
},
columnGroups: {
control: { type: 'radio' },
options: ['without groups', 'with groups'],
mapping: {
'without groups': [],
'with groups': [
{
id: 'top-group',
label: 'Group',
children: ['title', 'price'],
},
],
},
},
rows: {
control: false,
},
ref: {
control: false,
description:
'This will return an api for interacting with the grid externally.',
table: {
category: 'Advanced',
},
},
defaultSort: {
control: false,
table: {
category: 'Sorting',
},
},
sort: {
control: false,
table: {
category: 'Sorting',
},
},
sortMode: {
table: {
category: 'Sorting',
},
},
multiColumnSort: {
table: {
category: 'Sorting',
},
},
onSortChange: {
table: {
category: 'Sorting',
},
},
rangeSelection: {
table: {
category: 'Selection',
},
},
onRangeSelectionChange: {
table: {
category: 'Selection',
},
},
filter: {
table: {
category: 'Filtering',
},
},
filterMode: {
table: {
category: 'Filtering',
},
},
filteredIds: {
control: { type: 'radio' },
options: ['not filtered', 'filtered', 'no matches'],
mapping: {
'not filtered': undefined,
filtered: ['1002'],
'no matches': [],
},
table: {
category: 'Filtering',
},
},
preferencesAdapter: {
control: false,
table: {
category: 'Advanced',
},
},
expandedRows: {
control: false,
table: {
category: 'Tree',
},
},
onExpandedRowsChange: {
table: {
category: 'Tree',
},
},
actionsMenu: {
control: { type: 'radio' },
options: ['no actions menu', 'actions menu', 'actions menu async'],
mapping: {
'no actions menu': undefined,
'actions menu': () => (
<>
} label="Cut" />
} label="Copy" />
} label="Delete" />
>
),
'actions menu async': {
Menu({ row, menuProps }: GridActionsMenuFullProps) {
const [loading, setLoading] = React.useState(true)
React.useEffect(() => {
const t = setTimeout(() => setLoading(false), 500)
return () => clearTimeout(t)
}, [])
return (
)
},
},
},
table: {
category: 'Context Menu',
},
},
enableColumnVisibilityMenu: {
control: { type: 'boolean' },
table: {
category: 'Context Menu',
},
},
rowDrag: {
control: false,
table: {
category: 'Drag and Drop',
},
},
onCellChange: {
control: false,
table: {
category: 'Editing',
},
},
},
} satisfies Meta
type Row = {
id: string
title: string
price?: number
}
const rows = [
{ id: '1001', title: 'Journey to the Center of the Earth', price: 11.99 },
{
id: '1002',
title: 'Twenty Thousand Leagues Under the Sea',
price: 35.65,
},
{ id: '1003', title: 'Around the World In Eighty Days', price: 29.98 },
]
const currencyFormatter = new Intl.NumberFormat(undefined, {
style: 'currency',
currency: 'USD',
})
export const Default: StoryFn> = ({
columns: _columns,
rows: _rows,
...rest
}) => {
const columns = React.useMemo[]>(
() => [
{
id: 'id',
label: 'ID',
width: 100,
},
{
id: 'title',
label: 'Title',
width: 400,
},
{
id: 'price',
label: 'Price',
width: 200,
header: {
align: 'right',
},
cell: {
label: ({ value }: { value: number }) =>
currencyFormatter.format(value),
Renderer: ({ label, tabIndex }) => (
),
},
},
],
[]
)
return
}
Default.parameters = {
docs: {
source: {
code: `
type Book = {
id: number
title: string
price: number
}
const rows = React.useMemo(() => [
{ id: '1001', title: 'Journey to the Center of the Earth', price: 11.99 },
{
id: '1002',
title: 'Twenty Thousand Leagues Under the Sea',
price: 35.65,
},
{ id: '1003', title: 'Around the World In Eighty Days', price: 29.98 },
], [])
const columns = React.useMemo[]>(
() => [
{
id: 'id',
label: 'ID',
width: 100,
},
{
id: 'title',
label: 'Title',
width: 400,
},
{
id: 'price',
label: 'Price',
width: 200,
header: {
align: 'right',
},
cell: {
label: ({ value }: { value: number }) =>
currencyFormatter.format(value),
Renderer: ({ label, tabIndex }) => (
),
},
},
],
[]
)
return
`,
},
},
}
export const Editable: StoryFn> = ({
columns: _columns,
rows: _rows,
...rest
}) => {
const [data, setData] = React.useState(rows)
const columns = React.useMemo[]>(
() => [
{
id: 'id',
label: 'ID',
width: 100,
},
{
id: 'title',
label: 'Title',
width: 400,
cell: {
editable: true,
},
},
{
id: 'price',
label: 'Price',
width: 200,
header: {
align: 'right',
},
cell: {
editable: true,
label: ({ value }: { value: number }) =>
currencyFormatter.format(value),
Renderer: ({ label, tabIndex }) => (
),
Editor: GridEditorInputNumeric,
},
},
],
[]
)
return (
) => {
setData((prev) =>
prev.map((r) =>
r.id === payload.rowId
? { ...r, [payload.columnId]: payload.nextValue }
: r
)
)
}}
{...rest}
/>
)
}
Editable.parameters = {
docs: {
source: {
code: `
type Book = {
id: number
title: string
price: number
}
const rows = React.useMemo(() => [
{ id: '1001', title: 'Journey to the Center of the Earth', price: 11.99 },
{
id: '1002',
title: 'Twenty Thousand Leagues Under the Sea',
price: 35.65,
},
{ id: '1003', title: 'Around the World In Eighty Days', price: 29.98 },
], [])
const columns = React.useMemo[]>(
() => [
{
id: 'id',
label: 'ID',
width: 100,
},
{
id: 'title',
label: 'Title',
width: 400,
},
{
id: 'price',
label: 'Price',
width: 200,
header: {
align: 'right',
},
cell: {
label: ({ value }: { value: number }) =>
currencyFormatter.format(value),
Renderer: ({ label, tabIndex }) => (
),
},
},
],
[]
)
return
`,
},
},
}