Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
3b884fe457
|
@ -103,7 +103,7 @@ class ContextMenuController extends Component {
|
|||
}
|
||||
|
||||
onApiOpenContextMenu(x, y) {
|
||||
if ( !this.state.opened && $$('.dialog.modal-in').length < 1) {
|
||||
if ( !this.state.opened && $$('.dialog.modal-in').length < 1 && !$$('.popover.modal-in, .sheet-modal.modal-in').length) {
|
||||
this.setState({
|
||||
items: this.initMenuItems(),
|
||||
extraItems: this.initExtraItems()
|
||||
|
|
|
@ -406,7 +406,14 @@
|
|||
"textExternalLink": "External Link",
|
||||
"textDefault": "Selected range",
|
||||
"textInvalidRange": "Invalid cells range",
|
||||
"txtNotUrl": "This field should be a URL in the format \"http://www.example.com\""
|
||||
"txtNotUrl": "This field should be a URL in the format \"http://www.example.com\"",
|
||||
"textFilterOptions": "Filter Options",
|
||||
"textClearFilter": "Clear Filter",
|
||||
"textDeleteFilter": "Delete Filter",
|
||||
"textSelectAll": "Select All",
|
||||
"textEmptyItem": "{Blanks}",
|
||||
"textErrorTitle": "Warning",
|
||||
"textErrorMsg": "You must choose at least one value"
|
||||
},
|
||||
"Settings": {
|
||||
"textFindAndReplace": "Find and Replace",
|
||||
|
|
134
apps/spreadsheeteditor/mobile/src/controller/FilterOptions.jsx
Normal file
134
apps/spreadsheeteditor/mobile/src/controller/FilterOptions.jsx
Normal file
|
@ -0,0 +1,134 @@
|
|||
import React, { useEffect,useState } from 'react';
|
||||
import FilterView from '../../src/view/FilterOptions';
|
||||
import { f7,Sheet,Popover } from 'framework7-react';
|
||||
import { Device } from '../../../../common/mobile/utils/device';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const FilterOptionsController = () => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('View.Edit', {returnObjects: true});
|
||||
|
||||
const [configFilter, setConfig] = useState(null);
|
||||
const [listVal, setListValue] = useState([]);
|
||||
const [isValid, setIsValid] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
function onDocumentReady() {
|
||||
const api = Common.EditorApi.get();
|
||||
api.asc_registerCallback('asc_onSetAFDialog',onApiFilterOptions);
|
||||
}
|
||||
|
||||
if ( !Common.EditorApi ) {
|
||||
Common.Notifications.on('document:ready',onDocumentReady);
|
||||
} else {
|
||||
onDocumentReady();
|
||||
}
|
||||
|
||||
return () => {
|
||||
Common.Notifications.off('document:ready', onDocumentReady);
|
||||
const api = Common.EditorApi.get();
|
||||
api.asc_unregisterCallback('asc_onSetAFDialog',onApiFilterOptions);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onApiFilterOptions= (config) => {
|
||||
setDataFilterCells(config);
|
||||
setConfig(config);
|
||||
setClearDisable(config);
|
||||
|
||||
if (Device.phone) {
|
||||
f7.sheet.open('.picker__sheet');
|
||||
} else {
|
||||
let rect = config.asc_getCellCoord(),
|
||||
posX = rect.asc_getX() + rect.asc_getWidth() - 9,
|
||||
posY = rect.asc_getY() + rect.asc_getHeight() - 9;
|
||||
|
||||
let $target = $$('#idx-context-menu-target')
|
||||
.css({left: `${posX}px`, top: `${posY}px`});
|
||||
f7.popover.open('#picker-popover',$target);
|
||||
}
|
||||
}
|
||||
|
||||
const onSort = (type) => {
|
||||
const api = Common.EditorApi.get();
|
||||
api.asc_sortColFilter(type == 'sortdown' ? Asc.c_oAscSortOptions.Ascending : Asc.c_oAscSortOptions.Descending, configFilter.asc_getCellId(), configFilter.asc_getDisplayName(), undefined, true);
|
||||
};
|
||||
|
||||
const onClearFilter = () => {
|
||||
const api = Common.EditorApi.get();
|
||||
if(api) api.asc_clearFilter();
|
||||
};
|
||||
|
||||
const onDeleteFilter = () => {
|
||||
const api = Common.EditorApi.get();
|
||||
let formatTableInfo = api.asc_getCellInfo().asc_getFormatTableInfo();
|
||||
let tablename = (formatTableInfo) ? formatTableInfo.asc_getTableName() : undefined;
|
||||
if(api) {
|
||||
api.asc_changeAutoFilter(tablename, Asc.c_oAscChangeFilterOptions.filter, false);
|
||||
f7.sheet.close('.picker__sheet');
|
||||
f7.popover.close('#picker-popover');
|
||||
}
|
||||
};
|
||||
|
||||
const setClearDisable = (config) => {
|
||||
let arr = config.asc_getValues();
|
||||
let lenCheck = arr.filter((item) => item.visible == true).length;
|
||||
lenCheck == arr.length ? setIsValid(true) : setIsValid(false)
|
||||
};
|
||||
|
||||
const setDataFilterCells = (config) => {
|
||||
function isNumeric(value) {
|
||||
return !isNaN(parseFloat(value)) && isFinite(value);
|
||||
}
|
||||
|
||||
let value = null,
|
||||
isnumber = null,
|
||||
arrCells = [];
|
||||
|
||||
config.asc_getValues().forEach((item, index) => {
|
||||
value = item.asc_getText();
|
||||
isnumber = isNumeric(value);
|
||||
|
||||
arrCells.push({
|
||||
id : index,
|
||||
selected : false,
|
||||
cellvalue : value ? value : _t.textEmptyItem,
|
||||
value : isnumber ? value : (value.length > 0 ? value: _t.textEmptyItem),
|
||||
groupid : '1',
|
||||
check : item.asc_getVisible()
|
||||
});
|
||||
});
|
||||
|
||||
setListValue(arrCells);
|
||||
};
|
||||
|
||||
const onUpdateCell = (id, state) => {
|
||||
const api = Common.EditorApi.get();
|
||||
|
||||
if ( id == 'all' ) {
|
||||
listVal.forEach(item => item.check = state);
|
||||
} else {
|
||||
listVal[id].check = state;
|
||||
}
|
||||
|
||||
setListValue([...listVal]);
|
||||
|
||||
if ( listVal.some(item => item.check) ) {
|
||||
configFilter.asc_getValues().forEach(
|
||||
(item, index) => item.asc_setVisible(listVal[index].check)
|
||||
);
|
||||
|
||||
configFilter.asc_getFilterObj().asc_setType(Asc.c_oAscAutoFilterTypes.Filters);
|
||||
api.asc_applyAutoFilter(configFilter);
|
||||
}
|
||||
|
||||
setClearDisable(configFilter);
|
||||
};
|
||||
|
||||
return (
|
||||
<FilterView onSort={onSort} listVal={listVal} isValid={isValid} onUpdateCell={onUpdateCell}
|
||||
onDeleteFilter={onDeleteFilter} onClearFilter={onClearFilter}/>
|
||||
)
|
||||
};
|
||||
|
||||
export default FilterOptionsController;
|
|
@ -20,4 +20,16 @@
|
|||
appearance: none;
|
||||
}
|
||||
|
||||
// For filter options
|
||||
.radio-checkbox-item {
|
||||
label.item-content input[type=checkbox]:checked ~ .item-inner {
|
||||
background: no-repeat center;
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2013%2010'%3E%3Cpolygon%20fill%3D'%2340865c'%20points%3D'11.6%2C0%204.4%2C7.2%201.4%2C4.2%200%2C5.6%204.4%2C10%204.4%2C10%204.4%2C10%2013%2C1.4%20'%2F%3E%3C%2Fsvg%3E");
|
||||
background-position: calc(100% - 15px) center;
|
||||
background-size: 13px 10px;
|
||||
}
|
||||
.icon.icon-checkbox {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
// Colors
|
||||
@themeColorLight: #a2bdde;
|
||||
@navBarIconColor: #fff;
|
||||
@listButtonBorderColor: rgba(0,0,0,0.15);
|
||||
|
||||
|
||||
.device-android {
|
||||
--f7-navbar-bg-color: @themeColor;
|
||||
--f7-navbar-link-color: @navBarIconColor;
|
||||
--f7-navbar-text-color: @navBarIconColor;
|
||||
--f7-list-button-border-color: @listButtonBorderColor;
|
||||
|
||||
// Main Toolbar
|
||||
#editor-navbar.navbar .right {
|
||||
|
@ -21,4 +23,30 @@
|
|||
align-items: center;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// For filter options
|
||||
.radio-checkbox-item {
|
||||
.icon.icon-checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 20px;
|
||||
border: 2px solid var(--f7-checkbox-inactive-color);
|
||||
}
|
||||
label.item-checkbox input[type='checkbox']:checked ~ .icon-checkbox {
|
||||
border-color: @themeColor;
|
||||
background-color: transparent;
|
||||
}
|
||||
label.item-content i.icon.icon-checkbox:after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -5px;
|
||||
margin-top: -5px;
|
||||
background-color: @themeColor;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import CellEditor from '../controller/CellEditor';
|
|||
import Statusbar from '../controller/Statusbar';
|
||||
import AddOptions from "../view/add/Add";
|
||||
import EditOptions from "../view/edit/Edit";
|
||||
import FilterOptionsController from "../controller/FilterOptions";
|
||||
import { Search, SearchSettings } from '../controller/Search';
|
||||
import { f7 } from 'framework7-react';
|
||||
|
||||
|
@ -110,6 +111,9 @@ class MainPage extends Component {
|
|||
!this.state.collaborationVisible ? null :
|
||||
<CollaborationView onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
|
||||
}
|
||||
|
||||
<FilterOptionsController />
|
||||
|
||||
<Statusbar />
|
||||
|
||||
<FunctionGroups /> {/* hidden component*/}
|
||||
|
|
91
apps/spreadsheeteditor/mobile/src/view/FilterOptions.jsx
Normal file
91
apps/spreadsheeteditor/mobile/src/view/FilterOptions.jsx
Normal file
|
@ -0,0 +1,91 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {f7, List, Popover, Sheet, ListItem, Icon, Row, Button, ListButton, Page, Navbar, Segmented, BlockTitle, NavRight, Link, Toggle,View} from 'framework7-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Device } from '../../../../common/mobile/utils/device';
|
||||
|
||||
const FilterOptions = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('View.Edit', {returnObjects: true});
|
||||
|
||||
useEffect(() => {
|
||||
const is_all_checked = props.listVal.every(item => item.check);
|
||||
setAll(is_all_checked);
|
||||
});
|
||||
|
||||
const [all, setAll] = useState(false);
|
||||
|
||||
const HandleClearFilter = () => {
|
||||
props.onClearFilter();
|
||||
setAll(true);
|
||||
props.onUpdateCell('all', true);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={props.style}>
|
||||
<Page>
|
||||
<Navbar title={_t.textFilterOptions}>
|
||||
{Device.phone &&
|
||||
<NavRight>
|
||||
<Link sheetClose=".picker__sheet">
|
||||
<Icon icon='icon-expand-down'/>
|
||||
</Link>
|
||||
</NavRight>
|
||||
}
|
||||
</Navbar>
|
||||
<List>
|
||||
<ListItem className='buttons'>
|
||||
<Row>
|
||||
<a className='button' onClick={() => props.onSort('sortdown')}>
|
||||
<Icon slot="media" icon="sortdown" />
|
||||
</a>
|
||||
<a className='button' onClick={() => props.onSort('sortup')}>
|
||||
<Icon slot="media" icon="sortup" />
|
||||
</a>
|
||||
</Row>
|
||||
</ListItem>
|
||||
</List>
|
||||
<List >
|
||||
<ListButton color="black" className={props.isValid ? 'disabled' : ''} onClick={HandleClearFilter}>{_t.textClearFilter}</ListButton>
|
||||
<ListButton color="red" onClick={() => props.onDeleteFilter()} id="btn-delete-filter">{_t.textDeleteFilter}</ListButton>
|
||||
</List>
|
||||
<List>
|
||||
<ListItem className='radio-checkbox-item' onChange={e => props.onUpdateCell('all', e.target.checked)} name='filter-cellAll' checkbox checked={all}>{_t.textSelectAll}</ListItem>
|
||||
{props.listVal.map((value) =>
|
||||
<ListItem className='radio-checkbox-item' onChange={e => props.onUpdateCell(value.id, e.target.checked)} key={value.value} name='filter-cell' value={value.value} title={value.cellvalue} checkbox checked={value.check} />
|
||||
)}
|
||||
</List>
|
||||
</Page>
|
||||
</View>
|
||||
)
|
||||
};
|
||||
|
||||
const FilterView = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('View.Edit', {returnObjects: true});
|
||||
|
||||
const onClosed = () => {
|
||||
if ( props.listVal.every(item => !item.check) ) {
|
||||
f7.dialog.create({
|
||||
title: _t.textErrorTitle,
|
||||
text: _t.textErrorMsg,
|
||||
buttons: [
|
||||
{
|
||||
text: 'OK',
|
||||
}
|
||||
]
|
||||
}).open();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
!Device.phone ?
|
||||
<Popover id="picker-popover" className="popover__titled" onPopoverClosed={onClosed}>
|
||||
<FilterOptions style={{height: '410px'}} {...props}></FilterOptions>
|
||||
</Popover> :
|
||||
<Sheet className="picker__sheet" push onSheetClosed={onClosed}>
|
||||
<FilterOptions {...props}></FilterOptions>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
|
||||
export default FilterView;
|
Loading…
Reference in a new issue