Filter Options
This commit is contained in:
parent
9ad620c727
commit
10927e6f63
|
@ -103,7 +103,7 @@ class ContextMenuController extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onApiOpenContextMenu(x, y) {
|
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({
|
this.setState({
|
||||||
items: this.initMenuItems(),
|
items: this.initMenuItems(),
|
||||||
extraItems: this.initExtraItems()
|
extraItems: this.initExtraItems()
|
||||||
|
|
|
@ -406,7 +406,13 @@
|
||||||
"textExternalLink": "External Link",
|
"textExternalLink": "External Link",
|
||||||
"textDefault": "Selected range",
|
"textDefault": "Selected range",
|
||||||
"textInvalidRange": "Invalid cells 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",
|
||||||
|
"textEmptyItem": "{Blanks}",
|
||||||
|
"textErrorTitle": "Warning",
|
||||||
|
"textErrorMsg": "You must choose at least one value"
|
||||||
},
|
},
|
||||||
"Settings": {
|
"Settings": {
|
||||||
"textFindAndReplace": "Find and Replace",
|
"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;
|
|
@ -22,3 +22,33 @@
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon.icon-checkbox {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 2px solid #6d6d6d;
|
||||||
|
}
|
||||||
|
label.item-checkbox input[type='checkbox']:checked ~
|
||||||
|
.icon-checkbox, label.item-checkbox input[type='checkbox']:checked ~
|
||||||
|
* .icon-checkbox, .checkbox input[type='checkbox']:checked ~
|
||||||
|
i, label.item-checkbox input[type='checkbox']:indeterminate ~
|
||||||
|
.icon-checkbox, label.item-checkbox input[type='checkbox']:indeterminate ~ * .icon-checkbox, .checkbox input[type='checkbox']:indeterminate ~ i{
|
||||||
|
border-color: #40865c;
|
||||||
|
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: #40865c;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
.device-android .button-raised{
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
|
@ -474,4 +474,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
label.item-content input[type=checkbox]:checked~.item-inner,
|
||||||
|
label.item-content input[type=radio]: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: 90% center;
|
||||||
|
background-size: 13px 10px;
|
||||||
|
}
|
||||||
|
.icon.icon-checkbox{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import CellEditor from '../controller/CellEditor';
|
||||||
import Statusbar from '../controller/Statusbar';
|
import Statusbar from '../controller/Statusbar';
|
||||||
import AddOptions from "../view/add/Add";
|
import AddOptions from "../view/add/Add";
|
||||||
import EditOptions from "../view/edit/Edit";
|
import EditOptions from "../view/edit/Edit";
|
||||||
|
import FilterOptionsController from "../controller/FilterOptions";
|
||||||
import { Search, SearchSettings } from '../controller/Search';
|
import { Search, SearchSettings } from '../controller/Search';
|
||||||
import { f7 } from 'framework7-react';
|
import { f7 } from 'framework7-react';
|
||||||
|
|
||||||
|
@ -110,6 +111,11 @@ class MainPage extends Component {
|
||||||
!this.state.collaborationVisible ? null :
|
!this.state.collaborationVisible ? null :
|
||||||
<CollaborationView onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
|
<CollaborationView onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
<FilterOptionsController />
|
||||||
|
}
|
||||||
|
|
||||||
<Statusbar />
|
<Statusbar />
|
||||||
|
|
||||||
<FunctionGroups /> {/* hidden component*/}
|
<FunctionGroups /> {/* hidden component*/}
|
||||||
|
|
93
apps/spreadsheeteditor/mobile/src/view/FilterOptions.jsx
Normal file
93
apps/spreadsheeteditor/mobile/src/view/FilterOptions.jsx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
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 $clearFilter = $$("#button-clear-filter");
|
||||||
|
props.isValid ? $clearFilter.addClass('disabled') : $clearFilter.removeClass('disabled');
|
||||||
|
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="item-link button-raised" id='button-clear-filter' onClick={HandleClearFilter}>{_t.textClearFilter}</ListButton>
|
||||||
|
<ListButton color="red" onClick={() => props.onDeleteFilter()} id="btn-delete-filter">{_t.textDeleteFilter}</ListButton>
|
||||||
|
</List>
|
||||||
|
<List>
|
||||||
|
<ListItem onChange={e => props.onUpdateCell('all', e.target.checked)} name='filter-cellAll' checkbox checked={all}>Select All</ListItem>
|
||||||
|
{props.listVal.map((value) =>
|
||||||
|
<ListItem 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