@tcn/ui-table 2.3.6 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/cells/header_cell.d.ts.map +1 -1
- package/dist/components/cells/header_cell.js +6 -6
- package/dist/components/cells/header_cell.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/clear_button.d.ts +5 -0
- package/dist/components/table_filter_panel/field_filters/clear_button.d.ts.map +1 -0
- package/dist/components/table_filter_panel/field_filters/clear_button.js +15 -0
- package/dist/components/table_filter_panel/field_filters/clear_button.js.map +1 -0
- package/dist/components/table_filter_panel/field_filters/clearable_field.d.ts +9 -0
- package/dist/components/table_filter_panel/field_filters/clearable_field.d.ts.map +1 -0
- package/dist/components/table_filter_panel/field_filters/clearable_field.js +28 -0
- package/dist/components/table_filter_panel/field_filters/clearable_field.js.map +1 -0
- package/dist/components/table_filter_panel/field_filters/date_field_filter.d.ts.map +1 -1
- package/dist/components/table_filter_panel/field_filters/date_field_filter.js +45 -54
- package/dist/components/table_filter_panel/field_filters/date_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/mulit_select_field_filter.d.ts.map +1 -1
- package/dist/components/table_filter_panel/field_filters/mulit_select_field_filter.js +25 -28
- package/dist/components/table_filter_panel/field_filters/mulit_select_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/number_field_filter.d.ts.map +1 -1
- package/dist/components/table_filter_panel/field_filters/number_field_filter.js +37 -44
- package/dist/components/table_filter_panel/field_filters/number_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/number_range_field_filter.d.ts.map +1 -1
- package/dist/components/table_filter_panel/field_filters/number_range_field_filter.js +46 -55
- package/dist/components/table_filter_panel/field_filters/number_range_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/select_field_filter.d.ts.map +1 -1
- package/dist/components/table_filter_panel/field_filters/select_field_filter.js +19 -22
- package/dist/components/table_filter_panel/field_filters/select_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/string_field_filter.d.ts.map +1 -1
- package/dist/components/table_filter_panel/field_filters/string_field_filter.js +45 -53
- package/dist/components/table_filter_panel/field_filters/string_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/table_filter_panel.d.ts +4 -3
- package/dist/components/table_filter_panel/table_filter_panel.d.ts.map +1 -1
- package/dist/components/table_filter_panel/table_filter_panel.js +38 -10
- package/dist/components/table_filter_panel/table_filter_panel.js.map +1 -1
- package/dist/table_filter_panel.css +1 -0
- package/package.json +4 -4
- package/src/__stories__/aip_table.stories.tsx +3 -3
- package/src/__stories__/demo.stories.tsx +4 -4
- package/src/__stories__/table.stories.tsx +14 -11
- package/src/components/cells/header_cell.tsx +1 -1
- package/src/components/table_filter_panel/field_filters/clear_button.tsx +17 -0
- package/src/components/table_filter_panel/field_filters/clearable_field.tsx +33 -0
- package/src/components/table_filter_panel/field_filters/date_field_filter.tsx +26 -50
- package/src/components/table_filter_panel/field_filters/mulit_select_field_filter.tsx +3 -9
- package/src/components/table_filter_panel/field_filters/number_field_filter.tsx +10 -20
- package/src/components/table_filter_panel/field_filters/number_range_field_filter.tsx +29 -48
- package/src/components/table_filter_panel/field_filters/select_field_filter.tsx +3 -9
- package/src/components/table_filter_panel/field_filters/string_field_filter.tsx +10 -21
- package/src/components/table_filter_panel/table_filter_panel.module.css +11 -0
- package/src/components/table_filter_panel/table_filter_panel.tsx +33 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Meta } from '@storybook/react-vite';
|
|
2
2
|
import React, { useCallback, useState } from 'react';
|
|
3
3
|
|
|
4
|
+
import { CrossIcon } from '@tcn/icons/cross_icon.js';
|
|
4
5
|
import {
|
|
5
6
|
StaticDataSource,
|
|
6
7
|
StaticDateField,
|
|
@@ -9,8 +10,9 @@ import {
|
|
|
9
10
|
} from '@tcn/resource-store';
|
|
10
11
|
import { useSignalValue } from '@tcn/state';
|
|
11
12
|
import { Button } from '@tcn/ui/actions';
|
|
13
|
+
import { Footer, Header, HBody, Rail, Side, VBody } from '@tcn/ui/layouts';
|
|
12
14
|
import { Box, HStack, Spacer, VStack, ZStack } from '@tcn/ui/stacks';
|
|
13
|
-
import {
|
|
15
|
+
import { Panel } from '@tcn/ui/surfaces';
|
|
14
16
|
import { Title } from '@tcn/ui/typography';
|
|
15
17
|
import { GlobalSearch } from '../components/global_search.js';
|
|
16
18
|
import { Table } from '../components/table/table.js';
|
|
@@ -22,9 +24,7 @@ import { StringFieldFilter } from '../components/table_filter_panel/field_filter
|
|
|
22
24
|
import { TableFilterPanel } from '../components/table_filter_panel/table_filter_panel.js';
|
|
23
25
|
import { TablePager } from '../components/table_pager.js';
|
|
24
26
|
import { DataItem, items, stickyItems } from './sample_data.js';
|
|
25
|
-
import { CrossIcon } from '@tcn/icons/cross_icon.js';
|
|
26
27
|
import styles from './table.module.css';
|
|
27
|
-
import { Footer, Header, Main, Rail, Side, VBody } from '@tcn/ui/layouts';
|
|
28
28
|
|
|
29
29
|
const meta: Meta = {
|
|
30
30
|
title: 'Table',
|
|
@@ -302,7 +302,7 @@ export function WithTableHeaderAndTableFooter() {
|
|
|
302
302
|
|
|
303
303
|
return (
|
|
304
304
|
<StoryWrapper>
|
|
305
|
-
<
|
|
305
|
+
<Panel>
|
|
306
306
|
<Header className={styles.header} padding="8px" vAlign="center">
|
|
307
307
|
<Title>The Table</Title>
|
|
308
308
|
<Spacer />
|
|
@@ -327,7 +327,7 @@ export function WithTableHeaderAndTableFooter() {
|
|
|
327
327
|
<Footer>
|
|
328
328
|
<TablePager dataSource={source} />
|
|
329
329
|
</Footer>
|
|
330
|
-
</
|
|
330
|
+
</Panel>
|
|
331
331
|
</StoryWrapper>
|
|
332
332
|
);
|
|
333
333
|
}
|
|
@@ -409,11 +409,11 @@ export function WithFilterPanel() {
|
|
|
409
409
|
|
|
410
410
|
return (
|
|
411
411
|
<StoryWrapper>
|
|
412
|
-
<
|
|
412
|
+
<Panel height="100%">
|
|
413
413
|
<Header>The Table</Header>
|
|
414
414
|
<VBody>
|
|
415
415
|
<Rail>
|
|
416
|
-
<Side>
|
|
416
|
+
<Side padding="0px">
|
|
417
417
|
<Box
|
|
418
418
|
minWidth="300px"
|
|
419
419
|
enableResizeOnEnd
|
|
@@ -423,7 +423,10 @@ export function WithFilterPanel() {
|
|
|
423
423
|
scrollbarGutter: 'stable', // Not sure if there is a better way to prevent the scrollbar appearing causing a horizontal scroll - to to resizing, we fix the width of the content, so adding a vertical scrollbar causing a horizontal overflow.
|
|
424
424
|
}}
|
|
425
425
|
>
|
|
426
|
-
<TableFilterPanel
|
|
426
|
+
<TableFilterPanel
|
|
427
|
+
dataSource={source}
|
|
428
|
+
onClose={() => window.alert('Closed')}
|
|
429
|
+
>
|
|
427
430
|
<StringFieldFilter fieldName="name" label="Name (string)" />
|
|
428
431
|
<NumberFieldFilter fieldName="age" label="Age (number)" />
|
|
429
432
|
<DateFieldFilter fieldName="birthdate" label="Birthdate (date range)" />
|
|
@@ -431,7 +434,7 @@ export function WithFilterPanel() {
|
|
|
431
434
|
</TableFilterPanel>
|
|
432
435
|
</Box>
|
|
433
436
|
</Side>
|
|
434
|
-
<
|
|
437
|
+
<HBody>
|
|
435
438
|
<Table dataSource={source} height="100%" width="flex">
|
|
436
439
|
<TableColumn heading="Name" fieldName="name" sticky="start" />
|
|
437
440
|
<TableColumn heading="Age" fieldName="age" width={150} canSort />
|
|
@@ -446,13 +449,13 @@ export function WithFilterPanel() {
|
|
|
446
449
|
<TableColumn heading="Occupation" fieldName="occupation" width={200} />
|
|
447
450
|
<TableColumn heading="Active" fieldName="isActive" />
|
|
448
451
|
</Table>
|
|
449
|
-
</
|
|
452
|
+
</HBody>
|
|
450
453
|
</Rail>
|
|
451
454
|
</VBody>
|
|
452
455
|
<Footer>
|
|
453
456
|
<TablePager dataSource={source} />
|
|
454
457
|
</Footer>
|
|
455
|
-
</
|
|
458
|
+
</Panel>
|
|
456
459
|
</StoryWrapper>
|
|
457
460
|
);
|
|
458
461
|
}
|
|
@@ -2,9 +2,9 @@ import React, { useCallback } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Box, HStack } from '@tcn/ui/stacks';
|
|
4
4
|
|
|
5
|
+
import { TH } from '@tcn/ui/layouts';
|
|
5
6
|
import cellStyles from './cell.module.css';
|
|
6
7
|
import { SortControl, type SortControlProps } from './sort_control.js';
|
|
7
|
-
import { TH } from '@tcn/ui/layouts';
|
|
8
8
|
|
|
9
9
|
export interface HeaderCellProps extends SortControlProps {
|
|
10
10
|
heading: React.ReactNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
2
|
+
import { Button, type ButtonProps } from '@tcn/ui/actions';
|
|
3
|
+
|
|
4
|
+
export interface ClearFilterButtonProps extends Omit<ButtonProps, 'children'> {}
|
|
5
|
+
|
|
6
|
+
export function ClearFilterButton({
|
|
7
|
+
onClick,
|
|
8
|
+
hierarchy = 'tertiary',
|
|
9
|
+
utility = true,
|
|
10
|
+
...props
|
|
11
|
+
}: ClearFilterButtonProps) {
|
|
12
|
+
return (
|
|
13
|
+
<Button utility={utility} onClick={onClick} hierarchy={hierarchy} {...props}>
|
|
14
|
+
<CrossCircleIcon />
|
|
15
|
+
</Button>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HStack, VStack, type VStackProps } from '@tcn/ui/stacks';
|
|
2
|
+
import { ClearFilterButton } from './clear_button.js';
|
|
3
|
+
import { FieldLabel } from '@tcn/ui/form';
|
|
4
|
+
|
|
5
|
+
export interface ClearableFieldProps extends VStackProps {
|
|
6
|
+
label?: string;
|
|
7
|
+
onClear?: () => void;
|
|
8
|
+
isClearable?: boolean;
|
|
9
|
+
inline?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ClearableField({
|
|
13
|
+
children,
|
|
14
|
+
label,
|
|
15
|
+
onClear,
|
|
16
|
+
isClearable,
|
|
17
|
+
vAlign = 'start',
|
|
18
|
+
hAlign = 'start',
|
|
19
|
+
gap = '4px',
|
|
20
|
+
inline = false,
|
|
21
|
+
...props
|
|
22
|
+
}: ClearableFieldProps) {
|
|
23
|
+
return (
|
|
24
|
+
<VStack vAlign={vAlign} hAlign={hAlign} gap={gap} {...props}>
|
|
25
|
+
{!inline && <FieldLabel>{label}</FieldLabel>}
|
|
26
|
+
<HStack width="flex" gap="4px">
|
|
27
|
+
{inline && <FieldLabel>{label}</FieldLabel>}
|
|
28
|
+
<HStack width="flex">{children}</HStack>
|
|
29
|
+
<ClearFilterButton onClick={onClear} disabled={isClearable} />
|
|
30
|
+
</HStack>
|
|
31
|
+
</VStack>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
2
1
|
import { useSignalValue } from '@tcn/state';
|
|
3
|
-
import { Button } from '@tcn/ui/actions';
|
|
4
2
|
import { DatePickerInput } from '@tcn/ui/inputs';
|
|
5
|
-
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
6
|
-
import { BodyText, Title } from '@tcn/ui/typography';
|
|
7
|
-
import React from 'react';
|
|
8
3
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
9
4
|
import { DateFieldFilterPresenter } from './date_field_filter_presenter.js';
|
|
5
|
+
import { ClearableField } from './clearable_field.js';
|
|
6
|
+
import { FieldSet } from '@tcn/ui/form';
|
|
10
7
|
|
|
11
8
|
export function DateFieldFilter({
|
|
12
9
|
fieldName,
|
|
@@ -21,50 +18,29 @@ export function DateFieldFilter({
|
|
|
21
18
|
const endDate = useSignalValue(presenter.broadcasts.endDate);
|
|
22
19
|
|
|
23
20
|
return (
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<HStack gap="4px">
|
|
49
|
-
<Box width="auto">
|
|
50
|
-
<BodyText size="lg">End</BodyText>
|
|
51
|
-
</Box>
|
|
52
|
-
<Box width="flex">
|
|
53
|
-
<DatePickerInput
|
|
54
|
-
value={endDate}
|
|
55
|
-
onChange={value => presenter.setEndDate(value)}
|
|
56
|
-
/>
|
|
57
|
-
</Box>
|
|
58
|
-
<Box width="auto">
|
|
59
|
-
<Button
|
|
60
|
-
onClick={() => presenter.setEndDate(null)}
|
|
61
|
-
hierarchy="tertiary"
|
|
62
|
-
disabled={endDate == null}
|
|
63
|
-
>
|
|
64
|
-
<CrossCircleIcon />
|
|
65
|
-
</Button>
|
|
66
|
-
</Box>
|
|
67
|
-
</HStack>
|
|
68
|
-
</VStack>
|
|
21
|
+
<FieldSet legend={label} gap="4px">
|
|
22
|
+
<ClearableField
|
|
23
|
+
inline
|
|
24
|
+
label="Start"
|
|
25
|
+
onClear={() => presenter.setStartDate(null)}
|
|
26
|
+
isClearable={startDate == null}
|
|
27
|
+
>
|
|
28
|
+
<DatePickerInput
|
|
29
|
+
value={startDate}
|
|
30
|
+
onChange={value => presenter.setStartDate(value)}
|
|
31
|
+
/>
|
|
32
|
+
</ClearableField>
|
|
33
|
+
<ClearableField
|
|
34
|
+
inline
|
|
35
|
+
label="End"
|
|
36
|
+
onClear={() => presenter.setEndDate(null)}
|
|
37
|
+
isClearable={endDate == null}
|
|
38
|
+
>
|
|
39
|
+
<DatePickerInput
|
|
40
|
+
value={endDate}
|
|
41
|
+
onChange={value => presenter.setEndDate(value)}
|
|
42
|
+
/>
|
|
43
|
+
</ClearableField>
|
|
44
|
+
</FieldSet>
|
|
69
45
|
);
|
|
70
46
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
4
1
|
import { useSignalValue } from '@tcn/state';
|
|
5
|
-
import { Button } from '@tcn/ui/actions';
|
|
6
2
|
import { Multiselect, Option } from '@tcn/ui/inputs';
|
|
7
3
|
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
8
4
|
import { Title } from '@tcn/ui/typography';
|
|
9
5
|
import { FieldFilterProps } from './field_filter_props.js';
|
|
10
6
|
import { MultiSelectFieldFilterPresenter } from './multi_select_field_filter_presenter.js';
|
|
11
7
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
8
|
+
import { ClearFilterButton } from './clear_button.js';
|
|
12
9
|
|
|
13
10
|
export type MulitSelectFieldFilterProps = FieldFilterProps & {
|
|
14
11
|
options: { label: string; value: string | boolean | number }[];
|
|
@@ -55,13 +52,10 @@ export function MulitSelectFieldFilter({
|
|
|
55
52
|
))}
|
|
56
53
|
</Multiselect>
|
|
57
54
|
</Box>
|
|
58
|
-
<
|
|
55
|
+
<ClearFilterButton
|
|
59
56
|
onClick={() => presenter.setValue(null)}
|
|
60
|
-
hierarchy="tertiary"
|
|
61
57
|
disabled={values == null || values.length === 0}
|
|
62
|
-
|
|
63
|
-
<CrossCircleIcon />
|
|
64
|
-
</Button>
|
|
58
|
+
/>
|
|
65
59
|
</HStack>
|
|
66
60
|
</VStack>
|
|
67
61
|
);
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
2
1
|
import { useSignalValue } from '@tcn/state';
|
|
3
|
-
import {
|
|
4
|
-
import { Input, Option, Select } from '@tcn/ui/inputs';
|
|
5
|
-
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
6
|
-
import { Title } from '@tcn/ui/typography';
|
|
7
|
-
import React from 'react';
|
|
2
|
+
import { Input, Option, Select, InputGroup } from '@tcn/ui/inputs';
|
|
8
3
|
import { FieldFilterProps } from './field_filter_props.js';
|
|
9
4
|
import { NumberFieldFilterPresenter } from './number_field_filter_presenter.js';
|
|
10
5
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
11
6
|
import { ComparisonOperator } from '../types.js';
|
|
7
|
+
import { ClearableField } from './clearable_field.js';
|
|
12
8
|
|
|
13
9
|
const operators: ComparisonOperator[] = ['>=', '>', '<=', '<', '=', '!='];
|
|
14
10
|
|
|
@@ -19,11 +15,12 @@ export function NumberFieldFilter({ fieldName, label }: FieldFilterProps) {
|
|
|
19
15
|
const operator = useSignalValue(presenter.broadcasts.operator);
|
|
20
16
|
|
|
21
17
|
return (
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
<ClearableField
|
|
19
|
+
label={label}
|
|
20
|
+
onClear={() => presenter.setValue(null)}
|
|
21
|
+
isClearable={value == null}
|
|
22
|
+
>
|
|
23
|
+
<InputGroup>
|
|
27
24
|
<Select
|
|
28
25
|
value={operator}
|
|
29
26
|
onChange={value => presenter.setOperator(value as ComparisonOperator)}
|
|
@@ -40,14 +37,7 @@ export function NumberFieldFilter({ fieldName, label }: FieldFilterProps) {
|
|
|
40
37
|
value={String(value ?? '')}
|
|
41
38
|
onChange={value => presenter.setValue(Number(value))}
|
|
42
39
|
/>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
hierarchy="tertiary"
|
|
46
|
-
disabled={value == null}
|
|
47
|
-
>
|
|
48
|
-
<CrossCircleIcon />
|
|
49
|
-
</Button>
|
|
50
|
-
</HStack>
|
|
51
|
-
</VStack>
|
|
40
|
+
</InputGroup>
|
|
41
|
+
</ClearableField>
|
|
52
42
|
);
|
|
53
43
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
2
1
|
import { useSignalValue } from '@tcn/state';
|
|
3
|
-
import { Button } from '@tcn/ui/actions';
|
|
4
2
|
import { Input } from '@tcn/ui/inputs';
|
|
5
|
-
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
6
|
-
import { BodyText, Title } from '@tcn/ui/typography';
|
|
7
|
-
import React from 'react';
|
|
8
3
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
9
4
|
import { NumberRangeFieldFilterPresenter } from './number_range_field_filter_presenter.js';
|
|
5
|
+
import { FieldSet } from '@tcn/ui/form';
|
|
6
|
+
import { ClearableField } from './clearable_field.js';
|
|
10
7
|
|
|
11
8
|
export function NumberRangeFieldFilter({
|
|
12
9
|
fieldName,
|
|
@@ -21,48 +18,32 @@ export function NumberRangeFieldFilter({
|
|
|
21
18
|
const maxValue = useSignalValue(presenter.broadcasts.maxValue);
|
|
22
19
|
|
|
23
20
|
return (
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<Box width="flex">
|
|
52
|
-
<Input
|
|
53
|
-
type="number"
|
|
54
|
-
value={String(maxValue ?? '')}
|
|
55
|
-
onChange={value => presenter.setMaxValue(Number(value))}
|
|
56
|
-
/>
|
|
57
|
-
</Box>
|
|
58
|
-
<Button
|
|
59
|
-
onClick={() => presenter.setMaxValue(null)}
|
|
60
|
-
hierarchy="tertiary"
|
|
61
|
-
disabled={maxValue == null}
|
|
62
|
-
>
|
|
63
|
-
<CrossCircleIcon />
|
|
64
|
-
</Button>
|
|
65
|
-
</HStack>
|
|
66
|
-
</VStack>
|
|
21
|
+
<FieldSet legend={label} gap="4px">
|
|
22
|
+
<ClearableField
|
|
23
|
+
label="Min"
|
|
24
|
+
onClear={() => presenter.setMinValue(null)}
|
|
25
|
+
isClearable={minValue == null}
|
|
26
|
+
inline
|
|
27
|
+
>
|
|
28
|
+
<Input
|
|
29
|
+
type="number"
|
|
30
|
+
value={String(minValue ?? '')}
|
|
31
|
+
onChange={value => presenter.setMinValue(Number(value))}
|
|
32
|
+
/>
|
|
33
|
+
</ClearableField>
|
|
34
|
+
|
|
35
|
+
<ClearableField
|
|
36
|
+
label="Max"
|
|
37
|
+
onClear={() => presenter.setMaxValue(null)}
|
|
38
|
+
isClearable={maxValue == null}
|
|
39
|
+
inline
|
|
40
|
+
>
|
|
41
|
+
<Input
|
|
42
|
+
type="number"
|
|
43
|
+
value={String(maxValue ?? '')}
|
|
44
|
+
onChange={value => presenter.setMaxValue(Number(value))}
|
|
45
|
+
/>
|
|
46
|
+
</ClearableField>
|
|
47
|
+
</FieldSet>
|
|
67
48
|
);
|
|
68
49
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
4
1
|
import { useSignalValue } from '@tcn/state';
|
|
5
|
-
import { Button } from '@tcn/ui/actions';
|
|
6
2
|
import { Option, Select } from '@tcn/ui/inputs';
|
|
7
3
|
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
8
4
|
import { Title } from '@tcn/ui/typography';
|
|
9
5
|
import { FieldFilterProps } from './field_filter_props.js';
|
|
10
6
|
import { SelectFieldFilterPresenter } from './select_field_filter_presenter.js';
|
|
11
7
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
8
|
+
import { ClearFilterButton } from './clear_button.js';
|
|
12
9
|
|
|
13
10
|
export type SelectFieldFilterProps = FieldFilterProps & {
|
|
14
11
|
options: { label: string; value: string | boolean | number }[];
|
|
@@ -44,13 +41,10 @@ export function SelectFieldFilter({ fieldName, label, options }: SelectFieldFilt
|
|
|
44
41
|
</Option>
|
|
45
42
|
))}
|
|
46
43
|
</Select>
|
|
47
|
-
<
|
|
44
|
+
<ClearFilterButton
|
|
48
45
|
onClick={() => presenter.setValue(null)}
|
|
49
|
-
hierarchy="tertiary"
|
|
50
46
|
disabled={value == null}
|
|
51
|
-
|
|
52
|
-
<CrossCircleIcon />
|
|
53
|
-
</Button>
|
|
47
|
+
/>
|
|
54
48
|
</HStack>
|
|
55
49
|
</VStack>
|
|
56
50
|
);
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';
|
|
2
1
|
import { useSignalValue } from '@tcn/state';
|
|
3
|
-
import {
|
|
4
|
-
import { Input, Option, Select } from '@tcn/ui/inputs';
|
|
5
|
-
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
6
|
-
import { Title } from '@tcn/ui/typography';
|
|
7
|
-
import React from 'react';
|
|
2
|
+
import { Input, Option, Select, InputGroup } from '@tcn/ui/inputs';
|
|
8
3
|
import { FieldFilterProps } from './field_filter_props.js';
|
|
9
4
|
import { StringFieldFilterPresenter } from './string_field_filter_presenter.js';
|
|
10
5
|
import { ComparisonOperator } from '../types.js';
|
|
11
6
|
|
|
12
7
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
8
|
+
import { ClearableField } from './clearable_field.js';
|
|
13
9
|
|
|
14
10
|
const allOperators: ('is' | 'isNot' | 'has')[] = ['is', 'isNot', 'has'];
|
|
15
11
|
const operatorSymbols: Record<'is' | 'isNot' | 'has', string> = {
|
|
@@ -32,11 +28,12 @@ export function StringFieldFilter({ fieldName, label, operators }: FieldFilterPr
|
|
|
32
28
|
const availableOperators = operators || allOperators;
|
|
33
29
|
|
|
34
30
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
<ClearableField
|
|
32
|
+
label={label}
|
|
33
|
+
onClear={() => presenter.setValue(null)}
|
|
34
|
+
isClearable={value == null}
|
|
35
|
+
>
|
|
36
|
+
<InputGroup>
|
|
40
37
|
<Select
|
|
41
38
|
value={operator}
|
|
42
39
|
onChange={value => presenter.setOperator(value as ComparisonOperator)}
|
|
@@ -57,15 +54,7 @@ export function StringFieldFilter({ fieldName, label, operators }: FieldFilterPr
|
|
|
57
54
|
value={value ?? ''}
|
|
58
55
|
onChange={value => presenter.setValue(value)}
|
|
59
56
|
/>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
hierarchy="tertiary"
|
|
63
|
-
utility
|
|
64
|
-
disabled={value == null}
|
|
65
|
-
>
|
|
66
|
-
<CrossCircleIcon />
|
|
67
|
-
</Button>
|
|
68
|
-
</HStack>
|
|
69
|
-
</VStack>
|
|
57
|
+
</InputGroup>
|
|
58
|
+
</ClearableField>
|
|
70
59
|
);
|
|
71
60
|
}
|
|
@@ -1,22 +1,51 @@
|
|
|
1
1
|
import { DataSource } from '@tcn/resource-store';
|
|
2
|
-
import {
|
|
3
|
-
import React, { ReactElement, useState, createContext } from 'react';
|
|
2
|
+
import { ReactElement, useState, createContext } from 'react';
|
|
4
3
|
import { FieldFilterProps } from './field_filters/field_filter_props.js';
|
|
5
4
|
import { TableFilterPanelPresenter } from './table_filter_panel_presenter.js';
|
|
5
|
+
import { Panel } from '@tcn/ui/surfaces';
|
|
6
|
+
import { Header, Section, VBody } from '@tcn/ui/layouts';
|
|
7
|
+
import styles from './table_filter_panel.module.css';
|
|
8
|
+
import { Spacer } from '@tcn/ui/stacks';
|
|
9
|
+
import { Button } from '@tcn/ui/actions';
|
|
10
|
+
import { CrossIcon } from '@tcn/icons/cross_icon.js';
|
|
6
11
|
|
|
7
12
|
export type TableFilterPanelProps = {
|
|
8
13
|
children: ReactElement<FieldFilterProps>[] | ReactElement<FieldFilterProps>;
|
|
9
14
|
dataSource: DataSource<any>;
|
|
15
|
+
onClose?: () => void;
|
|
10
16
|
};
|
|
11
17
|
|
|
12
|
-
export function TableFilterPanel({
|
|
18
|
+
export function TableFilterPanel({
|
|
19
|
+
children,
|
|
20
|
+
dataSource,
|
|
21
|
+
onClose,
|
|
22
|
+
}: TableFilterPanelProps) {
|
|
13
23
|
const [presenter] = useState(() => {
|
|
14
24
|
return new TableFilterPanelPresenter(dataSource);
|
|
15
25
|
});
|
|
16
26
|
|
|
17
27
|
return (
|
|
18
28
|
<TableFilterPanelContext.Provider value={presenter}>
|
|
19
|
-
<
|
|
29
|
+
<Panel className={`${styles['table-filter-panel']} tcn-table-filter-panel`}>
|
|
30
|
+
<Header>
|
|
31
|
+
Table Filters
|
|
32
|
+
<Spacer />
|
|
33
|
+
{onClose && (
|
|
34
|
+
<Button utility hierarchy="tertiary" onClick={onClose}>
|
|
35
|
+
<CrossIcon />
|
|
36
|
+
</Button>
|
|
37
|
+
)}
|
|
38
|
+
</Header>
|
|
39
|
+
<VBody
|
|
40
|
+
className={`${styles['table-filter-panel-body']} tcn-table-filter-panel-body`}
|
|
41
|
+
>
|
|
42
|
+
<Section
|
|
43
|
+
className={`${styles['table-filter-panel-section']} tcn-table-filter-panel-section`}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</Section>
|
|
47
|
+
</VBody>
|
|
48
|
+
</Panel>
|
|
20
49
|
</TableFilterPanelContext.Provider>
|
|
21
50
|
);
|
|
22
51
|
}
|