Merge branch 'feature/mobile-apps-on-reactjs-statusbar' into feature/mobile-apps-on-reactjs

This commit is contained in:
JuliaSvinareva 2021-03-29 14:49:40 +03:00
commit 8d2f89b62e
10 changed files with 524 additions and 48 deletions

View file

@ -494,4 +494,17 @@
}
}
}
.actions-button {
background: rgba(255,255,255,.95);
}
.actions-button-text {
height: 57px;
line-height: 57px;
font-size: 20px;
color: @themeColor;
white-space: normal;
text-overflow: ellipsis;
}
}

View file

@ -431,4 +431,19 @@
}
}
}
.actions-button-text {
cursor: pointer;
line-height: 48px;
font-size: 16px;
color: rgba(0,0,0,.87);
}
@media (min-width: 496px) {
.actions-modal {
width: 100%;
left: auto;
margin-left: 0;
}
}
}

View file

@ -334,6 +334,25 @@
"textReplaceAll": "Replace All"
}
},
"Statusbar": {
"textDuplicate": "Duplicate",
"textDelete": "Delete",
"textHide": "Hide",
"textUnhide": "Unhide",
"textErrorLastSheet": "Workbook must have at least one visible worksheet.",
"textErrorRemoveSheet": "Can\"t delete the worksheet.",
"textWarnDeleteSheet": "The worksheet maybe has data. Proceed operation?",
"textSheet": "Sheet",
"textRename": "Rename",
"textErrNameExists": "Worksheet with such name already exist.",
"textErrNameWrongChar": "A sheet name cannot contains characters: \\, \/, *, ?, [, ], :",
"textErrNotEmpty": "Sheet name must not be empty",
"textRenameSheet": "Rename Sheet",
"textSheetName": "Sheet Name",
"textCancel": "Cancel",
"notcriticalErrorTitle": "Warning",
"textMore": "More"
},
"Common": {
"ThemeColorPalette": {
"textThemeColors": "Theme Colors",

View file

@ -1,19 +1,33 @@
import React, { useEffect } from 'react';
import { f7 } from 'framework7-react';
import StatusbarView from '../view/Statusbar';
import React, { Fragment, useEffect, useState } from 'react';
import {StatusbarView} from '../view/Statusbar';
import { inject } from 'mobx-react';
import { f7 } from 'framework7-react';
import { useTranslation } from 'react-i18next';
import { Device } from '../../../../common/mobile/utils/device';
const Statusbar = inject('sheets')(props => {
const {sheets} = props;
const Statusbar = inject('sheets', 'storeAppOptions', 'users')(props => {
const {sheets, storeAppOptions, users} = props;
const {t} = useTranslation();
const _t = t('Statusbar', {returnObjects: true});
// console.log(props);
let isEdit = storeAppOptions.isEdit;
let isDisconnected = users.isDisconnected;
useEffect(() => {
const on_api_created = api => {
api.asc_registerCallback('asc_onSheetsChanged', onApiSheetsChanged.bind(api));
api.asc_registerCallback('asc_onUpdateTabColor', onApiUpdateTabColor);
api.asc_registerCallback('asc_onWorkbookLocked', onWorkbookLocked);
api.asc_registerCallback('asc_onWorksheetLocked', onWorksheetLocked);
api.asc_registerCallback('asc_onSheetsChanged', onApiSheetsChanged);
api.asc_registerCallback('asc_onHidePopMenu', onApiHideTabContextMenu);
};
const on_main_view_click = e => {
// f7.popover.close('.document-menu.modal-in');
if(!e.target.closest('.tab.active')) {
f7.popover.close('.document-menu.modal-in', false);
}
};
Common.Notifications.on('document:ready', onApiSheetsChanged);
@ -25,20 +39,40 @@ const Statusbar = inject('sheets')(props => {
Common.Notifications.off('document:ready', onApiSheetsChanged);
Common.Notifications.off('engineCreated', on_api_created);
api.asc_unregisterCallback('asc_onUpdateTabColor', onApiUpdateTabColor);
api.asc_unregisterCallback('asc_onWorkbookLocked', onWorkbookLocked);
api.asc_unregisterCallback('asc_onWorksheetLocked', onWorksheetLocked);
api.asc_unregisterCallback('asc_onSheetsChanged', onApiSheetsChanged);
api.asc_unregisterCallback('asc_onHidePopMenu', onApiHideTabContextMenu);
$$('.view-main').off('click', on_main_view_click);
};
}, []);
const onApiSheetsChanged = api => {
console.log('on api sheets changed');
const onApiHideTabContextMenu = () => {
f7.popover.close('.document-menu.modal-in', false);
}
!api && (api = Common.EditorApi.get());
const onWorkbookLocked = locked => {
locked ? $$('.idx-btn-addtab').addClass('disabled') : $$('.idx-btn-addtab').removeClass('disabled');
};
const onWorksheetLocked = (index, locked) => {
let model = sheets.sheets.find(sheet => sheet.index === index);
if(model && model.locked != locked)
model.locked = locked;
};
const onApiSheetsChanged = () => {
// console.log('on api sheets changed');
const api = Common.EditorApi.get();
const sheets_count = api.asc_getWorksheetsCount();
const active_index = api.asc_getActiveWorksheetIndex();
let i = -1, items = [];
while ( ++i < sheets_count ) {
let i = -1, items = [], hiddentems = [];
while (++i < sheets_count) {
const tab = {
index : i,
active : active_index == i,
@ -48,48 +82,261 @@ const Statusbar = inject('sheets')(props => {
color : api.asc_getWorksheetTabColor(i)
};
items.push(tab);
(api.asc_isWorksheetHidden(i) ? hiddentems : items).push(tab);
// items.push(tab);
}
sheets.reset(items);
// this.hiddensheets.reset(hiddentems);
sheets.resetSheets(items);
sheets.resetHiddenSheets(hiddentems);
updateTabsColors();
};
// this.updateTabsColors();
const loadTabColor = sheetindex => {
const api = Common.EditorApi.get();
let tab = sheets.sheets.find(sheet => sheet.index === sheetindex);
if (tab) {
setTabLineColor(tab, api.asc_getWorksheetTabColor(sheetindex));
}
};
const onApiUpdateTabColor = index => {
loadTabColor(index);
};
const setTabLineColor = (tab, color) => {
if (tab) {
if (null !== color) {
color = '#' + Common.Utils.ThemeColor.getHexColor(color.get_r(), color.get_g(), color.get_b());
} else {
color = '';
}
if (color.length) {
if (!tab.active) {
color = '0px 4px 0 ' + Common.Utils.RGBColor(color).toRGBA(0.7) + ' inset';
} else {
color = '0px 4px 0 ' + color + ' inset';
}
$$('.sheet-tabs .tab').eq(tab.index).css('box-shadow', color);
} else {
$$('.sheet-tabs .tab').eq(tab.index).css('box-shadow', '');
}
}
};
const updateTabsColors = () => {
const api = Common.EditorApi.get();
sheets.sheets.forEach(model => {
setTabLineColor(model, api.asc_getWorksheetTabColor(model.index));
});
};
const onTabClicked = i => {
const model = sheets.at(i);
const api = Common.EditorApi.get();
api.asc_showWorksheet(model.index);
sheets.setActiveWorksheet(i);
Common.Notifications.trigger('sheet:active', model.index);
};
const createSheetName = () => {
const api = Common.EditorApi.get();
let items = [], wc = api.asc_getWorksheetsCount();
while (wc--) {
items.push(api.asc_getWorksheetName(wc).toLowerCase());
}
let index = 0, name;
while(++index < 1000) {
name = /*this.strSheet*/ 'Sheet' + index;
if (items.indexOf(name.toLowerCase()) < 0) break;
}
return name;
};
const onAddTabClicked = () => {
const api = Common.EditorApi.get();
api.asc_closeCellEditor();
const createSheetName = () => {
let items = [], wc = api.asc_getWorksheetsCount();
while (wc--) {
items.push(api.asc_getWorksheetName(wc).toLowerCase());
}
let index = 0, name;
while(++index < 1000) {
name = /*this.strSheet*/ 'Sheet' + index;
if (items.indexOf(name.toLowerCase()) < 0) break;
}
return name;
};
createSheetName();
api.asc_addWorksheet(createSheetName());
};
return <StatusbarView onTabClicked={onTabClicked} onAddTabClicked={onAddTabClicked} />
const onTabClick = (i, target) => {
const api = Common.EditorApi.get();
const model = sheets.at(i);
// console.log(model);
let opened = $$('.document-menu.modal-in').length;
let index = model.index;
f7.popover.close('.document-menu.modal-in', false);
if (index == api.asc_getActiveWorksheetIndex()) {
if (!opened) {
if (!isDisconnected) {
api.asc_closeCellEditor();
f7.popover.open('#idx-tab-context-menu-popover', target);
}
}
} else {
f7.popover.close('#idx-tab-context-menu-popover', false);
onTabClicked(i);
Common.Notifications.trigger('sheet:active', index);
}
};
const deleteWorksheet = () => {
const api = Common.EditorApi.get();
const visibleSheets = sheets.visibleWorksheets();
if (sheets.sheets.length == 1 || visibleSheets.length == 1) {
f7.dialog.alert(_t.textErrorLastSheet, _t.notcriticalErrorTitle);
} else {
f7.dialog.confirm(
_t.textWarnDeleteSheet,
_t.notcriticalErrorTitle,
() => {
if (!api.asc_deleteWorksheet()) {
f7.dialog.alert(_t.textErrorRemoveSheet, _t.notcriticalErrorTitle);
}
}
);
}
};
const renameWorksheet = () => {
const api = Common.EditorApi.get();
if (api.asc_getWorksheetsCount() > 0) {
let sindex = api.asc_getActiveWorksheetIndex();
if (api.asc_isWorksheetLockedOrDeleted(sindex)) {
return;
}
let current = api.asc_getWorksheetName(api.asc_getActiveWorksheetIndex());
f7.dialog.create({
title: _t.textRenameSheet,
content: Device.ios ?
'<div class="input-field"><input type="text" name="modal-sheet-name" maxlength="31" value="' + current + '" placeholder="' + _t.textSheetName + '" class="modal-text-input"></div>' :
'<div class="item-content item-input" style="margin-top: 15px; position: relative; padding-bottom: 10px;"><div class="item-inner"><div class="item-input-wrap" style="min-height: initial; width: 100%;"><input type="text" style="width: 100%;" name="modal-sheet-name" value="' + current + '" maxlength="31" placeholder="' + _t.textSheetName + '" /></div></div></div>',
buttons: [
{
text: 'OK',
bold: true,
onClick: function () {
let s = $$('input[name="modal-sheet-name"]').val(),
wc = api.asc_getWorksheetsCount(), items = [],
err = !s.trim().length ? _t.textErrNotEmpty : ((s.length > 2 && s[0] == '"' && s[s.length-1] == '"' || !/[:\\\/\*\?\[\]\']/.test(s)) ? null : _t.textErrNameWrongChar);
if (!err) {
while (wc--) {
if (sindex !== wc) {
items.push(api.asc_getWorksheetName(wc).toLowerCase());
}
}
if (items) {
let testval = s.toLowerCase();
for (var i = items.length - 1; i >= 0; --i) {
if (items[i] === testval) {
err = _t.textErrNameExists;
}
}
}
}
if (err) {
f7.dialog.alert(err, _t.notcriticalErrorTitle, () => {
renameWorksheet();
});
} else if (s != current)
api.asc_renameWorksheet(s);
}
},
{
text: _t.textCancel
}
]
}).open();
}
};
const createCopyName = (orig) => {
const api = Common.EditorApi.get();
let wc = api.asc_getWorksheetsCount(), names = [];
while (wc--) {
names.push(api.asc_getWorksheetName(wc).toLowerCase());
}
let re = /^(.*)\((\d)\)$/.exec(orig);
let first = re ? re[1] : orig + ' ';
let index = 1, name;
while(++index < 1000) {
name = first + '(' + index + ')';
if (names.indexOf(name.toLowerCase()) < 0) break;
}
return name;
};
const hideWorksheet = (hide, index) => {
const api = Common.EditorApi.get();
const visibleSheets = sheets.visibleWorksheets();
if(hide) {
visibleSheets.length == 1 ?
f7.dialog.alert(_t.textErrorLastSheet, _t.notcriticalErrorTitle) :
api['asc_hideWorksheet']([index]);
} else {
f7.popover.close('#idx-hidden-sheets-popover');
api['asc_showWorksheet'](index);
loadTabColor(index);
}
};
const onTabMenu = (event) => {
const api = Common.EditorApi.get();
let index = sheets.sheets.find(sheet => sheet.active).index;
f7.popover.close('.document-menu.modal-in', false);
switch (event) {
case 'del': deleteWorksheet(); break;
case 'hide': hideWorksheet(true, index); break;
case 'ins': api.asc_insertWorksheet(createSheetName()); break;
case 'copy':
let name = createCopyName(api.asc_getWorksheetName(api.asc_getActiveWorksheetIndex()));
api.asc_copyWorksheet(index, name);
break;
case 'ren': renameWorksheet(); break;
case 'unhide':
f7.popover.open('#idx-hidden-sheets-popover', '.active');
break;
case 'showMore':
f7.actions.open('#idx-tab-menu-actions');
break;
default:
let _re = /reveal\:(\d+)/.exec(event);
if (_re && !!_re[1]) {
hideWorksheet(false, parseInt(_re[1]));
}
}
};
return (
<StatusbarView onTabClick={onTabClick} onTabClicked={onTabClicked} onAddTabClicked={onAddTabClicked} onTabMenu={onTabMenu} />
)
});
export default Statusbar;

View file

@ -1,5 +1,23 @@
.device-ios {
input.modal-text-input {
box-sizing: border-box;
height: 26px;
background: #fff;
margin: 0;
margin-top: 15px;
padding: 0 5px;
border: 1px solid rgba(0,0,0,.3);
border-radius: 0;
width: 100%;
font-size: 14px;
font-family: inherit;
display: block;
box-shadow: 0 0 0 transparent;
-webkit-appearance: none;
-moz-appearance: none;
-ms-appearance: none;
appearance: none;
}
}

View file

@ -6,7 +6,6 @@
height: @statusbar-height;
min-height: @statusbar-height;
background-color: @background-normal;
display: flex;
.tab {
@ -26,13 +25,23 @@
}
.statusbar--box-tabs {
overflow-x: auto;
overflow-y: hidden;
&::-webkit-scrollbar {
-webkit-appearance: none;
display: none;
// width: 0;
// height: 0;
}
> ul {
padding: 0;
margin: 0;
height: 100%;
white-space: pre;
overflow: hidden;
// overflow: hidden;
// position: absolute;
// left: 0;
// top: 0;
> li {
a {
font-size: 12px;

View file

@ -10,6 +10,7 @@ import AddOptions from "../view/add/Add";
import EditOptions from "../view/edit/Edit";
import { Device } from '../../../../common/mobile/utils/device';
import { Search, SearchSettings } from '../controller/Search';
import { f7 } from 'framework7-react';
import {FunctionGroups} from "../controller/add/AddFunction";
import ContextMenu from '../controller/ContextMenu';
@ -27,6 +28,8 @@ export default class MainPage extends Component {
}
handleClickToOpenOptions = (opts, showOpts) => {
f7.popover.close('.document-menu.modal-in', false);
this.setState(state => {
if ( opts == 'edit' )
return {editOptionsVisible: true};

View file

@ -10,6 +10,7 @@ export class storeAppOptions {
});
}
isEdit = false;
config = {};
isEdit = false;

View file

@ -21,20 +21,28 @@ class Worksheet {
export class storeWorksheets {
sheets;
hiddensheets;
constructor() {
makeObservable(this, {
sheets: observable,
reset: action,
hiddensheets: observable,
resetSheets: action,
resetHiddenSheets: action,
setActiveWorksheet: action
});
this.sheets = [];
this.hiddensheets = [];
}
reset(sheets) {
resetSheets(sheets) {
this.sheets = Object.values(sheets)
}
resetHiddenSheets(hiddensheets) {
this.hiddensheets = Object.values(hiddensheets)
}
setActiveWorksheet(i) {
if ( !this.sheets[i].active ) {
this.sheets.forEach(model => {
@ -57,4 +65,8 @@ export class storeWorksheets {
hiddenWorksheets() {
return this.sheets.filter(model => model.hidden);
}
visibleWorksheets() {
return this.sheets.filter(model => !model.hidden);
}
}

View file

@ -1,18 +1,100 @@
import React from 'react';
import { View, Toolbar, Link, Icon } from 'framework7-react';
import React, { Fragment } from 'react';
import { View, Toolbar, Link, Icon, Popover, List, ListButton, Actions, ActionsGroup, ActionsButton } from 'framework7-react';
import { observer, inject } from "mobx-react";
import { useTranslation } from 'react-i18next';
import { Device } from '../../../../common/mobile/utils/device';
const viewStyle = {
height: 30
};
const StatusbarView = inject('sheets')(observer(props => {
const { t } = useTranslation();
const _t = t('Statusbar', {returnObjects: true});
const isAndroid = Device.android;
const isPhone = Device.isPhone;
const { sheets } = props;
const hiddenSheets = sheets.hiddensheets;
const getTabClassList = model => `tab ${model.active ? 'active' : ''} ${model.locked ? 'locked' : ''}`;
// const $boxTabs = $$('.sheet-tabs');
// const $statusBar = $$('.statusbar');
const getTabClassList = model =>
`tab ${model.active ? 'active':''} ${model.locked ? 'locked':''}`;
// $boxTabs.on('touchstart', onTouchStart);
// $boxTabs.on('touchmove', onTouchMove);
// $boxTabs.on('touchend', onTouchEnd);
return <View id="idx-statusbar" className="statusbar" style={viewStyle}>
// let touch = {};
// function hasInvisible() {
// let _left_bound_ = $boxTabs.offset().left,
// _right_bound_ = $boxTabs.width() + _left_bound_ - $statusBar.width();
// // _right_bound_ = _left_bound_ + $boxTabs.width();
// // console.log(_left_bound_);
// console.log(_right_bound_);
// let tab = $$('.sheet-tabs li')[0];
// let rect = tab.getBoundingClientRect();
// if (!(rect.left < _left_bound_)) {
// // tab = $$('.sheet-tabs li')[$$('.sheet-tabs li').length - 1];
// // rect = tab.getBoundingClientRect();
// // if (!((rect.right).toFixed(2) > _right_bound_))
// // return false;
// if(_right_bound_ <= 0) {
// return false;
// }
// }
// return true;
// }
// function onTouchStart(e) {
// if (hasInvisible()) {
// console.log(e);
// let touches = e.changedTouches;
// touch.startx = touches[0].clientX;
// touch.scrollx = $boxTabs.scrollLeft();
// // console.log(touch.scrollx);
// touch.timer = setTimeout(function () {
// // touch.longtouch = true;
// }, 500);
// // e.preventDefault();
// }
// }
// function onTouchMove(e) {
// if (touch.startx !== undefined) {
// // console.log(e);
// let touches = e.changedTouches;
// if (touch.longtouch) {}
// else {
// if (touch.timer) clearTimeout(touch.timer), delete touch.timer;
// let valueLeft = touch.scrollx + (touch.startx - touches[0].clientX);
// console.log(valueLeft);
// // $boxTabs.scrollLeft(valueLeft);
//
// }
// // e.preventDefault();
// }
// }
// function onTouchEnd(e) {
// if (touch.startx !== undefined) {
// // console.log(e);
// touch.longtouch = false;
// delete touch.startx;
// // e.preventDefault();
// }
// }
return (
<Fragment>
<View id="idx-statusbar" className="statusbar" style={viewStyle}>
<div id="idx-box-add-tab">
<Link href="false" id="idx-btn-addtab" className="tab" onClick={e => props.onAddTabClicked()}>
<Icon className="icon icon-plus" />
@ -22,13 +104,70 @@ const StatusbarView = inject('sheets')(observer(props => {
<ul className="sheet-tabs bottom">
{sheets.sheets.map((model,i) =>
model.hidden ? null :
<li className={getTabClassList(model)} key={i}>
<a onClick={e => props.onTabClicked(i)}>{model.name}</a>
<li className={getTabClassList(model)} key={i} onClick={(e) => props.onTabClick(i, e.target)}>
<a /* onClick={e => props.onTabClicked(i)} */>{model.name}</a>
</li>
)}
</ul>
</div>
</View>;
</View>
<Popover id="idx-tab-context-menu-popover"
className="document-menu"
backdrop={false}
closeByBackdropClick={false}
closeByOutsideClick={false}
>
{isPhone || isAndroid ? (
<List className="list-block">
<ListButton title={_t.textDuplicate} onClick={() => props.onTabMenu('copy')} />
<ListButton title={_t.textDelete} onClick={() => props.onTabMenu('del')} />
<ListButton title={_t.textMore} onClick={() => props.onTabMenu('showMore')} />
</List>
) : (
<List className="list-block">
<ListButton title={_t.textDuplicate} onClick={() => props.onTabMenu('copy')} />
<ListButton title={_t.textDelete} onClick={() => props.onTabMenu('del')} />
<ListButton title={_t.textRename} onClick={() => props.onTabMenu('ren')} />
<ListButton title={_t.textHide} onClick={() => props.onTabMenu('hide')} />
{hiddenSheets.length ? (
<ListButton title={_t.textUnhide} onClick={() => props.onTabMenu('unhide')} />
) : null}
</List>
)}
</Popover>
{isPhone || isAndroid ? (
<Actions id="idx-tab-menu-actions" backdrop={true} closeByBackdropClick={true}>
<ActionsGroup>
<ActionsButton onClick={() => props.onTabMenu('ren')}>{_t.textRename}</ActionsButton>
<ActionsButton onClick={() => props.onTabMenu('hide')}>{_t.textHide}</ActionsButton>
{hiddenSheets.length ? (
<ActionsButton onClick={() => props.onTabMenu('unhide')}>{_t.textUnhide}</ActionsButton>
) : null}
</ActionsGroup>
<ActionsGroup>
<ActionsButton bold={true}>{_t.textCancel}</ActionsButton>
</ActionsGroup>
</Actions>
) : null}
{hiddenSheets.length ? (
<Popover id="idx-hidden-sheets-popover"
className="document-menu"
backdrop={false}
closeByBackdropClick={false}
closeByOutsideClick={false}
>
<List className="list-block">
{hiddenSheets.map(sheet => {
return (
<ListButton key={sheet.index} data-event={`reveal:${sheet.index}`} title={sheet.name}
onClick={() => props.onTabMenu(`reveal:${sheet.index}`)} />
)
})}
</List>
</Popover>
) : null}
</Fragment>
)
}));
export default StatusbarView;
export {StatusbarView};