@tcn/ui-table 2.4.1 → 2.4.3
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 +17 -16
- package/dist/components/cells/header_cell.js.map +1 -1
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +35 -34
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/table/table_column.d.ts +1 -0
- package/dist/components/table/table_column.d.ts.map +1 -1
- package/dist/components/table/table_column.js.map +1 -1
- package/dist/components/table/table_presenter.d.ts +1 -0
- package/dist/components/table/table_presenter.d.ts.map +1 -1
- package/dist/components/table/table_presenter.js.map +1 -1
- package/dist/components/table_filter_panel/field_filters/field_filter_props.js +2 -0
- package/dist/components/table_filter_panel/field_filters/field_filter_props.js.map +1 -0
- package/dist/components/table_filter_panel/field_filters/field_filter_strategy.js +2 -0
- package/dist/components/table_filter_panel/field_filters/field_filter_strategy.js.map +1 -0
- 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 +28 -40
- package/dist/components/table_filter_panel/field_filters/mulit_select_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 +22 -36
- package/dist/components/table_filter_panel/field_filters/select_field_filter.js.map +1 -1
- package/dist/components/table_filter_panel/table_filter_panel.d.ts.map +1 -1
- package/dist/components/table_filter_panel/table_filter_panel.js +18 -18
- package/dist/components/table_filter_panel/table_filter_panel.js.map +1 -1
- package/dist/mulit_select_field_filter.css +1 -0
- package/dist/table.css +1 -1
- package/package.json +17 -17
- package/src/__stories__/sample_data.ts +25 -3
- package/src/__stories__/table.stories.tsx +59 -39
- package/src/components/cells/header_cell.tsx +2 -1
- package/src/components/table/table.module.css +20 -0
- package/src/components/table/table.tsx +6 -1
- package/src/components/table/table_column.tsx +1 -0
- package/src/components/table/table_presenter.ts +1 -0
- package/src/components/table_filter_panel/field_filters/mulit_select_field_filter.module.css +8 -0
- package/src/components/table_filter_panel/field_filters/mulit_select_field_filter.tsx +24 -35
- package/src/components/table_filter_panel/field_filters/select_field_filter.tsx +21 -32
- package/src/components/table_filter_panel/table_filter_panel.tsx +8 -10
- package/tsconfig.json +4 -33
|
@@ -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;AACjD,OAAO,EAAE,YAAY,EAA2B,MAAM,OAAO,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ1D,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC;IACzE,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;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,UAAU,EACV,SAAS,EACT,OAAO,EACP,KAAuB,EACvB,GAAG,KAAK,EACT,EAAE,qBAAqB,
|
|
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;AAC9E,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ1D,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC;IACzE,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;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,UAAU,EACV,SAAS,EACT,OAAO,EACP,KAAuB,EACvB,GAAG,KAAK,EACT,EAAE,qBAAqB,2CA6BvB;AAED,eAAO,MAAM,uBAAuB,oEAEnC,CAAC"}
|
|
@@ -2,13 +2,13 @@ import { jsx as e, jsxs as l } from "react/jsx-runtime";
|
|
|
2
2
|
import { createContext as f, useState as b } from "react";
|
|
3
3
|
import { TableFilterPanelPresenter as m } from "./table_filter_panel_presenter.js";
|
|
4
4
|
import { Aside as p } from "@tcn/ui/surfaces";
|
|
5
|
-
import { Header as d,
|
|
6
|
-
import { Spacer as
|
|
7
|
-
import { Button as
|
|
8
|
-
import { CrossIcon as
|
|
9
|
-
import { Title as
|
|
10
|
-
import '../../table_filter_panel.css';const
|
|
11
|
-
function
|
|
5
|
+
import { Header as d, Scaffold as u, Section as y } from "@tcn/ui/layouts";
|
|
6
|
+
import { Spacer as h } from "@tcn/ui/stacks";
|
|
7
|
+
import { Button as P } from "@tcn/ui/actions";
|
|
8
|
+
import { CrossIcon as F } from "@tcn/icons/cross_icon.js";
|
|
9
|
+
import { Title as x } from "@tcn/ui/typography";
|
|
10
|
+
import '../../table_filter_panel.css';const S = "_table-filter-panel-body_c016eb0", T = "_table-filter-panel-section_cfd2eb9", r = { "table-filter-panel-body": S, "table-filter-panel-section": T };
|
|
11
|
+
function H({
|
|
12
12
|
children: n,
|
|
13
13
|
dataSource: i,
|
|
14
14
|
className: a,
|
|
@@ -17,32 +17,32 @@ function I({
|
|
|
17
17
|
...c
|
|
18
18
|
}) {
|
|
19
19
|
const [s] = b(() => new m(i));
|
|
20
|
-
return /* @__PURE__ */ e(
|
|
20
|
+
return /* @__PURE__ */ e(_.Provider, { value: s, children: /* @__PURE__ */ l(p, { className: `tcn-table-filter-panel ${a}`, ...c, children: [
|
|
21
21
|
/* @__PURE__ */ l(d, { children: [
|
|
22
|
-
/* @__PURE__ */ e(
|
|
23
|
-
/* @__PURE__ */ e(
|
|
24
|
-
t && /* @__PURE__ */ e(
|
|
22
|
+
/* @__PURE__ */ e(x, { children: o }),
|
|
23
|
+
/* @__PURE__ */ e(h, {}),
|
|
24
|
+
t && /* @__PURE__ */ e(P, { utility: !0, hierarchy: "tertiary", onClick: t, children: /* @__PURE__ */ e(F, {}) })
|
|
25
25
|
] }),
|
|
26
26
|
/* @__PURE__ */ e(
|
|
27
|
-
|
|
27
|
+
u,
|
|
28
28
|
{
|
|
29
29
|
className: `${r["table-filter-panel-body"]} tcn-table-filter-panel-body`,
|
|
30
|
-
children: /* @__PURE__ */ e(
|
|
31
|
-
|
|
30
|
+
children: /* @__PURE__ */ e(
|
|
31
|
+
y,
|
|
32
32
|
{
|
|
33
33
|
className: `${r["table-filter-panel-section"]} tcn-table-filter-panel-section`,
|
|
34
34
|
children: n
|
|
35
35
|
}
|
|
36
|
-
)
|
|
36
|
+
)
|
|
37
37
|
}
|
|
38
38
|
)
|
|
39
39
|
] }) });
|
|
40
40
|
}
|
|
41
|
-
const
|
|
41
|
+
const _ = f(
|
|
42
42
|
null
|
|
43
43
|
);
|
|
44
44
|
export {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
H as TableFilterPanel,
|
|
46
|
+
_ as TableFilterPanelContext
|
|
47
47
|
};
|
|
48
48
|
//# 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 { ReactElement, useState, createContext } from 'react';\nimport { FieldFilterProps } from './field_filters/field_filter_props.js';\nimport { TableFilterPanelPresenter } from './table_filter_panel_presenter.js';\nimport { Aside, type PanelProps } from '@tcn/ui/surfaces';\nimport {
|
|
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 { Aside, type PanelProps } from '@tcn/ui/surfaces';\nimport { Header, Scaffold, Section } 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';\nimport { Title } from '@tcn/ui/typography';\n\nexport interface TableFilterPanelProps extends Omit<PanelProps, 'children'> {\n children: ReactElement<FieldFilterProps>[] | ReactElement<FieldFilterProps>;\n dataSource: DataSource<any>;\n onClose?: () => void;\n title?: string;\n}\n\nexport function TableFilterPanel({\n children,\n dataSource,\n className,\n onClose,\n title = 'Table Filters',\n ...props\n}: TableFilterPanelProps) {\n const [presenter] = useState(() => {\n return new TableFilterPanelPresenter(dataSource);\n });\n\n return (\n <TableFilterPanelContext.Provider value={presenter}>\n <Aside className={`tcn-table-filter-panel ${className}`} {...props}>\n <Header>\n <Title>{title}</Title>\n <Spacer />\n {onClose && (\n <Button utility hierarchy=\"tertiary\" onClick={onClose}>\n <CrossIcon />\n </Button>\n )}\n </Header>\n <Scaffold\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 </Scaffold>\n </Aside>\n </TableFilterPanelContext.Provider>\n );\n}\n\nexport const TableFilterPanelContext = createContext<TableFilterPanelPresenter | null>(\n null\n);\n"],"names":["TableFilterPanel","children","dataSource","className","onClose","title","props","presenter","useState","TableFilterPanelPresenter","jsx","TableFilterPanelContext","jsxs","Aside","Header","Title","Spacer","Button","CrossIcon","Scaffold","styles","Section","createContext"],"mappings":";;;;;;;;;;AAmBO,SAASA,EAAiB;AAAA,EAC/B,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,GAAGC;AACL,GAA0B;AACxB,QAAM,CAACC,CAAS,IAAIC,EAAS,MACpB,IAAIC,EAA0BP,CAAU,CAChD;AAED,SACE,gBAAAQ,EAACC,EAAwB,UAAxB,EAAiC,OAAOJ,GACvC,UAAA,gBAAAK,EAACC,GAAA,EAAM,WAAW,0BAA0BV,CAAS,IAAK,GAAGG,GAC3D,UAAA;AAAA,IAAA,gBAAAM,EAACE,GAAA,EACC,UAAA;AAAA,MAAA,gBAAAJ,EAACK,KAAO,UAAAV,EAAA,CAAM;AAAA,wBACbW,GAAA,EAAO;AAAA,MACPZ,KACC,gBAAAM,EAACO,GAAA,EAAO,SAAO,IAAC,WAAU,YAAW,SAASb,GAC5C,UAAA,gBAAAM,EAACQ,GAAA,CAAA,CAAU,EAAA,CACb;AAAA,IAAA,GAEJ;AAAA,IACA,gBAAAR;AAAA,MAACS;AAAA,MAAA;AAAA,QACC,WAAW,GAAGC,EAAO,yBAAyB,CAAC;AAAA,QAE/C,UAAA,gBAAAV;AAAA,UAACW;AAAA,UAAA;AAAA,YACC,WAAW,GAAGD,EAAO,4BAA4B,CAAC;AAAA,YAEjD,UAAAnB;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EACF,EAAA,CACF,EAAA,CACF;AAEJ;AAEO,MAAMU,IAA0BW;AAAA,EACrC;AACF;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer tcn-system{._multiselect-filter_193bbb6 .tcn-multiselect-values-container{padding:0}}
|
package/dist/table.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
._table-body_b8c928c{border-spacing:0;width:auto;min-width:100%;height:auto;min-height:100%;table-layout:fixed;display:table}:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) th[data-stick-to=start],:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) td[data-stick-to=start]{border-inline-end:1px solid #ccc}:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) th[data-stick-to=end],:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) td[data-stick-to=end]{border-inline-start:1px solid #ccc}._table-body_b8c928c tr[data-clickable=true]{cursor:pointer}._table-body_b8c928c thead tr th{position:sticky;top:0;z-index:1;box-sizing:border-box}._table-body_b8c928c thead{position:sticky;top:0;z-index:3}._table-body_b8c928c tbody{position:relative;z-index:1}._table-body_b8c928c tfoot{position:relative;z-index:2}._table-body_b8c928c th>div{display:flex;align-items:center}._table-body_b8c928c td>div{height:100%;display:flex;align-items:center}._table-body_b8c928c tfoot{position:sticky;bottom:0}._table-body_b8c928c td,._table-body_b8c928c th{vertical-align:middle}._table-body_b8c928c[data-is-clickable=true] td{cursor:pointer}
|
|
1
|
+
._table-body_b8c928c{border-spacing:0;width:auto;min-width:100%;height:auto;min-height:100%;table-layout:fixed;display:table}:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) th[data-stick-to=start],:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) td[data-stick-to=start]{border-inline-end:1px solid #ccc}:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) th[data-stick-to=end],:is(._table-body_b8c928c thead tr,._table-body_b8c928c tbody tr) td[data-stick-to=end]{border-inline-start:1px solid #ccc}._table-body_b8c928c tr[data-clickable=true]{cursor:pointer}._table-body_b8c928c thead tr th{position:sticky;top:0;z-index:1;box-sizing:border-box}._table-body_b8c928c thead{position:sticky;top:0;z-index:3}._table-body_b8c928c tbody{position:relative;z-index:1}._table-body_b8c928c tfoot{position:relative;z-index:2}._table-body_b8c928c thead tr th{padding-inline:0!important}._table-body_b8c928c thead tr th>div>*{padding-inline:var(--padding-medium)}._table-body_b8c928c thead tr th:first-child>div>*{padding-inline-start:var(--table-pad-inline)}._table-body_b8c928c thead tr th:last-child>div>*{padding-inline-end:var(--table-pad-inline)}._table-body_b8c928c th>div{display:flex;align-items:center}._table-body_b8c928c td>div{height:100%;display:flex;align-items:center}._table-body_b8c928c tfoot{position:sticky;bottom:0}._table-body_b8c928c td,._table-body_b8c928c th{vertical-align:middle}._table-body_b8c928c[data-is-clickable=true] td{cursor:pointer}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tcn/ui-table",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React table component library",
|
|
6
6
|
"author": "TCN",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"clarity-pattern-parser": "^11.5.4",
|
|
42
42
|
"@tcn/aip-160": "1.2.5",
|
|
43
|
-
"@tcn/state": "1.3.2",
|
|
44
|
-
"@tcn/resource-store": "2.5.6",
|
|
45
43
|
"@tcn/icons": "2.3.0",
|
|
46
|
-
"@tcn/
|
|
44
|
+
"@tcn/state": "1.3.3",
|
|
45
|
+
"@tcn/ui": "0.15.0",
|
|
46
|
+
"@tcn/resource-store": "2.5.7"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": "^18.2.0",
|
|
@@ -53,21 +53,21 @@
|
|
|
53
53
|
"start": "pnpm storybook",
|
|
54
54
|
"build": "vite build",
|
|
55
55
|
"clean": "rm -rf dist storybook-static",
|
|
56
|
-
"clean
|
|
56
|
+
"clean:all": "pnpm clean && rm -rf node_modules",
|
|
57
57
|
"test": "vitest run",
|
|
58
|
-
"test
|
|
59
|
-
"check
|
|
60
|
-
"check
|
|
61
|
-
"check
|
|
62
|
-
"check
|
|
63
|
-
"check
|
|
64
|
-
"fix
|
|
65
|
-
"fix
|
|
66
|
-
"fix
|
|
67
|
-
"fix
|
|
68
|
-
"publish
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"check:all": "concurrently 'pnpm check:types' 'pnpm run biome check .'",
|
|
60
|
+
"check:types": "tsc --project tsconfig.typecheck.json --noEmit",
|
|
61
|
+
"check:format": "pnpm run biome format .",
|
|
62
|
+
"check:lint": "pnpm run biome lint .",
|
|
63
|
+
"check:imports": "pnpm run biome check --formatter-enabled=false --linter-enabled=false --assist-enabled=true",
|
|
64
|
+
"fix:all": "pnpm run biome check --write",
|
|
65
|
+
"fix:format": "pnpm run biome format --write",
|
|
66
|
+
"fix:lint": "pnpm run biome lint --write",
|
|
67
|
+
"fix:imports": "pnpm run biome check --write --unsafe --formatter-enabled=false --linter-enabled=false --assist-enabled=true",
|
|
68
|
+
"publish:dry-run": "pnpm build && pnpm publish --dry-run --force --no-git-checks",
|
|
69
69
|
"biome": "pnpm exec biome",
|
|
70
|
-
"storybook": "bash ../../scripts/ensure-addon-built.sh && storybook dev",
|
|
70
|
+
"storybook": "bash ../../scripts/ensure-blackcat-addon-built.sh && storybook dev",
|
|
71
71
|
"storybook:silent": "storybook dev --no-open --disable-telemetry --quiet",
|
|
72
72
|
"storybook:build": "storybook build"
|
|
73
73
|
}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
export const OCCUPATIONS = [
|
|
2
|
+
'Adventurer',
|
|
3
|
+
'Artist',
|
|
4
|
+
'Assassin',
|
|
5
|
+
'Chef',
|
|
6
|
+
'Child',
|
|
7
|
+
'Detective',
|
|
8
|
+
'King',
|
|
9
|
+
'Merchant',
|
|
10
|
+
'Mistborn',
|
|
11
|
+
'Musician',
|
|
12
|
+
'Queen',
|
|
13
|
+
'Scientist',
|
|
14
|
+
'Soldier',
|
|
15
|
+
'Soother',
|
|
16
|
+
'Student',
|
|
17
|
+
'Tin-eye',
|
|
18
|
+
'Writer',
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export type Occupation = (typeof OCCUPATIONS)[number];
|
|
22
|
+
|
|
1
23
|
export type DataItem = {
|
|
2
24
|
id: string;
|
|
3
25
|
name: string;
|
|
@@ -6,12 +28,12 @@ export type DataItem = {
|
|
|
6
28
|
email: string;
|
|
7
29
|
city: string;
|
|
8
30
|
country: string;
|
|
9
|
-
occupation:
|
|
31
|
+
occupation: Occupation;
|
|
10
32
|
isActive: boolean;
|
|
11
33
|
birthdate: Date;
|
|
12
34
|
};
|
|
13
35
|
|
|
14
|
-
export const items = [
|
|
36
|
+
export const items: DataItem[] = [
|
|
15
37
|
{
|
|
16
38
|
id: 'one',
|
|
17
39
|
name: 'Sam Spade And the Longest Name that you have ever seen and it will just keep going cause we are going to test the table and see how it handles this',
|
|
@@ -1370,7 +1392,7 @@ export const items = [
|
|
|
1370
1392
|
},
|
|
1371
1393
|
];
|
|
1372
1394
|
|
|
1373
|
-
export const stickyItems = [
|
|
1395
|
+
export const stickyItems: DataItem[] = [
|
|
1374
1396
|
{
|
|
1375
1397
|
id: 'two',
|
|
1376
1398
|
name: 'Milo',
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { Signal, useSignalValue } from '@tcn/state';
|
|
13
13
|
import { Button } from '@tcn/ui/actions';
|
|
14
14
|
import { Checkbox } from '@tcn/ui/inputs';
|
|
15
|
-
import { Footer, Header, Rail, Side,
|
|
15
|
+
import { Footer, Header, Rail, Side, Scaffold } from '@tcn/ui/layouts';
|
|
16
16
|
import { Box, HStack, Spacer, VStack, ZStack } from '@tcn/ui/stacks';
|
|
17
17
|
import { Panel } from '@tcn/ui/surfaces';
|
|
18
18
|
import { Title } from '@tcn/ui/typography';
|
|
@@ -20,12 +20,14 @@ import { GlobalSearch } from '../components/global_search.js';
|
|
|
20
20
|
import { Table } from '../components/table/table.js';
|
|
21
21
|
import { TableColumn } from '../components/table/table_column.js';
|
|
22
22
|
import { DateFieldFilter } from '../components/table_filter_panel/field_filters/date_field_filter.js';
|
|
23
|
+
import { MulitSelectFieldFilter } from '../components/table_filter_panel/field_filters/mulit_select_field_filter.js';
|
|
23
24
|
import { NumberFieldFilter } from '../components/table_filter_panel/field_filters/number_field_filter.js';
|
|
24
25
|
import { NumberRangeFieldFilter } from '../components/table_filter_panel/field_filters/number_range_field_filter.js';
|
|
26
|
+
import { SelectFieldFilter } from '../components/table_filter_panel/field_filters/select_field_filter.js';
|
|
25
27
|
import { StringFieldFilter } from '../components/table_filter_panel/field_filters/string_field_filter.js';
|
|
26
28
|
import { TableFilterPanel } from '../components/table_filter_panel/table_filter_panel.js';
|
|
27
29
|
import { TablePager } from '../components/table_pager.js';
|
|
28
|
-
import { DataItem, items, stickyItems } from './sample_data.js';
|
|
30
|
+
import { DataItem, items, OCCUPATIONS, stickyItems } from './sample_data.js';
|
|
29
31
|
import styles from './table.module.css';
|
|
30
32
|
|
|
31
33
|
const meta: Meta = {
|
|
@@ -310,7 +312,7 @@ export function WithTableHeaderAndTableFooter() {
|
|
|
310
312
|
<Spacer />
|
|
311
313
|
<GlobalSearch dataSource={source} />
|
|
312
314
|
</Header>
|
|
313
|
-
<
|
|
315
|
+
<Scaffold>
|
|
314
316
|
<Table
|
|
315
317
|
dataSource={source}
|
|
316
318
|
onRowClick={item => {
|
|
@@ -325,7 +327,7 @@ export function WithTableHeaderAndTableFooter() {
|
|
|
325
327
|
<TableColumn heading="Occupation" fieldName="occupation" />
|
|
326
328
|
<TableColumn heading="Active" fieldName="isActive" />
|
|
327
329
|
</Table>
|
|
328
|
-
</
|
|
330
|
+
</Scaffold>
|
|
329
331
|
<Footer>
|
|
330
332
|
<TablePager dataSource={source} />
|
|
331
333
|
</Footer>
|
|
@@ -413,40 +415,49 @@ export function WithFilterPanel() {
|
|
|
413
415
|
<StoryWrapper>
|
|
414
416
|
<Panel height="100%">
|
|
415
417
|
<Header>The Table</Header>
|
|
416
|
-
<
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
<
|
|
420
|
-
<
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
<
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
fieldName="birthdate"
|
|
441
|
-
width={300}
|
|
442
|
-
render={(i: DataItem) => i.birthdate.toLocaleDateString()}
|
|
418
|
+
<Rail>
|
|
419
|
+
{/* TODO: Refactor to remove duplicate column logic (outer Side and inner Aside - in TableFilterPanel ) */}
|
|
420
|
+
<Side padding="0px">
|
|
421
|
+
<Box minWidth="300px" enableResizeOnEnd height="100%" maxHeight="100%">
|
|
422
|
+
<TableFilterPanel
|
|
423
|
+
dataSource={source}
|
|
424
|
+
onClose={() => window.alert('onClose callback called.')}
|
|
425
|
+
>
|
|
426
|
+
<StringFieldFilter fieldName="name" label="Name (string)" />
|
|
427
|
+
<NumberFieldFilter fieldName="age" label="Age (number)" />
|
|
428
|
+
<DateFieldFilter fieldName="birthdate" label="Birthdate (date range)" />
|
|
429
|
+
<NumberRangeFieldFilter fieldName="age" label="Age (number range)" />
|
|
430
|
+
<SelectFieldFilter
|
|
431
|
+
fieldName="isActive"
|
|
432
|
+
label="Active (select)"
|
|
433
|
+
options={[
|
|
434
|
+
{ label: 'Active', value: 'Yes' },
|
|
435
|
+
{ label: 'Inactive', value: 'No' },
|
|
436
|
+
]}
|
|
437
|
+
/>
|
|
438
|
+
<MulitSelectFieldFilter
|
|
439
|
+
fieldName="occupation"
|
|
440
|
+
label="Occupation (multi-select)"
|
|
441
|
+
options={OCCUPATIONS.map(o => ({ label: o, value: o }))}
|
|
443
442
|
/>
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
443
|
+
</TableFilterPanel>
|
|
444
|
+
</Box>
|
|
445
|
+
</Side>
|
|
446
|
+
<Table dataSource={source} height="100%">
|
|
447
|
+
<TableColumn heading="Name" fieldName="name" sticky="start" />
|
|
448
|
+
<TableColumn heading="Age" fieldName="age" width={150} canSort />
|
|
449
|
+
<TableColumn heading="Email" fieldName="email" width={300} />
|
|
450
|
+
<TableColumn heading="City" fieldName="city" width={300} />
|
|
451
|
+
<TableColumn
|
|
452
|
+
heading="Birthdate"
|
|
453
|
+
fieldName="birthdate"
|
|
454
|
+
width={300}
|
|
455
|
+
render={(i: DataItem) => i.birthdate.toLocaleDateString()}
|
|
456
|
+
/>
|
|
457
|
+
<TableColumn heading="Occupation" fieldName="occupation" width={200} />
|
|
458
|
+
<TableColumn heading="Active" fieldName="isActive" />
|
|
459
|
+
</Table>
|
|
460
|
+
</Rail>
|
|
450
461
|
<Footer>
|
|
451
462
|
<TablePager dataSource={source} />
|
|
452
463
|
</Footer>
|
|
@@ -548,6 +559,13 @@ const ColumnCheck = ({ presenter }: { presenter: SelectionPresenter<DataItem> })
|
|
|
548
559
|
);
|
|
549
560
|
};
|
|
550
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Demonstrates row selection with a checkbox column.
|
|
564
|
+
*
|
|
565
|
+
* Recommendation: set `canResize={false}` on fixed-width utility columns (e.g. checkboxes,
|
|
566
|
+
* icons, actions). The resize handle overlaps narrow column content and the column has no
|
|
567
|
+
* meaningful width to expose to the user.
|
|
568
|
+
*/
|
|
551
569
|
export function SelectionTable() {
|
|
552
570
|
const [source] = useState(() => {
|
|
553
571
|
return new StaticDataSource<DataItem>(items, [
|
|
@@ -568,12 +586,14 @@ export function SelectionTable() {
|
|
|
568
586
|
<StoryWrapper>
|
|
569
587
|
<Panel>
|
|
570
588
|
<Header>Selection Table</Header>
|
|
571
|
-
<
|
|
589
|
+
<Scaffold>
|
|
572
590
|
<Table dataSource={source}>
|
|
591
|
+
{/* canResize={false} recommended for fixed-width utility columns — resize handle overlaps narrow content */}
|
|
573
592
|
<TableColumn
|
|
574
593
|
heading={<ColumnCheck presenter={presenter} />}
|
|
575
594
|
sticky="start"
|
|
576
595
|
width={48}
|
|
596
|
+
canResize={false}
|
|
577
597
|
render={(item: DataItem) => <RowCheck item={item} presenter={presenter} />}
|
|
578
598
|
/>
|
|
579
599
|
<TableColumn heading="Name" fieldName="name" />
|
|
@@ -584,7 +604,7 @@ export function SelectionTable() {
|
|
|
584
604
|
<TableColumn heading="Occupation" fieldName="occupation" />
|
|
585
605
|
<TableColumn heading="Active" fieldName="isActive" />
|
|
586
606
|
</Table>
|
|
587
|
-
</
|
|
607
|
+
</Scaffold>
|
|
588
608
|
</Panel>
|
|
589
609
|
</StoryWrapper>
|
|
590
610
|
);
|
|
@@ -43,8 +43,9 @@ export function HeaderCell({
|
|
|
43
43
|
overflow="hidden"
|
|
44
44
|
minWidth="24px"
|
|
45
45
|
maxWidth="unset"
|
|
46
|
+
height="100%"
|
|
46
47
|
width={width}
|
|
47
|
-
enableResizeOnEnd
|
|
48
|
+
enableResizeOnEnd={onResize != null}
|
|
48
49
|
onWidthResize={handleResize}
|
|
49
50
|
onClick={e => e.stopPropagation()}
|
|
50
51
|
>
|
|
@@ -49,6 +49,26 @@
|
|
|
49
49
|
z-index: 2;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/* Shim: #277 — remove when #283 is resolved (Resizeable decorator on TH replaces this).
|
|
53
|
+
* Theme padding-inline on thead th conflicts with the resize Box sizing — the Box has the
|
|
54
|
+
* same fixed width as the TH, so theme padding causes the Box to overflow the TH content
|
|
55
|
+
* area, pushing the resize handle and sort action out of alignment.
|
|
56
|
+
* Zero out padding on the TH and re-apply it on the inner content child (under the Box)
|
|
57
|
+
* so it doesn't affect Box width or resize handle position. */
|
|
58
|
+
.table-body thead tr th {
|
|
59
|
+
padding-inline: 0 !important;
|
|
60
|
+
}
|
|
61
|
+
.table-body thead tr th > div > * {
|
|
62
|
+
padding-inline: var(--padding-medium);
|
|
63
|
+
}
|
|
64
|
+
.table-body thead tr th:first-child > div > * {
|
|
65
|
+
padding-inline-start: var(--table-pad-inline);
|
|
66
|
+
}
|
|
67
|
+
.table-body thead tr th:last-child > div > * {
|
|
68
|
+
padding-inline-end: var(--table-pad-inline);
|
|
69
|
+
}
|
|
70
|
+
/* End shim: #277 */
|
|
71
|
+
|
|
52
72
|
.table-body th > div {
|
|
53
73
|
display: flex;
|
|
54
74
|
align-items: center;
|
|
@@ -72,6 +72,7 @@ export function Table<T>({
|
|
|
72
72
|
width: column.props.width ?? 100,
|
|
73
73
|
sortMode: 'none',
|
|
74
74
|
canSort: column.props.canSort ?? false,
|
|
75
|
+
canResize: column.props.canResize ?? true,
|
|
75
76
|
heading: column.props.heading,
|
|
76
77
|
footer: column.props.footer,
|
|
77
78
|
sticky: column.props.sticky,
|
|
@@ -107,7 +108,11 @@ export function Table<T>({
|
|
|
107
108
|
index={index}
|
|
108
109
|
heading={wrapContent(column.heading)}
|
|
109
110
|
sticky={column.sticky}
|
|
110
|
-
onResize={
|
|
111
|
+
onResize={
|
|
112
|
+
column.canResize !== false
|
|
113
|
+
? newSize => presenter.setColumnWidth(index, newSize)
|
|
114
|
+
: undefined
|
|
115
|
+
}
|
|
111
116
|
width={column.width}
|
|
112
117
|
sortMode={column.sortMode}
|
|
113
118
|
onSortModeChange={() => presenter.setNextColumnSortMode(index)}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
@layer tcn-system {
|
|
2
|
+
/* FIXME: MultiselectValues renders with padding: 4px and min-width: 100% (content-box),
|
|
3
|
+
causing it to overflow its parent. Zeroing the padding here as a local workaround until
|
|
4
|
+
the component supports box-sizing: border-box or a className passthrough. */
|
|
5
|
+
.multiselect-filter :global(.tcn-multiselect-values-container) {
|
|
6
|
+
padding: 0;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { useSignalValue } from '@tcn/state';
|
|
2
2
|
import { Multiselect, Option } from '@tcn/ui/inputs';
|
|
3
|
-
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
4
|
-
import { Title } from '@tcn/ui/typography';
|
|
5
3
|
import { FieldFilterProps } from './field_filter_props.js';
|
|
6
4
|
import { MultiSelectFieldFilterPresenter } from './multi_select_field_filter_presenter.js';
|
|
7
5
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
8
|
-
import {
|
|
6
|
+
import { ClearableField } from './clearable_field.js';
|
|
7
|
+
import styles from './mulit_select_field_filter.module.css';
|
|
9
8
|
|
|
10
9
|
export type MulitSelectFieldFilterProps = FieldFilterProps & {
|
|
11
10
|
options: { label: string; value: string | boolean | number }[];
|
|
@@ -25,38 +24,28 @@ export function MulitSelectFieldFilter({
|
|
|
25
24
|
.filter(Boolean) ?? [];
|
|
26
25
|
|
|
27
26
|
return (
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
<ClearableField
|
|
28
|
+
label={label}
|
|
29
|
+
onClear={() => presenter.setValue(null)}
|
|
30
|
+
isClearable={values == null || values.length === 0}
|
|
31
|
+
className={styles['multiselect-filter']}
|
|
32
|
+
>
|
|
33
|
+
<Multiselect
|
|
34
|
+
value={selectedLabels}
|
|
35
|
+
onChange={selectedLabels => {
|
|
36
|
+
const realValues = selectedLabels
|
|
37
|
+
.map(label => options.find(option => option.label === label)?.value)
|
|
38
|
+
.filter(value => value !== undefined) as (string | boolean | number)[];
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
</Option>
|
|
52
|
-
))}
|
|
53
|
-
</Multiselect>
|
|
54
|
-
</Box>
|
|
55
|
-
<ClearFilterButton
|
|
56
|
-
onClick={() => presenter.setValue(null)}
|
|
57
|
-
disabled={values == null || values.length === 0}
|
|
58
|
-
/>
|
|
59
|
-
</HStack>
|
|
60
|
-
</VStack>
|
|
40
|
+
presenter.setValue(realValues.length > 0 ? realValues : null);
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{options.map(option => (
|
|
44
|
+
<Option key={option.value.toString()} value={option.label} label={option.label}>
|
|
45
|
+
{option.label}
|
|
46
|
+
</Option>
|
|
47
|
+
))}
|
|
48
|
+
</Multiselect>
|
|
49
|
+
</ClearableField>
|
|
61
50
|
);
|
|
62
51
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { useSignalValue } from '@tcn/state';
|
|
2
2
|
import { Option, Select } from '@tcn/ui/inputs';
|
|
3
|
-
import { Box, HStack, VStack } from '@tcn/ui/stacks';
|
|
4
|
-
import { Title } from '@tcn/ui/typography';
|
|
5
3
|
import { FieldFilterProps } from './field_filter_props.js';
|
|
6
4
|
import { SelectFieldFilterPresenter } from './select_field_filter_presenter.js';
|
|
7
5
|
import { useFieldFilterStrategy } from './use_field_filter_strategy.js';
|
|
8
|
-
import {
|
|
6
|
+
import { ClearableField } from './clearable_field.js';
|
|
9
7
|
|
|
10
8
|
export type SelectFieldFilterProps = FieldFilterProps & {
|
|
11
9
|
options: { label: string; value: string | boolean | number }[];
|
|
@@ -18,34 +16,25 @@ export function SelectFieldFilter({ fieldName, label, options }: SelectFieldFilt
|
|
|
18
16
|
const valueLabel = options.find(option => option.value === value)?.label ?? '';
|
|
19
17
|
|
|
20
18
|
return (
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
</Option>
|
|
42
|
-
))}
|
|
43
|
-
</Select>
|
|
44
|
-
<ClearFilterButton
|
|
45
|
-
onClick={() => presenter.setValue(null)}
|
|
46
|
-
disabled={value == null}
|
|
47
|
-
/>
|
|
48
|
-
</HStack>
|
|
49
|
-
</VStack>
|
|
19
|
+
<ClearableField
|
|
20
|
+
label={label}
|
|
21
|
+
onClear={() => presenter.setValue(null)}
|
|
22
|
+
isClearable={value == null}
|
|
23
|
+
>
|
|
24
|
+
<Select
|
|
25
|
+
value={valueLabel}
|
|
26
|
+
onChange={value => {
|
|
27
|
+
const realValue = options.find(option => option.label === value)?.value;
|
|
28
|
+
presenter.setValue(realValue ?? null);
|
|
29
|
+
}}
|
|
30
|
+
width="flex"
|
|
31
|
+
>
|
|
32
|
+
{options.map(option => (
|
|
33
|
+
<Option key={option.value.toString()} value={option.label} label={option.label}>
|
|
34
|
+
{option.label}
|
|
35
|
+
</Option>
|
|
36
|
+
))}
|
|
37
|
+
</Select>
|
|
38
|
+
</ClearableField>
|
|
50
39
|
);
|
|
51
40
|
}
|