@tcn/ui-table 2.3.7 → 2.3.9

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.
Files changed (43) hide show
  1. package/dist/components/table_filter_panel/field_filters/clear_button.d.ts +5 -0
  2. package/dist/components/table_filter_panel/field_filters/clear_button.d.ts.map +1 -0
  3. package/dist/components/table_filter_panel/field_filters/clear_button.js +15 -0
  4. package/dist/components/table_filter_panel/field_filters/clear_button.js.map +1 -0
  5. package/dist/components/table_filter_panel/field_filters/clearable_field.d.ts +9 -0
  6. package/dist/components/table_filter_panel/field_filters/clearable_field.d.ts.map +1 -0
  7. package/dist/components/table_filter_panel/field_filters/clearable_field.js +28 -0
  8. package/dist/components/table_filter_panel/field_filters/clearable_field.js.map +1 -0
  9. package/dist/components/table_filter_panel/field_filters/date_field_filter.d.ts.map +1 -1
  10. package/dist/components/table_filter_panel/field_filters/date_field_filter.js +45 -54
  11. package/dist/components/table_filter_panel/field_filters/date_field_filter.js.map +1 -1
  12. package/dist/components/table_filter_panel/field_filters/mulit_select_field_filter.d.ts.map +1 -1
  13. package/dist/components/table_filter_panel/field_filters/mulit_select_field_filter.js +25 -28
  14. package/dist/components/table_filter_panel/field_filters/mulit_select_field_filter.js.map +1 -1
  15. package/dist/components/table_filter_panel/field_filters/number_field_filter.d.ts.map +1 -1
  16. package/dist/components/table_filter_panel/field_filters/number_field_filter.js +37 -44
  17. package/dist/components/table_filter_panel/field_filters/number_field_filter.js.map +1 -1
  18. package/dist/components/table_filter_panel/field_filters/number_range_field_filter.d.ts.map +1 -1
  19. package/dist/components/table_filter_panel/field_filters/number_range_field_filter.js +46 -55
  20. package/dist/components/table_filter_panel/field_filters/number_range_field_filter.js.map +1 -1
  21. package/dist/components/table_filter_panel/field_filters/select_field_filter.d.ts.map +1 -1
  22. package/dist/components/table_filter_panel/field_filters/select_field_filter.js +19 -22
  23. package/dist/components/table_filter_panel/field_filters/select_field_filter.js.map +1 -1
  24. package/dist/components/table_filter_panel/field_filters/string_field_filter.d.ts.map +1 -1
  25. package/dist/components/table_filter_panel/field_filters/string_field_filter.js +45 -53
  26. package/dist/components/table_filter_panel/field_filters/string_field_filter.js.map +1 -1
  27. package/dist/components/table_filter_panel/table_filter_panel.d.ts +4 -3
  28. package/dist/components/table_filter_panel/table_filter_panel.d.ts.map +1 -1
  29. package/dist/components/table_filter_panel/table_filter_panel.js +38 -10
  30. package/dist/components/table_filter_panel/table_filter_panel.js.map +1 -1
  31. package/dist/table_filter_panel.css +1 -0
  32. package/package.json +4 -4
  33. package/src/__stories__/table.stories.tsx +5 -2
  34. package/src/components/table_filter_panel/field_filters/clear_button.tsx +17 -0
  35. package/src/components/table_filter_panel/field_filters/clearable_field.tsx +33 -0
  36. package/src/components/table_filter_panel/field_filters/date_field_filter.tsx +26 -50
  37. package/src/components/table_filter_panel/field_filters/mulit_select_field_filter.tsx +3 -9
  38. package/src/components/table_filter_panel/field_filters/number_field_filter.tsx +10 -20
  39. package/src/components/table_filter_panel/field_filters/number_range_field_filter.tsx +29 -48
  40. package/src/components/table_filter_panel/field_filters/select_field_filter.tsx +3 -9
  41. package/src/components/table_filter_panel/field_filters/string_field_filter.tsx +10 -21
  42. package/src/components/table_filter_panel/table_filter_panel.module.css +11 -0
  43. package/src/components/table_filter_panel/table_filter_panel.tsx +33 -4
@@ -1,28 +1,27 @@
1
1
  import { jsxs as i, jsx as l } from "react/jsx-runtime";
