socialcalc-ai 1.0.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 +268 -0
- package/components/CellEditModal/CellEditModal.css +584 -0
- package/components/CellEditModal/CellEditModal.tsx +817 -0
- package/components/DemoVideosModal/DemoVideosModal.css +307 -0
- package/components/DemoVideosModal/DemoVideosModal.tsx +289 -0
- package/components/EditableCellsModal/EditableCellsModal.css +346 -0
- package/components/EditableCellsModal/EditableCellsModal.tsx +555 -0
- package/components/HorizontalScrollBar.css +127 -0
- package/components/HorizontalScrollBar.tsx +140 -0
- package/components/RowActionPopover/RowActionPopover.css +95 -0
- package/components/RowActionPopover/RowActionPopover.tsx +82 -0
- package/core/constants.js +1039 -0
- package/core/core.js +7172 -0
- package/core/format-number.js +7034 -0
- package/core/formula.js +1675 -0
- package/core/index.js +28 -0
- package/core/spreadsheet-control.js +7646 -0
- package/core/table-editor.js +8156 -0
- package/index.d.ts +68 -0
- package/index.js +43 -0
- package/modules/device.js +22 -0
- package/modules/editable-cells.js +230 -0
- package/modules/exporters.js +22 -0
- package/modules/formatting.js +258 -0
- package/modules/grid-lines.js +127 -0
- package/modules/history.js +26 -0
- package/modules/horizontal-scroll.js +335 -0
- package/modules/init.js +94 -0
- package/modules/invoice.js +558 -0
- package/modules/listeners.js +531 -0
- package/modules/logos.js +191 -0
- package/modules/plugin-manager.js +208 -0
- package/modules/prompts.js +170 -0
- package/modules/row-col-headers.js +514 -0
- package/modules/sheets.js +151 -0
- package/modules/touch-scroll.js +474 -0
- package/modules/utils.js +317 -0
- package/modules/weight.js +287 -0
- package/package.json +67 -0
- package/utils/imageCompressor.ts +118 -0
- package/utils/scriptLoader.ts +39 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import React, { useEffect, useState, useCallback } from "react";
|
|
2
|
+
import {
|
|
3
|
+
getHorizontalScrollInfo,
|
|
4
|
+
scrollToColumn,
|
|
5
|
+
scrollHorizontalBy,
|
|
6
|
+
subscribeHorizontalScroll
|
|
7
|
+
} from "../modules/horizontal-scroll.js";
|
|
8
|
+
import "./HorizontalScrollBar.css";
|
|
9
|
+
|
|
10
|
+
export interface HorizontalScrollBarProps {
|
|
11
|
+
/** Optional custom step size for arrow button clicks (default: 1) */
|
|
12
|
+
step?: number;
|
|
13
|
+
/** Optional CSS class name */
|
|
14
|
+
className?: string;
|
|
15
|
+
/** Optional callback when column changes */
|
|
16
|
+
onColumnChange?: (colInfo: { firstCol: number; colName: string; totalCols: number }) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const HorizontalScrollBar: React.FC<HorizontalScrollBarProps> = ({
|
|
20
|
+
step = 1,
|
|
21
|
+
className = "",
|
|
22
|
+
onColumnChange
|
|
23
|
+
}) => {
|
|
24
|
+
const [scrollInfo, setScrollInfo] = useState(() => getHorizontalScrollInfo());
|
|
25
|
+
|
|
26
|
+
// Subscribe to external scroll events (e.g. from gestures or programmatic scrolls)
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
// Initial sync
|
|
29
|
+
setScrollInfo(getHorizontalScrollInfo());
|
|
30
|
+
|
|
31
|
+
const unsubscribe = subscribeHorizontalScroll((info) => {
|
|
32
|
+
setScrollInfo(info);
|
|
33
|
+
if (onColumnChange) {
|
|
34
|
+
onColumnChange({
|
|
35
|
+
firstCol: info.currentFirstCol,
|
|
36
|
+
colName: info.currentColName,
|
|
37
|
+
totalCols: info.totalCols
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Also listen to custom window events for extra compatibility
|
|
43
|
+
const handleCustomScroll = (e: any) => {
|
|
44
|
+
if (e.detail) {
|
|
45
|
+
setScrollInfo(e.detail);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
window.addEventListener("socialcalc:horizontal-scroll", handleCustomScroll);
|
|
49
|
+
|
|
50
|
+
// Periodic check to keep in sync if canvas/DOM scrolls without direct event
|
|
51
|
+
const timer = setInterval(() => {
|
|
52
|
+
const current = getHorizontalScrollInfo();
|
|
53
|
+
setScrollInfo((prev) => {
|
|
54
|
+
if (
|
|
55
|
+
prev.currentFirstCol !== current.currentFirstCol ||
|
|
56
|
+
prev.totalCols !== current.totalCols
|
|
57
|
+
) {
|
|
58
|
+
return current;
|
|
59
|
+
}
|
|
60
|
+
return prev;
|
|
61
|
+
});
|
|
62
|
+
}, 400);
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
unsubscribe();
|
|
66
|
+
window.removeEventListener("socialcalc:horizontal-scroll", handleCustomScroll);
|
|
67
|
+
clearInterval(timer);
|
|
68
|
+
};
|
|
69
|
+
}, [onColumnChange]);
|
|
70
|
+
|
|
71
|
+
const handlePrev = useCallback(() => {
|
|
72
|
+
scrollHorizontalBy(-step);
|
|
73
|
+
}, [step]);
|
|
74
|
+
|
|
75
|
+
const handleNext = useCallback(() => {
|
|
76
|
+
scrollHorizontalBy(step);
|
|
77
|
+
}, [step]);
|
|
78
|
+
|
|
79
|
+
const handleSliderChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
80
|
+
const val = parseInt(e.target.value, 10);
|
|
81
|
+
scrollToColumn(val);
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const isAtStart = scrollInfo.currentFirstCol <= 1;
|
|
85
|
+
const isAtEnd = scrollInfo.currentFirstCol >= scrollInfo.totalCols;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className={`sc-hscroll-container ${className}`}>
|
|
89
|
+
{/* Left Arrow Button */}
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
className="sc-hscroll-btn"
|
|
93
|
+
onClick={handlePrev}
|
|
94
|
+
disabled={isAtStart}
|
|
95
|
+
title={`Scroll Left (${step} Column)`}
|
|
96
|
+
aria-label="Scroll Left"
|
|
97
|
+
>
|
|
98
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
99
|
+
<polyline points="15 18 9 12 15 6" />
|
|
100
|
+
</svg>
|
|
101
|
+
</button>
|
|
102
|
+
|
|
103
|
+
{/* Slider and Pill */}
|
|
104
|
+
<div className="sc-hscroll-track-wrapper">
|
|
105
|
+
<input
|
|
106
|
+
type="range"
|
|
107
|
+
className="sc-hscroll-slider"
|
|
108
|
+
min={1}
|
|
109
|
+
max={scrollInfo.totalCols}
|
|
110
|
+
value={scrollInfo.currentFirstCol}
|
|
111
|
+
onChange={handleSliderChange}
|
|
112
|
+
aria-label="Horizontal Column Slider"
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<div className="sc-hscroll-indicator">
|
|
116
|
+
<span>Col {scrollInfo.currentColName}</span>
|
|
117
|
+
<span style={{ opacity: 0.65, fontSize: "10px" }}>
|
|
118
|
+
({scrollInfo.currentFirstCol}/{scrollInfo.totalCols})
|
|
119
|
+
</span>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{/* Right Arrow Button */}
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
className="sc-hscroll-btn"
|
|
127
|
+
onClick={handleNext}
|
|
128
|
+
disabled={isAtEnd}
|
|
129
|
+
title={`Scroll Right (${step} Column)`}
|
|
130
|
+
aria-label="Scroll Right"
|
|
131
|
+
>
|
|
132
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
133
|
+
<polyline points="9 18 15 12 9 6" />
|
|
134
|
+
</svg>
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export default HorizontalScrollBar;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Row Action Popover Styles — Clean, Professional */
|
|
2
|
+
|
|
3
|
+
.sc-row-popover-backdrop {
|
|
4
|
+
position: fixed;
|
|
5
|
+
top: 0;
|
|
6
|
+
left: 0;
|
|
7
|
+
right: 0;
|
|
8
|
+
bottom: 0;
|
|
9
|
+
z-index: 9999;
|
|
10
|
+
background: transparent;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.sc-row-popover-menu {
|
|
14
|
+
position: fixed;
|
|
15
|
+
background: #ffffff;
|
|
16
|
+
border-radius: 8px;
|
|
17
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.06);
|
|
18
|
+
padding: 4px;
|
|
19
|
+
min-width: 160px;
|
|
20
|
+
z-index: 10000;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
24
|
+
animation: scPopoverFade 0.12s ease-out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes scPopoverFade {
|
|
28
|
+
from { opacity: 0; transform: translateY(-2px); }
|
|
29
|
+
to { opacity: 1; transform: translateY(0); }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sc-row-popover-header {
|
|
33
|
+
padding: 6px 10px 4px 10px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.sc-row-popover-title {
|
|
37
|
+
font-size: 11px;
|
|
38
|
+
font-weight: 600;
|
|
39
|
+
color: #94a3b8;
|
|
40
|
+
text-transform: uppercase;
|
|
41
|
+
letter-spacing: 0.3px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.sc-row-popover-divider {
|
|
45
|
+
height: 1px;
|
|
46
|
+
background: #e2e8f0;
|
|
47
|
+
margin: 2px 6px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.sc-row-popover-item {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 8px;
|
|
54
|
+
padding: 7px 10px;
|
|
55
|
+
border-radius: 5px;
|
|
56
|
+
border: none;
|
|
57
|
+
background: transparent;
|
|
58
|
+
color: #334155;
|
|
59
|
+
font-size: 13px;
|
|
60
|
+
font-weight: 500;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
width: 100%;
|
|
63
|
+
text-align: left;
|
|
64
|
+
transition: background 0.1s ease;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.sc-row-popover-item:hover {
|
|
68
|
+
background: #f1f5f9;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.sc-row-popover-item:active {
|
|
72
|
+
background: #e2e8f0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.sc-row-popover-item.danger {
|
|
76
|
+
color: #dc2626;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.sc-row-popover-item.danger:hover {
|
|
80
|
+
background: #fef2f2;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.sc-row-popover-icon {
|
|
84
|
+
font-size: 14px;
|
|
85
|
+
font-weight: 700;
|
|
86
|
+
width: 18px;
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
color: #94a3b8;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.sc-row-popover-item.danger .sc-row-popover-icon {
|
|
94
|
+
color: #dc2626;
|
|
95
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "./RowActionPopover.css";
|
|
3
|
+
|
|
4
|
+
export interface RowActionPopoverProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
rowNum: number | null;
|
|
7
|
+
position: { x: number; y: number } | null;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
onInsertAbove: (rowNum: number) => void;
|
|
10
|
+
onInsertBelow: (rowNum: number) => void;
|
|
11
|
+
onDeleteRow: (rowNum: number) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const RowActionPopover: React.FC<RowActionPopoverProps> = ({
|
|
15
|
+
isOpen,
|
|
16
|
+
rowNum,
|
|
17
|
+
position,
|
|
18
|
+
onClose,
|
|
19
|
+
onInsertAbove,
|
|
20
|
+
onInsertBelow,
|
|
21
|
+
onDeleteRow,
|
|
22
|
+
}) => {
|
|
23
|
+
if (!isOpen || rowNum === null || !position) return null;
|
|
24
|
+
|
|
25
|
+
// Ensure popover stays within viewport bounds
|
|
26
|
+
const left = Math.min(window.innerWidth - 200, Math.max(10, position.x + 8));
|
|
27
|
+
const top = Math.min(window.innerHeight - 180, Math.max(10, position.y - 10));
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<div className="sc-row-popover-backdrop" onClick={onClose} />
|
|
32
|
+
<div
|
|
33
|
+
className="sc-row-popover-menu"
|
|
34
|
+
style={{ left: `${left}px`, top: `${top}px` }}
|
|
35
|
+
>
|
|
36
|
+
<div className="sc-row-popover-header">
|
|
37
|
+
<span className="sc-row-popover-title">Row {rowNum}</span>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<button
|
|
41
|
+
type="button"
|
|
42
|
+
className="sc-row-popover-item"
|
|
43
|
+
onClick={() => {
|
|
44
|
+
onInsertAbove(rowNum);
|
|
45
|
+
onClose();
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<span className="sc-row-popover-icon">+</span>
|
|
49
|
+
<span>Insert above</span>
|
|
50
|
+
</button>
|
|
51
|
+
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
className="sc-row-popover-item"
|
|
55
|
+
onClick={() => {
|
|
56
|
+
onInsertBelow(rowNum);
|
|
57
|
+
onClose();
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<span className="sc-row-popover-icon">+</span>
|
|
61
|
+
<span>Insert below</span>
|
|
62
|
+
</button>
|
|
63
|
+
|
|
64
|
+
<div className="sc-row-popover-divider" />
|
|
65
|
+
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
className="sc-row-popover-item danger"
|
|
69
|
+
onClick={() => {
|
|
70
|
+
onDeleteRow(rowNum);
|
|
71
|
+
onClose();
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<span className="sc-row-popover-icon">−</span>
|
|
75
|
+
<span>Delete row</span>
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default RowActionPopover;
|