Filter Options

This commit is contained in:
ShimaginAndrey 2021-05-23 18:27:21 +03:00
parent 9ad620c727
commit 10927e6f63
7 changed files with 281 additions and 2 deletions

View file

@ -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()

View file

@ -406,7 +406,13 @@
"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",
"textEmptyItem": "{Blanks}",
"textErrorTitle": "Warning",
"textErrorMsg": "You must choose at least one value"
},
"Settings": {
"textFindAndReplace": "Find and Replace",

View 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;

View file

@ -21,4 +21,34 @@
align-items: center;
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;
}

View file

@ -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;
}
}

View file

@ -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,11 @@ class MainPage extends Component {
!this.state.collaborationVisible ? null :
<CollaborationView onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
}
{
<FilterOptionsController />
}
<Statusbar />
<FunctionGroups /> {/* hidden component*/}

View 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;