diff --git a/.DS_Store b/.DS_Store index e08d61c..628bf99 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/components/.DS_Store b/components/.DS_Store index cde0503..346b9cb 100644 Binary files a/components/.DS_Store and b/components/.DS_Store differ diff --git a/components/expandable-row-table-component-v2/.DS_Store b/components/expandable-row-table-component-v2/.DS_Store new file mode 100644 index 0000000..7e590d2 Binary files /dev/null and b/components/expandable-row-table-component-v2/.DS_Store differ diff --git a/components/expandable-row-table-component-v2/README.md b/components/expandable-row-table-component-v2/README.md new file mode 100644 index 0000000..6503e6c --- /dev/null +++ b/components/expandable-row-table-component-v2/README.md @@ -0,0 +1,137 @@ +# Expandable Row Table for Retool + +A highly customizable expandable row table component built with React, TypeScript, and the Retool Custom Component SDK. + +This component is designed for advanced operational dashboards, analytics tooling, audit monitoring, transaction inspection, and enterprise-style data grid interactions inside Retool. + +--- + +## Features + +### Expandable Rows + +* Expandable detail sections +* Recursive nested JSON rendering +* Automatic key-value formatting +* Multi-key expandable support +* API response inspection +* Object and array visualization +* Long text handling +* URL rendering + +### Advanced Table Features + +* Dynamic column visibility +* Drag-and-drop column rearranging +* Persistent column ordering +* ASC → DESC → Reset sorting +* Search and filtering +* Pagination +* Responsive layout +* CSV export +* Interactive row selection + +### Styling Controls + +* Header colors +* Row colors +* Alternate row colors +* Font customization +* Footer styling +* Pagination styling +* Theme-aware design + +--- + +## Technology Stack + +* React 18 +* TypeScript +* Retool Custom Component SDK +* Lucide React Icons + +--- + +## Inputs + +| Property | Type | Description | +| ---------------------- | ------- | -------------------------------- | +| tableData | Array | Main table dataset | +| pageSize | Number | Rows per page | +| expandableDataKey | String | Expandable object field(s) | +| showToolbar | Boolean | Toggle toolbar | +| showSearchBar | Boolean | Toggle search | +| headerColor | String | Header background | +| headerTextColor | String | Header text color | +| rowBackground | String | Row background | +| alternateRowBackground | String | Alternate row background | +| accentColor | String | Accent/highlight color | +| footerBackgroundColor | String | Footer background | +| paginationTextColor | String | Pagination text color | +| rowHeight | Enum | Small / Medium / Large / Dynamic | +| columnWidthMode | Enum | Auto / Manual | + +--- + +## Outputs + +| Property | Type | Description | +| --------------- | ------ | -------------------------- | +| selectedRowData | Object | Selected row data | +| columnOrder | Array | Current column arrangement | +| visibleColumns | Array | Visible table columns | + +--- + +## Expandable Data Examples + +### Single Key + +```txt +metadata +``` + +### Multiple Keys + +```txt +metadata,api_response,tags +``` + +### JSON Array Format + +```json +["metadata","api_response","tags"] +``` + +--- + +## Sorting Behavior + +* First click → Ascending +* Second click → Descending +* Third click → Reset sorting + +--- + +## Column Rearranging + +Columns support drag-and-drop reordering and persist after refresh using Retool state storage. + +--- + +## Use Cases + +* Audit Logs +* Transaction Monitoring +* API Debugging +* Financial Dashboards +* Shipment Tracking +* Analytics Dashboards +* Internal Admin Tools +* Operational Monitoring + +--- + +## License + +MIT License diff --git a/components/expandable-row-table-component-v2/cover.png b/components/expandable-row-table-component-v2/cover.png new file mode 100644 index 0000000..1af05fe Binary files /dev/null and b/components/expandable-row-table-component-v2/cover.png differ diff --git a/components/expandable-row-table-component-v2/metadata.json b/components/expandable-row-table-component-v2/metadata.json new file mode 100644 index 0000000..b1938a4 --- /dev/null +++ b/components/expandable-row-table-component-v2/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "expandable-row-table", + "title": "Expandable Row Table", + "author": "@widlestudiollp", + "shortDescription": "An advanced expandable row table component for Retool with nested JSON rendering, drag-and-drop column rearranging, pagination, sorting, filtering, CSV export, and enterprise-style data grid interactions.", + "tags": [ + "Table", + "Data Grid", + "Expandable Rows", + "Analytics", + "JSON Viewer", + "Dashboard", + "React", + "TypeScript", + "Retool" + ] +} \ No newline at end of file diff --git a/components/expandable-row-table-component-v2/package.json b/components/expandable-row-table-component-v2/package.json new file mode 100644 index 0000000..73ab964 --- /dev/null +++ b/components/expandable-row-table-component-v2/package.json @@ -0,0 +1,47 @@ +{ + "name": "my-react-app", + "version": "0.1.0", + "private": true, + "dependencies": { + "@tryretool/custom-component-support": "latest", + "fuse.js": "^7.3.0", + "lucide-react": "^0.553.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "dev": "npx retool-ccl dev", + "deploy": "npx retool-ccl deploy" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@types/react": "^18.2.55", + "@typescript-eslint/eslint-plugin": "^7.3.1", + "@typescript-eslint/parser": "^7.3.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "postcss-modules": "^6.0.0", + "prettier": "^3.0.3" + }, + "retoolCustomComponentLibraryConfig": { + "name": "ExpandableRowTable", + "label": "Expandable Row Table", + "description": "Table with exapndable row feature with responsive.", + "entryPoint": "src/index.tsx", + "outputPath": "dist" + } +} \ No newline at end of file diff --git a/components/expandable-row-table-component-v2/src/index.tsx b/components/expandable-row-table-component-v2/src/index.tsx new file mode 100644 index 0000000..281af38 --- /dev/null +++ b/components/expandable-row-table-component-v2/src/index.tsx @@ -0,0 +1,1903 @@ +import React, { FC, useEffect, useMemo, useState } from 'react' +import { Retool } from '@tryretool/custom-component-support' +import { + ChevronDown, + ChevronRight, + Download, + RefreshCcw, +} from 'lucide-react' + +export const ExpandableTableComponent: FC = () => { + const [data] = Retool.useStateArray({ + name: 'tableData', + label: 'Table Data', + description: + 'Main dataset for the table component', + }) + + const [ + selectedRowData, + setSelectedRowData, + ] = Retool.useStateObject({ + name: 'selectedRowData', + label: 'Selected Row Data', + description: + 'Currently selected row output', + }) + + const [expandableDataKey] = + Retool.useStateString({ + name: 'expandableDataKey', + initialValue: '', + label: 'Expandable Data Keys', + description: + 'Single or multiple expandable keys. Supports: metadata OR metadata,api_response OR ["metadata","api_response"]', + }) + + const [pageSize] = + Retool.useStateNumber({ + name: 'pageSize', + initialValue: 10, + label: 'Page Size', + description: + 'Number of rows per page', + }) + + const [headerColor] = + Retool.useStateString({ + name: 'headerColor', + initialValue: '#f3f4f6', + label: 'Header Background', + description: + 'Table header background color', + }) + + const [headerTextColor] = + Retool.useStateString({ + name: 'headerTextColor', + initialValue: '#111827', + label: 'Header Text Color', + description: + 'Table header text color', + }) + + const [accentColor] = + Retool.useStateString({ + name: 'accentColor', + initialValue: '#2563eb', + label: 'Accent Color', + description: + 'Primary highlight color', + }) + + const [ + visibleColumns, + setVisibleColumns, + ] = Retool.useStateArray({ + name: 'visibleColumns', + label: 'Visible Columns', + description: + 'Columns visible in the table', + }) + + const [ + columnOrder, + setColumnOrder, + ] = Retool.useStateArray({ + name: 'columnOrder', + label: 'Column Order', + description: + 'Persisted draggable column order', + }) + + const [fontFamily] = + Retool.useStateString({ + name: 'fontFamily', + initialValue: + 'Inter, system-ui, sans-serif', + label: 'Font Family', + description: + 'Global table font family', + }) + + const [headerFontSize] = + Retool.useStateNumber({ + name: 'headerFontSize', + initialValue: 14, + label: 'Header Font Size', + description: + 'Header text font size', + }) + + const [bodyFontSize] = + Retool.useStateNumber({ + name: 'bodyFontSize', + initialValue: 14, + label: 'Body Font Size', + description: + 'Table body font size', + }) + + const [rowBackground] = + Retool.useStateString({ + name: 'rowBackground', + initialValue: '#ffffff', + label: 'Row Background', + description: + 'Default table row background', + }) + + const [alternateRowBackground] = + Retool.useStateString({ + name: + 'alternateRowBackground', + initialValue: '#fafafa', + label: + 'Alternate Row Background', + description: + 'Alternate row background color', + }) + + const [emptyStateMessage] = + Retool.useStateString({ + name: 'emptyStateMessage', + initialValue: 'No rows found', + label: 'Empty State Message', + description: + 'Shown when no table rows exist', + }) + + const [showSearchBar] = + Retool.useStateBoolean({ + name: 'showSearchBar', + initialValue: true, + label: 'Show Search Bar', + inspector: 'checkbox', + description: + 'Toggle search bar visibility', + }) + + const [showToolbar] = + Retool.useStateBoolean({ + name: 'showToolbar', + initialValue: true, + label: 'Show Toolbar', + inspector: 'checkbox', + description: + 'Toggle footer toolbar actions', + }) + + const [rowHeight] = + Retool.useStateEnumeration({ + name: 'rowHeight', + enumDefinition: [ + 'small', + 'medium', + 'large', + 'dynamic', + ], + enumLabels: { + small: 'Small', + medium: 'Medium', + large: 'Large', + dynamic: 'Dynamic', + }, + initialValue: 'medium', + inspector: 'select', + label: 'Row Height', + description: + 'Height style of table rows', + }) + + const [columnWidthMode] = + Retool.useStateEnumeration({ + name: 'columnWidthMode', + enumDefinition: [ + 'auto', + 'manual', + ], + enumLabels: { + auto: 'Auto', + manual: 'Manual', + }, + initialValue: 'auto', + inspector: 'select', + label: 'Column Width Mode', + description: + 'Auto width or manual resizable columns', + }) + + const [footerBackgroundColor] = + Retool.useStateString({ + name: + 'footerBackgroundColor', + initialValue: '#ffffff', + label: + 'Footer Background Color', + description: + 'Footer background color', + }) + + const [footerBorderColor] = + Retool.useStateString({ + name: + 'footerBorderColor', + initialValue: '#e5e7eb', + label: + 'Footer Border Color', + description: + 'Footer top border color', + }) + + const [paginationTextColor] = + Retool.useStateString({ + name: + 'paginationTextColor', + initialValue: '#111827', + label: + 'Pagination Text Color', + description: + 'Pagination text/icon color', + }) + + const [paginationButtonBackground] = + Retool.useStateString({ + name: + 'paginationButtonBackground', + initialValue: '#ffffff', + label: + 'Pagination Button Background', + description: + 'Pagination button background', + }) + + const [paginationActiveBackground] = + Retool.useStateString({ + name: + 'paginationActiveBackground', + initialValue: '#eff6ff', + label: + 'Pagination Active Background', + description: + 'Current page indicator background', + }) + + const [paginationFontSize] = + Retool.useStateNumber({ + name: + 'paginationFontSize', + initialValue: 14, + label: + 'Pagination Font Size', + description: + 'Pagination font size', + }) + + const safeData = Array.isArray(data) + ? data + : [] + + const safeHeaderColor = + headerColor || '#f3f4f6' + + const safeHeaderTextColor = + headerTextColor || '#111827' + + const safeAccentColor = + accentColor || '#2563eb' + + const safeFontFamily = + fontFamily || + 'Inter, system-ui, sans-serif' + + const safeRowBackground = + rowBackground || '#ffffff' + + const safeAlternateRowBackground = + alternateRowBackground || '#fafafa' + + const safeHeaderFontSize = + Number(headerFontSize) || 14 + + const safeBodyFontSize = + Number(bodyFontSize) || 14 + + const safePageSize = + Number(pageSize) || 10 + + const safeFooterBackgroundColor = + footerBackgroundColor || + '#ffffff' + + const safeFooterBorderColor = + footerBorderColor || + '#e5e7eb' + + const safePaginationTextColor = + paginationTextColor || + '#111827' + + const safePaginationButtonBackground = + paginationButtonBackground || + '#ffffff' + + const safePaginationActiveBackground = + paginationActiveBackground || + '#eff6ff' + + const safePaginationFontSize = + Number( + paginationFontSize + ) || 14 + + const [selectedRow, setSelectedRow] = + useState(null) + + const [expandedRow, setExpandedRow] = + useState(null) + + const [searchTerm, setSearchTerm] = + useState('') + + const [sortConfig, setSortConfig] = + useState<{ + key: string + direction: 'asc' | 'desc' + } | null>(null) + + const [currentPage, setCurrentPage] = + useState(1) + + const [ + draggedColumn, + setDraggedColumn, + ] = useState( + null + ) + + const getRowHeight = () => { + switch (rowHeight) { + case 'small': + return 38 + + case 'large': + return 72 + + case 'dynamic': + return 'auto' + + default: + return 52 + } + } + + useEffect(() => { + if ( + selectedRow !== null && + paginatedData.length + ) { + const matchedRow = + paginatedData.find( + (row, index) => + (row?.id ?? index) === + selectedRow + ) + + if (matchedRow) { + setSelectedRowData({ + ...matchedRow, + __selectedRowId: + selectedRow, + }) + } + } + }, [selectedRow]) + + const rowHeightValue = + getRowHeight() + + const allColumns = useMemo(() => { + if ( + !safeData.length || + typeof safeData[0] !== 'object' + ) { + return [] + } + + return Object.keys( + safeData[0] + ).filter((key) => { + if (!expandableDataKey) + return true + + return ( + key !== expandableDataKey + ) + }) + }, [safeData, expandableDataKey]) + + useEffect(() => { + if (!allColumns.length) { + return + } + + if ( + !Array.isArray( + columnOrder + ) || + !columnOrder.length + ) { + setColumnOrder(allColumns) + + return + } + + const missingColumns = + allColumns.filter( + (col) => + !columnOrder.includes(col) + ) + + if (missingColumns.length) { + setColumnOrder([ + ...columnOrder, + ...missingColumns, + ]) + } + }, [allColumns]) + + const tableHeaders = useMemo(() => { + const orderedColumns = + Array.isArray( + columnOrder + ) && + columnOrder.length + ? columnOrder.filter((col) => + allColumns.includes(col) + ) + : allColumns + + if ( + !Array.isArray( + visibleColumns + ) || + !visibleColumns.length + ) { + return orderedColumns + } + + return orderedColumns.filter((col) => + visibleColumns.includes(col) + ) + }, [ + allColumns, + visibleColumns, + columnOrder, + ]) + + const filteredSortedData = useMemo(() => { + let filtered = [...safeData] + + if (searchTerm) { + filtered = filtered.filter( + (row) => + Object.values( + row || {} + ).some((value) => + String(value) + .toLowerCase() + .includes( + searchTerm.toLowerCase() + ) + ) + ) + } + + if (sortConfig) { + filtered.sort((a, b) => { + const A = + a?.[sortConfig.key] + const B = + b?.[sortConfig.key] + + if (A < B) { + return sortConfig.direction === + 'asc' + ? -1 + : 1 + } + + if (A > B) { + return sortConfig.direction === + 'asc' + ? 1 + : -1 + } + + return 0 + }) + } + + return filtered + }, [ + safeData, + searchTerm, + sortConfig, + ]) + + const paginatedData = useMemo(() => { + const start = + (currentPage - 1) * + safePageSize + + return filteredSortedData.slice( + start, + start + safePageSize + ) + }, [ + filteredSortedData, + currentPage, + safePageSize, + ]) + + const totalPages = Math.max( + 1, + Math.ceil( + filteredSortedData.length / + safePageSize + ) + ) + + useEffect(() => { + if (paginatedData.length > 0) { + const existsOnPage = + paginatedData.some( + (row, index) => + (row?.id ?? index) === + selectedRow + ) + + if (!existsOnPage) { + const firstRow = + paginatedData[0] + + const rowId = + firstRow?.id ?? 0 + + setSelectedRow(rowId) + + setSelectedRowData({ + ...firstRow, + __selectedRowId: + rowId, + }) + } + } + }, [paginatedData]) + + const toggleRow = (id: any) => { + setExpandedRow((prev) => + prev === id ? null : id + ) + } + + const handleSort = ( + key: string + ) => { + if ( + !sortConfig || + sortConfig.key !== key + ) { + setSortConfig({ + key, + direction: 'asc', + }) + + return + } + + if ( + sortConfig.direction === + 'asc' + ) { + setSortConfig({ + key, + direction: 'desc', + }) + + return + } + + if ( + sortConfig.direction === + 'desc' + ) { + setSortConfig(null) + + return + } + } + + const exportCSV = () => { + const rows = [] + + rows.push(tableHeaders.join(',')) + + filteredSortedData.forEach( + (row) => { + rows.push( + tableHeaders + .map((header) => + JSON.stringify( + row?.[header] ?? '' + ) + ) + .join(',') + ) + } + ) + + const blob = new Blob( + [rows.join('\n')], + { + type: 'text/csv', + } + ) + + const url = + URL.createObjectURL(blob) + + const a = + document.createElement('a') + + a.href = url + + a.download = + 'table-data.csv' + + a.click() + + URL.revokeObjectURL(url) + } + + const formatLabel = (key: string) => { + return key + .split('.') + .map((part) => + part + .replace(/_/g, ' ') + .replace( + /([a-z])([A-Z])/g, + '$1 $2' + ) + .replace(/\b\w/g, (c) => + c.toUpperCase() + ) + ) + .join('.') + } + + const isDateValue = ( + value: any + ) => { + if ( + value === null || + value === undefined + ) { + return false + } + + if (value instanceof Date) { + return true + } + + if ( + typeof value !== 'string' + ) { + return false + } + + const parsedDate = + new Date(value) + + return ( + !isNaN( + parsedDate.getTime() + ) && + ( + /^\d{4}-\d{2}-\d{2}/.test( + value + ) || + value.includes('T') || + value.includes(':') + ) + ) + } + + const formatDateTime = ( + value: any + ) => { + try { + const date = + new Date(value) + + if ( + isNaN(date.getTime()) + ) { + return value + } + + return date.toLocaleString( + 'en-US', + { + month: 'long', + day: 'numeric', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true, + } + ) + } catch { + return value + } + } + + const normalizeExpandableData = ( + data: any, + parentKey = '' + ): any[] => { + if ( + data === null || + data === undefined || + data === '' + ) { + return [] + } + + if (typeof data === 'string') { + const trimmed = data.trim() + + if ( + (trimmed.startsWith('{') && + trimmed.endsWith('}')) || + (trimmed.startsWith('[') && + trimmed.endsWith(']')) + ) { + try { + const parsed = JSON.parse(trimmed) + + return normalizeExpandableData( + parsed, + parentKey + ) + } catch { + return [ + { + key: formatLabel( + parentKey || 'Value' + ), + value: isDateValue(data) + ? formatDateTime( + data + ) + : data, + }, + ] + } + } + + return [ + { + key: formatLabel( + parentKey || 'Value' + ), + value: isDateValue(data) + ? formatDateTime( + data + ) + : String(data), + }, + ] + } + + if (Array.isArray(data)) { + return data.flatMap( + (item, index) => + normalizeExpandableData( + item, + parentKey + ? `${parentKey}.${index}` + : `${index}` + ) + ) + } + + if ( + typeof data === 'object' && + data !== null + ) { + return Object.entries(data).flatMap( + ([key, value]) => + normalizeExpandableData( + value, + parentKey + ? `${parentKey}.${key}` + : key + ) + ) + } + + return [ + { + key: formatLabel( + parentKey || 'Value' + ), + value: isDateValue(data) + ? formatDateTime( + data + ) + : String(data), + }, + ] + } + + const getExpandableData = ( + item: any + ) => { + if (!expandableDataKey) { + return item + } + + let keys: string[] = [] + + if ( + typeof expandableDataKey === + 'string' + ) { + const trimmed = + expandableDataKey.trim() + + if ( + trimmed.startsWith('[') + ) { + try { + const parsed = + JSON.parse(trimmed) + + if ( + Array.isArray(parsed) + ) { + keys = parsed.map( + String + ) + } + } catch { + keys = trimmed + .split(',') + .map((k) => + k.trim() + ) + } + } else { + keys = trimmed + .split(',') + .map((k) => + k.trim() + ) + } + } + + if (!keys.length) { + return item + } + + const combined: Record< + string, + any + > = {} + + keys.forEach((key) => { + combined[key] = + item?.[key] + }) + + return combined + } + + const renderExpandableContent = ( + details: any + ) => { + const normalized = + normalizeExpandableData(details) + + if (!normalized.length) { + return ( +
+ {emptyStateMessage || + 'No data found'} +
+ ) + } + + return ( +
+ {normalized.map( + (item, index) => ( +
+
+ {item.key} +
+ +
+ {item.value} +
+
+ ) + )} +
+ ) + } + + return ( +
+
+ {/*
+
+ Visible Columns +
+ +
+ {allColumns.map((column) => { + const isSelected = + !visibleColumns?.length || + visibleColumns.includes( + column + ) + + return ( + + ) + })} +
+
*/} + {showSearchBar && ( +
+ + setSearchTerm( + e.target.value + ) + } + style={{ + width: 280, + height: 38, + padding: '0 14px', + border: + '1px solid #d1d5db', + borderRadius: 8, + outline: 'none', + fontSize: 14, + }} + /> +
+ )} + +
+ + + + + ) + )} + + + + + {!paginatedData.length ? ( + + + + ) : ( + paginatedData.map( + ( + item, + index + ) => { + const rowId = + item?.id ?? + index + + const isExpanded = + expandedRow === + rowId + + const expandableData = + getExpandableData(item) + + return ( + + { + setSelectedRow( + rowId + ) + + setSelectedRowData( + { + ...item, + __selectedRowId: + rowId, + } + ) + }} + style={{ + background: + selectedRow === + rowId + ? `${safeAccentColor}15` + : index % + 2 === + 0 + ? safeRowBackground + : safeAlternateRowBackground, + borderBottom: + '1px solid #e5e7eb', + height: + rowHeightValue, + cursor: + 'pointer', + transition: + '0.15s ease', + }} + > + + + {tableHeaders.map( + ( + key + ) => { + const value = + item[ + key + ] + + const renderCellValue = + () => { + if ( + value === + null || + value === + undefined || + value === + '' + ) { + return '—' + } + + if ( + typeof value === + 'boolean' + ) { + return value + ? 'Yes' + : 'No' + } + + if ( + typeof value === + 'number' + ) { + return value.toLocaleString() + } + + if ( + Array.isArray( + value + ) + ) { + return ( +
+ {value.map( + ( + v, + i + ) => ( + + {typeof v === + 'object' + ? JSON.stringify( + v + ) + : String( + v + )} + + ) + )} +
+ ) + } + if ( + isDateValue(value) + ) { + return formatDateTime( + value + ) + } + if ( + typeof value === + 'object' && + value !== + null + ) { + return ( +
+
+                                          {JSON.stringify(
+                                            value,
+                                            null,
+                                            2
+                                          )}
+                                        
+
+ ) + } + + if ( + typeof value === + 'string' && + /^(https?:\/\/)/i.test( + value + ) + ) { + return ( + + { + value + } + + ) + } + + if ( + typeof value === + 'string' && + value.length > + 120 + ) { + return ( +
+ { + value + } +
+ ) + } + + return String( + value + ) + } + + return ( + + ) + } + )} + + + {isExpanded && ( + + + + )} + + ) + } + ) + )} + +
+ + {tableHeaders.map( + (key) => ( + { + setDraggedColumn(key) + }} + onDragOver={(e) => { + e.preventDefault() + }} + onDrop={() => { + if ( + !draggedColumn || + draggedColumn === key + ) { + return + } + + const updated = [ + ...columnOrder, + ] + + const fromIndex = + updated.indexOf( + draggedColumn + ) + + const toIndex = + updated.indexOf(key) + + updated.splice( + fromIndex, + 1 + ) + + updated.splice( + toIndex, + 0, + draggedColumn + ) + + setColumnOrder(updated) + }} + onClick={() => + handleSort( + key + ) + } + style={{ + padding: + '14px 16px', + textAlign: + 'left', + cursor: + 'pointer', + fontWeight: 600, + fontSize: + safeHeaderFontSize, + resize: + columnWidthMode === + 'manual' + ? 'horizontal' + : 'none', + overflow: + 'auto', + minWidth: 160, + color: + safeHeaderTextColor, + borderBottom: + '1px solid #d1d5db', + whiteSpace: + 'nowrap', + userSelect: + 'none', + opacity: + draggedColumn === key + ? 0.5 + : 1, + }} + > +
+ {key + .replace( + /_/g, + ' ' + ) + .replace( + /\b\w/g, + ( + c + ) => + c.toUpperCase() + )} + + {sortConfig?.key === + key && + (sortConfig.direction === + 'asc' + ? '↑' + : sortConfig.direction === + 'desc' + ? '↓' + : '')} +
+
+ {emptyStateMessage || + 'No rows found'} +
{ + e.stopPropagation() + + toggleRow( + rowId + ) + }} + > + {isExpanded ? ( + + ) : ( + + )} + + {renderCellValue()} +
+ {renderExpandableContent( + expandableData + )} +
+
+ +
+
+ Showing{' '} + {(currentPage - 1) * + safePageSize + + 1} + - + {Math.min( + currentPage * + safePageSize, + filteredSortedData.length + )}{' '} + of{' '} + { + filteredSortedData.length + } +
+ +
+ + +
+
+ {currentPage} +
+ + + of {totalPages} + +
+ + +
+ + {showToolbar && ( +
+ + + +
+ )} +
+
+
+ ) +} \ No newline at end of file diff --git a/components/geo-map-component/.DS_Store b/components/geo-map-component/.DS_Store new file mode 100644 index 0000000..7e590d2 Binary files /dev/null and b/components/geo-map-component/.DS_Store differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1b53979 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "custom-component-gallery", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}