Skip to content

Commit

Permalink
Convert FiltersMenu to Typescript (part 1) (actualbudget#2231)
Browse files Browse the repository at this point in the history
* migration work

* notes

* typecheck

* typecheck fixes

* fixes

* merge fixes

* typecheck updates

* review fixes
  • Loading branch information
carkom authored Feb 2, 2024
1 parent 7752be5 commit 54e0a97
Show file tree
Hide file tree
Showing 14 changed files with 351 additions and 264 deletions.
55 changes: 55 additions & 0 deletions packages/desktop-client/src/components/filters/AppliedFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';

import { type RuleConditionEntity } from 'loot-core/types/models';

import { View } from '../common/View';

import { FilterExpression } from './FilterExpression';
import { CondOpMenu } from './SavedFilters';

type AppliedFiltersProps = {
filters: RuleConditionEntity[];
onUpdate: (
filter: RuleConditionEntity,
newFilter: RuleConditionEntity,
) => RuleConditionEntity;
onDelete: (filter: RuleConditionEntity) => void;
conditionsOp: string;
onCondOpChange: () => void;
};

export function AppliedFilters({
filters,
onUpdate,
onDelete,
conditionsOp,
onCondOpChange,
}: AppliedFiltersProps) {
return (
<View
style={{
flexDirection: 'row',
alignItems: 'flex-start',
flexWrap: 'wrap',
}}
>
<CondOpMenu
conditionsOp={conditionsOp}
onCondOpChange={onCondOpChange}
filters={filters}
/>
{filters.map((filter: RuleConditionEntity, i: number) => (
<FilterExpression
key={i}
customName={filter.customName}
field={filter.field}
op={filter.op}
value={filter.value}
options={filter.options}
onChange={newFilter => onUpdate(filter, newFilter)}
onDelete={() => onDelete(filter)}
/>
))}
</View>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
// @ts-strict-ignore
import React from 'react';

import { SvgFilter } from '../../icons/v1';
import { Button } from '../common/Button';

type CompactFiltersButtonProps = {
onClick: (newValue) => void;
};

export function CompactFiltersButton({ onClick }: CompactFiltersButtonProps) {
export function CompactFiltersButton({ onClick }: { onClick: () => void }) {
return (
<Button type="bare" onClick={onClick}>
<SvgFilter width={15} height={15} />
Expand Down
108 changes: 108 additions & 0 deletions packages/desktop-client/src/components/filters/FilterExpression.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useState } from 'react';

import { mapField, friendlyOp } from 'loot-core/src/shared/rules';
import { integerToCurrency } from 'loot-core/src/shared/util';
import {
type RuleConditionOp,
type RuleConditionEntity,
} from 'loot-core/src/types/models';

import { SvgDelete } from '../../icons/v0';
import { type CSSProperties, theme } from '../../style';
import { Button } from '../common/Button';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { Value } from '../rules/Value';

import { FilterEditor } from './FiltersMenu';
import { subfieldFromFilter } from './subfieldFromFilter';

type FilterExpressionProps = {
field: string | undefined;
customName: string | undefined;
op: RuleConditionOp | undefined;
value: string | string[] | number | boolean | undefined;
options: RuleConditionEntity['options'];
style?: CSSProperties;
onChange: (cond: RuleConditionEntity) => RuleConditionEntity;
onDelete: () => void;
};

export function FilterExpression({
field: originalField,
customName,
op,
value,
options,
style,
onChange,
onDelete,
}: FilterExpressionProps) {
const [editing, setEditing] = useState(false);

const field = subfieldFromFilter({ field: originalField, value });

return (
<View
style={{
backgroundColor: theme.pillBackground,
borderRadius: 4,
flexDirection: 'row',
alignItems: 'center',
marginRight: 10,
marginTop: 10,
...style,
}}
>
<Button
type="bare"
disabled={customName != null}
onClick={() => setEditing(true)}
style={{ marginRight: -7 }}
>
<div style={{ paddingBlock: 1, paddingLeft: 5, paddingRight: 2 }}>
{customName ? (
<Text style={{ color: theme.pageTextPositive }}>{customName}</Text>
) : (
<>
<Text style={{ color: theme.pageTextPositive }}>
{mapField(field, options)}
</Text>{' '}
<Text>{friendlyOp(op, null)}</Text>{' '}
<Value
value={value}
field={field}
inline={true}
valueIsRaw={op === 'contains' || op === 'doesNotContain'}
/>
</>
)}
</div>
</Button>
<Button type="bare" onClick={onDelete} aria-label="Delete filter">
<SvgDelete
style={{
width: 8,
height: 8,
margin: 5,
marginLeft: 3,
}}
/>
</Button>
{editing && (
<FilterEditor
field={originalField}
op={op}
value={
field === 'amount' && typeof value === 'number'
? integerToCurrency(value)
: value
}
options={options}
onSave={onChange}
onClose={() => setEditing(false)}
/>
)}
</View>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
// @ts-strict-ignore
import React from 'react';

import { SvgSettingsSliderAlternate } from '../../icons/v2';
import { Button } from '../common/Button';

type FiltersButtonProps = {
onClick: (newValue) => void;
};

export function FiltersButton({ onClick }: FiltersButtonProps) {
export function FiltersButton({ onClick }: { onClick: () => void }) {
return (
<Button type="bare" onClick={onClick} title="Filters">
<SvgSettingsSliderAlternate
Expand Down
Loading

0 comments on commit 54e0a97

Please sign in to comment.