Skip to content

Commit 69c1a24

Browse files
authored
feat: add formula ribbon toolbar items (#65)
Co-authored-by: Sia <me@siamand.cc>
1 parent 3de70cf commit 69c1a24

9 files changed

Lines changed: 101 additions & 18 deletions

File tree

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
crossorigin="anonymous">
1111
<link rel="icon" href="/OpenWebSheet/favicon.ico">
1212
<title>Open web sheet</title>
13-
<script type="module" crossorigin src="/OpenWebSheet/assets/index-Vds5Mw57.js"></script>
14-
<link rel="stylesheet" crossorigin href="/OpenWebSheet/assets/index-Bu-CVJUF.css">
13+
<script type="module" crossorigin src="/OpenWebSheet/assets/index-BChN-qVl.js"></script>
14+
<link rel="stylesheet" crossorigin href="/OpenWebSheet/assets/index-Cs_txOUy.css">
1515
</head>
1616
<body>
1717
<noscript>

src/app/App.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,18 @@ export default function App() {
4141
setFileMode(mode);
4242
};
4343

44-
const onAction = (action: AppAction) => action.actionName === 'save-ows'
45-
? saveDocument(uiRef.current)
46-
: action.actionName === 'load-ows'
47-
? loadDocument(uiRef.current)
48-
: uiRef.current && uiRef.current.execCmd(action.actionName, action.args);
44+
const onAction = (action: AppAction) => {
45+
if (action.actionName === 'save-ows') {
46+
return saveDocument(uiRef.current);
47+
}
48+
if (action.actionName === 'load-ows') {
49+
return loadDocument(uiRef.current);
50+
}
51+
if (action.actionName === 'formula-template') {
52+
return setSheetState({...sheetState, value: action.args});
53+
}
54+
return uiRef.current && uiRef.current.execCmd(action.actionName, action.args);
55+
};
4956

5057
return React.createElement('div', {className: 'ows-app'},
5158
React.createElement(RibbonMenu, {appearance, fileMode, onAction, onModeChanged, state: sheetState}),

src/app/styles.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@
114114
@apply text-green-700;
115115
}
116116

117+
.ows-formula-tools {
118+
@apply flex flex-wrap items-center justify-center gap-1.5;
119+
}
120+
117121
.ows-formula-input {
118122
@apply flex-1 h-7 mx-1 border-l border-gray-300 px-2 outline-none;
119123
}
Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
11
import React from 'react';
2+
import { AppAction } from '@/app/types';
23
import { ControlGroup } from '@/shared/ui/ControlGroup';
4+
import { FormulaFunctionGroup } from './formulas/FormulaFunctionGroup';
35

4-
export function FormulaRibbon() {
6+
interface FormulaRibbonProps {
7+
onAction: (action: AppAction) => void;
8+
}
9+
10+
const statistics = [
11+
{icon: 'fa fa-plus', label: 'SUM', template: '=SUM('},
12+
{icon: 'fa fa-arrow-down', label: 'MIN', template: '=MIN('},
13+
{icon: 'fa fa-arrow-up', label: 'MAX', template: '=MAX('},
14+
];
15+
16+
const mathematical = [
17+
{icon: 'fa fa-square-root-alt', label: 'SQRT', template: '=SQRT('},
18+
{icon: 'fa fa-plus-circle', label: 'ABS', template: '=ABS('},
19+
{icon: 'fa fa-chart-line', label: 'LOG', template: '=LOG('},
20+
{icon: 'fa fa-superscript', label: 'EXP', template: '=EXP('},
21+
];
22+
23+
const trigonometry = [
24+
{icon: 'fa fa-wave-square', label: 'SIN', template: '=SIN('},
25+
{icon: 'fa fa-wave-square', label: 'COS', template: '=COS('},
26+
{icon: 'fa fa-wave-square', label: 'TAN', template: '=TAN('},
27+
];
28+
29+
const constants = [
30+
{icon: 'fa fa-circle-notch', label: 'PI', template: '=PI'},
31+
{icon: 'fa fa-italic', label: 'E', template: '=E'},
32+
];
33+
34+
export function FormulaRibbon(props: FormulaRibbonProps) {
535
return React.createElement(
636
'div',
737
{className: 'ows-ribbon-panel'},
8-
React.createElement(ControlGroup, {label: 'Mathematical', wide: true},
9-
React.createElement('span', {className: 'ows-muted'}, 'Function tools'),
10-
),
11-
React.createElement(ControlGroup, {label: 'Statistics', wide: true},
12-
React.createElement('span', {className: 'ows-muted'}, 'Analysis helpers'),
13-
),
38+
React.createElement(FormulaFunctionGroup, {functions: statistics, label: 'Statistics', onAction: props.onAction}),
39+
React.createElement(FormulaFunctionGroup, {functions: mathematical, label: 'Mathematical', onAction: props.onAction}),
40+
React.createElement(FormulaFunctionGroup, {functions: trigonometry, label: 'Trigonometry', onAction: props.onAction}),
41+
React.createElement(FormulaFunctionGroup, {functions: constants, label: 'Constants', onAction: props.onAction}),
1442
React.createElement(ControlGroup, {label: 'User Defined Functions', wide: true},
15-
React.createElement('span', {className: 'ows-muted'}, 'Custom formulas'),
43+
React.createElement('span', {className: 'ows-muted'}, 'Custom functions are planned'),
1644
),
1745
);
1846
}

src/features/ribbon/RibbonMenu.test.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ describe('RibbonMenu', () => {
2626
selectTab('Formulas');
2727
expect(await screen.findByText('Mathematical')).toBeInTheDocument();
2828
expect(screen.getByText('User Defined Functions')).toBeInTheDocument();
29+
expect(screen.getByTitle('Insert SUM')).toBeInTheDocument();
30+
expect(screen.getByTitle('Insert SQRT')).toBeInTheDocument();
2931

3032
selectTab('Data');
3133
expect(await screen.findByText('Open Office')).toBeInTheDocument();
@@ -63,4 +65,13 @@ describe('RibbonMenu', () => {
6365

6466
expect(headings.closest('button')).not.toHaveClass('ows-button-active');
6567
});
68+
69+
it('emits formula template actions from the Formulas tab', async () => {
70+
const onAction = renderRibbon();
71+
72+
selectTab('Formulas');
73+
fireEvent.click(await screen.findByTitle('Insert SUM'));
74+
75+
expect(onAction).toHaveBeenCalledWith({actionName: 'formula-template', args: '=SUM('});
76+
});
6677
});

src/features/ribbon/RibbonMenu.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export function RibbonMenu(props: RibbonProps) {
2929
)),
3030
),
3131
React.createElement(TabsContent, {value: 'home'}, React.createElement(HomeRibbon, props)),
32-
React.createElement(TabsContent, {value: 'formulas'}, React.createElement(FormulaRibbon)),
32+
React.createElement(TabsContent, {value: 'formulas'}, React.createElement(FormulaRibbon, {
33+
onAction: props.onAction,
34+
})),
3335
React.createElement(TabsContent, {value: 'data'}, React.createElement(DataRibbon)),
3436
React.createElement(TabsContent, {value: 'view'}, React.createElement(ViewRibbon)),
3537
React.createElement(TabsContent, {value: 'about'}, React.createElement(AboutRibbon)),
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import { AppAction } from '@/app/types';
3+
import { Button } from '@/shared/ui/Button';
4+
import { ControlGroup } from '@/shared/ui/ControlGroup';
5+
6+
interface FormulaAction {
7+
icon: string;
8+
label: string;
9+
template: string;
10+
}
11+
12+
interface FormulaFunctionGroupProps {
13+
functions: FormulaAction[];
14+
label: string;
15+
onAction: (action: AppAction) => void;
16+
}
17+
18+
export function FormulaFunctionGroup(props: FormulaFunctionGroupProps) {
19+
return React.createElement(
20+
ControlGroup,
21+
{label: props.label, wide: true},
22+
React.createElement('div', {className: 'ows-formula-tools'},
23+
props.functions.map((formula) => React.createElement(Button, {
24+
icon: formula.icon,
25+
key: formula.label,
26+
onClick: () => props.onAction({actionName: 'formula-template', args: formula.template}),
27+
title: `Insert ${formula.label}`,
28+
}, formula.label)),
29+
),
30+
);
31+
}

0 commit comments

Comments
 (0)