Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
crossorigin="anonymous">
<link rel="icon" href="/OpenWebSheet/favicon.ico">
<title>Open web sheet</title>
<script type="module" crossorigin src="/OpenWebSheet/assets/index-Vds5Mw57.js"></script>
<link rel="stylesheet" crossorigin href="/OpenWebSheet/assets/index-Bu-CVJUF.css">
<script type="module" crossorigin src="/OpenWebSheet/assets/index-BChN-qVl.js"></script>
<link rel="stylesheet" crossorigin href="/OpenWebSheet/assets/index-Cs_txOUy.css">
</head>
<body>
<noscript>
Expand Down
17 changes: 12 additions & 5 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,18 @@ export default function App() {
setFileMode(mode);
};

const onAction = (action: AppAction) => action.actionName === 'save-ows'
? saveDocument(uiRef.current)
: action.actionName === 'load-ows'
? loadDocument(uiRef.current)
: uiRef.current && uiRef.current.execCmd(action.actionName, action.args);
const onAction = (action: AppAction) => {
if (action.actionName === 'save-ows') {
return saveDocument(uiRef.current);
}
if (action.actionName === 'load-ows') {
return loadDocument(uiRef.current);
}
if (action.actionName === 'formula-template') {
return setSheetState({...sheetState, value: action.args});
}
return uiRef.current && uiRef.current.execCmd(action.actionName, action.args);
};

return React.createElement('div', {className: 'ows-app'},
React.createElement(RibbonMenu, {appearance, fileMode, onAction, onModeChanged, state: sheetState}),
Expand Down
4 changes: 4 additions & 0 deletions src/app/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@
@apply text-green-700;
}

.ows-formula-tools {
@apply flex flex-wrap items-center justify-center gap-1.5;
}

.ows-formula-input {
@apply flex-1 h-7 mx-1 border-l border-gray-300 px-2 outline-none;
}
Expand Down
44 changes: 36 additions & 8 deletions src/features/ribbon/FormulaRibbon.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
import React from 'react';
import { AppAction } from '@/app/types';
import { ControlGroup } from '@/shared/ui/ControlGroup';
import { FormulaFunctionGroup } from './formulas/FormulaFunctionGroup';

export function FormulaRibbon() {
interface FormulaRibbonProps {
onAction: (action: AppAction) => void;
}

const statistics = [
{icon: 'fa fa-plus', label: 'SUM', template: '=SUM('},
{icon: 'fa fa-arrow-down', label: 'MIN', template: '=MIN('},
{icon: 'fa fa-arrow-up', label: 'MAX', template: '=MAX('},
];

const mathematical = [
{icon: 'fa fa-square-root-alt', label: 'SQRT', template: '=SQRT('},
{icon: 'fa fa-plus-circle', label: 'ABS', template: '=ABS('},
{icon: 'fa fa-chart-line', label: 'LOG', template: '=LOG('},
{icon: 'fa fa-superscript', label: 'EXP', template: '=EXP('},
];

const trigonometry = [
{icon: 'fa fa-wave-square', label: 'SIN', template: '=SIN('},
{icon: 'fa fa-wave-square', label: 'COS', template: '=COS('},
{icon: 'fa fa-wave-square', label: 'TAN', template: '=TAN('},
];

const constants = [
{icon: 'fa fa-circle-notch', label: 'PI', template: '=PI'},
{icon: 'fa fa-italic', label: 'E', template: '=E'},
];

export function FormulaRibbon(props: FormulaRibbonProps) {
return React.createElement(
'div',
{className: 'ows-ribbon-panel'},
React.createElement(ControlGroup, {label: 'Mathematical', wide: true},
React.createElement('span', {className: 'ows-muted'}, 'Function tools'),
),
React.createElement(ControlGroup, {label: 'Statistics', wide: true},
React.createElement('span', {className: 'ows-muted'}, 'Analysis helpers'),
),
React.createElement(FormulaFunctionGroup, {functions: statistics, label: 'Statistics', onAction: props.onAction}),
React.createElement(FormulaFunctionGroup, {functions: mathematical, label: 'Mathematical', onAction: props.onAction}),
React.createElement(FormulaFunctionGroup, {functions: trigonometry, label: 'Trigonometry', onAction: props.onAction}),
React.createElement(FormulaFunctionGroup, {functions: constants, label: 'Constants', onAction: props.onAction}),
React.createElement(ControlGroup, {label: 'User Defined Functions', wide: true},
React.createElement('span', {className: 'ows-muted'}, 'Custom formulas'),
React.createElement('span', {className: 'ows-muted'}, 'Custom functions are planned'),
),
);
}
11 changes: 11 additions & 0 deletions src/features/ribbon/RibbonMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ describe('RibbonMenu', () => {
selectTab('Formulas');
expect(await screen.findByText('Mathematical')).toBeInTheDocument();
expect(screen.getByText('User Defined Functions')).toBeInTheDocument();
expect(screen.getByTitle('Insert SUM')).toBeInTheDocument();
expect(screen.getByTitle('Insert SQRT')).toBeInTheDocument();

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

expect(headings.closest('button')).not.toHaveClass('ows-button-active');
});

it('emits formula template actions from the Formulas tab', async () => {
const onAction = renderRibbon();

selectTab('Formulas');
fireEvent.click(await screen.findByTitle('Insert SUM'));

expect(onAction).toHaveBeenCalledWith({actionName: 'formula-template', args: '=SUM('});
});
});
4 changes: 3 additions & 1 deletion src/features/ribbon/RibbonMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export function RibbonMenu(props: RibbonProps) {
)),
),
React.createElement(TabsContent, {value: 'home'}, React.createElement(HomeRibbon, props)),
React.createElement(TabsContent, {value: 'formulas'}, React.createElement(FormulaRibbon)),
React.createElement(TabsContent, {value: 'formulas'}, React.createElement(FormulaRibbon, {
onAction: props.onAction,
})),
React.createElement(TabsContent, {value: 'data'}, React.createElement(DataRibbon)),
React.createElement(TabsContent, {value: 'view'}, React.createElement(ViewRibbon)),
React.createElement(TabsContent, {value: 'about'}, React.createElement(AboutRibbon)),
Expand Down
31 changes: 31 additions & 0 deletions src/features/ribbon/formulas/FormulaFunctionGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { AppAction } from '@/app/types';
import { Button } from '@/shared/ui/Button';
import { ControlGroup } from '@/shared/ui/ControlGroup';

interface FormulaAction {
icon: string;
label: string;
template: string;
}

interface FormulaFunctionGroupProps {
functions: FormulaAction[];
label: string;
onAction: (action: AppAction) => void;
}

export function FormulaFunctionGroup(props: FormulaFunctionGroupProps) {
return React.createElement(
ControlGroup,
{label: props.label, wide: true},
React.createElement('div', {className: 'ows-formula-tools'},
props.functions.map((formula) => React.createElement(Button, {
icon: formula.icon,
key: formula.label,
onClick: () => props.onAction({actionName: 'formula-template', args: formula.template}),
title: `Insert ${formula.label}`,
}, formula.label)),
),
);
}