snapshot of https://github.com/dream-num/univer.git (dev, 2026-09-16; blobs > 131072B omitted)
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { ITableFilterColorItem, ITableFilterColorList } from '../../types';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import { clsx, resetButtonClassName } from '@univerjs/design';
|
||||
import { AIcon, BanIcon, CheckMarkIcon } from '@univerjs/icons';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
|
||||
interface IMobileSheetTableColorFilterPanelProps {
|
||||
colors: ITableFilterColorList;
|
||||
onChange: (colors: ITableFilterColorList) => void;
|
||||
}
|
||||
|
||||
function MobileColorOption(props: {
|
||||
item: ITableFilterColorItem;
|
||||
label: string;
|
||||
textColor?: boolean;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const { item, label, textColor = false, onClick } = props;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={item.color ?? label}
|
||||
aria-pressed={item.checked}
|
||||
className={clsx(resetButtonClassName, `
|
||||
univer-relative univer-flex univer-size-12 univer-items-center univer-justify-center univer-rounded-full
|
||||
univer-border univer-border-solid univer-border-gray-200 univer-bg-gray-0
|
||||
dark:!univer-border-gray-600 dark:!univer-bg-gray-800
|
||||
`)}
|
||||
style={!textColor && item.color ? { backgroundColor: item.color } : undefined}
|
||||
onClick={onClick}
|
||||
>
|
||||
{!item.color && <BanIcon className="univer-size-7" />}
|
||||
{textColor && item.color && <AIcon className="univer-size-7" style={{ color: item.color }} />}
|
||||
{item.checked && (
|
||||
<span
|
||||
className="
|
||||
univer-absolute -univer-bottom-1 -univer-right-1 univer-flex univer-size-5 univer-items-center
|
||||
univer-justify-center univer-rounded-full univer-bg-primary-600 univer-text-gray-0
|
||||
"
|
||||
>
|
||||
<CheckMarkIcon className="univer-size-3" />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function MobileSheetTableColorFilterPanel(props: IMobileSheetTableColorFilterPanelProps) {
|
||||
const { colors, onChange } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const fillLabel = localeService.t<LocaleKey>('sheets-table-ui.filter.filter-by-cell-fill-color');
|
||||
const textLabel = localeService.t<LocaleKey>('sheets-table-ui.filter.filter-by-cell-text-color');
|
||||
const hasMultipleColors = colors.cellFillColors.length > 1 || colors.cellTextColors.length > 1;
|
||||
|
||||
function toggleColor(item: ITableFilterColorItem, fillColor: boolean) {
|
||||
const key = fillColor ? 'cellFillColors' : 'cellTextColors';
|
||||
const otherKey = fillColor ? 'cellTextColors' : 'cellFillColors';
|
||||
onChange({
|
||||
...colors,
|
||||
[key]: colors[key].map((color) => color.color === item.color
|
||||
? { ...color, checked: !color.checked }
|
||||
: color),
|
||||
[otherKey]: colors[otherKey].map((color) => ({ ...color, checked: false })),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-u-comp="mobile-sheet-table-filter-by-colors" className="univer-grid univer-gap-6">
|
||||
{colors.cellFillColors.length > 1 && (
|
||||
<section>
|
||||
<h3 className="univer-m-0 univer-mb-4 univer-text-base univer-font-medium">{fillLabel}</h3>
|
||||
<div className="univer-flex univer-flex-wrap univer-gap-4">
|
||||
{colors.cellFillColors.map((item) => (
|
||||
<MobileColorOption
|
||||
key={item.color ?? 'none'}
|
||||
item={item}
|
||||
label={fillLabel}
|
||||
onClick={() => toggleColor(item, true)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
{colors.cellTextColors.length > 1 && (
|
||||
<section>
|
||||
<h3 className="univer-m-0 univer-mb-4 univer-text-base univer-font-medium">{textLabel}</h3>
|
||||
<div className="univer-flex univer-flex-wrap univer-gap-4">
|
||||
{colors.cellTextColors.map((item) => (
|
||||
<MobileColorOption
|
||||
key={item.color ?? 'none'}
|
||||
item={item}
|
||||
label={textLabel}
|
||||
textColor
|
||||
onClick={() => toggleColor(item, false)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
{!hasMultipleColors && (
|
||||
<div
|
||||
className="
|
||||
univer-flex univer-min-h-40 univer-items-center univer-justify-center univer-text-gray-500
|
||||
"
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.filter-by-color-none')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ITableFilterItem } from '@univerjs/sheets-table';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type {
|
||||
IConditionCompareTypeEnum,
|
||||
IConditionExpect,
|
||||
IConditionInfo,
|
||||
ITableConditionTypeEnumWithoutLogic,
|
||||
} from './type';
|
||||
import { Injector, LocaleService } from '@univerjs/core';
|
||||
import {
|
||||
borderClassName,
|
||||
CascaderList,
|
||||
clsx,
|
||||
DatePicker,
|
||||
DateRangePicker,
|
||||
Input,
|
||||
InputNumber,
|
||||
MobileDropdown,
|
||||
MobileSelect,
|
||||
} from '@univerjs/design';
|
||||
import { MoreDownIcon } from '@univerjs/icons';
|
||||
import { TableConditionTypeEnum, TableDateCompareTypeEnum, TableStringCompareTypeEnum } from '@univerjs/sheets-table';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useState } from 'react';
|
||||
import { ConditionSubComponentEnum } from './type';
|
||||
import { datePickerSet, getCascaderListOptions, getConditionDateSelect, getSubComponentType } from './util';
|
||||
|
||||
interface IMobileSheetTableConditionPanelProps {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
tableFilter: ITableFilterItem | undefined;
|
||||
tableId: string;
|
||||
columnIndex: number;
|
||||
conditionInfo: IConditionInfo;
|
||||
onChange: (conditionInfo: IConditionInfo) => void;
|
||||
}
|
||||
|
||||
export function MobileSheetTableConditionPanel(props: IMobileSheetTableConditionPanelProps) {
|
||||
const { conditionInfo, onChange } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const injector = useDependency(Injector);
|
||||
const [conditionVisible, setConditionVisible] = useState(false);
|
||||
const [defaultDate] = useState(() => new Date());
|
||||
const cascaderOptions = getCascaderListOptions(injector);
|
||||
|
||||
function handleConditionInfo(
|
||||
info: IConditionExpect,
|
||||
type?: ITableConditionTypeEnumWithoutLogic,
|
||||
compare?: IConditionCompareTypeEnum
|
||||
) {
|
||||
onChange({
|
||||
type: type ?? conditionInfo.type,
|
||||
compare: compare ?? conditionInfo.compare,
|
||||
info,
|
||||
});
|
||||
}
|
||||
|
||||
function handleChange(value: string[]) {
|
||||
const type = value[0] as ITableConditionTypeEnumWithoutLogic;
|
||||
const compare = value[1] as IConditionCompareTypeEnum;
|
||||
if (compare) {
|
||||
setConditionVisible(false);
|
||||
}
|
||||
|
||||
const info: IConditionExpect = {};
|
||||
if (type === TableConditionTypeEnum.Date) {
|
||||
if (compare === TableDateCompareTypeEnum.Quarter) {
|
||||
info.dateSelect = TableDateCompareTypeEnum.Q1;
|
||||
} else if (compare === TableDateCompareTypeEnum.Month) {
|
||||
info.dateSelect = TableDateCompareTypeEnum.M1;
|
||||
} else if (datePickerSet.has(compare)) {
|
||||
info.date = new Date();
|
||||
} else {
|
||||
info.dateRange = [new Date(), new Date()];
|
||||
}
|
||||
} else if (type === TableConditionTypeEnum.Number) {
|
||||
info.number = 0;
|
||||
} else if (type === TableConditionTypeEnum.String) {
|
||||
info.string = '';
|
||||
}
|
||||
handleConditionInfo(info, type, compare ?? TableStringCompareTypeEnum.Equal);
|
||||
}
|
||||
|
||||
const subComponentType = getSubComponentType(conditionInfo.type, conditionInfo.compare);
|
||||
const selectType = conditionInfo.compare
|
||||
? `${localeService.t(`sheets-table-ui.condition.${conditionInfo.type}`)} - ${localeService.t(`sheets-table-ui.${conditionInfo.type}.compare.${conditionInfo.compare}`)}`
|
||||
: localeService.t(`sheets-table-ui.condition.${conditionInfo.type}`);
|
||||
const conditionDateOptions = getConditionDateSelect(injector, conditionInfo.compare as TableDateCompareTypeEnum);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MobileDropdown
|
||||
align="start"
|
||||
open={conditionVisible}
|
||||
onOpenChange={setConditionVisible}
|
||||
overlay={(
|
||||
<CascaderList
|
||||
value={[conditionInfo.type, conditionInfo.compare!]}
|
||||
options={cascaderOptions}
|
||||
onChange={handleChange}
|
||||
contentClassName="univer-flex-1"
|
||||
wrapperClassName="!univer-h-[150px]"
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(`
|
||||
univer-box-border univer-flex univer-h-8 univer-w-full univer-items-center univer-justify-between
|
||||
univer-rounded-md univer-bg-gray-0 univer-px-2 univer-text-sm univer-transition-colors
|
||||
univer-duration-200
|
||||
hover:univer-border-primary-600
|
||||
focus:univer-border-primary-600 focus:univer-outline-none focus:univer-ring-2
|
||||
dark:!univer-bg-gray-700 dark:!univer-text-gray-0
|
||||
`, borderClassName)}
|
||||
>
|
||||
<span>{selectType}</span>
|
||||
<MoreDownIcon />
|
||||
</div>
|
||||
</MobileDropdown>
|
||||
|
||||
<div className="univer-mt-3 univer-w-full">
|
||||
{subComponentType === ConditionSubComponentEnum.Input && (
|
||||
<>
|
||||
{conditionInfo.type === TableConditionTypeEnum.String
|
||||
? (
|
||||
<Input
|
||||
className="univer-w-full"
|
||||
placeholder={localeService.t<LocaleKey>('sheets-table-ui.filter.input-values-placeholder')}
|
||||
value={conditionInfo.info.string}
|
||||
onChange={(value) => handleConditionInfo({ string: value })}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<InputNumber
|
||||
className="univer-h-7 univer-w-full"
|
||||
value={conditionInfo.info.number}
|
||||
controls={false}
|
||||
onChange={(value) => {
|
||||
if (value !== null) {
|
||||
handleConditionInfo({ number: value });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{subComponentType === ConditionSubComponentEnum.DatePicker && (
|
||||
<div id="univer-table-date-picker-wrapper">
|
||||
<DatePicker
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.date ?? defaultDate}
|
||||
onValueChange={(value) => handleConditionInfo({ date: value })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{subComponentType === ConditionSubComponentEnum.DateRange && (
|
||||
<div id="univer-table-date-range-wrapper">
|
||||
<DateRangePicker
|
||||
className="univer-w-full"
|
||||
value={[
|
||||
conditionInfo.info.dateRange?.[0] ?? defaultDate,
|
||||
conditionInfo.info.dateRange?.[1] ?? defaultDate,
|
||||
]}
|
||||
onValueChange={(value) => handleConditionInfo(value ? { dateRange: value } : {})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{subComponentType === ConditionSubComponentEnum.Inputs && (
|
||||
<div className="univer-flex univer-items-center univer-gap-2">
|
||||
<InputNumber
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.numberRange?.[0]}
|
||||
controls={false}
|
||||
onChange={(value) => {
|
||||
if (value !== null) {
|
||||
handleConditionInfo({ numberRange: [value, conditionInfo.info.numberRange?.[1]] });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span> - </span>
|
||||
<InputNumber
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.numberRange?.[1]}
|
||||
controls={false}
|
||||
onChange={(value) => {
|
||||
if (value !== null) {
|
||||
handleConditionInfo({ numberRange: [conditionInfo.info.numberRange?.[0], value] });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{subComponentType === ConditionSubComponentEnum.Select && (
|
||||
<MobileSelect
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.dateSelect ?? conditionDateOptions[0].value}
|
||||
options={conditionDateOptions}
|
||||
onChange={(value) => handleConditionInfo({ dateSelect: value })}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISortRangeCommandParams } from '@univerjs/sheets-sort';
|
||||
import type {
|
||||
ITableColorFilterItem,
|
||||
ITableConditionFilterItem,
|
||||
ITableManualFilterItem,
|
||||
} from '@univerjs/sheets-table';
|
||||
import type { MobileDrawerSnap } from '@univerjs/ui';
|
||||
import type { ITableFilterPanelInfo } from '../../controllers/sheet-table-component.controller';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { ITableFilterColorList } from '../../types';
|
||||
import type { IConditionInfo } from './type';
|
||||
import { generateRandomId, ICommandService, IPermissionService, IUndoRedoService, LocaleService } from '@univerjs/core';
|
||||
import { Button, MobileActionRow, MobileActionRowGroup, resetButtonClassName } from '@univerjs/design';
|
||||
import {
|
||||
AscendingIcon,
|
||||
DeleteColumnDoubleIcon,
|
||||
DescendingIcon,
|
||||
LeftInsertColumnDoubleIcon,
|
||||
MoreLeftIcon,
|
||||
MoreRightIcon,
|
||||
RightInsertColumnDoubleIcon,
|
||||
} from '@univerjs/icons';
|
||||
import { WorkbookEditablePermission } from '@univerjs/sheets';
|
||||
import { SortRangeCommand, SortType } from '@univerjs/sheets-sort';
|
||||
import {
|
||||
SetSheetTableSortStateCommand,
|
||||
SheetsTableSortStateEnum,
|
||||
SheetTableInsertColumnAtCommand,
|
||||
SheetTableRemoveColumnAtCommand,
|
||||
TABLE_FILTER_EMPTY_VALUE,
|
||||
TableColumnFilterTypeEnum,
|
||||
TableDateCompareTypeEnum,
|
||||
TableManager,
|
||||
} from '@univerjs/sheets-table';
|
||||
import { MobileDrawer, useDependency } from '@univerjs/ui';
|
||||
import { useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { SheetsTableMobileComponentController } from '../../controllers/mobile/sheet-table-component.controller';
|
||||
import { SheetsTableUiService } from '../../services/sheets-table-ui.service';
|
||||
import { FilterByEnum } from '../../types';
|
||||
import { MobileSheetTableColorFilterPanel } from './MobileSheetTableColorFilterPanel';
|
||||
import { MobileSheetTableConditionPanel } from './MobileSheetTableConditionPanel';
|
||||
import { MobileSheetTableItemsFilterPanel } from './MobileSheetTableItemsFilterPanel';
|
||||
import { getInitConditionInfo } from './util';
|
||||
|
||||
const FILTER_BY_OPTIONS: Array<{ label: LocaleKey; value: FilterByEnum }> = [
|
||||
{ label: 'sheets-table-ui.filter.by-values', value: FilterByEnum.Items },
|
||||
{ label: 'sheets-table-ui.filter.by-colors', value: FilterByEnum.Color },
|
||||
{ label: 'sheets-table-ui.filter.by-conditions', value: FilterByEnum.Condition },
|
||||
];
|
||||
|
||||
export function MobileSheetTableFilterPanel() {
|
||||
const sheetsTableComponentController = useDependency(SheetsTableMobileComponentController);
|
||||
const tableFilterPanelInfo = sheetsTableComponentController.getCurrentTableFilterInfo();
|
||||
if (!tableFilterPanelInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <MobileSheetTableFilterPanelContent tableFilterPanelInfo={tableFilterPanelInfo} />;
|
||||
}
|
||||
|
||||
interface IMobileSheetTableFilterPanelContentProps {
|
||||
tableFilterPanelInfo: ITableFilterPanelInfo;
|
||||
}
|
||||
|
||||
function MobileSheetTableFilterPanelContent(props: IMobileSheetTableFilterPanelContentProps) {
|
||||
const { tableFilterPanelInfo } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const tableUiService = useDependency(SheetsTableUiService);
|
||||
const tableManager = useDependency(TableManager);
|
||||
const commandService = useDependency(ICommandService);
|
||||
const permissionService = useDependency(IPermissionService);
|
||||
const undoRedoService = useDependency(IUndoRedoService);
|
||||
const sheetsTableComponentController = useDependency(SheetsTableMobileComponentController);
|
||||
const [detailFilterBy, setDetailFilterBy] = useState<FilterByEnum | null>(null);
|
||||
const [drawerSnap, setDrawerSnap] = useState<MobileDrawerSnap>('expanded');
|
||||
const layerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const panelProps = tableUiService.getTableFilterPanelInitProps(
|
||||
tableFilterPanelInfo.unitId,
|
||||
tableFilterPanelInfo.subUnitId,
|
||||
tableFilterPanelInfo.tableId,
|
||||
tableFilterPanelInfo.column
|
||||
);
|
||||
const { unitId, subUnitId, tableId, tableFilter, columnIndex } = panelProps;
|
||||
const currentTable = tableManager.getTable(unitId, tableId)!;
|
||||
|
||||
const items = tableUiService.getTableFilterItems(unitId, subUnitId, tableId, columnIndex);
|
||||
const checkedItems = tableUiService.getTableFilterCheckedItems(unitId, tableId, columnIndex);
|
||||
const [checkedItemSet, setCheckedItemSet] = useState<Set<string>>(() => new Set(
|
||||
tableFilter?.filterType === TableColumnFilterTypeEnum.manual
|
||||
? checkedItems
|
||||
: items.data.map((item) => item.title)
|
||||
));
|
||||
const [conditionInfo, setConditionInfo] = useState<IConditionInfo>(
|
||||
() => getInitConditionInfo(tableFilter) as IConditionInfo
|
||||
);
|
||||
const [colors, setColors] = useState<ITableFilterColorList>(
|
||||
() => tableUiService.getTableFilterColors(unitId, subUnitId, tableId, columnIndex)
|
||||
);
|
||||
const tableRange = currentTable.getRange();
|
||||
const canDeleteColumn = tableRange.endColumn > tableRange.startColumn;
|
||||
const absoluteColumn = tableFilterPanelInfo.column;
|
||||
const editable = permissionService.getPermissionPoint(new WorkbookEditablePermission(unitId).id)?.value;
|
||||
|
||||
function closePanel() {
|
||||
sheetsTableComponentController.closeFilterPanel();
|
||||
}
|
||||
|
||||
async function applySort(ascending: boolean) {
|
||||
const range = currentTable.getTableFilterRange();
|
||||
const undoRedoGroup = undoRedoService.beginUndoRedoGroup(unitId, generateRandomId(), 'append');
|
||||
try {
|
||||
const success = await commandService.executeCommand<ISortRangeCommandParams>(SortRangeCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
range,
|
||||
orderRules: [{ colIndex: columnIndex + range.startColumn, type: ascending ? SortType.ASC : SortType.DESC }],
|
||||
hasTitle: false,
|
||||
});
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sortStateSet = await commandService.executeCommand(SetSheetTableSortStateCommand.id, {
|
||||
unitId,
|
||||
tableId,
|
||||
sortInfo: {
|
||||
columnIndex,
|
||||
sortState: ascending ? SheetsTableSortStateEnum.Asc : SheetsTableSortStateEnum.Desc,
|
||||
},
|
||||
});
|
||||
if (sortStateSet) {
|
||||
closePanel();
|
||||
}
|
||||
} finally {
|
||||
undoRedoGroup.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
async function insertColumn(side: 'left' | 'right') {
|
||||
const success = await commandService.executeCommand(SheetTableInsertColumnAtCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
tableId,
|
||||
index: side === 'left' ? absoluteColumn : absoluteColumn + 1,
|
||||
count: 1,
|
||||
});
|
||||
if (success) {
|
||||
closePanel();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteColumn() {
|
||||
if (!canDeleteColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await commandService.executeCommand(SheetTableRemoveColumnAtCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
tableId,
|
||||
index: absoluteColumn,
|
||||
count: 1,
|
||||
});
|
||||
if (success) {
|
||||
closePanel();
|
||||
}
|
||||
}
|
||||
|
||||
async function applyFilter() {
|
||||
let filter: ITableColorFilterItem | ITableConditionFilterItem | ITableManualFilterItem | undefined;
|
||||
if (detailFilterBy === FilterByEnum.Items) {
|
||||
const emptyLabel = localeService.t<LocaleKey>('sheets-table-ui.condition.empty');
|
||||
const values = items.data
|
||||
.filter((item) => checkedItemSet.has(item.title))
|
||||
.map((item) => item.title === emptyLabel ? TABLE_FILTER_EMPTY_VALUE : item.title);
|
||||
filter = values.length === items.data.length
|
||||
? undefined
|
||||
: { filterType: TableColumnFilterTypeEnum.manual, values } satisfies ITableManualFilterItem;
|
||||
} else if (detailFilterBy === FilterByEnum.Color) {
|
||||
const cellFillColors = colors.cellFillColors.filter((item) => item.checked).map((item) => item.color);
|
||||
const cellTextColors = colors.cellTextColors.filter((item) => item.checked).map((item) => item.color);
|
||||
if (cellFillColors.length) {
|
||||
filter = { filterType: TableColumnFilterTypeEnum.color, cellFillColors } satisfies ITableColorFilterItem;
|
||||
} else if (cellTextColors.length) {
|
||||
filter = { filterType: TableColumnFilterTypeEnum.color, cellTextColors } satisfies ITableColorFilterItem;
|
||||
}
|
||||
} else if (detailFilterBy === FilterByEnum.Condition) {
|
||||
const dynamicCompare = conditionInfo.compare === TableDateCompareTypeEnum.Quarter
|
||||
|| conditionInfo.compare === TableDateCompareTypeEnum.Month;
|
||||
filter = {
|
||||
filterType: TableColumnFilterTypeEnum.condition,
|
||||
filterInfo: {
|
||||
conditionType: conditionInfo.type,
|
||||
compareType: dynamicCompare
|
||||
? Object.values(conditionInfo.info)[0] as IConditionInfo['compare']
|
||||
: conditionInfo.compare,
|
||||
expectedValue: dynamicCompare ? undefined : Object.values(conditionInfo.info)[0],
|
||||
} as ITableConditionFilterItem['filterInfo'],
|
||||
} satisfies ITableConditionFilterItem;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await tableUiService.setTableFilter(unitId, tableId, columnIndex, filter);
|
||||
if (success) {
|
||||
closePanel();
|
||||
}
|
||||
}
|
||||
|
||||
async function clearFilter() {
|
||||
const success = await tableUiService.setTableFilter(unitId, tableId, columnIndex, undefined);
|
||||
if (success) {
|
||||
closePanel();
|
||||
}
|
||||
}
|
||||
|
||||
function openDetail(filterBy: FilterByEnum) {
|
||||
if (filterBy === FilterByEnum.Items) {
|
||||
setCheckedItemSet(new Set(
|
||||
tableFilter?.filterType === TableColumnFilterTypeEnum.manual
|
||||
? checkedItems
|
||||
: items.data.map((item) => item.title)
|
||||
));
|
||||
} else if (filterBy === FilterByEnum.Color) {
|
||||
setColors(tableUiService.getTableFilterColors(unitId, subUnitId, tableId, columnIndex));
|
||||
} else if (filterBy === FilterByEnum.Condition) {
|
||||
setConditionInfo(getInitConditionInfo(tableFilter) as IConditionInfo);
|
||||
}
|
||||
setDetailFilterBy(filterBy);
|
||||
setDrawerSnap('expanded');
|
||||
}
|
||||
|
||||
function closeDetail() {
|
||||
setDetailFilterBy(null);
|
||||
}
|
||||
|
||||
const detailOption = FILTER_BY_OPTIONS.find((option) => option.value === detailFilterBy);
|
||||
const detailTitle = detailOption ? localeService.t<LocaleKey>(detailOption.label) : '';
|
||||
const detailDrawer = detailFilterBy
|
||||
? createPortal(
|
||||
<div ref={layerRef} className="univer-pointer-events-none univer-fixed univer-inset-0 univer-z-[1300]">
|
||||
<MobileDrawer
|
||||
layerRef={layerRef}
|
||||
componentName="mobile-sheet-table-filter-detail"
|
||||
openMode="push"
|
||||
snap={drawerSnap}
|
||||
expandLabel={localeService.t<LocaleKey>('sheets-table-ui.filter.resize')}
|
||||
collapseLabel={localeService.t<LocaleKey>('sheets-table-ui.filter.resize')}
|
||||
onSnapChange={setDrawerSnap}
|
||||
onClose={closeDetail}
|
||||
role="dialog"
|
||||
ariaLabel={detailTitle}
|
||||
panelClassName="univer-pointer-events-auto"
|
||||
contentClassName="univer-min-h-0 univer-px-4 univer-pb-4"
|
||||
header={(
|
||||
<header
|
||||
className="
|
||||
univer-grid univer-h-12 univer-flex-1 univer-grid-cols-[48px_1fr_48px] univer-items-center
|
||||
"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={localeService.t<LocaleKey>('sheets-table-ui.filter.back')}
|
||||
className={`
|
||||
${resetButtonClassName}
|
||||
univer-flex univer-size-12 univer-items-center univer-justify-center
|
||||
`}
|
||||
onClick={closeDetail}
|
||||
>
|
||||
<MoreLeftIcon className="univer-size-6" />
|
||||
</button>
|
||||
<div className="univer-truncate univer-text-center univer-text-base univer-font-semibold">
|
||||
{detailTitle}
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
footer={(
|
||||
<footer
|
||||
className="
|
||||
univer-shrink-0 univer-border-0 univer-border-t univer-border-solid univer-border-gray-200
|
||||
univer-p-4
|
||||
dark:!univer-border-gray-700
|
||||
"
|
||||
>
|
||||
<MobileActionRowGroup>
|
||||
<Button onClick={closeDetail}>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.cancel')}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={() => applyFilter().catch(() => undefined)}>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.confirm')}
|
||||
</Button>
|
||||
</MobileActionRowGroup>
|
||||
</footer>
|
||||
)}
|
||||
>
|
||||
{detailFilterBy === FilterByEnum.Items && (
|
||||
<MobileSheetTableItemsFilterPanel
|
||||
items={items}
|
||||
checkedItemSet={checkedItemSet}
|
||||
setCheckedItemSet={setCheckedItemSet}
|
||||
/>
|
||||
)}
|
||||
{detailFilterBy === FilterByEnum.Color && (
|
||||
<MobileSheetTableColorFilterPanel colors={colors} onChange={setColors} />
|
||||
)}
|
||||
{detailFilterBy === FilterByEnum.Condition && (
|
||||
<MobileSheetTableConditionPanel
|
||||
tableFilter={tableFilter}
|
||||
unitId={unitId}
|
||||
subUnitId={subUnitId}
|
||||
tableId={tableId}
|
||||
columnIndex={columnIndex}
|
||||
conditionInfo={conditionInfo}
|
||||
onChange={setConditionInfo}
|
||||
/>
|
||||
)}
|
||||
</MobileDrawer>
|
||||
</div>,
|
||||
document.body
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="univer-flex univer-flex-col univer-gap-2">
|
||||
{editable && (
|
||||
<>
|
||||
<MobileActionRow
|
||||
variant="subtle"
|
||||
icon={<LeftInsertColumnDoubleIcon />}
|
||||
title={localeService.t<LocaleKey>('sheets-table-ui.columnMenu.insert-left')}
|
||||
onClick={() => insertColumn('left').catch(() => undefined)}
|
||||
/>
|
||||
<MobileActionRow
|
||||
variant="subtle"
|
||||
icon={<RightInsertColumnDoubleIcon />}
|
||||
title={localeService.t<LocaleKey>('sheets-table-ui.columnMenu.insert-right')}
|
||||
onClick={() => insertColumn('right').catch(() => undefined)}
|
||||
/>
|
||||
<MobileActionRow
|
||||
variant="subtle"
|
||||
icon={<DeleteColumnDoubleIcon />}
|
||||
title={localeService.t<LocaleKey>('sheets-table-ui.columnMenu.delete')}
|
||||
disabled={!canDeleteColumn}
|
||||
onClick={() => deleteColumn().catch(() => undefined)}
|
||||
/>
|
||||
<MobileActionRow
|
||||
variant="subtle"
|
||||
icon={<AscendingIcon />}
|
||||
title={localeService.t<LocaleKey>('sheets-table-ui.sort.sort-asc')}
|
||||
onClick={() => applySort(true).catch(() => undefined)}
|
||||
/>
|
||||
<MobileActionRow
|
||||
variant="subtle"
|
||||
icon={<DescendingIcon />}
|
||||
title={localeService.t<LocaleKey>('sheets-table-ui.sort.sort-desc')}
|
||||
onClick={() => applySort(false).catch(() => undefined)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{FILTER_BY_OPTIONS.map((option) => (
|
||||
<MobileActionRow
|
||||
key={option.value}
|
||||
variant="subtle"
|
||||
title={localeService.t<LocaleKey>(option.label)}
|
||||
trailing={<MoreRightIcon className="univer-size-5 univer-text-gray-400" />}
|
||||
onClick={() => openDetail(option.value)}
|
||||
/>
|
||||
))}
|
||||
<MobileActionRow
|
||||
variant="subtle"
|
||||
title={localeService.t<LocaleKey>('sheets-table-ui.filter.clear-filter')}
|
||||
disabled={!tableFilter}
|
||||
onClick={() => clearFilter().catch(() => undefined)}
|
||||
/>
|
||||
</div>
|
||||
{detailDrawer}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { ITableFilterItemList } from '../../types';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import { Checkbox, Input } from '@univerjs/design';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
interface IMobileSheetTableItemsFilterPanelProps {
|
||||
items: ITableFilterItemList;
|
||||
checkedItemSet: Set<string>;
|
||||
setCheckedItemSet: Dispatch<SetStateAction<Set<string>>>;
|
||||
}
|
||||
|
||||
export function MobileSheetTableItemsFilterPanel(props: IMobileSheetTableItemsFilterPanelProps) {
|
||||
const { items, checkedItemSet, setCheckedItemSet } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const displayItems = useMemo(() => {
|
||||
const normalizedSearchText = searchText.toLowerCase();
|
||||
return normalizedSearchText
|
||||
? items.data.filter((item) => String(item.title).toLowerCase().includes(normalizedSearchText))
|
||||
: items.data;
|
||||
}, [items.data, searchText]);
|
||||
const allDisplayedChecked = displayItems.length > 0
|
||||
&& displayItems.every((item) => checkedItemSet.has(item.title));
|
||||
const someDisplayedChecked = displayItems.some((item) => checkedItemSet.has(item.title));
|
||||
|
||||
function toggleAllDisplayed() {
|
||||
setCheckedItemSet((current) => {
|
||||
const next = new Set(current);
|
||||
for (const item of displayItems) {
|
||||
if (allDisplayedChecked) {
|
||||
next.delete(item.title);
|
||||
} else {
|
||||
next.add(item.title);
|
||||
}
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
function toggleItem(title: string) {
|
||||
setCheckedItemSet((current) => {
|
||||
const next = new Set(current);
|
||||
if (next.has(title)) {
|
||||
next.delete(title);
|
||||
} else {
|
||||
next.add(title);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-u-comp="mobile-sheet-table-filter-by-values" className="univer-grid univer-gap-3">
|
||||
<Input
|
||||
value={searchText}
|
||||
placeholder={localeService.t<LocaleKey>('sheets-table-ui.filter.search-placeholder')}
|
||||
onChange={setSearchText}
|
||||
/>
|
||||
<div
|
||||
className="
|
||||
univer-overflow-hidden univer-rounded-xl univer-bg-gray-0
|
||||
dark:!univer-bg-gray-800
|
||||
"
|
||||
>
|
||||
<div
|
||||
className="
|
||||
univer-border-0 univer-border-b univer-border-solid univer-border-gray-200 univer-px-4
|
||||
dark:!univer-border-gray-700
|
||||
"
|
||||
>
|
||||
<Checkbox
|
||||
className="univer-flex univer-min-h-12 univer-items-center"
|
||||
checked={allDisplayedChecked}
|
||||
indeterminate={!allDisplayedChecked && someDisplayedChecked}
|
||||
disabled={displayItems.length === 0}
|
||||
onChange={toggleAllDisplayed}
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.select-all')}
|
||||
</Checkbox>
|
||||
</div>
|
||||
{displayItems.map((item) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="
|
||||
univer-border-0 univer-border-b univer-border-solid univer-border-gray-200 univer-px-4
|
||||
last:univer-border-b-0
|
||||
dark:!univer-border-gray-700
|
||||
"
|
||||
>
|
||||
<Checkbox
|
||||
className="univer-flex univer-min-h-12 univer-items-center"
|
||||
checked={checkedItemSet.has(item.title)}
|
||||
onChange={() => toggleItem(item.title)}
|
||||
>
|
||||
<span className="univer-flex univer-min-w-0 univer-flex-1 univer-items-center univer-gap-2">
|
||||
<span className="univer-min-w-0 univer-flex-1 univer-truncate">{item.title}</span>
|
||||
<span className="univer-shrink-0 univer-text-gray-400">
|
||||
{`(${items.itemsCountMap.get(item.title) ?? 0})`}
|
||||
</span>
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISheetTableMenuExtraProps, SheetTableMenuAction } from './SheetTableMenu';
|
||||
import { MobileActionRow } from '@univerjs/design';
|
||||
|
||||
const ACTIONS: SheetTableMenuAction[] = ['rename', 'update-range', 'set-theme', 'delete'];
|
||||
|
||||
export function MobileSheetTableMenu({ menu }: { menu?: ISheetTableMenuExtraProps }) {
|
||||
if (!menu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="univer-flex univer-flex-col univer-gap-2">
|
||||
{ACTIONS.map((action) => (
|
||||
<MobileActionRow
|
||||
key={action}
|
||||
title={menu.labels[action]}
|
||||
aria-label={menu.labels[action]}
|
||||
variant="subtle"
|
||||
onClick={() => menu.onSelect(action)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISheetTableRenameDialogProps } from './SheetTableRenameDialog';
|
||||
import { MobileActionRowGroup } from '@univerjs/design';
|
||||
import { SheetTableRenameDialog } from './SheetTableRenameDialog';
|
||||
|
||||
export function MobileSheetTableRenameDialog(props: ISheetTableRenameDialogProps) {
|
||||
return <SheetTableRenameDialog {...props} ActionRowComponent={MobileActionRowGroup} />;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISheetTableSelectorProps } from './SheetTableSelector';
|
||||
import { MobileActionRowGroup } from '@univerjs/design';
|
||||
import { SheetTableSelector } from './SheetTableSelector';
|
||||
|
||||
export function MobileSheetTableSelector(props: ISheetTableSelectorProps) {
|
||||
return <SheetTableSelector {...props} ActionRowComponent={MobileActionRowGroup} />;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISheetTableThemePanelProps } from './SheetTableThemePanel';
|
||||
import { MobileColorPicker, MobileDropdown } from '@univerjs/design';
|
||||
import { SheetTableThemePanel } from './SheetTableThemePanel';
|
||||
|
||||
export function MobileSheetTableThemePanel(props: ISheetTableThemePanelProps) {
|
||||
return (
|
||||
<SheetTableThemePanel
|
||||
{...props}
|
||||
ColorPickerComponent={MobileColorPicker}
|
||||
DropdownComponent={MobileDropdown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { ITableFilterColorItem, ITableFilterColorList } from '../../types';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import { clsx, resetButtonClassName } from '@univerjs/design';
|
||||
import { AIcon, BanIcon, SuccessIcon } from '@univerjs/icons';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
|
||||
interface ISheetTableColorFilterPanelProps {
|
||||
colors: ITableFilterColorList;
|
||||
onChange: (colors: ITableFilterColorList) => void;
|
||||
}
|
||||
|
||||
function ColorOption(props: {
|
||||
item: ITableFilterColorItem;
|
||||
label: string;
|
||||
textColor?: boolean;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const { item, label, textColor = false, onClick } = props;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={item.color ?? label}
|
||||
aria-pressed={item.checked}
|
||||
className={clsx(resetButtonClassName, `
|
||||
univer-relative univer-flex univer-size-6 univer-items-center univer-justify-center univer-rounded-full
|
||||
univer-border univer-border-solid univer-border-gray-200 univer-bg-gray-0
|
||||
hover:univer-ring-2 hover:univer-ring-offset-2
|
||||
dark:!univer-border-gray-600 dark:!univer-bg-gray-800
|
||||
`)}
|
||||
style={!textColor && item.color ? { backgroundColor: item.color } : undefined}
|
||||
onClick={onClick}
|
||||
>
|
||||
{!item.color && <BanIcon className="univer-size-5" />}
|
||||
{textColor && item.color && <AIcon className="univer-size-5" style={{ color: item.color }} />}
|
||||
{item.checked && (
|
||||
<SuccessIcon
|
||||
className="
|
||||
univer-absolute -univer-bottom-1 -univer-right-1 univer-size-3 univer-rounded-full
|
||||
univer-bg-gray-0 univer-text-green-600
|
||||
"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function SheetTableColorFilterPanel(props: ISheetTableColorFilterPanelProps) {
|
||||
const { colors, onChange } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const fillLabel = localeService.t<LocaleKey>('sheets-table-ui.filter.filter-by-cell-fill-color');
|
||||
const textLabel = localeService.t<LocaleKey>('sheets-table-ui.filter.filter-by-cell-text-color');
|
||||
const hasMultipleColors = colors.cellFillColors.length > 1 || colors.cellTextColors.length > 1;
|
||||
|
||||
function toggleColor(item: ITableFilterColorItem, fillColor: boolean) {
|
||||
const key = fillColor ? 'cellFillColors' : 'cellTextColors';
|
||||
const otherKey = fillColor ? 'cellTextColors' : 'cellFillColors';
|
||||
onChange({
|
||||
...colors,
|
||||
[key]: colors[key].map((color) => color.color === item.color
|
||||
? { ...color, checked: !color.checked }
|
||||
: color),
|
||||
[otherKey]: colors[otherKey].map((color) => ({ ...color, checked: false })),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="univer-grid univer-h-full univer-gap-4 univer-overflow-auto univer-rounded-md univer-p-2">
|
||||
{colors.cellFillColors.length > 1 && (
|
||||
<section>
|
||||
<h3 className="univer-m-0 univer-mb-2 univer-text-sm univer-font-normal">{fillLabel}</h3>
|
||||
<div className="univer-grid univer-grid-cols-8 univer-gap-2">
|
||||
{colors.cellFillColors.map((item) => (
|
||||
<ColorOption
|
||||
key={item.color ?? 'none'}
|
||||
item={item}
|
||||
label={fillLabel}
|
||||
onClick={() => toggleColor(item, true)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
{colors.cellTextColors.length > 1 && (
|
||||
<section>
|
||||
<h3 className="univer-m-0 univer-mb-2 univer-text-sm univer-font-normal">{textLabel}</h3>
|
||||
<div className="univer-grid univer-grid-cols-8 univer-gap-2">
|
||||
{colors.cellTextColors.map((item) => (
|
||||
<ColorOption
|
||||
key={item.color ?? 'none'}
|
||||
item={item}
|
||||
label={textLabel}
|
||||
textColor
|
||||
onClick={() => toggleColor(item, false)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
{!hasMultipleColors && (
|
||||
<div className="univer-flex univer-size-full univer-items-center univer-justify-center univer-text-sm">
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.filter-by-color-none')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ITableFilterItem } from '@univerjs/sheets-table';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { IConditionCompareTypeEnum, IConditionExpect, IConditionInfo, ITableConditionTypeEnumWithoutLogic } from './type';
|
||||
import { Injector, LocaleService } from '@univerjs/core';
|
||||
import { borderClassName, CascaderList, clsx, DatePicker, DateRangePicker, Dropdown, Input, InputNumber, Select } from '@univerjs/design';
|
||||
import { MoreDownIcon } from '@univerjs/icons';
|
||||
import { TableConditionTypeEnum, TableDateCompareTypeEnum, TableStringCompareTypeEnum } from '@univerjs/sheets-table';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useState } from 'react';
|
||||
import { ConditionSubComponentEnum } from './type';
|
||||
import { datePickerSet, getCascaderListOptions, getConditionDateSelect, getSubComponentType } from './util';
|
||||
|
||||
interface IConditionFilterProps {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
tableFilter: ITableFilterItem | undefined;
|
||||
tableId: string;
|
||||
columnIndex: number;
|
||||
conditionInfo: IConditionInfo;
|
||||
onChange: (conditionInfo: IConditionInfo) => void;
|
||||
}
|
||||
|
||||
export const SheetTableConditionPanel = (props: IConditionFilterProps) => {
|
||||
const { conditionInfo, onChange } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
|
||||
const [conditionVisible, setConditionVisible] = useState(false);
|
||||
|
||||
const injector = useDependency(Injector);
|
||||
|
||||
const cascaderOptions = getCascaderListOptions(injector);
|
||||
|
||||
const handleConditionInfo = (info: IConditionExpect, type?: ITableConditionTypeEnumWithoutLogic, compare?: IConditionCompareTypeEnum) => {
|
||||
onChange({
|
||||
type: type ?? conditionInfo.type,
|
||||
compare: compare ?? conditionInfo.compare,
|
||||
info,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = (value: string[]) => {
|
||||
const type = value[0] as ITableConditionTypeEnumWithoutLogic;
|
||||
const compare = value[1] as IConditionCompareTypeEnum;
|
||||
if (compare) {
|
||||
setConditionVisible(false);
|
||||
};
|
||||
const info: IConditionExpect = {};
|
||||
if (type === TableConditionTypeEnum.Date) {
|
||||
if (compare === TableDateCompareTypeEnum.Quarter) {
|
||||
info.dateSelect = TableDateCompareTypeEnum.Q1;
|
||||
} else if (compare === TableDateCompareTypeEnum.Month) {
|
||||
info.dateSelect = TableDateCompareTypeEnum.M1;
|
||||
} else if (datePickerSet.has(compare)) {
|
||||
info.date = new Date();
|
||||
} else {
|
||||
info.dateRange = [new Date(), new Date()];
|
||||
}
|
||||
} else if (type === TableConditionTypeEnum.Number) {
|
||||
info.number = 0;
|
||||
} else if (type === TableConditionTypeEnum.String) {
|
||||
info.string = '';
|
||||
}
|
||||
handleConditionInfo(info, value[0] as ITableConditionTypeEnumWithoutLogic, (value[1] as IConditionCompareTypeEnum ?? TableStringCompareTypeEnum.Equal));
|
||||
};
|
||||
|
||||
const subComponentType = getSubComponentType(conditionInfo.type, conditionInfo.compare);
|
||||
|
||||
let selectType = '';
|
||||
if (conditionInfo.compare) {
|
||||
selectType = `${localeService.t(`sheets-table-ui.condition.${conditionInfo.type}`)} - ${localeService.t(`sheets-table-ui.${conditionInfo.type}.compare.${conditionInfo.compare}`)}`;
|
||||
} else {
|
||||
selectType = localeService.t(`sheets-table-ui.condition.${conditionInfo.type}`);
|
||||
}
|
||||
|
||||
const conditionDateOptions = getConditionDateSelect(injector, conditionInfo.compare as TableDateCompareTypeEnum);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dropdown
|
||||
align="start"
|
||||
open={conditionVisible}
|
||||
onOpenChange={setConditionVisible}
|
||||
overlay={(
|
||||
<CascaderList
|
||||
value={[conditionInfo.type, conditionInfo.compare!]}
|
||||
options={cascaderOptions}
|
||||
onChange={handleChange}
|
||||
contentClassName="univer-flex-1"
|
||||
wrapperClassName="!univer-h-[150px]"
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(`
|
||||
univer-box-border univer-flex univer-h-8 univer-w-full univer-items-center univer-justify-between
|
||||
univer-rounded-md univer-bg-gray-0 univer-px-2 univer-text-sm univer-transition-colors
|
||||
univer-duration-200
|
||||
hover:univer-border-primary-600
|
||||
focus:univer-border-primary-600 focus:univer-outline-none focus:univer-ring-2
|
||||
dark:!univer-bg-gray-700 dark:!univer-text-gray-0
|
||||
`, borderClassName)}
|
||||
>
|
||||
<span>
|
||||
{selectType}
|
||||
</span>
|
||||
|
||||
<MoreDownIcon />
|
||||
</div>
|
||||
</Dropdown>
|
||||
|
||||
<div className="univer-mt-3 univer-w-full">
|
||||
{subComponentType === ConditionSubComponentEnum.Input && (
|
||||
<>
|
||||
{conditionInfo.type === TableConditionTypeEnum.String
|
||||
? (
|
||||
<Input
|
||||
className="univer-w-full"
|
||||
placeholder={localeService.t<LocaleKey>('sheets-table-ui.filter.input-values-placeholder')}
|
||||
value={conditionInfo.info.string}
|
||||
onChange={(v) => handleConditionInfo({ string: v })}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<InputNumber
|
||||
className="univer-h-7 univer-w-full"
|
||||
value={conditionInfo.info.number}
|
||||
controls={false}
|
||||
onChange={(v) => {
|
||||
if (v !== null) {
|
||||
handleConditionInfo({ number: v });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!!(subComponentType === ConditionSubComponentEnum.DatePicker) && (
|
||||
<div id="univer-table-date-picker-wrapper">
|
||||
<DatePicker
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.date ?? new Date()}
|
||||
onValueChange={(v) => handleConditionInfo({ date: v })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!!(subComponentType === ConditionSubComponentEnum.DateRange) && (
|
||||
<div id="univer-table-date-range-wrapper">
|
||||
<DateRangePicker
|
||||
className="univer-w-full"
|
||||
value={[conditionInfo.info.dateRange?.[0] ?? new Date(), conditionInfo.info.dateRange?.[1] ?? new Date()]}
|
||||
onValueChange={(v) => {
|
||||
if (v) {
|
||||
handleConditionInfo({ dateRange: v });
|
||||
} else {
|
||||
handleConditionInfo({});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{subComponentType === ConditionSubComponentEnum.Inputs && (
|
||||
<div className="univer-flex univer-items-center univer-gap-2">
|
||||
<InputNumber
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.numberRange?.[0]}
|
||||
onChange={(v) => {
|
||||
if (v !== null) {
|
||||
handleConditionInfo({ numberRange: [v, conditionInfo.info.numberRange?.[1]] });
|
||||
}
|
||||
}}
|
||||
controls={false}
|
||||
/>
|
||||
<span> - </span>
|
||||
<InputNumber
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.numberRange?.[1]}
|
||||
controls={false}
|
||||
onChange={(v) => {
|
||||
if (v !== null) {
|
||||
handleConditionInfo({ numberRange: [conditionInfo.info.numberRange?.[0], v] });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!!(subComponentType === ConditionSubComponentEnum.Select) && (
|
||||
<Select
|
||||
className="univer-w-full"
|
||||
value={conditionInfo.info.dateSelect ?? conditionDateOptions[0].value}
|
||||
options={conditionDateOptions}
|
||||
onChange={(v) => handleConditionInfo({ dateSelect: v })}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISortRangeCommandParams } from '@univerjs/sheets-sort';
|
||||
import type {
|
||||
ITableColorFilterItem,
|
||||
ITableConditionFilterItem,
|
||||
ITableManualFilterItem,
|
||||
} from '@univerjs/sheets-table';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { ITableFilterColorList } from '../../types';
|
||||
import type { IConditionInfo } from './type';
|
||||
import { generateRandomId, ICommandService, IPermissionService, IUndoRedoService, LocaleService } from '@univerjs/core';
|
||||
import { ActionRow, Button, ButtonGroup, Segmented } from '@univerjs/design';
|
||||
import {
|
||||
AscendingIcon,
|
||||
DeleteColumnDoubleIcon,
|
||||
DescendingIcon,
|
||||
LeftInsertColumnDoubleIcon,
|
||||
RightInsertColumnDoubleIcon,
|
||||
} from '@univerjs/icons';
|
||||
import { WorkbookEditablePermission } from '@univerjs/sheets';
|
||||
import { SortRangeCommand, SortType } from '@univerjs/sheets-sort';
|
||||
import {
|
||||
isManualTableFilter,
|
||||
SetSheetTableSortStateCommand,
|
||||
SheetsTableSortStateEnum,
|
||||
SheetTableInsertColumnAtCommand,
|
||||
SheetTableRemoveColumnAtCommand,
|
||||
TABLE_FILTER_EMPTY_VALUE,
|
||||
TableColumnFilterTypeEnum,
|
||||
TableDateCompareTypeEnum,
|
||||
TableManager,
|
||||
} from '@univerjs/sheets-table';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useState } from 'react';
|
||||
import { SheetsTableComponentController } from '../../controllers/sheet-table-component.controller';
|
||||
import { SheetsTableUiService } from '../../services/sheets-table-ui.service';
|
||||
import { FilterByEnum } from '../../types';
|
||||
import { SheetTableColorFilterPanel } from './SheetTableColorFilterPanel';
|
||||
import { SheetTableConditionPanel } from './SheetTableConditionPanel';
|
||||
import { SheetTableItemsFilterPanel } from './SheetTableItemsFilterPanel';
|
||||
import { getInitConditionInfo } from './util';
|
||||
|
||||
const FILTER_BY_OPTIONS: Array<{ label: LocaleKey; value: FilterByEnum }> = [
|
||||
{ label: 'sheets-table-ui.filter.by-values', value: FilterByEnum.Items },
|
||||
{ label: 'sheets-table-ui.filter.by-colors', value: FilterByEnum.Color },
|
||||
{ label: 'sheets-table-ui.filter.by-conditions', value: FilterByEnum.Condition },
|
||||
];
|
||||
|
||||
export function SheetTableFilterPanel() {
|
||||
const localeService = useDependency(LocaleService);
|
||||
const filterByItems = FILTER_BY_OPTIONS.map((option) => ({ ...option, label: localeService.t(option.label) }));
|
||||
const tableUiService = useDependency(SheetsTableUiService);
|
||||
const tableManager = useDependency(TableManager);
|
||||
const commandService = useDependency(ICommandService);
|
||||
const permissionService = useDependency(IPermissionService);
|
||||
const undoRedoService = useDependency(IUndoRedoService);
|
||||
const sheetsTableComponentController = useDependency(SheetsTableComponentController);
|
||||
|
||||
const tableFilterPanelInfo = sheetsTableComponentController.getCurrentTableFilterInfo()!;
|
||||
const panelProps = tableUiService.getTableFilterPanelInitProps(
|
||||
tableFilterPanelInfo.unitId,
|
||||
tableFilterPanelInfo.subUnitId,
|
||||
tableFilterPanelInfo.tableId,
|
||||
tableFilterPanelInfo.column
|
||||
);
|
||||
|
||||
const { unitId, subUnitId, tableId, tableFilter, currentFilterBy, columnIndex } = panelProps;
|
||||
|
||||
const { data } = tableUiService.getTableFilterItems(unitId, subUnitId, tableId, columnIndex);
|
||||
const checkedItems = tableUiService.getTableFilterCheckedItems(unitId, tableId, columnIndex);
|
||||
|
||||
const [checkedItemSet, setCheckedItemSet] = useState<Set<string>>(() => new Set<string>(
|
||||
isManualTableFilter(tableFilter) ? checkedItems : data.map((item) => item.title)
|
||||
));
|
||||
const [filterBy, setFilterBy] = useState(currentFilterBy || FilterByEnum.Items);
|
||||
|
||||
const [conditionInfo, setConditionInfo] = useState<IConditionInfo>(() => {
|
||||
const tableFilter = panelProps.tableFilter;
|
||||
return getInitConditionInfo(tableFilter) as IConditionInfo;
|
||||
});
|
||||
const [colors, setColors] = useState<ITableFilterColorList>(
|
||||
() => tableUiService.getTableFilterColors(unitId, subUnitId, tableId, columnIndex)
|
||||
);
|
||||
|
||||
const table = tableManager.getTable(unitId, tableId);
|
||||
if (!table) return null;
|
||||
|
||||
const tableRange = table.getRange();
|
||||
const absoluteColumn = tableFilterPanelInfo.column;
|
||||
const canDeleteColumn = tableRange.endColumn > tableRange.startColumn;
|
||||
|
||||
const closeDialog = (): void => {
|
||||
sheetsTableComponentController.closeFilterPanel();
|
||||
};
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
const applySort = async (asc: boolean) => {
|
||||
const range = table.getTableFilterRange();
|
||||
const undoRedoGroup = undoRedoService.beginUndoRedoGroup(unitId, generateRandomId(), 'append');
|
||||
try {
|
||||
const success = await commandService.executeCommand<ISortRangeCommandParams>(SortRangeCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
range,
|
||||
orderRules: [{ colIndex: columnIndex + range.startColumn, type: asc ? SortType.ASC : SortType.DESC }],
|
||||
hasTitle: false,
|
||||
});
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sortStateSet = await commandService.executeCommand(SetSheetTableSortStateCommand.id, {
|
||||
unitId,
|
||||
tableId,
|
||||
sortInfo: {
|
||||
columnIndex,
|
||||
sortState: asc ? SheetsTableSortStateEnum.Asc : SheetsTableSortStateEnum.Desc,
|
||||
},
|
||||
});
|
||||
if (sortStateSet) {
|
||||
closeDialog();
|
||||
}
|
||||
} finally {
|
||||
undoRedoGroup.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
const insertColumn = async (side: 'left' | 'right') => {
|
||||
const success = await commandService.executeCommand(SheetTableInsertColumnAtCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
tableId,
|
||||
index: side === 'left' ? absoluteColumn : absoluteColumn + 1,
|
||||
count: 1,
|
||||
});
|
||||
if (success) {
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
|
||||
const deleteColumn = async () => {
|
||||
if (!canDeleteColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await commandService.executeCommand(SheetTableRemoveColumnAtCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
tableId,
|
||||
index: absoluteColumn,
|
||||
count: 1,
|
||||
});
|
||||
if (success) {
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
|
||||
const onApply = async () => {
|
||||
let filter: ITableColorFilterItem | ITableConditionFilterItem | ITableManualFilterItem | undefined;
|
||||
if (filterBy === FilterByEnum.Items) {
|
||||
// do items
|
||||
const filteredItems: string[] = [];
|
||||
const emptyLabel = localeService.t<LocaleKey>('sheets-table-ui.condition.empty');
|
||||
for (const itemInfo of data) {
|
||||
if (checkedItemSet.has(itemInfo.title)) {
|
||||
filteredItems.push(itemInfo.title === emptyLabel ? TABLE_FILTER_EMPTY_VALUE : itemInfo.title);
|
||||
}
|
||||
}
|
||||
const originFilter = table.getTableFilterColumn(columnIndex);
|
||||
if (isManualTableFilter(originFilter)) {
|
||||
const originValue = originFilter.values;
|
||||
if (originValue.join(',') === filteredItems.join(',')) {
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
} else if (filteredItems.length === data.length && !originFilter) {
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
filter = filteredItems.length === data.length
|
||||
? undefined
|
||||
: {
|
||||
filterType: TableColumnFilterTypeEnum.manual,
|
||||
values: filteredItems,
|
||||
};
|
||||
} else if (filterBy === FilterByEnum.Color) {
|
||||
const cellFillColors = colors.cellFillColors.filter((item) => item.checked).map((item) => item.color);
|
||||
const cellTextColors = colors.cellTextColors.filter((item) => item.checked).map((item) => item.color);
|
||||
if (cellFillColors.length) {
|
||||
filter = { filterType: TableColumnFilterTypeEnum.color, cellFillColors };
|
||||
} else if (cellTextColors.length) {
|
||||
filter = { filterType: TableColumnFilterTypeEnum.color, cellTextColors };
|
||||
}
|
||||
} else {
|
||||
let filterInfo;
|
||||
if (conditionInfo.compare === TableDateCompareTypeEnum.Quarter || conditionInfo.compare === TableDateCompareTypeEnum.Month) {
|
||||
filterInfo = {
|
||||
conditionType: conditionInfo.type,
|
||||
compareType: Object.values(conditionInfo.info)[0],
|
||||
};
|
||||
} else {
|
||||
filterInfo = {
|
||||
conditionType: conditionInfo.type,
|
||||
compareType: conditionInfo.compare,
|
||||
expectedValue: Object.values(conditionInfo.info)[0],
|
||||
};
|
||||
}
|
||||
filter = {
|
||||
filterType: TableColumnFilterTypeEnum.condition,
|
||||
// @ts-ignore
|
||||
filterInfo,
|
||||
};
|
||||
}
|
||||
const success = await tableUiService.setTableFilter(unitId, tableId, columnIndex, filter);
|
||||
if (success) {
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
const onClearFilter = async () => {
|
||||
const success = await tableUiService.setTableFilter(unitId, tableId, columnIndex, undefined);
|
||||
if (success) {
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
|
||||
const workbookEditableId = new WorkbookEditablePermission(unitId).id;
|
||||
const editable = permissionService.getPermissionPoint(workbookEditableId)?.value;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
univer-box-border univer-flex univer-w-[400px] univer-flex-col univer-rounded-[10px] univer-bg-gray-0
|
||||
univer-p-4 univer-shadow-lg
|
||||
dark:!univer-border-gray-600 dark:!univer-bg-gray-700
|
||||
`}
|
||||
>
|
||||
{editable && (
|
||||
<>
|
||||
<div
|
||||
className={`
|
||||
-univer-mx-4 -univer-mt-2 univer-mb-3 univer-border-0 univer-border-b univer-border-solid
|
||||
univer-border-gray-200 univer-py-1
|
||||
`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={`
|
||||
univer-box-border univer-flex univer-h-10 univer-w-full univer-cursor-pointer
|
||||
univer-items-center univer-gap-3 univer-border-none univer-bg-transparent univer-px-4
|
||||
univer-text-left univer-text-sm univer-text-gray-900
|
||||
hover:univer-bg-gray-100
|
||||
disabled:univer-cursor-not-allowed disabled:univer-text-gray-400
|
||||
dark:!univer-text-gray-0
|
||||
dark:hover:!univer-bg-gray-600
|
||||
`}
|
||||
onClick={() => insertColumn('left').catch(() => undefined)}
|
||||
>
|
||||
<LeftInsertColumnDoubleIcon className="univer-size-5" extend={{ colorChannel1: 'var(--univer-primary-600)' }} />
|
||||
<span>{localeService.t<LocaleKey>('sheets-table-ui.columnMenu.insert-left')}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`
|
||||
univer-box-border univer-flex univer-h-10 univer-w-full univer-cursor-pointer
|
||||
univer-items-center univer-gap-3 univer-border-none univer-bg-transparent univer-px-4
|
||||
univer-text-left univer-text-sm univer-text-gray-900
|
||||
hover:univer-bg-gray-100
|
||||
disabled:univer-cursor-not-allowed disabled:univer-text-gray-400
|
||||
dark:!univer-text-gray-0
|
||||
dark:hover:!univer-bg-gray-600
|
||||
`}
|
||||
onClick={() => insertColumn('right').catch(() => undefined)}
|
||||
>
|
||||
<RightInsertColumnDoubleIcon className="univer-size-5" extend={{ colorChannel1: 'var(--univer-primary-600)' }} />
|
||||
<span>{localeService.t<LocaleKey>('sheets-table-ui.columnMenu.insert-right')}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`
|
||||
univer-box-border univer-flex univer-h-10 univer-w-full univer-cursor-pointer
|
||||
univer-items-center univer-gap-3 univer-border-none univer-bg-transparent univer-px-4
|
||||
univer-text-left univer-text-sm univer-text-gray-900
|
||||
hover:univer-bg-gray-100
|
||||
disabled:univer-cursor-not-allowed disabled:univer-text-gray-400
|
||||
dark:!univer-text-gray-0
|
||||
dark:hover:!univer-bg-gray-600
|
||||
`}
|
||||
disabled={!canDeleteColumn}
|
||||
onClick={() => deleteColumn().catch(() => undefined)}
|
||||
>
|
||||
<DeleteColumnDoubleIcon className="univer-size-5" extend={{ colorChannel1: 'var(--univer-primary-600)' }} />
|
||||
<span>{localeService.t<LocaleKey>('sheets-table-ui.columnMenu.delete')}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="univer-mb-3 univer-flex">
|
||||
<ButtonGroup className="univer-mb-3 !univer-flex univer-w-full">
|
||||
<Button className="univer-w-1/2" onClick={() => applySort(true).catch(() => undefined)}>
|
||||
<AscendingIcon className="univer-mr-1" />
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.sort.sort-asc')}
|
||||
</Button>
|
||||
<Button className="univer-w-1/2" onClick={() => applySort(false).catch(() => undefined)}>
|
||||
<DescendingIcon className="univer-mr-1" />
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.sort.sort-desc')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="univer-w-full">
|
||||
<Segmented
|
||||
value={filterBy}
|
||||
items={filterByItems}
|
||||
onChange={(value) => setFilterBy(value as FilterByEnum)}
|
||||
/>
|
||||
</div>
|
||||
<div className="univer-z-10 univer-h-60">
|
||||
<div className="univer-mt-3 univer-size-full">
|
||||
{filterBy === FilterByEnum.Items && (
|
||||
<SheetTableItemsFilterPanel
|
||||
tableFilter={tableFilter}
|
||||
unitId={unitId}
|
||||
subUnitId={subUnitId}
|
||||
tableId={tableId}
|
||||
columnIndex={columnIndex}
|
||||
checkedItemSet={checkedItemSet}
|
||||
setCheckedItemSet={setCheckedItemSet}
|
||||
/>
|
||||
)}
|
||||
{filterBy === FilterByEnum.Color && (
|
||||
<SheetTableColorFilterPanel colors={colors} onChange={setColors} />
|
||||
)}
|
||||
{filterBy === FilterByEnum.Condition && (
|
||||
<SheetTableConditionPanel
|
||||
tableFilter={tableFilter}
|
||||
unitId={unitId}
|
||||
subUnitId={subUnitId}
|
||||
tableId={tableId}
|
||||
columnIndex={columnIndex}
|
||||
conditionInfo={conditionInfo}
|
||||
onChange={setConditionInfo}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<ActionRow
|
||||
className={`
|
||||
univer-flex-wrap-nowrap univer-mt-4 univer-inline-flex univer-flex-shrink-0 univer-flex-grow-0
|
||||
univer-justify-between univer-gap-6 univer-overflow-hidden
|
||||
`}
|
||||
>
|
||||
<Button
|
||||
disabled={tableFilter === undefined}
|
||||
onClick={() => onClearFilter().catch(() => undefined)}
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.clear-filter')}
|
||||
</Button>
|
||||
<ActionRow className="univer-flex univer-flex-1 univer-gap-x-2">
|
||||
<Button onClick={onCancel}>{localeService.t<LocaleKey>('sheets-table-ui.filter.cancel')}</Button>
|
||||
<Button variant="primary" onClick={() => onApply().catch(() => undefined)}>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.filter.confirm')}
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</ActionRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ITableFilterItem } from '@univerjs/sheets-table';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import { borderClassName, Checkbox, clsx, Input, scrollbarClassName } from '@univerjs/design';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { SheetsTableUiService } from '../../services/sheets-table-ui.service';
|
||||
|
||||
interface ISheetTableItemsFilterPanelProps {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
tableFilter: ITableFilterItem | undefined;
|
||||
tableId: string;
|
||||
columnIndex: number;
|
||||
checkedItemSet: Set<string>;
|
||||
setCheckedItemSet: Dispatch<SetStateAction<Set<string>>>;
|
||||
}
|
||||
|
||||
const getCheckedItemCount = (map: Map<string, number>, checkedItemSet: Set<string>) => {
|
||||
let count = 0;
|
||||
checkedItemSet.forEach((item) => {
|
||||
count += map.get(item) ?? 0;
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
export function SheetTableItemsFilterPanel(props: ISheetTableItemsFilterPanelProps) {
|
||||
const { unitId, tableId, subUnitId, columnIndex, checkedItemSet, setCheckedItemSet, tableFilter } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const tableService = useDependency(SheetsTableUiService);
|
||||
|
||||
const { data: items, itemsCountMap, allItemsCount } = tableService.getTableFilterItems(unitId, subUnitId, tableId, columnIndex);
|
||||
|
||||
// const allChecked = tableFilter === undefined ? true : checkedItemSet.size === itemsCountMap.size;
|
||||
const [allChecked, setAllChecked] = useState<boolean>(tableFilter === undefined ? true : checkedItemSet.size === itemsCountMap.size);
|
||||
|
||||
const [checkedCount, setCheckedCount] = useState<number>(() => allChecked ? allItemsCount : getCheckedItemCount(itemsCountMap, checkedItemSet));
|
||||
|
||||
const indeterminate = !allChecked && checkedItemSet.size > 0;
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
const displayItems = useMemo(() => {
|
||||
return searchText
|
||||
? items.filter((item: { title: string }) => {
|
||||
return String(item.title).toLowerCase().includes(searchText.toLowerCase());
|
||||
})
|
||||
: items;
|
||||
}, [searchText, items]);
|
||||
|
||||
const onCheckAllToggled = useCallback(() => {
|
||||
if (allChecked) {
|
||||
checkedItemSet.clear();
|
||||
setCheckedItemSet(new Set(checkedItemSet));
|
||||
setAllChecked(false);
|
||||
} else {
|
||||
displayItems.forEach((item) => {
|
||||
checkedItemSet.add(item.title);
|
||||
});
|
||||
setCheckedItemSet(new Set(checkedItemSet));
|
||||
setAllChecked(true);
|
||||
}
|
||||
}, [allChecked]);
|
||||
|
||||
const onSearchValueChange = useCallback((str: string) => {
|
||||
if (str === '') {
|
||||
setAllChecked(true);
|
||||
items.forEach((item) => {
|
||||
checkedItemSet.add(item.title);
|
||||
});
|
||||
setCheckedCount(allItemsCount);
|
||||
} else {
|
||||
checkedItemSet.clear();
|
||||
setAllChecked(false);
|
||||
setCheckedCount(0);
|
||||
}
|
||||
setSearchText(str);
|
||||
}, []);
|
||||
|
||||
const onCheckItemToggled = (key: string) => {
|
||||
if (allChecked) {
|
||||
setAllChecked(false);
|
||||
const newSet = new Set<string>();
|
||||
for (const { title } of items) {
|
||||
if (key !== title) {
|
||||
newSet.add(title);
|
||||
}
|
||||
}
|
||||
setCheckedCount(allItemsCount - itemsCountMap.get(key)!);
|
||||
setCheckedItemSet(newSet);
|
||||
} else {
|
||||
if (checkedItemSet.has(key)) {
|
||||
checkedItemSet.delete(key);
|
||||
setCheckedCount(checkedCount - itemsCountMap.get(key)!);
|
||||
} else {
|
||||
checkedItemSet.add(key);
|
||||
setCheckedCount(checkedCount + itemsCountMap.get(key)!);
|
||||
}
|
||||
setCheckedItemSet(new Set(checkedItemSet));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="univer-flex univer-h-full univer-flex-col">
|
||||
<Input autoFocus value={searchText} placeholder={localeService.t<LocaleKey>('sheets-table-ui.filter.search-placeholder')} onChange={onSearchValueChange} />
|
||||
<div
|
||||
className={clsx(`
|
||||
univer-mt-2 univer-box-border univer-flex univer-h-[180px] univer-max-h-[180px] univer-flex-grow
|
||||
univer-flex-col univer-overflow-hidden univer-rounded-md univer-py-1.5 univer-pl-2
|
||||
`, borderClassName)}
|
||||
>
|
||||
<div
|
||||
className={clsx('univer-h-40 univer-min-w-0 univer-overflow-y-auto univer-py-1 univer-pl-2', scrollbarClassName)}
|
||||
>
|
||||
<div className="univer-h-full">
|
||||
<div className="univer-flex univer-items-center univer-px-2 univer-py-1">
|
||||
<Checkbox
|
||||
className="univer-min-w-0 univer-flex-1"
|
||||
contentClassName="univer-flex-1 univer-min-w-0"
|
||||
indeterminate={indeterminate}
|
||||
disabled={items.length === 0}
|
||||
checked={allChecked}
|
||||
onChange={onCheckAllToggled}
|
||||
>
|
||||
<div className="univer-flex univer-h-5 univer-flex-1 univer-items-center univer-text-sm">
|
||||
<span className="univer-flex-1 univer-truncate">{`${localeService.t<LocaleKey>('sheets-table-ui.filter.select-all')}`}</span>
|
||||
<span className="univer-ml univer-text-gray-400">{`(${checkedCount}/${searchText ? displayItems.length : allItemsCount})`}</span>
|
||||
</div>
|
||||
</Checkbox>
|
||||
</div>
|
||||
{displayItems.map((item) => {
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
className="univer-flex univer-items-center univer-px-2 univer-py-1"
|
||||
>
|
||||
<Checkbox
|
||||
className="univer-min-w-0 univer-flex-1"
|
||||
contentClassName="univer-flex-1 univer-min-w-0"
|
||||
checked={allChecked || checkedItemSet.has(item.title)}
|
||||
onChange={() => { onCheckItemToggled(item.title); }}
|
||||
>
|
||||
<span
|
||||
className={`
|
||||
univer-flex univer-h-5 univer-flex-1 univer-items-center univer-text-sm
|
||||
`}
|
||||
>
|
||||
<span className="univer-flex-1 univer-truncate">
|
||||
{item.title}
|
||||
</span>
|
||||
<span
|
||||
className={`
|
||||
univer-ml-1 univer-inline-flex univer-h-full univer-items-center
|
||||
univer-text-gray-400
|
||||
`}
|
||||
>
|
||||
{`(${itemsCountMap.get(item.title) || 0})`}
|
||||
</span>
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ICanvasPopup } from '@univerjs/sheets-ui';
|
||||
import { DropdownMenu } from '@univerjs/design';
|
||||
|
||||
export type SheetTableMenuAction = 'rename' | 'update-range' | 'set-theme' | 'delete';
|
||||
|
||||
export interface ISheetTableMenuExtraProps {
|
||||
anchorWidth: number;
|
||||
tableName: string;
|
||||
labels: Record<SheetTableMenuAction, string>;
|
||||
onSelect: (action: SheetTableMenuAction) => void | Promise<void>;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
interface ISheetTableMenuProps {
|
||||
popup: ICanvasPopup;
|
||||
}
|
||||
|
||||
export function SheetTableMenu({ popup }: ISheetTableMenuProps) {
|
||||
const menu = popup.extraProps as unknown as ISheetTableMenuExtraProps | undefined;
|
||||
|
||||
if (!menu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
align="start"
|
||||
className="univer-min-w-40"
|
||||
items={[
|
||||
{ type: 'item', children: menu.labels.rename, onSelect: () => menu.onSelect('rename') },
|
||||
{ type: 'item', children: menu.labels['update-range'], onSelect: () => menu.onSelect('update-range') },
|
||||
{ type: 'item', children: menu.labels['set-theme'], onSelect: () => menu.onSelect('set-theme') },
|
||||
{ type: 'item', children: menu.labels.delete, variant: 'destructive', onSelect: () => menu.onSelect('delete') },
|
||||
]}
|
||||
open
|
||||
sideOffset={0}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
menu.onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={menu.tableName}
|
||||
className="univer-block univer-h-px univer-opacity-0"
|
||||
style={{ width: menu.anchorWidth }}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import { ICommandService, IUniverInstanceService, LocaleService } from '@univerjs/core';
|
||||
import { ActionRow, Button, Input } from '@univerjs/design';
|
||||
import { IDefinedNamesService } from '@univerjs/engine-formula';
|
||||
import {
|
||||
getExistingNamesSet,
|
||||
SetSheetTableCommand,
|
||||
TableManager,
|
||||
validateSheetTableName,
|
||||
} from '@univerjs/sheets-table';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
export interface ISheetTableRenameDialogProps {
|
||||
unitId: string;
|
||||
tableId: string;
|
||||
onClose: () => void;
|
||||
ActionRowComponent?: typeof ActionRow;
|
||||
}
|
||||
|
||||
export function SheetTableRenameDialog(props: ISheetTableRenameDialogProps) {
|
||||
const { unitId, tableId, onClose, ActionRowComponent = ActionRow } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
const commandService = useDependency(ICommandService);
|
||||
const tableManager = useDependency<TableManager>(TableManager);
|
||||
const univerInstanceService = useDependency(IUniverInstanceService);
|
||||
const definedNamesService = useDependency<IDefinedNamesService>(IDefinedNamesService);
|
||||
const table = tableManager.getTableById(unitId, tableId);
|
||||
const [value, setValue] = useState(table?.getDisplayName() ?? '');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const existingNames = useMemo(() => {
|
||||
const names = getExistingNamesSet(unitId, {
|
||||
univerInstanceService,
|
||||
tableManager,
|
||||
definedNamesService,
|
||||
});
|
||||
const currentName = table?.getDisplayName().toLowerCase();
|
||||
|
||||
if (currentName) {
|
||||
names.delete(currentName);
|
||||
}
|
||||
|
||||
return names;
|
||||
}, [definedNamesService, table, tableManager, unitId, univerInstanceService]);
|
||||
|
||||
const handleConfirm = () => {
|
||||
const nextName = value.trim();
|
||||
if (!table || nextName === table.getDisplayName()) {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
const validation = validateSheetTableName(nextName, existingNames);
|
||||
if (!validation.valid) {
|
||||
setError(localeService.t<LocaleKey>('sheets-table-ui.tableNameError'));
|
||||
return;
|
||||
}
|
||||
|
||||
commandService.executeCommand(SetSheetTableCommand.id, {
|
||||
unitId,
|
||||
tableId,
|
||||
name: nextName,
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
univer-box-border univer-flex univer-w-full univer-flex-col univer-gap-4 univer-pb-3 univer-pt-2
|
||||
`}
|
||||
>
|
||||
<Input
|
||||
size="middle"
|
||||
value={value}
|
||||
placeholder={localeService.t<LocaleKey>('sheets-table-ui.renamePlaceholder')}
|
||||
onChange={(nextValue) => {
|
||||
setValue(nextValue);
|
||||
setError('');
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleConfirm();
|
||||
}
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
{error ? <div className="-univer-mt-2 univer-text-sm univer-text-red-500">{error}</div> : null}
|
||||
<ActionRowComponent
|
||||
className="univer-flex univer-w-full univer-items-center univer-justify-end univer-gap-2"
|
||||
>
|
||||
<Button className="univer-min-w-16" onClick={onClose}>{localeService.t<LocaleKey>('sheets-table-ui.cancel')}</Button>
|
||||
<Button className="univer-min-w-16" variant="primary" onClick={handleConfirm}>{localeService.t<LocaleKey>('sheets-table-ui.confirm')}</Button>
|
||||
</ActionRowComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ITableSelectionInfo } from '../../commands/operations/open-table-selector.operation';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import { IUniverInstanceService, LocaleService, Rectangle } from '@univerjs/core';
|
||||
import { ActionRow, Button } from '@univerjs/design';
|
||||
import { deserializeRangeWithSheet, serializeRange } from '@univerjs/engine-formula';
|
||||
import { getSheetCommandTarget } from '@univerjs/sheets';
|
||||
import { RangeSelector } from '@univerjs/sheets-formula-ui';
|
||||
import { TableManager } from '@univerjs/sheets-table';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface ISheetTableSelectorProps extends ITableSelectionInfo {
|
||||
onConfirm: (info: ITableSelectionInfo) => void;
|
||||
onCancel: () => void;
|
||||
ActionRowComponent?: typeof ActionRow;
|
||||
}
|
||||
|
||||
export const SheetTableSelector = (props: ISheetTableSelectorProps) => {
|
||||
const { unitId, subUnitId, range, onCancel, onConfirm, tableId, ActionRowComponent = ActionRow } = props;
|
||||
|
||||
const tableManager = useDependency(TableManager);
|
||||
const [selectedRange, setSelectedRange] = useState(range);
|
||||
const [rangeError, setRangeError] = useState('');
|
||||
const localeService = useDependency(LocaleService);
|
||||
const univerInstanceService = useDependency(IUniverInstanceService);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RangeSelector
|
||||
maxRangeCount={1}
|
||||
unitId={unitId}
|
||||
subUnitId={subUnitId}
|
||||
initialValue={serializeRange(range)}
|
||||
onChange={(_, text) => {
|
||||
const originValue = serializeRange(range);
|
||||
const newRange = deserializeRangeWithSheet(text).range;
|
||||
const target = getSheetCommandTarget(univerInstanceService, { unitId, subUnitId });
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const worksheet = target.worksheet;
|
||||
const merges = worksheet.getMergeData();
|
||||
const hasOverlapWithMerge = merges.some((merge) => {
|
||||
return Rectangle.intersects(newRange, merge);
|
||||
});
|
||||
|
||||
if (hasOverlapWithMerge) {
|
||||
setRangeError(localeService.t<LocaleKey>('sheets-table-ui.tableRangeWithMergeError'));
|
||||
return;
|
||||
}
|
||||
|
||||
const hasOverlapWithOtherTable = tableManager.getTablesBySubunitId(unitId, subUnitId).some((table) => {
|
||||
if (table.getId() === tableId) {
|
||||
return false;
|
||||
}
|
||||
const tableRange = table.getRange();
|
||||
return Rectangle.intersects(newRange, tableRange);
|
||||
});
|
||||
|
||||
if (hasOverlapWithOtherTable) {
|
||||
setRangeError(localeService.t<LocaleKey>('sheets-table-ui.tableRangeWithOtherTableError'));
|
||||
return;
|
||||
}
|
||||
const { startRow, endRow } = newRange;
|
||||
const isSingleRow = startRow === endRow;
|
||||
if (isSingleRow) {
|
||||
setRangeError(localeService.t<LocaleKey>('sheets-table-ui.tableRangeSingleRowError'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (originValue === text) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tableId) {
|
||||
const table = tableManager.getTableById(unitId, tableId);
|
||||
if (table) {
|
||||
const oldRange = table.getRange();
|
||||
if (Rectangle.intersects(newRange, oldRange) && oldRange.startRow === newRange.startRow) {
|
||||
setSelectedRange(newRange);
|
||||
setRangeError('');
|
||||
onConfirm({
|
||||
unitId,
|
||||
subUnitId,
|
||||
range: newRange,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
setRangeError(localeService.t<LocaleKey>('sheets-table-ui.updateError'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedRange(newRange);
|
||||
setRangeError('');
|
||||
}}
|
||||
supportAcrossSheet={false}
|
||||
/>
|
||||
{rangeError && (
|
||||
<div className="univer-mt-1 univer-text-xs univer-text-red-500">
|
||||
{rangeError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ActionRowComponent className="univer-mt-4 univer-flex univer-justify-end univer-gap-2">
|
||||
<Button onClick={onCancel}>{localeService.t<LocaleKey>('sheets-table-ui.cancel')}</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
if (rangeError) {
|
||||
return;
|
||||
}
|
||||
onConfirm({
|
||||
unitId,
|
||||
subUnitId,
|
||||
range: selectedRange,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.confirm')}
|
||||
</Button>
|
||||
</ActionRowComponent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,431 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IRangeThemeStyleItem, ISetRangeThemeMutationParams } from '@univerjs/sheets';
|
||||
import type { IAddTableThemeCommandParams, ISetSheetTableCommandParams, ITableSetConfig } from '@univerjs/sheets-table';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import { ColorKit, ErrorService, ICommandService, LocaleService } from '@univerjs/core';
|
||||
import { borderClassName, clsx, ColorPicker, Dropdown } from '@univerjs/design';
|
||||
import { CloseIcon, DownIcon, IncreaseIcon } from '@univerjs/icons';
|
||||
import { RangeThemeStyle, SetRangeThemeMutation, SheetRangeThemeModel } from '@univerjs/sheets';
|
||||
import {
|
||||
AddTableThemeCommand,
|
||||
customEmptyThemeWithBorderStyle,
|
||||
processStyleWithBorderStyle,
|
||||
RemoveTableThemeCommand,
|
||||
SetSheetTableCommand,
|
||||
TableManager,
|
||||
} from '@univerjs/sheets-table';
|
||||
import { useDependency, useObservable } from '@univerjs/ui';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
TABLE_BORDER_DEFAULT,
|
||||
TABLE_BORDER_NONE,
|
||||
TABLE_CUSTOM_NAME_PREFIX,
|
||||
TABLE_DEFAULT_BG_COLOR,
|
||||
TABLE_DEFAULT_NAME_PREFIX,
|
||||
} from '../../const';
|
||||
import { SheetTableThemeUIController } from '../../controllers/sheet-table-theme-ui.controller';
|
||||
|
||||
export interface ISheetTableThemePanelProps {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
tableId: string;
|
||||
oldConfig: ITableSetConfig;
|
||||
ColorPickerComponent?: typeof ColorPicker;
|
||||
DropdownComponent?: typeof Dropdown;
|
||||
}
|
||||
|
||||
interface ITableThemeStyle {
|
||||
headerRowStyle?: IRangeThemeStyleItem;
|
||||
firstRowStyle?: IRangeThemeStyleItem;
|
||||
secondRowStyle?: IRangeThemeStyleItem;
|
||||
lastRowStyle?: IRangeThemeStyleItem;
|
||||
}
|
||||
|
||||
export const SheetTableThemePanel = (props: ISheetTableThemePanelProps) => {
|
||||
const {
|
||||
unitId,
|
||||
subUnitId,
|
||||
tableId,
|
||||
ColorPickerComponent = ColorPicker,
|
||||
DropdownComponent = Dropdown,
|
||||
} = props;
|
||||
const commandService = useDependency(ICommandService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const tableManager = useDependency(TableManager);
|
||||
const table = tableManager.getTableById(unitId, tableId);
|
||||
const rangeThemeModel = useDependency(SheetRangeThemeModel);
|
||||
const sheetTableThemeUIController = useDependency(SheetTableThemeUIController);
|
||||
|
||||
const rangeThemeMapChanged = useObservable(rangeThemeModel.rangeThemeMapChange$);
|
||||
const tableRefresh = useObservable(sheetTableThemeUIController.refreshTable$);
|
||||
|
||||
const errorService = useDependency(ErrorService);
|
||||
|
||||
const [, setRefresh] = useState(Math.random);
|
||||
|
||||
const themeConfig = useObservable(tableManager.tableThemeChanged$, {
|
||||
theme: table?.getTableStyleId(),
|
||||
oldTheme: table?.getTableStyleId(),
|
||||
unitId,
|
||||
subUnitId,
|
||||
tableId,
|
||||
});
|
||||
|
||||
const defaultRangeThemes = rangeThemeModel.getRegisteredRangeThemes().filter((item) => item?.startsWith(TABLE_DEFAULT_NAME_PREFIX));
|
||||
const customRangeThemes = rangeThemeModel.getALLRegisteredTheme(unitId).filter((item) => item?.startsWith(TABLE_CUSTOM_NAME_PREFIX));
|
||||
const selectedTheme = table?.getTableStyleId();
|
||||
const customSelected = customRangeThemes.find((item) => item === selectedTheme);
|
||||
const customStyleName = customSelected || customRangeThemes[0];
|
||||
const customStyle = rangeThemeModel.getCustomRangeThemeStyle(unitId, customStyleName);
|
||||
const headerBg = customStyle?.getHeaderRowStyle()?.bg?.rgb ?? TABLE_DEFAULT_BG_COLOR;
|
||||
const firstRowBg = customStyle?.getFirstRowStyle()?.bg?.rgb ?? TABLE_DEFAULT_BG_COLOR;
|
||||
const secondRowBg = customStyle?.getSecondRowStyle()?.bg?.rgb ?? TABLE_DEFAULT_BG_COLOR;
|
||||
const lastRowBg = customStyle?.getLastRowStyle()?.bg?.rgb ?? TABLE_DEFAULT_BG_COLOR;
|
||||
const direction = useObservable(localeService.direction$, localeService.getDirection());
|
||||
|
||||
const handleThemeChange = (theme: string) => {
|
||||
commandService.executeCommand<ISetSheetTableCommandParams>(SetSheetTableCommand.id, {
|
||||
unitId,
|
||||
tableId,
|
||||
theme,
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddCustomTheme = () => {
|
||||
if (customRangeThemes.length >= 11) {
|
||||
errorService.emit(localeService.t<LocaleKey>('sheets-table-ui.customTooMore'));
|
||||
return;
|
||||
}
|
||||
const lastCustomTheme = customRangeThemes[customRangeThemes.length - 1];
|
||||
let newThemeName = `${TABLE_CUSTOM_NAME_PREFIX}1`;
|
||||
if (lastCustomTheme) {
|
||||
const index = Number(lastCustomTheme.split('-')[2]);
|
||||
newThemeName = `${TABLE_CUSTOM_NAME_PREFIX}${index + 1}`;
|
||||
}
|
||||
const newTheme = new RangeThemeStyle(newThemeName, { ...customEmptyThemeWithBorderStyle });
|
||||
commandService.executeCommand<IAddTableThemeCommandParams>(AddTableThemeCommand.id, {
|
||||
unitId,
|
||||
tableId,
|
||||
themeStyle: newTheme,
|
||||
});
|
||||
};
|
||||
|
||||
const setCustomTheme = (themeName: string, tableThemeStyle: ITableThemeStyle) => {
|
||||
commandService.executeCommand<ISetRangeThemeMutationParams>(SetRangeThemeMutation.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
styleName: themeName,
|
||||
style: tableThemeStyle,
|
||||
});
|
||||
};
|
||||
|
||||
const removeCustomTheme = (themeName: string) => {
|
||||
commandService.executeCommand(RemoveTableThemeCommand.id, {
|
||||
unitId,
|
||||
tableId,
|
||||
themeName,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setRefresh(Math.random());
|
||||
}, [rangeThemeMapChanged, tableRefresh]);
|
||||
|
||||
if (!table) return null;
|
||||
|
||||
const headerBgIsDark = new ColorKit(headerBg).isDark();
|
||||
const firstRowBgIsDark = new ColorKit(firstRowBg).isDark();
|
||||
const secondRowBgIsDark = new ColorKit(secondRowBg).isDark();
|
||||
const lastRowBgIsDark = new ColorKit(lastRowBg).isDark();
|
||||
|
||||
const customThemeRows = [
|
||||
{
|
||||
key: 'header',
|
||||
label: localeService.t<LocaleKey>('sheets-table-ui.header'),
|
||||
background: headerBg,
|
||||
isDark: headerBgIsDark,
|
||||
onChange: (value: string) => {
|
||||
const headerRowStyle = processStyleWithBorderStyle('headerRowStyle', {
|
||||
bg: { rgb: value },
|
||||
cl: { rgb: new ColorKit(value).isDark() ? '#fff' : '#000' },
|
||||
});
|
||||
setCustomTheme(table.getTableStyleId(), { headerRowStyle });
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'first-row',
|
||||
label: localeService.t<LocaleKey>('sheets-table-ui.firstLine'),
|
||||
background: firstRowBg,
|
||||
isDark: firstRowBgIsDark,
|
||||
onChange: (value: string) => {
|
||||
setCustomTheme(table.getTableStyleId(), {
|
||||
firstRowStyle: {
|
||||
bg: { rgb: value },
|
||||
cl: { rgb: new ColorKit(value).isDark() ? '#fff' : '#000' },
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'second-row',
|
||||
label: localeService.t<LocaleKey>('sheets-table-ui.secondLine'),
|
||||
background: secondRowBg,
|
||||
isDark: secondRowBgIsDark,
|
||||
onChange: (value: string) => {
|
||||
setCustomTheme(table.getTableStyleId(), {
|
||||
secondRowStyle: {
|
||||
bg: { rgb: value },
|
||||
cl: { rgb: new ColorKit(value).isDark() ? '#fff' : '#000' },
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'last-row',
|
||||
label: localeService.t<LocaleKey>('sheets-table-ui.footer'),
|
||||
background: lastRowBg,
|
||||
isDark: lastRowBgIsDark,
|
||||
onChange: (value: string) => {
|
||||
const lastRowStyle = processStyleWithBorderStyle('lastRowStyle', {
|
||||
bg: { rgb: value },
|
||||
cl: { rgb: new ColorKit(value).isDark() ? '#fff' : '#000' },
|
||||
});
|
||||
setCustomTheme(table.getTableStyleId(), { lastRowStyle });
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div dir={direction} className="univer-flex univer-flex-col univer-gap-5 univer-pb-4">
|
||||
<section className="univer-flex univer-flex-col univer-gap-2">
|
||||
<h5
|
||||
className="
|
||||
univer-m-0 univer-text-xs univer-font-medium univer-text-gray-500
|
||||
dark:!univer-text-gray-300
|
||||
"
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.defaultStyle')}
|
||||
</h5>
|
||||
<div className="univer-grid univer-grid-cols-6 univer-gap-2">
|
||||
{defaultRangeThemes.map((item) => {
|
||||
const rangeThemeItem = rangeThemeModel.getDefaultRangeThemeStyle(item);
|
||||
const rowBackgrounds = [
|
||||
rangeThemeItem?.getHeaderRowStyle()?.bg?.rgb || TABLE_DEFAULT_BG_COLOR,
|
||||
rangeThemeItem?.getFirstRowStyle()?.bg?.rgb || TABLE_DEFAULT_BG_COLOR,
|
||||
rangeThemeItem?.getSecondRowStyle()?.bg?.rgb || TABLE_DEFAULT_BG_COLOR,
|
||||
rangeThemeItem?.getLastRowStyle()?.bg?.rgb || TABLE_DEFAULT_BG_COLOR,
|
||||
];
|
||||
const selected = item === themeConfig.theme;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item}
|
||||
type="button"
|
||||
title={item}
|
||||
aria-label={item}
|
||||
aria-pressed={selected}
|
||||
className={clsx(`
|
||||
univer-flex univer-h-12 univer-w-full univer-cursor-pointer univer-flex-col
|
||||
univer-overflow-hidden univer-rounded-lg univer-bg-gray-0 univer-p-1 univer-shadow-sm
|
||||
univer-transition-all
|
||||
hover:univer-border-gray-400 hover:univer-shadow-md
|
||||
focus:univer-outline-none focus:univer-ring-2 focus:univer-ring-primary-200
|
||||
dark:!univer-bg-gray-800
|
||||
`, borderClassName, {
|
||||
'!univer-border-primary-500 univer-ring-2 univer-ring-primary-100': selected,
|
||||
})}
|
||||
onClick={() => handleThemeChange(item)}
|
||||
>
|
||||
{rowBackgrounds.map((background, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="univer-min-h-0 univer-w-full univer-flex-1"
|
||||
style={{
|
||||
background,
|
||||
border: background ? TABLE_BORDER_NONE : TABLE_BORDER_DEFAULT,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="univer-flex univer-flex-col univer-gap-2">
|
||||
<h5
|
||||
className="
|
||||
univer-m-0 univer-text-xs univer-font-medium univer-text-gray-500
|
||||
dark:!univer-text-gray-300
|
||||
"
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-table-ui.customStyle')}
|
||||
</h5>
|
||||
<div
|
||||
className="
|
||||
univer-grid univer-grid-cols-5 univer-gap-2 univer-rounded-lg univer-bg-gray-50 univer-p-2
|
||||
dark:!univer-bg-gray-800
|
||||
"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-u-comp="sheet-table-theme-add"
|
||||
aria-label={localeService.t<LocaleKey>('sheets-table-ui.setTheme')}
|
||||
className="
|
||||
univer-flex univer-h-12 univer-w-full univer-cursor-pointer univer-items-center
|
||||
univer-justify-center univer-rounded-lg univer-border univer-border-dashed
|
||||
univer-border-gray-300 univer-bg-gray-0 univer-p-0 univer-text-gray-500
|
||||
univer-transition-colors
|
||||
hover:univer-border-primary-500 hover:univer-text-primary-600
|
||||
focus:univer-outline-none focus:univer-ring-2 focus:univer-ring-primary-200
|
||||
dark:!univer-border-gray-600 dark:!univer-bg-gray-900 dark:!univer-text-gray-300
|
||||
"
|
||||
onClick={handleAddCustomTheme}
|
||||
>
|
||||
<IncreaseIcon className="univer-size-4" />
|
||||
</button>
|
||||
{customRangeThemes.map((item) => {
|
||||
const rangeThemeItem = rangeThemeModel.getCustomRangeThemeStyle(unitId, item);
|
||||
const rowBackgrounds = [
|
||||
rangeThemeItem?.getHeaderRowStyle()?.bg?.rgb,
|
||||
rangeThemeItem?.getFirstRowStyle()?.bg?.rgb,
|
||||
rangeThemeItem?.getSecondRowStyle()?.bg?.rgb,
|
||||
rangeThemeItem?.getLastRowStyle()?.bg?.rgb,
|
||||
];
|
||||
const selected = item === themeConfig.theme;
|
||||
|
||||
return (
|
||||
<div key={item} className="univer-group univer-relative univer-h-12 univer-min-w-0">
|
||||
<button
|
||||
type="button"
|
||||
title={item}
|
||||
aria-label={item}
|
||||
aria-pressed={selected}
|
||||
className={clsx(`
|
||||
univer-flex univer-size-full univer-cursor-pointer univer-flex-col
|
||||
univer-overflow-hidden univer-rounded-lg univer-bg-gray-0 univer-p-1
|
||||
univer-shadow-sm univer-transition-all
|
||||
hover:univer-border-gray-400 hover:univer-shadow-md
|
||||
focus:univer-outline-none focus:univer-ring-2 focus:univer-ring-primary-200
|
||||
dark:!univer-bg-gray-900
|
||||
`, borderClassName, {
|
||||
'!univer-border-primary-500 univer-ring-2 univer-ring-primary-100': selected,
|
||||
})}
|
||||
onClick={() => handleThemeChange(item)}
|
||||
>
|
||||
{rowBackgrounds.map((background, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="univer-min-h-0 univer-w-full univer-flex-1"
|
||||
style={{
|
||||
background: background ?? TABLE_BORDER_NONE,
|
||||
border: background ? TABLE_BORDER_NONE : TABLE_BORDER_DEFAULT,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-u-comp="sheet-table-theme-remove"
|
||||
aria-label={`${localeService.t<LocaleKey>('sheets-table-ui.customStyle')} ${item}`}
|
||||
className={clsx(`
|
||||
univer-absolute univer-right-[-6px] univer-top-[-6px] univer-z-10 univer-flex
|
||||
univer-size-5 univer-cursor-pointer univer-items-center univer-justify-center
|
||||
univer-rounded-full univer-border univer-border-solid univer-border-gray-200
|
||||
univer-bg-gray-0 univer-p-0 univer-text-gray-500 univer-shadow-sm
|
||||
univer-transition-all
|
||||
hover:univer-border-red-200 hover:univer-bg-red-50 hover:univer-text-red-600
|
||||
focus:univer-opacity-100 focus:univer-outline-none focus:univer-ring-2
|
||||
focus:univer-ring-primary-200
|
||||
rtl:univer-left-[-6px] rtl:univer-right-auto
|
||||
dark:!univer-border-gray-600 dark:!univer-bg-gray-900
|
||||
`, {
|
||||
'univer-opacity-100': selected,
|
||||
'univer-opacity-0 group-focus-within:univer-opacity-100 group-hover:univer-opacity-100': !selected,
|
||||
})}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
removeCustomTheme(item);
|
||||
}}
|
||||
>
|
||||
<CloseIcon className="univer-size-3" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{customSelected && (
|
||||
<div
|
||||
className="
|
||||
univer-flex univer-flex-col univer-gap-2 univer-rounded-lg univer-bg-gray-50 univer-p-3
|
||||
dark:!univer-bg-gray-800
|
||||
"
|
||||
>
|
||||
{customThemeRows.map((row) => (
|
||||
<div key={row.key} className="univer-flex univer-h-10 univer-min-w-0 univer-gap-2">
|
||||
<div
|
||||
className={clsx(`
|
||||
univer-flex univer-min-w-0 univer-flex-1 univer-items-center univer-rounded-md
|
||||
univer-px-3 univer-text-sm univer-font-medium
|
||||
rtl:univer-text-right
|
||||
`, borderClassName, {
|
||||
'univer-text-gray-0': row.isDark,
|
||||
'univer-text-gray-900': !row.isDark,
|
||||
})}
|
||||
style={{ background: row.background }}
|
||||
>
|
||||
<span className="univer-truncate">{row.label}</span>
|
||||
</div>
|
||||
<DropdownComponent
|
||||
overlay={(
|
||||
<div dir={direction} className="univer-p-2">
|
||||
<ColorPickerComponent
|
||||
value={new ColorKit(row.background).toHexString()}
|
||||
onChange={row.onChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={`${row.label} ${localeService.t<LocaleKey>('sheets-table-ui.setTheme')}`}
|
||||
className={clsx(`
|
||||
univer-flex univer-h-10 univer-cursor-pointer univer-items-center univer-gap-2
|
||||
univer-rounded-md univer-bg-gray-0 univer-px-2 univer-text-gray-600
|
||||
univer-transition-colors
|
||||
hover:univer-bg-gray-100
|
||||
focus:univer-outline-none focus:univer-ring-2 focus:univer-ring-primary-200
|
||||
dark:!univer-bg-gray-900 dark:!univer-text-gray-300
|
||||
`, borderClassName)}
|
||||
>
|
||||
<span
|
||||
className={clsx('univer-size-4 univer-rounded-full', borderClassName)}
|
||||
style={{ background: row.background }}
|
||||
/>
|
||||
<DownIcon className="univer-size-3" />
|
||||
</button>
|
||||
</DropdownComponent>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ITableFilterItem } from '@univerjs/sheets-table';
|
||||
import {
|
||||
TableColumnFilterTypeEnum,
|
||||
TableConditionTypeEnum,
|
||||
TableDateCompareTypeEnum,
|
||||
TableNumberCompareTypeEnum,
|
||||
TableStringCompareTypeEnum,
|
||||
} from '@univerjs/sheets-table';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ConditionSubComponentEnum } from '../type';
|
||||
import { getInitConditionInfo, getSubComponentType } from '../util';
|
||||
|
||||
describe('sheet table condition utilities', () => {
|
||||
it('initializes a new condition filter as a text equality rule', () => {
|
||||
expect(getInitConditionInfo()).toEqual({
|
||||
type: TableConditionTypeEnum.String,
|
||||
compare: TableStringCompareTypeEnum.Equal,
|
||||
info: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('maps condition compare types to the editor control needed by users', () => {
|
||||
expect(getSubComponentType(TableConditionTypeEnum.String, TableStringCompareTypeEnum.Contains)).toBe(ConditionSubComponentEnum.Input);
|
||||
expect(getSubComponentType(TableConditionTypeEnum.Number, TableNumberCompareTypeEnum.Between)).toBe(ConditionSubComponentEnum.Inputs);
|
||||
expect(getSubComponentType(TableConditionTypeEnum.Date, TableDateCompareTypeEnum.Between)).toBe(ConditionSubComponentEnum.DateRange);
|
||||
expect(getSubComponentType(TableConditionTypeEnum.Date, TableDateCompareTypeEnum.Quarter)).toBe(ConditionSubComponentEnum.Select);
|
||||
expect(getSubComponentType(TableConditionTypeEnum.Date, TableDateCompareTypeEnum.After)).toBe(ConditionSubComponentEnum.DatePicker);
|
||||
expect(getSubComponentType(TableConditionTypeEnum.Date, TableDateCompareTypeEnum.Today)).toBe(ConditionSubComponentEnum.None);
|
||||
});
|
||||
|
||||
it('hydrates saved date filters back into panel state without losing relative date choices', () => {
|
||||
const absoluteDateFilter: ITableFilterItem = {
|
||||
filterType: TableColumnFilterTypeEnum.condition,
|
||||
filterInfo: {
|
||||
conditionType: TableConditionTypeEnum.Date,
|
||||
compareType: TableDateCompareTypeEnum.Between,
|
||||
expectedValue: ['2026-01-01', '2026-01-31'],
|
||||
},
|
||||
};
|
||||
const quarterFilter: ITableFilterItem = {
|
||||
filterType: TableColumnFilterTypeEnum.condition,
|
||||
filterInfo: {
|
||||
conditionType: TableConditionTypeEnum.Date,
|
||||
compareType: TableDateCompareTypeEnum.Q3,
|
||||
},
|
||||
};
|
||||
const relativeDateFilter: ITableFilterItem = {
|
||||
filterType: TableColumnFilterTypeEnum.condition,
|
||||
filterInfo: {
|
||||
conditionType: TableConditionTypeEnum.Date,
|
||||
compareType: TableDateCompareTypeEnum.ThisMonth,
|
||||
},
|
||||
};
|
||||
|
||||
expect(getInitConditionInfo(absoluteDateFilter)).toMatchObject({
|
||||
type: TableConditionTypeEnum.Date,
|
||||
compare: TableDateCompareTypeEnum.Between,
|
||||
info: {
|
||||
dateRange: [
|
||||
new Date('2026-01-01'),
|
||||
new Date('2026-01-31'),
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(getInitConditionInfo(quarterFilter)).toEqual({
|
||||
type: TableConditionTypeEnum.Date,
|
||||
compare: TableDateCompareTypeEnum.Quarter,
|
||||
info: {
|
||||
dateSelect: TableDateCompareTypeEnum.Q3,
|
||||
},
|
||||
});
|
||||
expect(getInitConditionInfo(relativeDateFilter)).toEqual({
|
||||
type: TableConditionTypeEnum.Date,
|
||||
compare: TableDateCompareTypeEnum.ThisMonth,
|
||||
info: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
45
packages/sheets-table-ui/src/views/components/type.ts
Normal file
45
packages/sheets-table-ui/src/views/components/type.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { TableConditionTypeEnum, TableDateCompareTypeEnum, TableNumberCompareTypeEnum, TableStringCompareTypeEnum } from '@univerjs/sheets-table';
|
||||
|
||||
export enum ConditionSubComponentEnum {
|
||||
DatePicker = 'DatePicker',
|
||||
DateRange = 'DateRange',
|
||||
Input = 'Input',
|
||||
Inputs = 'Inputs',
|
||||
Select = 'Select',
|
||||
None = 'None',
|
||||
}
|
||||
|
||||
export type IConditionCompareTypeEnum = TableStringCompareTypeEnum | TableDateCompareTypeEnum | TableNumberCompareTypeEnum;
|
||||
|
||||
export type ITableConditionTypeEnumWithoutLogic = Exclude<TableConditionTypeEnum, TableConditionTypeEnum.Logic>;
|
||||
|
||||
export interface IConditionInfo {
|
||||
type: ITableConditionTypeEnumWithoutLogic;
|
||||
compare: IConditionCompareTypeEnum;
|
||||
info: IConditionExpect;
|
||||
}
|
||||
|
||||
export interface IConditionExpect {
|
||||
date?: Date;
|
||||
dateRange?: [Date, Date];
|
||||
dateSelect?: string;
|
||||
number?: number;
|
||||
numberRange?: [number?, number?];
|
||||
string?: string;
|
||||
}
|
||||
446
packages/sheets-table-ui/src/views/components/util.ts
Normal file
446
packages/sheets-table-ui/src/views/components/util.ts
Normal file
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Injector } from '@univerjs/core';
|
||||
import type { ITableFilterItem } from '@univerjs/sheets-table';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { IConditionCompareTypeEnum } from './type';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import {
|
||||
TableColumnFilterTypeEnum,
|
||||
TableConditionTypeEnum,
|
||||
TableDateCompareTypeEnum,
|
||||
TableNumberCompareTypeEnum,
|
||||
TableStringCompareTypeEnum,
|
||||
} from '@univerjs/sheets-table';
|
||||
import { ConditionSubComponentEnum } from './type';
|
||||
|
||||
export function getCascaderListOptions(injector: Injector) {
|
||||
const localeService = injector.get(LocaleService);
|
||||
return [
|
||||
{
|
||||
value: TableConditionTypeEnum.String,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.condition.${TableConditionTypeEnum.String}`),
|
||||
children: [
|
||||
{
|
||||
value: TableStringCompareTypeEnum.Equal,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.string.compare.${TableStringCompareTypeEnum.Equal}`),
|
||||
},
|
||||
{
|
||||
value: TableStringCompareTypeEnum.NotEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.string.compare.${TableStringCompareTypeEnum.NotEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableStringCompareTypeEnum.Contains,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.string.compare.${TableStringCompareTypeEnum.Contains}`),
|
||||
},
|
||||
{
|
||||
value: TableStringCompareTypeEnum.NotContains,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.string.compare.${TableStringCompareTypeEnum.NotContains}`),
|
||||
},
|
||||
{
|
||||
value: TableStringCompareTypeEnum.StartsWith,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.string.compare.${TableStringCompareTypeEnum.StartsWith}`),
|
||||
},
|
||||
{
|
||||
value: TableStringCompareTypeEnum.EndsWith,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.string.compare.${TableStringCompareTypeEnum.EndsWith}`),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: TableConditionTypeEnum.Number,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.condition.${TableConditionTypeEnum.Number}`),
|
||||
children: [
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.Equal,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.Equal}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.NotEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.NotEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.GreaterThan,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.GreaterThan}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.GreaterThanOrEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.GreaterThanOrEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.LessThan,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.LessThan}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.LessThanOrEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.LessThanOrEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.Between,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.Between}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.NotBetween,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.NotBetween}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.Above,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.Above}`),
|
||||
},
|
||||
{
|
||||
value: TableNumberCompareTypeEnum.Below,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.Below}`),
|
||||
},
|
||||
// {
|
||||
// value: TableNumberCompareTypeEnum.TopN,
|
||||
// label: localeService.t<LocaleKey>(`sheets-table-ui.number.compare.${TableNumberCompareTypeEnum.TopN}`),
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: TableConditionTypeEnum.Date,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.condition.${TableConditionTypeEnum.Date}`),
|
||||
children: [
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Equal,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Equal}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.NotEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.NotEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.After,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.After}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.AfterOrEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.AfterOrEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Before,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Before}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.BeforeOrEqual,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.BeforeOrEqual}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Between,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Between}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.NotBetween,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.NotBetween}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Today,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Today}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Yesterday,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Yesterday}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Tomorrow,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Tomorrow}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.ThisWeek,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.ThisWeek}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.LastWeek,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.LastWeek}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.NextWeek,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.NextWeek}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.ThisMonth,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.ThisMonth}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.LastMonth,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.LastMonth}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.NextMonth,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.NextMonth}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.ThisYear,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.ThisYear}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.LastYear,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.LastYear}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.NextYear,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.NextYear}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Quarter,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Quarter}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Month,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Month}`),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getConditionDateSelect(injector: Injector, dateType?: TableDateCompareTypeEnum) {
|
||||
if (!dateType) {
|
||||
return [];
|
||||
}
|
||||
const localeService = injector.get(LocaleService);
|
||||
switch (dateType) {
|
||||
case TableDateCompareTypeEnum.Quarter:
|
||||
return [
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Q1,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Q1}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Q2,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Q2}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Q3,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Q3}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.Q4,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.Q4}`),
|
||||
},
|
||||
];
|
||||
case TableDateCompareTypeEnum.Month:
|
||||
return [
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M1,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M1}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M2,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M2}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M3,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M3}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M4,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M4}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M5,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M5}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M6,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M6}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M7,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M7}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M8,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M8}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M9,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M9}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M10,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M10}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M11,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M11}`),
|
||||
},
|
||||
{
|
||||
value: TableDateCompareTypeEnum.M12,
|
||||
label: localeService.t<LocaleKey>(`sheets-table-ui.date.compare.${TableDateCompareTypeEnum.M12}`),
|
||||
},
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export const datePickerSet: Set<IConditionCompareTypeEnum> = new Set([
|
||||
TableDateCompareTypeEnum.Equal,
|
||||
TableDateCompareTypeEnum.NotEqual,
|
||||
TableDateCompareTypeEnum.After,
|
||||
TableDateCompareTypeEnum.AfterOrEqual,
|
||||
TableDateCompareTypeEnum.Before,
|
||||
TableDateCompareTypeEnum.BeforeOrEqual,
|
||||
]);
|
||||
|
||||
export function getSubComponentType(type: TableConditionTypeEnum, compare?: TableStringCompareTypeEnum | TableNumberCompareTypeEnum | TableDateCompareTypeEnum): ConditionSubComponentEnum {
|
||||
if (!compare) {
|
||||
return ConditionSubComponentEnum.None;
|
||||
}
|
||||
if (type === TableConditionTypeEnum.String) {
|
||||
return ConditionSubComponentEnum.Input;
|
||||
} else if (type === TableConditionTypeEnum.Number) {
|
||||
if (compare === TableNumberCompareTypeEnum.Between || compare === TableNumberCompareTypeEnum.NotBetween) {
|
||||
return ConditionSubComponentEnum.Inputs;
|
||||
} else {
|
||||
return ConditionSubComponentEnum.Input;
|
||||
}
|
||||
} else if (type === TableConditionTypeEnum.Date) {
|
||||
if (compare === TableDateCompareTypeEnum.Between || compare === TableDateCompareTypeEnum.NotBetween) {
|
||||
return ConditionSubComponentEnum.DateRange;
|
||||
} else if (compare === TableDateCompareTypeEnum.Quarter || compare === TableDateCompareTypeEnum.Month) {
|
||||
return ConditionSubComponentEnum.Select;
|
||||
} else if (datePickerSet.has(compare as TableDateCompareTypeEnum)) {
|
||||
return ConditionSubComponentEnum.DatePicker;
|
||||
}
|
||||
return ConditionSubComponentEnum.None;
|
||||
}
|
||||
return ConditionSubComponentEnum.None;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
export function getInitConditionInfo(tableFilter?: ITableFilterItem) {
|
||||
if (!tableFilter || tableFilter.filterType !== TableColumnFilterTypeEnum.condition) {
|
||||
return {
|
||||
type: TableConditionTypeEnum.String,
|
||||
compare: TableStringCompareTypeEnum.Equal,
|
||||
info: {},
|
||||
};
|
||||
}
|
||||
|
||||
const filterInfo = tableFilter.filterInfo;
|
||||
const { conditionType, compareType } = filterInfo;
|
||||
if (conditionType === TableConditionTypeEnum.Date) {
|
||||
if (compareType === TableDateCompareTypeEnum.Between || compareType === TableDateCompareTypeEnum.NotBetween) {
|
||||
// ensure expectedValue is an array of two dates
|
||||
let dateRange: [Date, Date] | undefined;
|
||||
if (Array.isArray(filterInfo.expectedValue)) {
|
||||
dateRange = filterInfo.expectedValue.map((i) => (typeof i === 'string' ? new Date(i) : i)) as [Date, Date];
|
||||
}
|
||||
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: compareType,
|
||||
info: {
|
||||
dateRange,
|
||||
},
|
||||
};
|
||||
} else if (
|
||||
compareType === TableDateCompareTypeEnum.Today
|
||||
|| compareType === TableDateCompareTypeEnum.Yesterday
|
||||
|| compareType === TableDateCompareTypeEnum.Tomorrow
|
||||
|| compareType === TableDateCompareTypeEnum.ThisWeek
|
||||
|| compareType === TableDateCompareTypeEnum.LastWeek
|
||||
|| compareType === TableDateCompareTypeEnum.NextWeek
|
||||
|| compareType === TableDateCompareTypeEnum.ThisMonth
|
||||
|| compareType === TableDateCompareTypeEnum.LastMonth
|
||||
|| compareType === TableDateCompareTypeEnum.NextMonth
|
||||
|| compareType === TableDateCompareTypeEnum.ThisYear
|
||||
|| compareType === TableDateCompareTypeEnum.LastYear
|
||||
|| compareType === TableDateCompareTypeEnum.NextYear
|
||||
) {
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: compareType,
|
||||
info: {},
|
||||
};
|
||||
} else if (datePickerSet.has(compareType as TableDateCompareTypeEnum)) {
|
||||
// ensure expectedValue is a date or an array of two dates
|
||||
let date: Date | [Date, Date] | undefined;
|
||||
|
||||
if (typeof filterInfo.expectedValue === 'string') {
|
||||
date = new Date(filterInfo.expectedValue);
|
||||
} else if (Array.isArray(filterInfo.expectedValue)) {
|
||||
for (let i = 0; i < filterInfo.expectedValue.length; i++) {
|
||||
if (typeof filterInfo.expectedValue[i] === 'string') {
|
||||
filterInfo.expectedValue[i] = new Date(filterInfo.expectedValue[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: compareType,
|
||||
info: {
|
||||
date,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const quarter = new Set([TableDateCompareTypeEnum.Q1, TableDateCompareTypeEnum.Q2, TableDateCompareTypeEnum.Q3, TableDateCompareTypeEnum.Q4]);
|
||||
if (quarter.has(compareType as TableDateCompareTypeEnum)) {
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: TableDateCompareTypeEnum.Quarter,
|
||||
info: {
|
||||
dateSelect: filterInfo.compareType,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: TableDateCompareTypeEnum.Month,
|
||||
info: {
|
||||
dateSelect: filterInfo.compareType,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (conditionType === TableConditionTypeEnum.Number) {
|
||||
if (compareType === TableNumberCompareTypeEnum.Between || compareType === TableNumberCompareTypeEnum.NotBetween) {
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: compareType,
|
||||
info: {
|
||||
numberRange: filterInfo.expectedValue,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: compareType,
|
||||
info: {
|
||||
number: filterInfo.expectedValue,
|
||||
},
|
||||
};
|
||||
}
|
||||
} else if (conditionType === TableConditionTypeEnum.String) {
|
||||
return {
|
||||
type: conditionType,
|
||||
compare: compareType,
|
||||
info: {
|
||||
string: filterInfo.expectedValue,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: TableConditionTypeEnum.String,
|
||||
compare: TableStringCompareTypeEnum.Equal,
|
||||
info: {},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user