import * as React from 'react' import type { StoryFn, Meta } from '@storybook/react-webpack5' import type { Column, GridRowMeta } from '..' import { Grid, GridCellDefault, useGridRow } from '..' import { ButtonEmpty, ButtonGroup } from '@planview/pv-uikit' import { theme, spacing } from '@planview/pv-utilities' import { useGridRowMeta } from '../context/grid-context' import { Activity, ActivityParent, FilePowerpoint, FileWord, Folder, Milestone, } from '@planview/pv-icons' export default { title: 'pv-grid/Examples/Drag and Drop', tags: ['hidden'], } satisfies Meta export const FlatGrid: StoryFn = () => { type Fruit = { id: string name: string count: number } const [multiple, setMultiple] = React.useState(false) const rows = React.useMemo>( () => ({ 1: { id: '1', name: 'Apples', count: 250, }, 2: { id: '2', name: 'Oranges', count: 75, }, 3: { id: '3', name: 'Grapes', count: 87, }, 4: { id: '4', name: 'Watermelons', count: 5, }, }), [] ) const [ids, setIds] = React.useState(() => Object.keys(rows)) const columns = React.useMemo[]>( () => [ { id: 'name', label: 'Name', width: 400, sortable: false, movable: false, }, { id: 'count', label: 'Count', width: 200, sortable: false, movable: false, header: { align: 'right', }, cell: { Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return (
setMultiple(false)} > Single setMultiple(true)} > Multiple
) } export const TreeGridOrdering: StoryFn = () => { type PlanItem = { id: number name: string type: 'plan' | 'milestone' parentId?: number | null index: number risk?: string } const [planData, setPlanData] = React.useState>([ { id: 1, index: 0, name: 'Build a spaceship', type: 'plan' }, { id: 11, index: 0, name: 'Purchase materials', type: 'plan', parentId: 1, risk: 'Low', }, { id: 12, index: 1, name: 'Produce parts', type: 'plan', parentId: 1, risk: 'Low', }, { id: 13, index: 2, name: 'Assemble parts', type: 'plan', parentId: 1, risk: 'Medium', }, { id: 14, index: 3, name: 'Spaceship finished', type: 'milestone', parentId: 1, }, { id: 2, index: 1, name: 'Send spaceship to Mars', type: 'plan', risk: 'High', }, { id: 21, index: 0, name: 'Schedule launch with mission control', type: 'milestone', parentId: 2, }, { id: 22, index: 1, name: 'Launch spaceship', type: 'milestone', parentId: 2, }, { id: 3, index: 2, name: 'Achieve interstellar fame', type: 'plan', risk: 'Low', }, ]) const rows = React.useMemo(() => { const ids: number[] = [] const data = new Map() const meta = new Map>() const sortedData = planData.concat() sortedData.sort((a, b) => a.index - b.index) sortedData.forEach((p) => { data.set(p.id, p) if (p.parentId != null) { const children = meta.get(p.parentId)?.children ?? [] children.push(p.id) meta.set(p.parentId, { type: 'tree', children }) } else { ids.push(p.id) } }) return { ids, data, meta } }, [planData]) const columns = React.useMemo[]>( () => [ { id: 'name', label: 'Name', width: 400, tree: true, movable: false, lockedLocation: 'left', sortable: false, cell: { Renderer({ rowId, ...props }) { const row = useGridRow(rowId) const meta = useGridRowMeta(rowId) const Icon = meta.type === 'tree' ? ActivityParent : row.type === 'milestone' ? Milestone : Activity return ( } {...props} /> ) }, }, }, { id: 'type', label: 'Type', sortable: false, cell: { value({ row }) { return row.type === 'milestone' ? 'Milestone' : 'Plan' }, }, }, { id: 'risk', label: 'Risk', sortable: false, }, ], [] ) return ( { // This example is purely for the demo // A more robust application of this would be needed // in a production environment (or it would be // handled by an API endpoint) if (resultIds) { // The item was dropped at a specific location // Update all the siblings with the correct index return plans.map((p) => { if (resultIds.includes(p.id)) { return { ...p, index: resultIds.indexOf(p.id), parentId: targetParentId, } } return p }) } else { // The item was dropped on a parent // Choose the last index const children = targetParent ? (targetParent.rowMeta.children ?? []) : rows.ids const maxIndex = children.filter( (c) => !draggedRowIds.includes(c) ).length return plans.map((p) => { if (draggedRowIds.includes(p.id)) { return { ...p, index: maxIndex, parentId: targetParentId, } } return p }) } }) }, }} /> ) } export const TreeGridParent: StoryFn = () => { type FileItem = { id: number name: string type: 'slides' | 'folder' | 'document' parentId: number | null } const [documentData, setDocumentData] = React.useState>([ { id: 1, name: 'Presentations', type: 'folder', parentId: null }, { id: 11, name: 'Introduction to Grid', type: 'slides', parentId: 1 }, { id: 12, name: 'Advanced Grid Usage', type: 'slides', parentId: 1 }, { id: 2, name: 'Documents', type: 'folder', parentId: null }, { id: 21, name: 'Maintaining the Grid', type: 'document', parentId: 2 }, { id: 22, name: 'Glossary', type: 'document', parentId: 2 }, { id: 23, name: 'Meeting notes', type: 'document', parentId: 2 }, { id: 24, name: 'Archive', type: 'folder', parentId: 2 }, { id: 231, name: '2021 Notes', type: 'document', parentId: 24 }, { id: 3, name: 'Getting started', type: 'slides', parentId: null }, { id: 4, name: 'Read Me', type: 'document', parentId: null, }, ]) const rows = React.useMemo(() => { const ids: number[] = [] const data = new Map() const meta = new Map>() documentData.forEach((d) => { data.set(d.id, d) if (d.parentId != null) { const children = meta.get(d.parentId)?.children ?? [] children.push(d.id) meta.set(d.parentId, { type: 'tree', children }) } else { ids.push(d.id) } if (d.type === 'folder') { meta.set(d.id, { type: 'tree', ...meta.get(d.id) }) } }) return { ids, data, meta } }, [documentData]) const columns = React.useMemo[]>( () => [ { id: 'name', label: 'Name', width: 400, tree: true, movable: false, lockedLocation: 'left', cell: { value({ row }) { return row.type !== 'folder' ? `_${row.name}` : row.name }, label({ row }) { return row.name }, Renderer({ rowId, ...props }) { const row = useGridRow(rowId) const icon = row.type === 'folder' ? ( ) : row.type === 'document' ? ( ) : ( ) return }, }, }, { id: 'type', label: 'Type', cell: { value({ row }) { return row.type === 'document' ? 'Document' : row.type === 'slides' ? 'Slides' : 'Folder' }, }, }, ], [] ) return ( documents.map((d) => { if (draggedRowIds.includes(d.id)) { return { ...d, parentId: targetParentId, } } return d }) ) }, }} /> ) }