@xcelsior/ui-spreadsheets 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Spreadsheet.stories.tsx +285 -25
- package/src/components/Spreadsheet.tsx +17 -19
- package/src/components/SpreadsheetCell.tsx +0 -5
- package/src/components/SpreadsheetHeader.tsx +0 -5
package/package.json
CHANGED
|
@@ -1614,10 +1614,123 @@ interface StockData {
|
|
|
1614
1614
|
profit: number;
|
|
1615
1615
|
}
|
|
1616
1616
|
|
|
1617
|
-
const tickers = [
|
|
1618
|
-
|
|
1617
|
+
const tickers = [
|
|
1618
|
+
'AAPL',
|
|
1619
|
+
'MSFT',
|
|
1620
|
+
'GOOGL',
|
|
1621
|
+
'AMZN',
|
|
1622
|
+
'META',
|
|
1623
|
+
'TSLA',
|
|
1624
|
+
'NVDA',
|
|
1625
|
+
'JPM',
|
|
1626
|
+
'V',
|
|
1627
|
+
'JNJ',
|
|
1628
|
+
'WMT',
|
|
1629
|
+
'PG',
|
|
1630
|
+
'MA',
|
|
1631
|
+
'UNH',
|
|
1632
|
+
'HD',
|
|
1633
|
+
'DIS',
|
|
1634
|
+
'BAC',
|
|
1635
|
+
'XOM',
|
|
1636
|
+
'PFE',
|
|
1637
|
+
'KO',
|
|
1638
|
+
'PEP',
|
|
1639
|
+
'CSCO',
|
|
1640
|
+
'ADBE',
|
|
1641
|
+
'NFLX',
|
|
1642
|
+
'INTC',
|
|
1643
|
+
'CRM',
|
|
1644
|
+
'ABT',
|
|
1645
|
+
'NKE',
|
|
1646
|
+
'MRK',
|
|
1647
|
+
'TMO',
|
|
1648
|
+
'ORCL',
|
|
1649
|
+
'ACN',
|
|
1650
|
+
'MDT',
|
|
1651
|
+
'COST',
|
|
1652
|
+
'LLY',
|
|
1653
|
+
'AVGO',
|
|
1654
|
+
'TXN',
|
|
1655
|
+
'NEE',
|
|
1656
|
+
'UNP',
|
|
1657
|
+
'DHR',
|
|
1658
|
+
'QCOM',
|
|
1659
|
+
'PM',
|
|
1660
|
+
'BMY',
|
|
1661
|
+
'RTX',
|
|
1662
|
+
'HON',
|
|
1663
|
+
'UPS',
|
|
1664
|
+
'LOW',
|
|
1665
|
+
'AMGN',
|
|
1666
|
+
'SBUX',
|
|
1667
|
+
'IBM',
|
|
1668
|
+
];
|
|
1669
|
+
const companies = [
|
|
1670
|
+
'Apple Inc.',
|
|
1671
|
+
'Microsoft Corp.',
|
|
1672
|
+
'Alphabet Inc.',
|
|
1673
|
+
'Amazon.com Inc.',
|
|
1674
|
+
'Meta Platforms',
|
|
1675
|
+
'Tesla Inc.',
|
|
1676
|
+
'NVIDIA Corp.',
|
|
1677
|
+
'JPMorgan Chase',
|
|
1678
|
+
'Visa Inc.',
|
|
1679
|
+
'Johnson & Johnson',
|
|
1680
|
+
'Walmart Inc.',
|
|
1681
|
+
'Procter & Gamble',
|
|
1682
|
+
'Mastercard',
|
|
1683
|
+
'UnitedHealth',
|
|
1684
|
+
'Home Depot',
|
|
1685
|
+
'Walt Disney',
|
|
1686
|
+
'Bank of America',
|
|
1687
|
+
'Exxon Mobil',
|
|
1688
|
+
'Pfizer Inc.',
|
|
1689
|
+
'Coca-Cola',
|
|
1690
|
+
'PepsiCo Inc.',
|
|
1691
|
+
'Cisco Systems',
|
|
1692
|
+
'Adobe Inc.',
|
|
1693
|
+
'Netflix Inc.',
|
|
1694
|
+
'Intel Corp.',
|
|
1695
|
+
'Salesforce',
|
|
1696
|
+
'Abbott Labs',
|
|
1697
|
+
'Nike Inc.',
|
|
1698
|
+
'Merck & Co.',
|
|
1699
|
+
'Thermo Fisher',
|
|
1700
|
+
'Oracle Corp.',
|
|
1701
|
+
'Accenture',
|
|
1702
|
+
'Medtronic',
|
|
1703
|
+
'Costco',
|
|
1704
|
+
'Eli Lilly',
|
|
1705
|
+
'Broadcom',
|
|
1706
|
+
'Texas Instruments and technology and HSBC',
|
|
1707
|
+
'NextEra Energy',
|
|
1708
|
+
'Union Pacific',
|
|
1709
|
+
'Danaher',
|
|
1710
|
+
'Qualcomm',
|
|
1711
|
+
'Philip Morris',
|
|
1712
|
+
'Bristol-Myers',
|
|
1713
|
+
'Raytheon',
|
|
1714
|
+
'Honeywell',
|
|
1715
|
+
'UPS',
|
|
1716
|
+
'Lowes',
|
|
1717
|
+
'Amgen',
|
|
1718
|
+
'Starbucks',
|
|
1719
|
+
'IBM',
|
|
1720
|
+
];
|
|
1619
1721
|
const sectors = ['Technology', 'Healthcare', 'Financials', 'Consumer', 'Energy', 'Industrials'];
|
|
1620
|
-
const industries = [
|
|
1722
|
+
const industries = [
|
|
1723
|
+
'Software',
|
|
1724
|
+
'Hardware',
|
|
1725
|
+
'Biotech',
|
|
1726
|
+
'Banking',
|
|
1727
|
+
'Retail',
|
|
1728
|
+
'Semiconductors',
|
|
1729
|
+
'Pharma',
|
|
1730
|
+
'Media',
|
|
1731
|
+
'Oil & Gas',
|
|
1732
|
+
'Aerospace',
|
|
1733
|
+
];
|
|
1621
1734
|
|
|
1622
1735
|
const sampleStocks: StockData[] = Array.from({ length: 50 }, (_, i) => ({
|
|
1623
1736
|
id: i + 1,
|
|
@@ -1644,31 +1757,171 @@ const sampleStocks: StockData[] = Array.from({ length: 50 }, (_, i) => ({
|
|
|
1644
1757
|
const stockColumns: SpreadsheetColumn<StockData>[] = [
|
|
1645
1758
|
{ id: 'ticker', label: 'Ticker', width: 80, sortable: true, filterable: true },
|
|
1646
1759
|
{ id: 'company', label: 'Company', width: 180, sortable: true, filterable: true },
|
|
1647
|
-
{
|
|
1760
|
+
{
|
|
1761
|
+
id: 'sector',
|
|
1762
|
+
label: 'Sector',
|
|
1763
|
+
width: 120,
|
|
1764
|
+
sortable: true,
|
|
1765
|
+
filterable: true,
|
|
1766
|
+
type: 'select',
|
|
1767
|
+
options: sectors,
|
|
1768
|
+
},
|
|
1648
1769
|
{ id: 'industry', label: 'Industry', width: 140, sortable: true, filterable: true },
|
|
1649
|
-
{
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1770
|
+
{
|
|
1771
|
+
id: 'marketCap',
|
|
1772
|
+
label: 'Market Cap',
|
|
1773
|
+
width: 130,
|
|
1774
|
+
sortable: true,
|
|
1775
|
+
type: 'number',
|
|
1776
|
+
align: 'right',
|
|
1777
|
+
render: (v) => `$${(v / 1e9).toFixed(1)}B`,
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
id: 'price',
|
|
1781
|
+
label: 'Price',
|
|
1782
|
+
width: 90,
|
|
1783
|
+
sortable: true,
|
|
1784
|
+
type: 'number',
|
|
1785
|
+
align: 'right',
|
|
1786
|
+
render: (v) => `$${v.toFixed(2)}`,
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
id: 'change',
|
|
1790
|
+
label: 'Change',
|
|
1791
|
+
width: 90,
|
|
1792
|
+
sortable: true,
|
|
1793
|
+
type: 'number',
|
|
1794
|
+
align: 'right',
|
|
1795
|
+
render: (v) => `${v >= 0 ? '+' : ''}${v.toFixed(2)}`,
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
id: 'changePercent',
|
|
1799
|
+
label: 'Change %',
|
|
1800
|
+
width: 100,
|
|
1801
|
+
sortable: true,
|
|
1802
|
+
type: 'number',
|
|
1803
|
+
align: 'right',
|
|
1804
|
+
render: (v) => `${v >= 0 ? '+' : ''}${v.toFixed(2)}%`,
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
id: 'volume',
|
|
1808
|
+
label: 'Volume',
|
|
1809
|
+
width: 110,
|
|
1810
|
+
sortable: true,
|
|
1811
|
+
type: 'number',
|
|
1812
|
+
align: 'right',
|
|
1813
|
+
render: (v) => `${(v / 1e6).toFixed(1)}M`,
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
id: 'avgVolume',
|
|
1817
|
+
label: 'Avg Volume',
|
|
1818
|
+
width: 110,
|
|
1819
|
+
sortable: true,
|
|
1820
|
+
type: 'number',
|
|
1821
|
+
align: 'right',
|
|
1822
|
+
render: (v) => `${(v / 1e6).toFixed(1)}M`,
|
|
1823
|
+
},
|
|
1655
1824
|
{ id: 'pe', label: 'P/E', width: 80, sortable: true, type: 'number', align: 'right' },
|
|
1656
|
-
{
|
|
1657
|
-
|
|
1825
|
+
{
|
|
1826
|
+
id: 'eps',
|
|
1827
|
+
label: 'EPS',
|
|
1828
|
+
width: 80,
|
|
1829
|
+
sortable: true,
|
|
1830
|
+
type: 'number',
|
|
1831
|
+
align: 'right',
|
|
1832
|
+
render: (v) => `$${v.toFixed(2)}`,
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
id: 'dividend',
|
|
1836
|
+
label: 'Dividend %',
|
|
1837
|
+
width: 100,
|
|
1838
|
+
sortable: true,
|
|
1839
|
+
type: 'number',
|
|
1840
|
+
align: 'right',
|
|
1841
|
+
render: (v) => `${v.toFixed(2)}%`,
|
|
1842
|
+
},
|
|
1658
1843
|
{ id: 'beta', label: 'Beta', width: 70, sortable: true, type: 'number', align: 'right' },
|
|
1659
|
-
{
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1844
|
+
{
|
|
1845
|
+
id: 'week52High',
|
|
1846
|
+
label: '52W High',
|
|
1847
|
+
width: 100,
|
|
1848
|
+
sortable: true,
|
|
1849
|
+
type: 'number',
|
|
1850
|
+
align: 'right',
|
|
1851
|
+
render: (v) => `$${v.toFixed(2)}`,
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
id: 'week52Low',
|
|
1855
|
+
label: '52W Low',
|
|
1856
|
+
width: 100,
|
|
1857
|
+
sortable: true,
|
|
1858
|
+
type: 'number',
|
|
1859
|
+
align: 'right',
|
|
1860
|
+
render: (v) => `$${v.toFixed(2)}`,
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
id: 'revenue',
|
|
1864
|
+
label: 'Revenue',
|
|
1865
|
+
width: 120,
|
|
1866
|
+
sortable: true,
|
|
1867
|
+
type: 'number',
|
|
1868
|
+
align: 'right',
|
|
1869
|
+
render: (v) => `$${(v / 1e9).toFixed(1)}B`,
|
|
1870
|
+
},
|
|
1871
|
+
{
|
|
1872
|
+
id: 'profit',
|
|
1873
|
+
label: 'Net Income',
|
|
1874
|
+
width: 120,
|
|
1875
|
+
sortable: true,
|
|
1876
|
+
type: 'number',
|
|
1877
|
+
align: 'right',
|
|
1878
|
+
render: (v) => `$${(v / 1e9).toFixed(1)}B`,
|
|
1879
|
+
},
|
|
1663
1880
|
];
|
|
1664
1881
|
|
|
1665
1882
|
const stockColumnGroups: SpreadsheetColumnGroup[] = [
|
|
1666
|
-
{
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1883
|
+
{
|
|
1884
|
+
id: 'identity',
|
|
1885
|
+
label: 'Identity',
|
|
1886
|
+
columns: ['ticker', 'company'],
|
|
1887
|
+
collapsible: true,
|
|
1888
|
+
headerColor: '#dbeafe',
|
|
1889
|
+
},
|
|
1890
|
+
{
|
|
1891
|
+
id: 'classification',
|
|
1892
|
+
label: 'Classification',
|
|
1893
|
+
columns: ['sector', 'industry'],
|
|
1894
|
+
collapsible: true,
|
|
1895
|
+
headerColor: '#fef3c7',
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
id: 'market',
|
|
1899
|
+
label: 'Market Data',
|
|
1900
|
+
columns: ['marketCap', 'price', 'change', 'changePercent', 'volume', 'avgVolume'],
|
|
1901
|
+
collapsible: true,
|
|
1902
|
+
headerColor: '#d1fae5',
|
|
1903
|
+
},
|
|
1904
|
+
{
|
|
1905
|
+
id: 'fundamentals',
|
|
1906
|
+
label: 'Fundamentals',
|
|
1907
|
+
columns: ['pe', 'eps', 'dividend', 'beta'],
|
|
1908
|
+
collapsible: true,
|
|
1909
|
+
headerColor: '#fce7f3',
|
|
1910
|
+
},
|
|
1911
|
+
{
|
|
1912
|
+
id: 'range',
|
|
1913
|
+
label: '52-Week Range',
|
|
1914
|
+
columns: ['week52High', 'week52Low'],
|
|
1915
|
+
collapsible: true,
|
|
1916
|
+
headerColor: '#e0e7ff',
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
id: 'financials',
|
|
1920
|
+
label: 'Financials',
|
|
1921
|
+
columns: ['revenue', 'profit'],
|
|
1922
|
+
collapsible: true,
|
|
1923
|
+
headerColor: '#fef9c3',
|
|
1924
|
+
},
|
|
1672
1925
|
];
|
|
1673
1926
|
|
|
1674
1927
|
export const PinnedColumnsWithGroups: Story = {
|
|
@@ -1686,10 +1939,17 @@ export const PinnedColumnsWithGroups: Story = {
|
|
|
1686
1939
|
columns.
|
|
1687
1940
|
</p>
|
|
1688
1941
|
<ul className="text-sm text-blue-700 space-y-1 ml-4 list-disc">
|
|
1689
|
-
<li
|
|
1690
|
-
|
|
1942
|
+
<li>
|
|
1943
|
+
<strong>Ticker & Company</strong> are pinned to the left
|
|
1944
|
+
</li>
|
|
1945
|
+
<li>
|
|
1946
|
+
<strong>19 columns</strong> across 6 collapsible groups
|
|
1947
|
+
</li>
|
|
1691
1948
|
<li>Click the pin icon on any column header to pin/unpin</li>
|
|
1692
|
-
<li>
|
|
1949
|
+
<li>
|
|
1950
|
+
Collapse groups to verify pinned columns from collapsed groups still
|
|
1951
|
+
work
|
|
1952
|
+
</li>
|
|
1693
1953
|
</ul>
|
|
1694
1954
|
</div>
|
|
1695
1955
|
|
|
@@ -26,7 +26,12 @@ import { useSpreadsheetComments } from '../hooks/useSpreadsheetComments';
|
|
|
26
26
|
import { useSpreadsheetUndoRedo } from '../hooks/useSpreadsheetUndoRedo';
|
|
27
27
|
import { useSpreadsheetKeyboardShortcuts } from '../hooks/useSpreadsheetKeyboardShortcuts';
|
|
28
28
|
import { useSpreadsheetSelection } from '../hooks/useSpreadsheetSelection';
|
|
29
|
-
import type {
|
|
29
|
+
import type {
|
|
30
|
+
CellEdit,
|
|
31
|
+
SpreadsheetColumn,
|
|
32
|
+
SpreadsheetColumnGroup,
|
|
33
|
+
SpreadsheetProps,
|
|
34
|
+
} from '../types';
|
|
30
35
|
|
|
31
36
|
type SingleCellEdit = {
|
|
32
37
|
rowId: string | number;
|
|
@@ -645,7 +650,11 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
645
650
|
}
|
|
646
651
|
|
|
647
652
|
type ColumnItem = { type: 'column'; column: SpreadsheetColumn<T> };
|
|
648
|
-
type PlaceholderItem = {
|
|
653
|
+
type PlaceholderItem = {
|
|
654
|
+
type: 'collapsed-placeholder';
|
|
655
|
+
groupId: string;
|
|
656
|
+
headerColor?: string;
|
|
657
|
+
};
|
|
649
658
|
type RenderItem = ColumnItem | PlaceholderItem;
|
|
650
659
|
|
|
651
660
|
const leftPinnedItems: ColumnItem[] = [];
|
|
@@ -683,9 +692,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
683
692
|
}
|
|
684
693
|
|
|
685
694
|
// Add any columns not in any group
|
|
686
|
-
const allGroupedIds = new Set(
|
|
687
|
-
columnGroups.flatMap((g) => g.columns)
|
|
688
|
-
);
|
|
695
|
+
const allGroupedIds = new Set(columnGroups.flatMap((g) => g.columns));
|
|
689
696
|
for (const col of visibleColumns) {
|
|
690
697
|
if (!allGroupedIds.has(col.id)) {
|
|
691
698
|
const pinSide = pinnedColumns.get(col.id);
|
|
@@ -842,7 +849,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
842
849
|
zoom: zoom / 100,
|
|
843
850
|
}}
|
|
844
851
|
>
|
|
845
|
-
<table className="
|
|
852
|
+
<table className="border-separate border-spacing-0 text-xs select-none">
|
|
846
853
|
<thead>
|
|
847
854
|
{/* Column Group Headers */}
|
|
848
855
|
{columnGroups && groupHeaderItems && (
|
|
@@ -859,9 +866,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
859
866
|
/>
|
|
860
867
|
{groupHeaderItems.map((item) => {
|
|
861
868
|
if (item.type === 'pinned-column') {
|
|
862
|
-
const col = columns.find(
|
|
863
|
-
(c) => c.id === item.columnId
|
|
864
|
-
);
|
|
869
|
+
const col = columns.find((c) => c.id === item.columnId);
|
|
865
870
|
const isPinnedLeft = item.pinSide === 'left';
|
|
866
871
|
return (
|
|
867
872
|
<th
|
|
@@ -872,8 +877,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
872
877
|
)}
|
|
873
878
|
style={{
|
|
874
879
|
backgroundColor:
|
|
875
|
-
item.headerColor ||
|
|
876
|
-
'rgb(243 244 246)',
|
|
880
|
+
item.headerColor || 'rgb(243 244 246)',
|
|
877
881
|
position: 'sticky',
|
|
878
882
|
left: isPinnedLeft
|
|
879
883
|
? `${getColumnLeftOffset(item.columnId)}px`
|
|
@@ -881,12 +885,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
881
885
|
right: !isPinnedLeft
|
|
882
886
|
? `${getColumnRightOffset(item.columnId)}px`
|
|
883
887
|
: undefined,
|
|
884
|
-
minWidth:
|
|
885
|
-
col?.minWidth || col?.width,
|
|
886
|
-
width:
|
|
887
|
-
col?.minWidth || col?.width,
|
|
888
|
-
maxWidth:
|
|
889
|
-
col?.minWidth || col?.width,
|
|
888
|
+
minWidth: col?.minWidth || col?.width,
|
|
890
889
|
}}
|
|
891
890
|
/>
|
|
892
891
|
);
|
|
@@ -947,8 +946,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
947
946
|
className="border border-gray-200 px-2 py-1 text-center text-gray-400"
|
|
948
947
|
style={{
|
|
949
948
|
backgroundColor:
|
|
950
|
-
item.headerColor ||
|
|
951
|
-
'rgb(243 244 246)',
|
|
949
|
+
item.headerColor || 'rgb(243 244 246)',
|
|
952
950
|
minWidth: '30px',
|
|
953
951
|
}}
|
|
954
952
|
>
|
|
@@ -273,11 +273,6 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
|
|
|
273
273
|
style={{
|
|
274
274
|
backgroundColor: isInSelection ? 'rgb(239 246 255)' : getBackgroundColor(),
|
|
275
275
|
minWidth: column.minWidth || column.width,
|
|
276
|
-
// Pinned columns need fixed width so sticky offset calculations are accurate
|
|
277
|
-
...(isPinned && {
|
|
278
|
-
width: column.minWidth || column.width,
|
|
279
|
-
maxWidth: column.minWidth || column.width,
|
|
280
|
-
}),
|
|
281
276
|
...positionStyles,
|
|
282
277
|
...selectionBorderStyles,
|
|
283
278
|
}}
|
|
@@ -71,11 +71,6 @@ export const SpreadsheetHeader: React.FC<
|
|
|
71
71
|
style={{
|
|
72
72
|
backgroundColor: highlightColor || 'rgb(243 244 246)', // gray-100
|
|
73
73
|
minWidth: column.minWidth || column.width,
|
|
74
|
-
// Pinned columns need fixed width so sticky offset calculations are accurate
|
|
75
|
-
...(isPinned && {
|
|
76
|
-
width: column.minWidth || column.width,
|
|
77
|
-
maxWidth: column.minWidth || column.width,
|
|
78
|
-
}),
|
|
79
74
|
top: 0, // For sticky header
|
|
80
75
|
...positionStyles,
|
|
81
76
|
}}
|