2
- import { CrossCircleIcon as d } from "@tcn/icons/cross_circle_icon.js";
3
- import { useSignalValue as s } from "@tcn/state";
4
- import { Button as f } from "@tcn/ui/actions";
5
- import { Select as h, Option as p } from "@tcn/ui/inputs";
6
- import { VStack as b, Box as v, HStack as S } from "@tcn/ui/stacks";
7
- import { Title as x } from "@tcn/ui/typography";
8
- import { SelectFieldFilterPresenter as F } from "./select_field_filter_presenter.js";
9
- import { useFieldFilterStrategy as g } from "./use_field_filter_strategy.js";
10
- function I({ fieldName: n, label: o, options: r }) {
11
- const t = g(F, n), a = s(t.broadcasts.value), c = r.find((e) => e.value === a)?.label ?? "";
2
+ import { useSignalValue as d } from "@tcn/state";
3
+ import { Select as s, Option as f } from "@tcn/ui/inputs";
4
+ import { VStack as b, Box as h, HStack as p } from "@tcn/ui/stacks";
5
+ import { Title as v } from "@tcn/ui/typography";
6
+ import { SelectFieldFilterPresenter as S } from "./select_field_filter_presenter.js";
7
+ import { useFieldFilterStrategy as x } from "./use_field_filter_strategy.js";
8
+ import { ClearFilterButton as F } from "./clear_button.js";
9
+ function z({ fieldName: n, label: u, options: r }) {
10
+ const t = x(S, n), a = d(t.broadcasts.value), o = r.find((e) => e.value === a)?.label ?? "";
12
11
  return /* @__PURE__ */ i(b, { gap: "4px", children: [
13
- /* @__PURE__ */ l(v, { width: "flex", children: /* @__PURE__ */ l(x, { size: "md", children: o }) }),
14
- /* @__PURE__ */ i(S, { children: [
12
+ /* @__PURE__ */ l(h, { width: "flex", children: /* @__PURE__ */ l(v, { size: "md", children: u }) }),
13
+ /* @__PURE__ */ i(p, { children: [
15
14
  /* @__PURE__ */ l(
16
- h,
15
+ s,
17
16
  {
18
- value: c,
17
+ value: o,
19
18
  onChange: (e) => {
20
- const m = r.find((u) => u.label === e)?.value;
21
- t.setValue(m ?? null);
19
+ const c = r.find((m) => m.label === e)?.value;
20
+ t.setValue(c ?? null);
22
21
  },
23
22
  width: "flex",
24
23
  children: r.map((e) => /* @__PURE__ */ l(
25
- p,
24
+ f,
26
25
  {
27
26
  value: e.label,
28
27
  label: e.label,
@@ -33,18 +32,16 @@ function I({ fieldName: n, label: o, options: r }) {
33
32
  }
34
33
  ),
35
34
  /* @__PURE__ */ l(
36
- f,
35
+ F,
37
36
  {
38
37
  onClick: () => t.setValue(null),
39
- hierarchy: "tertiary",
40
- disabled: a == null,
41
- children: /* @__PURE__ */ l(d, {})
38
+ disabled: a == null
42
39
  }
43
40
  )
44
41
  ] })
45
42
  ] });
46
43
  }
47
44
  export {
48
- I as SelectFieldFilter
45
+ z as SelectFieldFilter
49
46
  };
50
47
  //# sourceMappingURL=select_field_filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"select_field_filter.js","sources":["../../../../src/components/table_filter_panel/field_filters/select_field_filter.tsx"],"sourcesContent":["import React from 'react';\n\nimport { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';\nimport { useSignalValue } from '@tcn/state';\nimport { Button } from '@tcn/ui/actions';\nimport { Option, Select } from '@tcn/ui/inputs';\nimport { Box, HStack, VStack } from '@tcn/ui/stacks';\nimport { Title } from '@tcn/ui/typography';\nimport { FieldFilterProps } from './field_filter_props.js';\nimport { SelectFieldFilterPresenter } from './select_field_filter_presenter.js';\nimport { useFieldFilterStrategy } from './use_field_filter_strategy.js';\n\nexport type SelectFieldFilterProps = FieldFilterProps & {\n options: { label: string; value: string | boolean | number }[];\n};\n\nexport function SelectFieldFilter({ fieldName, label, options }: SelectFieldFilterProps) {\n const presenter = useFieldFilterStrategy(SelectFieldFilterPresenter, fieldName);\n\n const value = useSignalValue(presenter.broadcasts.value);\n const valueLabel = options.find(option => option.value === value)?.label ?? '';\n\n return (\n <VStack gap=\"4px\">\n <Box width=\"flex\">\n <Title size=\"md\">{label}</Title>\n </Box>\n <HStack>\n <Select\n value={valueLabel}\n onChange={value => {\n const realValue = options.find(option => option.label === value)?.value;\n presenter.setValue(realValue ?? null);\n }}\n width=\"flex\"\n >\n {options.map(option => (\n <Option\n key={option.value.toString()}\n value={option.label}\n label={option.label}\n >\n {option.label}\n </Option>\n ))}\n </Select>\n <Button\n onClick={() => presenter.setValue(null)}\n hierarchy=\"tertiary\"\n disabled={value == null}\n >\n <CrossCircleIcon />\n </Button>\n </HStack>\n </VStack>\n );\n}\n"],"names":["SelectFieldFilter","fieldName","label","options","presenter","useFieldFilterStrategy","SelectFieldFilterPresenter","value","useSignalValue","valueLabel","option","jsxs","VStack","jsx","Box","Title","HStack","Select","realValue","Option","Button","CrossCircleIcon"],"mappings":";;;;;;;;;AAgBO,SAASA,EAAkB,EAAE,WAAAC,GAAW,OAAAC,GAAO,SAAAC,KAAmC;AACvF,QAAMC,IAAYC,EAAuBC,GAA4BL,CAAS,GAExEM,IAAQC,EAAeJ,EAAU,WAAW,KAAK,GACjDK,IAAaN,EAAQ,KAAK,CAAAO,MAAUA,EAAO,UAAUH,CAAK,GAAG,SAAS;AAE5E,SACE,gBAAAI,EAACC,GAAA,EAAO,KAAI,OACV,UAAA;AAAA,IAAA,gBAAAC,EAACC,GAAA,EAAI,OAAM,QACT,UAAA,gBAAAD,EAACE,KAAM,MAAK,MAAM,aAAM,EAAA,CAC1B;AAAA,sBACCC,GAAA,EACC,UAAA;AAAA,MAAA,gBAAAH;AAAA,QAACI;AAAA,QAAA;AAAA,UACC,OAAOR;AAAA,UACP,UAAU,CAAAF,MAAS;AACjB,kBAAMW,IAAYf,EAAQ,KAAK,OAAUO,EAAO,UAAUH,CAAK,GAAG;AAClE,YAAAH,EAAU,SAASc,KAAa,IAAI;AAAA,UACtC;AAAA,UACA,OAAM;AAAA,UAEL,UAAAf,EAAQ,IAAI,CAAAO,MACX,gBAAAG;AAAA,YAACM;AAAA,YAAA;AAAA,cAEC,OAAOT,EAAO;AAAA,cACd,OAAOA,EAAO;AAAA,cAEb,UAAAA,EAAO;AAAA,YAAA;AAAA,YAJHA,EAAO,MAAM,SAAA;AAAA,UAAS,CAM9B;AAAA,QAAA;AAAA,MAAA;AAAA,MAEH,gBAAAG;AAAA,QAACO;AAAA,QAAA;AAAA,UACC,SAAS,MAAMhB,EAAU,SAAS,IAAI;AAAA,UACtC,WAAU;AAAA,UACV,UAAUG,KAAS;AAAA,UAEnB,4BAACc,GAAA,CAAA,CAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IACnB,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"select_field_filter.js","sources":["../../../../src/components/table_filter_panel/field_filters/select_field_filter.tsx"],"sourcesContent":["import { useSignalValue } from '@tcn/state';\nimport { Option, Select } from '@tcn/ui/inputs';\nimport { Box, HStack, VStack } from '@tcn/ui/stacks';\nimport { Title } from '@tcn/ui/typography';\nimport { FieldFilterProps } from './field_filter_props.js';\nimport { SelectFieldFilterPresenter } from './select_field_filter_presenter.js';\nimport { useFieldFilterStrategy } from './use_field_filter_strategy.js';\nimport { ClearFilterButton } from './clear_button.js';\n\nexport type SelectFieldFilterProps = FieldFilterProps & {\n options: { label: string; value: string | boolean | number }[];\n};\n\nexport function SelectFieldFilter({ fieldName, label, options }: SelectFieldFilterProps) {\n const presenter = useFieldFilterStrategy(SelectFieldFilterPresenter, fieldName);\n\n const value = useSignalValue(presenter.broadcasts.value);\n const valueLabel = options.find(option => option.value === value)?.label ?? '';\n\n return (\n <VStack gap=\"4px\">\n <Box width=\"flex\">\n <Title size=\"md\">{label}</Title>\n </Box>\n <HStack>\n <Select\n value={valueLabel}\n onChange={value => {\n const realValue = options.find(option => option.label === value)?.value;\n presenter.setValue(realValue ?? null);\n }}\n width=\"flex\"\n >\n {options.map(option => (\n <Option\n key={option.value.toString()}\n value={option.label}\n label={option.label}\n >\n {option.label}\n </Option>\n ))}\n </Select>\n <ClearFilterButton\n onClick={() => presenter.setValue(null)}\n disabled={value == null}\n />\n </HStack>\n </VStack>\n );\n}\n"],"names":["SelectFieldFilter","fieldName","label","options","presenter","useFieldFilterStrategy","SelectFieldFilterPresenter","value","useSignalValue","valueLabel","option","jsxs","VStack","jsx","Box","Title","HStack","Select","realValue","Option","ClearFilterButton"],"mappings":";;;;;;;;AAaO,SAASA,EAAkB,EAAE,WAAAC,GAAW,OAAAC,GAAO,SAAAC,KAAmC;AACvF,QAAMC,IAAYC,EAAuBC,GAA4BL,CAAS,GAExEM,IAAQC,EAAeJ,EAAU,WAAW,KAAK,GACjDK,IAAaN,EAAQ,KAAK,CAAAO,MAAUA,EAAO,UAAUH,CAAK,GAAG,SAAS;AAE5E,SACE,gBAAAI,EAACC,GAAA,EAAO,KAAI,OACV,UAAA;AAAA,IAAA,gBAAAC,EAACC,GAAA,EAAI,OAAM,QACT,UAAA,gBAAAD,EAACE,KAAM,MAAK,MAAM,aAAM,EAAA,CAC1B;AAAA,sBACCC,GAAA,EACC,UAAA;AAAA,MAAA,gBAAAH;AAAA,QAACI;AAAA,QAAA;AAAA,UACC,OAAOR;AAAA,UACP,UAAU,CAAAF,MAAS;AACjB,kBAAMW,IAAYf,EAAQ,KAAK,OAAUO,EAAO,UAAUH,CAAK,GAAG;AAClE,YAAAH,EAAU,SAASc,KAAa,IAAI;AAAA,UACtC;AAAA,UACA,OAAM;AAAA,UAEL,UAAAf,EAAQ,IAAI,CAAAO,MACX,gBAAAG;AAAA,YAACM;AAAA,YAAA;AAAA,cAEC,OAAOT,EAAO;AAAA,cACd,OAAOA,EAAO;AAAA,cAEb,UAAAA,EAAO;AAAA,YAAA;AAAA,YAJHA,EAAO,MAAM,SAAA;AAAA,UAAS,CAM9B;AAAA,QAAA;AAAA,MAAA;AAAA,MAEH,gBAAAG;AAAA,QAACO;AAAA,QAAA;AAAA,UACC,SAAS,MAAMhB,EAAU,SAAS,IAAI;AAAA,UACtC,UAAUG,KAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IACrB,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"string_field_filter.d.ts","sourceRoot":"","sources":["../../../../src/components/table_filter_panel/field_filters/string_field_filter.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAkB3D,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,gBAAgB,2CA6ClF"}
1
+ {"version":3,"file":"string_field_filter.d.ts","sourceRoot":"","sources":["../../../../src/components/table_filter_panel/field_filters/string_field_filter.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAmB3D,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,gBAAgB,2CAsClF"}
@@ -1,13 +1,10 @@
1
- import { jsxs as o, jsx as e } from "react/jsx-runtime";
2
- import { CrossCircleIcon as m } from "@tcn/icons/cross_circle_icon.js";
3
- import { useSignalValue as l } from "@tcn/state";
4
- import { Button as d } from "@tcn/ui/actions";
5
- import { Select as h, Option as u, Input as f } from "@tcn/ui/inputs";
6
- import { VStack as x, Box as b, HStack as S } from "@tcn/ui/stacks";
7
- import { Title as g } from "@tcn/ui/typography";
8
- import { StringFieldFilterPresenter as v } from "./string_field_filter_presenter.js";
9
- import { useFieldFilterStrategy as y } from "./use_field_filter_strategy.js";
10
- const F = ["is", "isNot", "has"], C = {
1
+ import { jsx as r, jsxs as c } from "react/jsx-runtime";
2
+ import { useSignalValue as o } from "@tcn/state";
3
+ import { InputGroup as m, Select as d, Option as h, Input as b } from "@tcn/ui/inputs";
4
+ import { StringFieldFilterPresenter as f } from "./string_field_filter_presenter.js";
5
+ import { useFieldFilterStrategy as F } from "./use_field_filter_strategy.js";
6
+ import { ClearableField as g } from "./clearable_field.js";
7
+ const v = ["is", "isNot", "has"], S = {
11
8
  is: "=",
12
9
  isNot: "!=",
13
10
  has: ":"
@@ -16,50 +13,45 @@ const F = ["is", "isNot", "has"], C = {
16
13
  isNot: "is not",
17
14
  has: "has"
18
15
  };
19
- function L({ fieldName: a, label: n, operators: c }) {
20
- const r = y(v, a), i = l(r.broadcasts.value), p = l(r.broadcasts.operator);
21
- return /* @__PURE__ */ o(x, { gap: "4px", children: [
22
- /* @__PURE__ */ e(b, { width: "flex", children: /* @__PURE__ */ e(g, { size: "md", children: n }) }),
23
- /* @__PURE__ */ o(S, { width: "flex", children: [
24
- /* @__PURE__ */ e(
25
- h,
26
- {
27
- value: p,
28
- onChange: (t) => r.setOperator(t),
29
- width: "65px",
30
- children: (c || F).map((t) => /* @__PURE__ */ e(
31
- u,
32
- {
33
- value: C[t],
34
- label: s[t],
35
- children: s[t]
36
- },
37
- t
38
- ))
39
- }
40
- ),
41
- /* @__PURE__ */ e(
42
- f,
43
- {
44
- type: "text",
45
- value: i ?? "",
46
- onChange: (t) => r.setValue(t)
47
- }
48
- ),
49
- /* @__PURE__ */ e(
50
- d,
51
- {
52
- onClick: () => r.setValue(null),
53
- hierarchy: "tertiary",
54
- utility: !0,
55
- disabled: i == null,
56
- children: /* @__PURE__ */ e(m, {})
57
- }
58
- )
59
- ] })
60
- ] });
16
+ function j({ fieldName: a, label: i, operators: n }) {
17
+ const t = F(f, a), l = o(t.broadcasts.value), p = o(t.broadcasts.operator), u = n || v;
18
+ return /* @__PURE__ */ r(
19
+ g,
20
+ {
21
+ label: i,
22
+ onClear: () => t.setValue(null),
23
+ isClearable: l == null,
24
+ children: /* @__PURE__ */ c(m, { children: [
25
+ /* @__PURE__ */ r(
26
+ d,
27
+ {
28
+ value: p,
29
+ onChange: (e) => t.setOperator(e),
30
+ width: "65px",
31
+ children: u.map((e) => /* @__PURE__ */ r(
32
+ h,
33
+ {
34
+ value: S[e],
35
+ label: s[e],
36
+ children: s[e]
37
+ },
38
+ e
39
+ ))
40
+ }
41
+ ),
42
+ /* @__PURE__ */ r(
43
+ b,
44
+ {
45
+ type: "text",
46
+ value: l ?? "",
47
+ onChange: (e) => t.setValue(e)
48
+ }
49
+ )
50
+ ] })
51
+ }
52
+ );
61
53
  }
62
54
  export {
63
- L as StringFieldFilter
55
+ j as StringFieldFilter
64
56
  };
65
57
  //# sourceMappingURL=string_field_filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"string_field_filter.js","sources":["../../../../src/components/table_filter_panel/field_filters/string_field_filter.tsx"],"sourcesContent":["import { CrossCircleIcon } from '@tcn/icons/cross_circle_icon.js';\nimport { useSignalValue } from '@tcn/state';\nimport { Button } from '@tcn/ui/actions';\nimport { Input, Option, Select } from '@tcn/ui/inputs';\nimport { Box, HStack, VStack } from '@tcn/ui/stacks';\nimport { Title } from '@tcn/ui/typography';\nimport React from 'react';\nimport { FieldFilterProps } from './field_filter_props.js';\nimport { StringFieldFilterPresenter } from './string_field_filter_presenter.js';\nimport { ComparisonOperator } from '../types.js';\n\nimport { useFieldFilterStrategy } from './use_field_filter_strategy.js';\n\nconst allOperators: ('is' | 'isNot' | 'has')[] = ['is', 'isNot', 'has'];\nconst operatorSymbols: Record<'is' | 'isNot' | 'has', string> = {\n is: '=',\n isNot: '!=',\n has: ':',\n};\nconst operatorLabels: Record<'is' | 'isNot' | 'has', string> = {\n is: 'is',\n isNot: 'is not',\n has: 'has',\n};\n\nexport function StringFieldFilter({ fieldName, label, operators }: FieldFilterProps) {\n const presenter = useFieldFilterStrategy(StringFieldFilterPresenter, fieldName);\n\n const value = useSignalValue(presenter.broadcasts.value);\n const operator = useSignalValue(presenter.broadcasts.operator);\n\n const availableOperators = operators || allOperators;\n\n return (\n <VStack gap=\"4px\">\n <Box width=\"flex\">\n <Title size=\"md\">{label}</Title>\n </Box>\n <HStack width=\"flex\">\n <Select\n value={operator}\n onChange={value => presenter.setOperator(value as ComparisonOperator)}\n width=\"65px\"\n >\n {availableOperators.map(operator => (\n <Option\n key={operator}\n value={operatorSymbols[operator]}\n label={operatorLabels[operator]}\n >\n {operatorLabels[operator]}\n </Option>\n ))}\n </Select>\n <Input\n type=\"text\"\n value={value ?? ''}\n onChange={value => presenter.setValue(value)}\n />\n <Button\n onClick={() => presenter.setValue(null)}\n hierarchy=\"tertiary\"\n utility\n disabled={value == null}\n >\n <CrossCircleIcon />\n </Button>\n </HStack>\n </VStack>\n );\n}\n"],"names":["allOperators","operatorSymbols","operatorLabels","StringFieldFilter","fieldName","label","operators","presenter","useFieldFilterStrategy","StringFieldFilterPresenter","value","useSignalValue","operator","jsxs","VStack","jsx","Box","Title","HStack","Select","Option","Input","Button","CrossCircleIcon"],"mappings":";;;;;;;;;AAaA,MAAMA,IAA2C,CAAC,MAAM,SAAS,KAAK,GAChEC,IAA0D;AAAA,EAC9D,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AACP,GACMC,IAAyD;AAAA,EAC7D,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AACP;AAEO,SAASC,EAAkB,EAAE,WAAAC,GAAW,OAAAC,GAAO,WAAAC,KAA+B;AACnF,QAAMC,IAAYC,EAAuBC,GAA4BL,CAAS,GAExEM,IAAQC,EAAeJ,EAAU,WAAW,KAAK,GACjDK,IAAWD,EAAeJ,EAAU,WAAW,QAAQ;AAI7D,SACE,gBAAAM,EAACC,GAAA,EAAO,KAAI,OACV,UAAA;AAAA,IAAA,gBAAAC,EAACC,GAAA,EAAI,OAAM,QACT,UAAA,gBAAAD,EAACE,KAAM,MAAK,MAAM,aAAM,EAAA,CAC1B;AAAA,IACA,gBAAAJ,EAACK,GAAA,EAAO,OAAM,QACZ,UAAA;AAAA,MAAA,gBAAAH;AAAA,QAACI;AAAA,QAAA;AAAA,UACC,OAAOP;AAAA,UACP,UAAU,CAAAF,MAASH,EAAU,YAAYG,CAA2B;AAAA,UACpE,OAAM;AAAA,UAEL,WAbkBJ,KAAaN,GAaZ,IAAI,CAAAY,MACtB,gBAAAG;AAAA,YAACK;AAAA,YAAA;AAAA,cAEC,OAAOnB,EAAgBW,CAAQ;AAAA,cAC/B,OAAOV,EAAeU,CAAQ;AAAA,cAE7B,YAAeA,CAAQ;AAAA,YAAA;AAAA,YAJnBA;AAAAA,UAAA,CAMR;AAAA,QAAA;AAAA,MAAA;AAAA,MAEH,gBAAAG;AAAA,QAACM;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAOX,KAAS;AAAA,UAChB,UAAU,CAAAA,MAASH,EAAU,SAASG,CAAK;AAAA,QAAA;AAAA,MAAA;AAAA,MAE7C,gBAAAK;AAAA,QAACO;AAAA,QAAA;AAAA,UACC,SAAS,MAAMf,EAAU,SAAS,IAAI;AAAA,UACtC,WAAU;AAAA,UACV,SAAO;AAAA,UACP,UAAUG,KAAS;AAAA,UAEnB,4BAACa,GAAA,CAAA,CAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IACnB,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"string_field_filter.js","sources":["../../../../src/components/table_filter_panel/field_filters/string_field_filter.tsx"],"sourcesContent":["import { useSignalValue } from '@tcn/state';\nimport { Input, Option, Select, InputGroup } from '@tcn/ui/inputs';\nimport { FieldFilterProps } from './field_filter_props.js';\nimport { StringFieldFilterPresenter } from './string_field_filter_presenter.js';\nimport { ComparisonOperator } from '../types.js';\n\nimport { useFieldFilterStrategy } from './use_field_filter_strategy.js';\nimport { ClearableField } from './clearable_field.js';\n\nconst allOperators: ('is' | 'isNot' | 'has')[] = ['is', 'isNot', 'has'];\nconst operatorSymbols: Record<'is' | 'isNot' | 'has', string> = {\n is: '=',\n isNot: '!=',\n has: ':',\n};\nconst operatorLabels: Record<'is' | 'isNot' | 'has', string> = {\n is: 'is',\n isNot: 'is not',\n has: 'has',\n};\n\nexport function StringFieldFilter({ fieldName, label, operators }: FieldFilterProps) {\n const presenter = useFieldFilterStrategy(StringFieldFilterPresenter, fieldName);\n\n const value = useSignalValue(presenter.broadcasts.value);\n const operator = useSignalValue(presenter.broadcasts.operator);\n\n const availableOperators = operators || allOperators;\n\n return (\n <ClearableField\n label={label}\n onClear={() => presenter.setValue(null)}\n isClearable={value == null}\n >\n <InputGroup>\n <Select\n value={operator}\n onChange={value => presenter.setOperator(value as ComparisonOperator)}\n width=\"65px\"\n >\n {availableOperators.map(operator => (\n <Option\n key={operator}\n value={operatorSymbols[operator]}\n label={operatorLabels[operator]}\n >\n {operatorLabels[operator]}\n </Option>\n ))}\n </Select>\n <Input\n type=\"text\"\n value={value ?? ''}\n onChange={value => presenter.setValue(value)}\n />\n </InputGroup>\n </ClearableField>\n );\n}\n"],"names":["allOperators","operatorSymbols","operatorLabels","StringFieldFilter","fieldName","label","operators","presenter","useFieldFilterStrategy","StringFieldFilterPresenter","value","useSignalValue","operator","availableOperators","jsx","ClearableField","InputGroup","Select","Option","Input"],"mappings":";;;;;;AASA,MAAMA,IAA2C,CAAC,MAAM,SAAS,KAAK,GAChEC,IAA0D;AAAA,EAC9D,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AACP,GACMC,IAAyD;AAAA,EAC7D,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AACP;AAEO,SAASC,EAAkB,EAAE,WAAAC,GAAW,OAAAC,GAAO,WAAAC,KAA+B;AACnF,QAAMC,IAAYC,EAAuBC,GAA4BL,CAAS,GAExEM,IAAQC,EAAeJ,EAAU,WAAW,KAAK,GACjDK,IAAWD,EAAeJ,EAAU,WAAW,QAAQ,GAEvDM,IAAqBP,KAAaN;AAExC,SACE,gBAAAc;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,OAAAV;AAAA,MACA,SAAS,MAAME,EAAU,SAAS,IAAI;AAAA,MACtC,aAAaG,KAAS;AAAA,MAEtB,4BAACM,GAAA,EACC,UAAA;AAAA,QAAA,gBAAAF;AAAA,UAACG;AAAA,UAAA;AAAA,YACC,OAAOL;AAAA,YACP,UAAU,CAAAF,MAASH,EAAU,YAAYG,CAA2B;AAAA,YACpE,OAAM;AAAA,YAEL,UAAAG,EAAmB,IAAI,CAAAD,MACtB,gBAAAE;AAAA,cAACI;AAAA,cAAA;AAAA,gBAEC,OAAOjB,EAAgBW,CAAQ;AAAA,gBAC/B,OAAOV,EAAeU,CAAQ;AAAA,gBAE7B,YAAeA,CAAQ;AAAA,cAAA;AAAA,cAJnBA;AAAAA,YAAA,CAMR;AAAA,UAAA;AAAA,QAAA;AAAA,QAEH,gBAAAE;AAAA,UAACK;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAOT,KAAS;AAAA,YAChB,UAAU,CAAAA,MAASH,EAAU,SAASG,CAAK;AAAA,UAAA;AAAA,QAAA;AAAA,MAC7C,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -1,11 +1,12 @@
1
1
  import { DataSource } from '@tcn/resource-store';
2
- import { default as React, ReactElement } from 'react';
2
+ import { ReactElement } from 'react';
3
3
  import { FieldFilterProps } from './field_filters/field_filter_props.js';
4
4
  import { TableFilterPanelPresenter } from './table_filter_panel_presenter.js';
5
5
  export type TableFilterPanelProps = {
6
6
  children: ReactElement<FieldFilterProps>[] | ReactElement<FieldFilterProps>;
7
7
  dataSource: DataSource<any>;
8
+ onClose?: () => void;
8
9
  };
9
- export declare function TableFilterPanel({ children, dataSource }: TableFilterPanelProps): import("react/jsx-runtime").JSX.Element;
10
- export declare const TableFilterPanelContext: React.Context<TableFilterPanelPresenter<unknown> | null>;
10
+ export declare function TableFilterPanel({ children, dataSource, onClose, }: TableFilterPanelProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare const TableFilterPanelContext: import('react').Context<TableFilterPanelPresenter<unknown> | null>;
11
12
  //# sourceMappingURL=table_filter_panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"table_filter_panel.d.ts","sourceRoot":"","sources":["../../../src/components/table_filter_panel/table_filter_panel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,KAAK,EAAE,EAAE,YAAY,EAA2B,MAAM,OAAO,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;IAC5E,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;CAC7B,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,qBAAqB,2CAU/E;AAED,eAAO,MAAM,uBAAuB,0DAEnC,CAAC"}
1
+ {"version":3,"file":"table_filter_panel.d.ts","sourceRoot":"","sources":["../../../src/components/table_filter_panel/table_filter_panel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,YAAY,EAA2B,MAAM,OAAO,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAQ9E,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;IAC5E,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,UAAU,EACV,OAAO,GACR,EAAE,qBAAqB,2CA6BvB;AAED,eAAO,MAAM,uBAAuB,oEAEnC,CAAC"}
@@ -1,16 +1,44 @@
1
- import { jsx as e } from "react/jsx-runtime";
2
- import { VStack as o } from "@tcn/ui/stacks";
3
- import { createContext as l, useState as a } from "react";
4
- import { TableFilterPanelPresenter as i } from "./table_filter_panel_presenter.js";
5
- function f({ children: r, dataSource: t }) {
6
- const [n] = a(() => new i(t));
7
- return /* @__PURE__ */ e(m.Provider, { value: n, children: /* @__PURE__ */ e(o, { gap: "16px", children: r }) });
1
+ import { jsx as e, jsxs as r } from "react/jsx-runtime";
2
+ import { createContext as o, useState as c } from "react";
3
+ import { TableFilterPanelPresenter as s } from "./table_filter_panel_presenter.js";
4
+ import { Panel as b } from "@tcn/ui/surfaces";
5
+ import { Header as p, VBody as f, Section as m } from "@tcn/ui/layouts";
6
+ import { Spacer as d } from "@tcn/ui/stacks";
7
+ import { Button as y } from "@tcn/ui/actions";
8
+ import { CrossIcon as u } from "@tcn/icons/cross_icon.js";
9
+ import '../../table_filter_panel.css';const P = "_table-filter-panel_124e30e", h = "_table-filter-panel-body_c016eb0", F = "_table-filter-panel-section_cfd2eb9", t = { "table-filter-panel": P, "table-filter-panel-body": h, "table-filter-panel-section": F };
10
+ function v({
11
+ children: a,
12
+ dataSource: n,
13
+ onClose: l
14
+ }) {
15
+ const [i] = c(() => new s(n));
16
+ return /* @__PURE__ */ e(_.Provider, { value: i, children: /* @__PURE__ */ r(b, { className: `${t["table-filter-panel"]} tcn-table-filter-panel`, children: [
17
+ /* @__PURE__ */ r(p, { children: [
18
+ "Table Filters",
19
+ /* @__PURE__ */ e(d, {}),
20
+ l && /* @__PURE__ */ e(y, { utility: !0, hierarchy: "tertiary", onClick: l, children: /* @__PURE__ */ e(u, {}) })
21
+ ] }),
22
+ /* @__PURE__ */ e(
23
+ f,
24
+ {
25
+ className: `${t["table-filter-panel-body"]} tcn-table-filter-panel-body`,
26
+ children: /* @__PURE__ */ e(
27
+ m,
28
+ {
29
+ className: `${t["table-filter-panel-section"]} tcn-table-filter-panel-section`,
30
+ children: a
31
+ }
32
+ )
33
+ }
34
+ )
35
+ ] }) });
8
36
  }
9
- const m = l(
37
+ const _ = o(
10
38
  null
11
39
  );
12
40
  export {
13
- f as TableFilterPanel,
14
- m as TableFilterPanelContext
41
+ v as TableFilterPanel,
42
+ _ as TableFilterPanelContext
15
43
  };
16
44
  //# sourceMappingURL=table_filter_panel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"table_filter_panel.js","sources":["../../../src/components/table_filter_panel/table_filter_panel.tsx"],"sourcesContent":["import { DataSource } from '@tcn/resource-store';\nimport { VStack } from '@tcn/ui/stacks';\nimport React, { ReactElement, useState, createContext } from 'react';\nimport { FieldFilterProps } from './field_filters/field_filter_props.js';\nimport { TableFilterPanelPresenter } from './table_filter_panel_presenter.js';\n\nexport type TableFilterPanelProps = {\n children: ReactElement<FieldFilterProps>[] | ReactElement<FieldFilterProps>;\n dataSource: DataSource<any>;\n};\n\nexport function TableFilterPanel({ children, dataSource }: TableFilterPanelProps) {\n const [presenter] = useState(() => {\n return new TableFilterPanelPresenter(dataSource);\n });\n\n return (\n <TableFilterPanelContext.Provider value={presenter}>\n <VStack gap=\"16px\">{children}</VStack>\n </TableFilterPanelContext.Provider>\n );\n}\n\nexport const TableFilterPanelContext = createContext<TableFilterPanelPresenter | null>(\n null\n);\n"],"names":["TableFilterPanel","children","dataSource","presenter","useState","TableFilterPanelPresenter","jsx","TableFilterPanelContext","VStack","createContext"],"mappings":";;;;AAWO,SAASA,EAAiB,EAAE,UAAAC,GAAU,YAAAC,KAAqC;AAChF,QAAM,CAACC,CAAS,IAAIC,EAAS,MACpB,IAAIC,EAA0BH,CAAU,CAChD;AAED,SACE,gBAAAI,EAACC,EAAwB,UAAxB,EAAiC,OAAOJ,GACvC,UAAA,gBAAAG,EAACE,GAAA,EAAO,KAAI,QAAQ,UAAAP,EAAA,CAAS,EAAA,CAC/B;AAEJ;AAEO,MAAMM,IAA0BE;AAAA,EACrC;AACF;"}
1
+ {"version":3,"file":"table_filter_panel.js","sources":["../../../src/components/table_filter_panel/table_filter_panel.tsx"],"sourcesContent":["import { DataSource } from '@tcn/resource-store';\nimport { ReactElement, useState, createContext } from 'react';\nimport { FieldFilterProps } from './field_filters/field_filter_props.js';\nimport { TableFilterPanelPresenter } from './table_filter_panel_presenter.js';\nimport { Panel } from '@tcn/ui/surfaces';\nimport { Header, Section, VBody } from '@tcn/ui/layouts';\nimport styles from './table_filter_panel.module.css';\nimport { Spacer } from '@tcn/ui/stacks';\nimport { Button } from '@tcn/ui/actions';\nimport { CrossIcon } from '@tcn/icons/cross_icon.js';\n\nexport type TableFilterPanelProps = {\n children: ReactElement<FieldFilterProps>[] | ReactElement<FieldFilterProps>;\n dataSource: DataSource<any>;\n onClose?: () => void;\n};\n\nexport function TableFilterPanel({\n children,\n dataSource,\n onClose,\n}: TableFilterPanelProps) {\n const [presenter] = useState(() => {\n return new TableFilterPanelPresenter(dataSource);\n });\n\n return (\n <TableFilterPanelContext.Provider value={presenter}>\n <Panel className={`${styles['table-filter-panel']} tcn-table-filter-panel`}>\n <Header>\n Table Filters\n <Spacer />\n {onClose && (\n <Button utility hierarchy=\"tertiary\" onClick={onClose}>\n <CrossIcon />\n </Button>\n )}\n </Header>\n <VBody\n className={`${styles['table-filter-panel-body']} tcn-table-filter-panel-body`}\n >\n <Section\n className={`${styles['table-filter-panel-section']} tcn-table-filter-panel-section`}\n >\n {children}\n </Section>\n </VBody>\n </Panel>\n </TableFilterPanelContext.Provider>\n );\n}\n\nexport const TableFilterPanelContext = createContext<TableFilterPanelPresenter | null>(\n null\n);\n"],"names":["TableFilterPanel","children","dataSource","onClose","presenter","useState","TableFilterPanelPresenter","jsx","TableFilterPanelContext","jsxs","Panel","styles","Header","Spacer","Button","CrossIcon","VBody","Section","createContext"],"mappings":";;;;;;;;;AAiBO,SAASA,EAAiB;AAAA,EAC/B,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,SAAAC;AACF,GAA0B;AACxB,QAAM,CAACC,CAAS,IAAIC,EAAS,MACpB,IAAIC,EAA0BJ,CAAU,CAChD;AAED,SACE,gBAAAK,EAACC,EAAwB,UAAxB,EAAiC,OAAOJ,GACvC,UAAA,gBAAAK,EAACC,GAAA,EAAM,WAAW,GAAGC,EAAO,oBAAoB,CAAC,2BAC/C,UAAA;AAAA,IAAA,gBAAAF,EAACG,GAAA,EAAO,UAAA;AAAA,MAAA;AAAA,wBAELC,GAAA,EAAO;AAAA,MACPV,KACC,gBAAAI,EAACO,GAAA,EAAO,SAAO,IAAC,WAAU,YAAW,SAASX,GAC5C,UAAA,gBAAAI,EAACQ,GAAA,CAAA,CAAU,EAAA,CACb;AAAA,IAAA,GAEJ;AAAA,IACA,gBAAAR;AAAA,MAACS;AAAA,MAAA;AAAA,QACC,WAAW,GAAGL,EAAO,yBAAyB,CAAC;AAAA,QAE/C,UAAA,gBAAAJ;AAAA,UAACU;AAAA,UAAA;AAAA,YACC,WAAW,GAAGN,EAAO,4BAA4B,CAAC;AAAA,YAEjD,UAAAV;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EACF,EAAA,CACF,EAAA,CACF;AAEJ;AAEO,MAAMO,IAA0BU;AAAA,EACrC;AACF;"}
@@ -0,0 +1 @@
1
+ ._table-filter-panel_124e30e{--material: #fafafa;background:var(--material)}._table-filter-panel-body_c016eb0{padding-block:8px}._table-filter-panel-body_c016eb0 ._table-filter-panel-section_cfd2eb9{gap:8px}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tcn/ui-table",
3
- "version": "2.3.7",
3
+ "version": "2.3.9",
4
4
  "type": "module",
5
5
  "description": "React table component library",
6
6
  "author": "TCN",
@@ -39,11 +39,11 @@
39
39
  ],
40
40
  "dependencies": {
41
41
  "clarity-pattern-parser": "^11.5.4",
42
+ "@tcn/icons": "2.3.0",
42
43
  "@tcn/aip-160": "1.2.5",
43
- "@tcn/icons": "2.2.1",
44
44
  "@tcn/state": "1.2.0",
45
- "@tcn/ui": "0.10.0",
46
- "@tcn/resource-store": "2.5.1"
45
+ "@tcn/resource-store": "2.5.1",
46
+ "@tcn/ui": "0.12.0"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "react": "^18.2.0",
@@ -413,7 +413,7 @@ export function WithFilterPanel() {
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 dataSource={source}>
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)" />
@@ -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
- <VStack gap="4px">
25
- <Box width="flex">
26
- <Title size="md">{label}</Title>
27
- </Box>
28
- <HStack gap="4px">
29
- <Box width="auto">
30
- <BodyText size="lg">Start</BodyText>
31
- </Box>
32
- <Box width="flex">
33
- <DatePickerInput
34
- value={startDate}
35
- onChange={value => presenter.setStartDate(value)}
36
- />
37
- </Box>
38
- <Box width="auto">
39
- <Button
40
- onClick={() => presenter.setStartDate(null)}
41
- hierarchy="tertiary"
42
- disabled={startDate == null}
43
- >
44
- <CrossCircleIcon />
45
- </Button>
46
- </Box>
47
- </HStack>
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
- <Button
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 { Button } from '@tcn/ui/actions';
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
- <VStack gap="4px">
23
- <Box width="flex">
24
- <Title size="md">{label}</Title>
25
- </Box>
26
- <HStack width="flex">
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
- <Button
44
- onClick={() => presenter.setValue(null)}
45
- hierarchy="tertiary"
46
- disabled={value == null}
47
- >
48
- <CrossCircleIcon />
49
- </Button>
50
- </HStack>
51
- </VStack>
40
+ </InputGroup>
41
+ </ClearableField>
52
42
  );
53
43
  }