@papernote/ui 1.0.0 → 1.1.0
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/README.md +13 -3
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/Spreadsheet.css +216 -0
- package/dist/components/Spreadsheet.d.ts +129 -0
- package/dist/components/Spreadsheet.d.ts.map +1 -0
- package/dist/components/Tabs.d.ts +5 -1
- package/dist/components/Tabs.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.d.ts +134 -3
- package/dist/index.esm.js +50720 -69
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +50717 -46
- package/dist/index.js.map +1 -1
- package/dist/styles.css +3492 -2518
- package/package.json +13 -3
- package/src/components/DataTable.stories.tsx +87 -0
- package/src/components/DataTable.tsx +71 -23
- package/src/components/Spreadsheet.css +216 -0
- package/src/components/Spreadsheet.stories.tsx +362 -0
- package/src/components/Spreadsheet.tsx +351 -0
- package/src/components/SpreadsheetSimple.stories.tsx +27 -0
- package/src/components/Tabs.stories.tsx +31 -0
- package/src/components/Tabs.tsx +152 -128
- package/src/components/TimePicker.tsx +1 -1
- package/src/components/Toast.tsx +9 -9
- package/src/components/__tests__/Input.test.tsx +22 -26
- package/src/components/index.ts +6 -2
- package/src/styles/index.css +3 -2
- package/src/utils/sqlToNaturalLanguage.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papernote/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A modern React component library with a paper notebook aesthetic - minimal, professional, and expressive",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,8 +31,12 @@
|
|
|
31
31
|
"test:watch": "jest --watch",
|
|
32
32
|
"test:coverage": "jest --coverage",
|
|
33
33
|
"prepublishOnly": "npm run build",
|
|
34
|
+
"prerelease": "bash scripts/pre-publish-check.sh",
|
|
34
35
|
"storybook": "storybook dev -p 6006",
|
|
35
|
-
"build-storybook": "storybook build"
|
|
36
|
+
"build-storybook": "storybook build",
|
|
37
|
+
"docs:dev": "vitepress dev docs-site",
|
|
38
|
+
"docs:build": "vitepress build docs-site",
|
|
39
|
+
"docs:preview": "vitepress preview docs-site"
|
|
36
40
|
},
|
|
37
41
|
"peerDependencies": {
|
|
38
42
|
"lucide-react": "^0.553.0",
|
|
@@ -64,6 +68,7 @@
|
|
|
64
68
|
"jest-environment-jsdom": "^30.2.0",
|
|
65
69
|
"lucide-react": "^0.554.0",
|
|
66
70
|
"postcss": "^8.4.0",
|
|
71
|
+
"postcss-import": "^16.1.1",
|
|
67
72
|
"react": "^19.2.0",
|
|
68
73
|
"react-dom": "^19.2.0",
|
|
69
74
|
"react-router-dom": "^7.9.6",
|
|
@@ -76,7 +81,8 @@
|
|
|
76
81
|
"ts-jest": "^29.4.5",
|
|
77
82
|
"tslib": "^2.6.0",
|
|
78
83
|
"typescript": "^5.0.0",
|
|
79
|
-
"vite": "^7.2.4"
|
|
84
|
+
"vite": "^7.2.4",
|
|
85
|
+
"vitepress": "^1.6.4"
|
|
80
86
|
},
|
|
81
87
|
"keywords": [
|
|
82
88
|
"react",
|
|
@@ -99,5 +105,9 @@
|
|
|
99
105
|
"homepage": "https://github.com/kwhittenberger/papernote-ui#readme",
|
|
100
106
|
"bugs": {
|
|
101
107
|
"url": "https://github.com/kwhittenberger/papernote-ui/issues"
|
|
108
|
+
},
|
|
109
|
+
"dependencies": {
|
|
110
|
+
"react-spreadsheet": "^0.10.1",
|
|
111
|
+
"xlsx": "^0.18.5"
|
|
102
112
|
}
|
|
103
113
|
}
|
|
@@ -334,3 +334,90 @@ export const FullFeatured: Story = {
|
|
|
334
334
|
),
|
|
335
335
|
},
|
|
336
336
|
};
|
|
337
|
+
|
|
338
|
+
export const WithSecondaryRows: Story = {
|
|
339
|
+
args: {
|
|
340
|
+
data: sampleUsers,
|
|
341
|
+
columns: [
|
|
342
|
+
{
|
|
343
|
+
key: 'name',
|
|
344
|
+
header: 'Name',
|
|
345
|
+
sortable: true,
|
|
346
|
+
renderSecondary: (user: User) => (
|
|
347
|
+
<span className="text-xs text-ink-500">Member since {user.joinedAt}</span>
|
|
348
|
+
),
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
key: 'email',
|
|
352
|
+
header: 'Email',
|
|
353
|
+
sortable: true,
|
|
354
|
+
renderSecondary: (user: User) => (
|
|
355
|
+
<span className="text-xs text-ink-500">{user.role}</span>
|
|
356
|
+
),
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
key: 'status',
|
|
360
|
+
header: 'Status',
|
|
361
|
+
render: (user: User) => (
|
|
362
|
+
<Badge variant={user.status === 'active' ? 'success' : user.status === 'inactive' ? 'error' : 'warning'}>
|
|
363
|
+
{user.status}
|
|
364
|
+
</Badge>
|
|
365
|
+
),
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
selectable: true,
|
|
369
|
+
actions: [
|
|
370
|
+
{
|
|
371
|
+
label: 'Edit',
|
|
372
|
+
icon: <Edit className="h-4 w-4" />,
|
|
373
|
+
onClick: (user: User) => alert(`Edit ${user.name}`),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
label: 'View',
|
|
377
|
+
icon: <Eye className="h-4 w-4" />,
|
|
378
|
+
onClick: (user: User) => alert(`View ${user.name}`),
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export const FullWidthWrappingText: Story = {
|
|
385
|
+
args: {
|
|
386
|
+
data: [
|
|
387
|
+
{ id: '1', name: 'ADOBE Adobe Systems SAN JOSE CA - Expense - Monthly subscription for creative cloud services' },
|
|
388
|
+
{ id: '2', name: 'AMAZON PRIME Amazon.com SEATTLE WA - Entertainment - Annual membership fee' },
|
|
389
|
+
{ id: '3', name: 'SPOTIFY Spotify USA NEW YORK NY - Music streaming service monthly payment' },
|
|
390
|
+
{ id: '4', name: 'NETFLIX Netflix.com LOS GATOS CA - Video streaming monthly subscription' },
|
|
391
|
+
{ id: '5', name: 'MICROSOFT Microsoft Corporation REDMOND WA - Office 365 business subscription annual fee' },
|
|
392
|
+
],
|
|
393
|
+
columns: [
|
|
394
|
+
{
|
|
395
|
+
key: 'name',
|
|
396
|
+
header: 'Transaction Name',
|
|
397
|
+
flex: 1,
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
export const FullWidthWithSecondaryRow: Story = {
|
|
404
|
+
args: {
|
|
405
|
+
data: [
|
|
406
|
+
{ id: '1', name: 'ADOBE Adobe Systems SAN JOSE CA - Expense', frequency: 'Monthly', amount: '$16.52' },
|
|
407
|
+
{ id: '2', name: 'AMAZON PRIME Amazon.com SEATTLE WA - Entertainment', frequency: 'Monthly', amount: '$25.00' },
|
|
408
|
+
{ id: '3', name: 'SPOTIFY Spotify USA NEW YORK NY - Music streaming service', frequency: 'Monthly', amount: '$9.99' },
|
|
409
|
+
{ id: '4', name: 'NETFLIX Netflix.com LOS GATOS CA - Video streaming', frequency: 'Monthly', amount: '$15.49' },
|
|
410
|
+
{ id: '5', name: 'MICROSOFT Microsoft Corporation REDMOND WA - Office 365 business subscription', frequency: 'Annual', amount: '$132.14' },
|
|
411
|
+
],
|
|
412
|
+
columns: [
|
|
413
|
+
{
|
|
414
|
+
key: 'name',
|
|
415
|
+
header: 'Name',
|
|
416
|
+
flex: 1,
|
|
417
|
+
renderSecondary: (item: any) => (
|
|
418
|
+
<span className="text-xs text-ink-500">{item.frequency} • {item.amount}</span>
|
|
419
|
+
),
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
},
|
|
423
|
+
};
|
|
@@ -502,6 +502,9 @@ export default function DataTable<T extends BaseDataItem = BaseDataItem>({
|
|
|
502
502
|
const [scrollTop, setScrollTop] = useState(0);
|
|
503
503
|
const tableContainerRef = useRef<HTMLDivElement>(null);
|
|
504
504
|
|
|
505
|
+
// Row hover state (for coordinating primary + secondary row highlighting)
|
|
506
|
+
const [hoveredRowKey, setHoveredRowKey] = useState<string | null>(null);
|
|
507
|
+
|
|
505
508
|
// Filter columns based on hiddenColumns
|
|
506
509
|
const baseVisibleColumns = columns.filter(
|
|
507
510
|
col => !hiddenColumns.includes(String(col.key))
|
|
@@ -980,50 +983,83 @@ export default function DataTable<T extends BaseDataItem = BaseDataItem>({
|
|
|
980
983
|
const isSelected = selectedRowsSet.has(rowKey);
|
|
981
984
|
const isExpanded = expandedRowsSet.has(rowKey);
|
|
982
985
|
const rowBgClass = getRowBackgroundClass(item, index);
|
|
983
|
-
const hoverClass = disableHover ? '' : 'hover:bg-paper-100';
|
|
984
986
|
const borderClass = bordered ? `border-b ${borderColor}` : (!visibleColumns.some(col => !!col.renderSecondary) ? `border-b ${borderColor}` : '');
|
|
987
|
+
const hasSecondaryRow = visibleColumns.some(col => !!col.renderSecondary);
|
|
988
|
+
|
|
989
|
+
// Hover state for row pair (primary + secondary)
|
|
990
|
+
const isHovered = hoveredRowKey === rowKey;
|
|
991
|
+
const hoverClass = disableHover ? '' : (isHovered ? 'bg-paper-100' : '');
|
|
985
992
|
|
|
986
993
|
return (
|
|
987
994
|
<React.Fragment key={rowKey}>
|
|
988
995
|
<tr
|
|
989
|
-
className={
|
|
996
|
+
className={`table-row-stable ${onRowDoubleClick || onRowClick || onEdit || expandedRowConfig?.edit || expandedRowConfig?.details || expandedRowConfig?.addRelated?.length || expandedRowConfig?.manageRelated?.length ? 'cursor-pointer' : ''} ${isSelected ? 'bg-accent-50 border-l-2 border-accent-500' : hoverClass || rowBgClass} ${borderClass}`}
|
|
997
|
+
onMouseEnter={() => !disableHover && setHoveredRowKey(rowKey)}
|
|
998
|
+
onMouseLeave={() => !disableHover && setHoveredRowKey(null)}
|
|
990
999
|
onClick={() => onRowClick?.(item)}
|
|
991
1000
|
onDoubleClick={() => {
|
|
992
|
-
//
|
|
993
|
-
if (
|
|
1001
|
+
// Priority 1: If there's an onEdit handler (legacy), trigger it
|
|
1002
|
+
if (onEdit) {
|
|
1003
|
+
onEdit(item);
|
|
1004
|
+
}
|
|
1005
|
+
// Priority 2: If there's an expandable edit mode, trigger it
|
|
1006
|
+
else if (expandedRowConfig?.edit) {
|
|
994
1007
|
handleExpansionWithMode(rowKey, 'edit');
|
|
995
1008
|
}
|
|
996
|
-
//
|
|
997
|
-
else if (expandedRowConfig?.details
|
|
1009
|
+
// Priority 3: If there's an expandable details mode, trigger it
|
|
1010
|
+
else if (expandedRowConfig?.details) {
|
|
998
1011
|
handleExpansionWithMode(rowKey, 'details');
|
|
999
1012
|
}
|
|
1000
|
-
//
|
|
1013
|
+
// Priority 4: If there's any addRelated mode, trigger the first one
|
|
1014
|
+
else if (expandedRowConfig?.addRelated && expandedRowConfig.addRelated.length > 0) {
|
|
1015
|
+
handleExpansionWithMode(rowKey, `addRelated-${expandedRowConfig.addRelated[0].key}`);
|
|
1016
|
+
}
|
|
1017
|
+
// Priority 5: If there's any manageRelated mode, trigger the first one
|
|
1018
|
+
else if (expandedRowConfig?.manageRelated && expandedRowConfig.manageRelated.length > 0) {
|
|
1019
|
+
handleExpansionWithMode(rowKey, `manageRelated-${expandedRowConfig.manageRelated[0].key}`);
|
|
1020
|
+
}
|
|
1021
|
+
// Priority 6: Legacy onRowDoubleClick handler
|
|
1001
1022
|
else {
|
|
1002
1023
|
onRowDoubleClick?.(item);
|
|
1003
1024
|
}
|
|
1004
1025
|
}}
|
|
1005
1026
|
title={
|
|
1006
|
-
|
|
1007
|
-
expandedRowConfig?.
|
|
1008
|
-
|
|
1009
|
-
|
|
1027
|
+
onEdit ? 'Double-click to edit' :
|
|
1028
|
+
expandedRowConfig?.edit ? 'Double-click to edit inline' :
|
|
1029
|
+
expandedRowConfig?.details ? 'Double-click to view details' :
|
|
1030
|
+
expandedRowConfig?.addRelated && expandedRowConfig.addRelated.length > 0 ? `Double-click to ${expandedRowConfig.addRelated[0].label}` :
|
|
1031
|
+
expandedRowConfig?.manageRelated && expandedRowConfig.manageRelated.length > 0 ? `Double-click to ${expandedRowConfig.manageRelated[0].label}` :
|
|
1032
|
+
onRowDoubleClick ? 'Double-click for details' :
|
|
1033
|
+
onRowClick ? 'Click to select' :
|
|
1010
1034
|
undefined
|
|
1011
1035
|
}
|
|
1012
1036
|
>
|
|
1013
1037
|
{selectable && (
|
|
1014
|
-
<td
|
|
1038
|
+
<td
|
|
1039
|
+
className={`sticky left-0 z-10 ${bordered ? `border ${borderColor}` : ''}`}
|
|
1040
|
+
style={{
|
|
1041
|
+
backgroundColor: 'inherit',
|
|
1042
|
+
verticalAlign: 'middle',
|
|
1043
|
+
padding: '0.375rem 0.75rem',
|
|
1044
|
+
textAlign: 'center'
|
|
1045
|
+
}}
|
|
1046
|
+
rowSpan={hasSecondaryRow ? 2 : 1}
|
|
1047
|
+
>
|
|
1015
1048
|
<input
|
|
1016
1049
|
type="checkbox"
|
|
1017
1050
|
checked={isSelected}
|
|
1018
1051
|
onChange={() => handleRowSelect(rowKey)}
|
|
1019
1052
|
className="w-4 h-4 text-accent-600 border-paper-300 rounded focus:ring-accent-400"
|
|
1020
|
-
style={{ verticalAlign: 'middle' }}
|
|
1021
1053
|
aria-label={`Select row ${rowKey}`}
|
|
1022
1054
|
/>
|
|
1023
1055
|
</td>
|
|
1024
1056
|
)}
|
|
1025
1057
|
{((expandable || expandedRowConfig) && showExpandChevron) && (
|
|
1026
|
-
<td
|
|
1058
|
+
<td
|
|
1059
|
+
className={`sticky left-0 px-2 ${currentDensity.cell} z-10 ${bordered ? `border ${borderColor}` : ''}`}
|
|
1060
|
+
style={{ backgroundColor: 'inherit', verticalAlign: 'middle' }}
|
|
1061
|
+
rowSpan={hasSecondaryRow ? 2 : 1}
|
|
1062
|
+
>
|
|
1027
1063
|
<button
|
|
1028
1064
|
onClick={() => {
|
|
1029
1065
|
// NEW: Enhanced logic for expandedRowConfig
|
|
@@ -1059,14 +1095,14 @@ export default function DataTable<T extends BaseDataItem = BaseDataItem>({
|
|
|
1059
1095
|
verticalAlign: 'middle'
|
|
1060
1096
|
}}
|
|
1061
1097
|
onClick={(e) => e.stopPropagation()}
|
|
1062
|
-
rowSpan={
|
|
1098
|
+
rowSpan={hasSecondaryRow ? 2 : 1}
|
|
1063
1099
|
>
|
|
1064
1100
|
<div style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '28px' }}>
|
|
1065
1101
|
<ActionMenu actions={allActions} item={item} />
|
|
1066
1102
|
</div>
|
|
1067
1103
|
</td>
|
|
1068
1104
|
)}
|
|
1069
|
-
{visibleColumns.map((column) => {
|
|
1105
|
+
{visibleColumns.map((column, colIdx) => {
|
|
1070
1106
|
const columnKey = String(column.key);
|
|
1071
1107
|
const dynamicWidth = columnWidths[columnKey];
|
|
1072
1108
|
const value = typeof column.key === 'string'
|
|
@@ -1075,10 +1111,14 @@ export default function DataTable<T extends BaseDataItem = BaseDataItem>({
|
|
|
1075
1111
|
|
|
1076
1112
|
const primaryContent = column.render ? column.render(item, value) : String(value || '');
|
|
1077
1113
|
|
|
1114
|
+
// Reduce left padding on first column when there are action buttons
|
|
1115
|
+
const isFirstColumn = colIdx === 0;
|
|
1116
|
+
const paddingClass = isFirstColumn && allActions.length > 0 ? 'pl-3' : '';
|
|
1117
|
+
|
|
1078
1118
|
return (
|
|
1079
1119
|
<td
|
|
1080
1120
|
key={`${item.id}-${columnKey}`}
|
|
1081
|
-
className={`${currentDensity.cell} ${column.className || ''} ${bordered ? `border ${borderColor}` : ''}`}
|
|
1121
|
+
className={`${currentDensity.cell} ${paddingClass} ${column.className || ''} ${bordered ? `border ${borderColor}` : ''}`}
|
|
1082
1122
|
style={getColumnStyle(column, dynamicWidth)}
|
|
1083
1123
|
>
|
|
1084
1124
|
<div className={`${currentDensity.text} leading-tight`}>{primaryContent}</div>
|
|
@@ -1088,12 +1128,16 @@ export default function DataTable<T extends BaseDataItem = BaseDataItem>({
|
|
|
1088
1128
|
</tr>
|
|
1089
1129
|
|
|
1090
1130
|
{/* Secondary row - only render if any column has renderSecondary */}
|
|
1091
|
-
{
|
|
1092
|
-
<tr
|
|
1093
|
-
|
|
1094
|
-
{(
|
|
1131
|
+
{hasSecondaryRow && (
|
|
1132
|
+
<tr
|
|
1133
|
+
className={`secondary-row ${isSelected ? 'bg-accent-50 border-l-2 border-accent-500' : hoverClass || rowBgClass} border-b ${borderColor}`}
|
|
1134
|
+
onMouseEnter={() => !disableHover && setHoveredRowKey(rowKey)}
|
|
1135
|
+
onMouseLeave={() => !disableHover && setHoveredRowKey(null)}
|
|
1136
|
+
>
|
|
1137
|
+
{/* Selectable checkbox uses rowspan from primary row, no cell needed here */}
|
|
1138
|
+
{/* Expand chevron uses rowspan from primary row, no cell needed here */}
|
|
1095
1139
|
{/* Actions column uses rowspan from primary row, no cell needed here */}
|
|
1096
|
-
{visibleColumns.map((column) => {
|
|
1140
|
+
{visibleColumns.map((column, colIdx) => {
|
|
1097
1141
|
const columnKey = String(column.key);
|
|
1098
1142
|
const dynamicWidth = columnWidths[columnKey];
|
|
1099
1143
|
const value = typeof column.key === 'string'
|
|
@@ -1101,10 +1145,14 @@ export default function DataTable<T extends BaseDataItem = BaseDataItem>({
|
|
|
1101
1145
|
: item[column.key];
|
|
1102
1146
|
const secondaryContent = column.renderSecondary ? column.renderSecondary(item, value) : null;
|
|
1103
1147
|
|
|
1148
|
+
// Reduce left padding on first column when there are action buttons
|
|
1149
|
+
const isFirstColumn = colIdx === 0;
|
|
1150
|
+
const paddingClass = isFirstColumn && allActions.length > 0 ? 'pl-3' : '';
|
|
1151
|
+
|
|
1104
1152
|
return (
|
|
1105
1153
|
<td
|
|
1106
1154
|
key={`${item.id}-${columnKey}-secondary`}
|
|
1107
|
-
className={`${currentDensity.cell} py-0.5 ${column.className || ''} ${bordered ? `border ${borderColor}` : ''}`}
|
|
1155
|
+
className={`${currentDensity.cell} py-0.5 ${paddingClass} ${column.className || ''} ${bordered ? `border ${borderColor}` : ''}`}
|
|
1108
1156
|
style={getColumnStyle(column, dynamicWidth)}
|
|
1109
1157
|
>
|
|
1110
1158
|
<div className="text-xs text-ink-500 leading-tight">
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spreadsheet Component Styles
|
|
3
|
+
*
|
|
4
|
+
* Custom styling for react-spreadsheet to match notebook-ui's paper aesthetic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* Container */
|
|
8
|
+
.spreadsheet-container {
|
|
9
|
+
width: 100%;
|
|
10
|
+
overflow: auto;
|
|
11
|
+
background-color: #fafaf9; /* paper-50 */
|
|
12
|
+
border-radius: 0.5rem;
|
|
13
|
+
border: 1px solid #e7e5e4; /* stone-200 */
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Spreadsheet base */
|
|
17
|
+
.notebook-spreadsheet {
|
|
18
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
19
|
+
font-size: 0.875rem;
|
|
20
|
+
color: #1c1917; /* ink-900 */
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Table styling */
|
|
24
|
+
.notebook-spreadsheet table {
|
|
25
|
+
border-collapse: separate;
|
|
26
|
+
border-spacing: 0;
|
|
27
|
+
background-color: #ffffff;
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Cell styling */
|
|
32
|
+
.notebook-spreadsheet td {
|
|
33
|
+
border: 1px solid #e7e5e4; /* stone-200 */
|
|
34
|
+
padding: 0;
|
|
35
|
+
background-color: #ffffff;
|
|
36
|
+
transition: background-color 0.15s ease;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Cell hover */
|
|
40
|
+
.notebook-spreadsheet td:hover {
|
|
41
|
+
background-color: #fafaf9; /* paper-50 */
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Selected cell */
|
|
45
|
+
.notebook-spreadsheet td.Spreadsheet__active-cell {
|
|
46
|
+
border: 2px solid #334155; /* primary-700 */
|
|
47
|
+
box-shadow: 0 0 0 1px #334155;
|
|
48
|
+
background-color: #ffffff;
|
|
49
|
+
z-index: 10;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Header cells (row/column labels) */
|
|
53
|
+
.notebook-spreadsheet th {
|
|
54
|
+
background-color: #f5f5f4; /* stone-100 */
|
|
55
|
+
color: #57534e; /* ink-600 */
|
|
56
|
+
border: 1px solid #e7e5e4; /* stone-200 */
|
|
57
|
+
padding: 0.5rem;
|
|
58
|
+
font-weight: 600;
|
|
59
|
+
font-size: 0.75rem;
|
|
60
|
+
text-align: center;
|
|
61
|
+
position: sticky;
|
|
62
|
+
z-index: 5;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Column headers */
|
|
66
|
+
.notebook-spreadsheet thead th {
|
|
67
|
+
top: 0;
|
|
68
|
+
z-index: 10;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Row headers */
|
|
72
|
+
.notebook-spreadsheet tbody th {
|
|
73
|
+
left: 0;
|
|
74
|
+
z-index: 5;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Corner header (top-left cell) */
|
|
78
|
+
.notebook-spreadsheet thead th:first-child {
|
|
79
|
+
left: 0;
|
|
80
|
+
z-index: 15;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Cell input */
|
|
84
|
+
.notebook-spreadsheet .Spreadsheet__data-editor {
|
|
85
|
+
width: 100%;
|
|
86
|
+
height: 100%;
|
|
87
|
+
border: none;
|
|
88
|
+
outline: none;
|
|
89
|
+
padding: 0.5rem;
|
|
90
|
+
font-family: inherit;
|
|
91
|
+
font-size: inherit;
|
|
92
|
+
color: inherit;
|
|
93
|
+
background-color: #ffffff;
|
|
94
|
+
box-sizing: border-box;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.notebook-spreadsheet .Spreadsheet__data-editor:focus {
|
|
98
|
+
background-color: #ffffff;
|
|
99
|
+
border: 2px solid #334155; /* primary-700 */
|
|
100
|
+
padding: calc(0.5rem - 1px);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Cell with formula indicator */
|
|
104
|
+
.notebook-spreadsheet .Spreadsheet__cell--formula {
|
|
105
|
+
font-style: italic;
|
|
106
|
+
background-color: #f0fdf4; /* success-50 */
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Read-only cells */
|
|
110
|
+
.notebook-spreadsheet .Spreadsheet__cell--readonly {
|
|
111
|
+
background-color: #f5f5f4; /* stone-100 */
|
|
112
|
+
color: #78716c; /* ink-500 */
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Selection highlight */
|
|
116
|
+
.notebook-spreadsheet .Spreadsheet__cell--selected {
|
|
117
|
+
background-color: #e0f2fe; /* primary-100 */
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Cell error state */
|
|
121
|
+
.notebook-spreadsheet .Spreadsheet__cell--error {
|
|
122
|
+
background-color: #fef2f2; /* error-50 */
|
|
123
|
+
color: #991b1b; /* error-800 */
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Cell value display */
|
|
127
|
+
.notebook-spreadsheet .Spreadsheet__value {
|
|
128
|
+
padding: 0.5rem;
|
|
129
|
+
min-height: 2rem;
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Number cells - align right */
|
|
135
|
+
.notebook-spreadsheet .Spreadsheet__cell--number .Spreadsheet__value {
|
|
136
|
+
justify-content: flex-end;
|
|
137
|
+
font-variant-numeric: tabular-nums;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Empty cells */
|
|
141
|
+
.notebook-spreadsheet .Spreadsheet__cell--empty .Spreadsheet__value {
|
|
142
|
+
color: #a8a29e; /* ink-400 */
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Copy/paste indicator */
|
|
146
|
+
.notebook-spreadsheet .Spreadsheet__floating-rect {
|
|
147
|
+
border: 2px dashed #334155; /* primary-700 */
|
|
148
|
+
background-color: rgba(51, 65, 85, 0.1);
|
|
149
|
+
pointer-events: none;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Scrollbar styling for webkit browsers */
|
|
153
|
+
.spreadsheet-container::-webkit-scrollbar {
|
|
154
|
+
width: 12px;
|
|
155
|
+
height: 12px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.spreadsheet-container::-webkit-scrollbar-track {
|
|
159
|
+
background-color: #fafaf9; /* paper-50 */
|
|
160
|
+
border-radius: 0.5rem;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.spreadsheet-container::-webkit-scrollbar-thumb {
|
|
164
|
+
background-color: #d6d3d1; /* stone-300 */
|
|
165
|
+
border-radius: 0.5rem;
|
|
166
|
+
border: 2px solid #fafaf9;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.spreadsheet-container::-webkit-scrollbar-thumb:hover {
|
|
170
|
+
background-color: #a8a29e; /* ink-400 */
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Loading state */
|
|
174
|
+
.spreadsheet-container.loading {
|
|
175
|
+
opacity: 0.6;
|
|
176
|
+
pointer-events: none;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Responsive adjustments */
|
|
180
|
+
@media (max-width: 768px) {
|
|
181
|
+
.notebook-spreadsheet {
|
|
182
|
+
font-size: 0.75rem;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.notebook-spreadsheet .Spreadsheet__value {
|
|
186
|
+
padding: 0.375rem;
|
|
187
|
+
min-height: 1.75rem;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.notebook-spreadsheet .Spreadsheet__data-editor {
|
|
191
|
+
padding: 0.375rem;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.notebook-spreadsheet th {
|
|
195
|
+
padding: 0.375rem;
|
|
196
|
+
font-size: 0.6875rem;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Print styles */
|
|
201
|
+
@media print {
|
|
202
|
+
.spreadsheet-container {
|
|
203
|
+
border: none;
|
|
204
|
+
overflow: visible;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.notebook-spreadsheet td,
|
|
208
|
+
.notebook-spreadsheet th {
|
|
209
|
+
border-color: #000;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.notebook-spreadsheet .Spreadsheet__active-cell {
|
|
213
|
+
border: 1px solid #000;
|
|
214
|
+
box-shadow: none;
|
|
215
|
+
}
|
|
216
|
+
}
|