@designa8/vibedesign-stripe-deepseek-85ea 0.2.3 → 0.2.4
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/README.md +1 -1
- package/dist/index.cjs +18 -9
- package/dist/index.js +18 -9
- package/dist/styles.css +5 -0
- package/package.json +1 -1
- package/src/atoms/DataTable/DataTable.tsx +8 -0
- package/src/atoms/DataTable/preview.tsx +43 -0
- package/src/atoms/DataTableDesktop/DataTableDesktop.tsx +95 -80
- package/src/atoms/DataTableMobile/DataTableMobile.tsx +90 -70
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @designa8/vibedesign-stripe-deepseek-85ea
|
|
2
2
|
|
|
3
|
-
Complete design system generated by Atoms Design, version **0.2.
|
|
3
|
+
Complete design system generated by Atoms Design, version **0.2.4**.
|
|
4
4
|
Tokens, atoms, molecules, organisms, and templates — import from the package root;
|
|
5
5
|
never hardcode visual values in consumer code.
|
|
6
6
|
|
package/dist/index.cjs
CHANGED
|
@@ -5032,6 +5032,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5032
5032
|
onFilterChange,
|
|
5033
5033
|
filterBy,
|
|
5034
5034
|
searchPlaceholder,
|
|
5035
|
+
searchable = true,
|
|
5035
5036
|
query,
|
|
5036
5037
|
onQueryChange,
|
|
5037
5038
|
searchBy,
|
|
@@ -5195,6 +5196,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5195
5196
|
const headCell = HEAD_CELL;
|
|
5196
5197
|
const bodyCell = dense ? "px-sm py-sm align-middle text-body-sm" : "px-sm py-md align-middle";
|
|
5197
5198
|
const rangeTriggerLabel = activeRange?.detail ?? activeRange?.label ?? "";
|
|
5199
|
+
const showToolRow = searchable || activeFilters.length > 0 || Boolean(activeRange) || Boolean(toolbar);
|
|
5198
5200
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5199
5201
|
"div",
|
|
5200
5202
|
{
|
|
@@ -5210,8 +5212,8 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5210
5212
|
title ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-body-lg text-ink", children: title }) : null,
|
|
5211
5213
|
description ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-body-sm text-muted", children: description }) : null
|
|
5212
5214
|
] }) : null,
|
|
5213
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap items-center gap-sm", children: [
|
|
5214
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-full shrink-0 sm:w-64", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5215
|
+
showToolRow ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap items-center gap-sm", children: [
|
|
5216
|
+
searchable ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-full shrink-0 sm:w-64", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5215
5217
|
Input,
|
|
5216
5218
|
{
|
|
5217
5219
|
size: "md",
|
|
@@ -5222,7 +5224,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5222
5224
|
placeholder: searchPlaceholder ?? "Search",
|
|
5223
5225
|
leading: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Icon, { name: "search", size: 16, strokeWidth: 2, tone: "default" })
|
|
5224
5226
|
}
|
|
5225
|
-
) }),
|
|
5227
|
+
) }) : null,
|
|
5226
5228
|
activeFilters.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex flex-wrap items-center gap-xs", children: activeFilters.map((item) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5227
5229
|
Chip,
|
|
5228
5230
|
{
|
|
@@ -5278,7 +5280,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5278
5280
|
}
|
|
5279
5281
|
) }) : null,
|
|
5280
5282
|
toolbar ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "ml-auto flex shrink-0 items-center gap-xs", children: toolbar }) : null
|
|
5281
|
-
] })
|
|
5283
|
+
] }) : null
|
|
5282
5284
|
] }),
|
|
5283
5285
|
activeRange && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
|
|
5284
5286
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
@@ -5704,6 +5706,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5704
5706
|
onFilterChange,
|
|
5705
5707
|
filterBy,
|
|
5706
5708
|
searchPlaceholder,
|
|
5709
|
+
searchable = true,
|
|
5707
5710
|
query,
|
|
5708
5711
|
onQueryChange,
|
|
5709
5712
|
searchBy,
|
|
@@ -5790,6 +5793,10 @@ function DataTableMobileInner(props, ref) {
|
|
|
5790
5793
|
const subtitleText = activeSpec?.subtitle;
|
|
5791
5794
|
const trailingText = activeSpec?.trailing;
|
|
5792
5795
|
const metaText = activeSpec?.meta;
|
|
5796
|
+
const hasRanges = Boolean(ranges && ranges.length > 0);
|
|
5797
|
+
const hasHeader = Boolean(
|
|
5798
|
+
title || description || searchable || activeFilters.length > 0 || hasRanges || toolbar
|
|
5799
|
+
);
|
|
5793
5800
|
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
5794
5801
|
"div",
|
|
5795
5802
|
{
|
|
@@ -5800,12 +5807,12 @@ function DataTableMobileInner(props, ref) {
|
|
|
5800
5807
|
),
|
|
5801
5808
|
...rest,
|
|
5802
5809
|
children: [
|
|
5803
|
-
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex w-full flex-col gap-base px-lg pt-lg pb-base", children: [
|
|
5810
|
+
hasHeader ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex w-full flex-col gap-base px-lg pt-lg pb-base", children: [
|
|
5804
5811
|
title || description ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-xs", children: [
|
|
5805
5812
|
title ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-body-lg text-ink", children: title }) : null,
|
|
5806
5813
|
description ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-body-sm text-muted", children: description }) : null
|
|
5807
5814
|
] }) : null,
|
|
5808
|
-
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
5815
|
+
searchable ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
5809
5816
|
"div",
|
|
5810
5817
|
{
|
|
5811
5818
|
className: cn24(
|
|
@@ -5830,7 +5837,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5830
5837
|
}
|
|
5831
5838
|
)
|
|
5832
5839
|
}
|
|
5833
|
-
),
|
|
5840
|
+
) : null,
|
|
5834
5841
|
activeFilters.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex flex-wrap items-center gap-xs", children: activeFilters.map((item) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
5835
5842
|
Chip,
|
|
5836
5843
|
{
|
|
@@ -5845,7 +5852,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5845
5852
|
},
|
|
5846
5853
|
item.value
|
|
5847
5854
|
)) }) : null,
|
|
5848
|
-
|
|
5855
|
+
hasRanges && ranges ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex flex-wrap items-center gap-base", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
5849
5856
|
FilterPill,
|
|
5850
5857
|
{
|
|
5851
5858
|
menuLabel: "Date range",
|
|
@@ -5858,7 +5865,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5858
5865
|
}
|
|
5859
5866
|
) }) : null,
|
|
5860
5867
|
toolbar ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex flex-wrap items-center gap-xs", children: toolbar }) : null
|
|
5861
|
-
] }),
|
|
5868
|
+
] }) : null,
|
|
5862
5869
|
bulkActions && selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-wrap items-center gap-sm border-t border-hairline bg-surface-soft px-lg py-sm", children: [
|
|
5863
5870
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "text-body-sm text-ink", children: [
|
|
5864
5871
|
selected.length,
|
|
@@ -6013,6 +6020,7 @@ function DataTableInner(props, ref) {
|
|
|
6013
6020
|
onFilterChange,
|
|
6014
6021
|
filterBy,
|
|
6015
6022
|
searchPlaceholder,
|
|
6023
|
+
searchable,
|
|
6016
6024
|
query,
|
|
6017
6025
|
onQueryChange,
|
|
6018
6026
|
searchBy,
|
|
@@ -6074,6 +6082,7 @@ function DataTableInner(props, ref) {
|
|
|
6074
6082
|
onFilterChange: handleFilter,
|
|
6075
6083
|
filterBy,
|
|
6076
6084
|
searchPlaceholder,
|
|
6085
|
+
searchable,
|
|
6077
6086
|
query: activeQuery,
|
|
6078
6087
|
onQueryChange: handleQuery,
|
|
6079
6088
|
searchBy,
|
package/dist/index.js
CHANGED
|
@@ -4895,6 +4895,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
4895
4895
|
onFilterChange,
|
|
4896
4896
|
filterBy,
|
|
4897
4897
|
searchPlaceholder,
|
|
4898
|
+
searchable = true,
|
|
4898
4899
|
query,
|
|
4899
4900
|
onQueryChange,
|
|
4900
4901
|
searchBy,
|
|
@@ -5058,6 +5059,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5058
5059
|
const headCell = HEAD_CELL;
|
|
5059
5060
|
const bodyCell = dense ? "px-sm py-sm align-middle text-body-sm" : "px-sm py-md align-middle";
|
|
5060
5061
|
const rangeTriggerLabel = activeRange?.detail ?? activeRange?.label ?? "";
|
|
5062
|
+
const showToolRow = searchable || activeFilters.length > 0 || Boolean(activeRange) || Boolean(toolbar);
|
|
5061
5063
|
return /* @__PURE__ */ jsxs26(
|
|
5062
5064
|
"div",
|
|
5063
5065
|
{
|
|
@@ -5073,8 +5075,8 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5073
5075
|
title ? /* @__PURE__ */ jsx29("p", { className: "text-body-lg text-ink", children: title }) : null,
|
|
5074
5076
|
description ? /* @__PURE__ */ jsx29("p", { className: "text-body-sm text-muted", children: description }) : null
|
|
5075
5077
|
] }) : null,
|
|
5076
|
-
/* @__PURE__ */ jsxs26("div", { className: "flex flex-wrap items-center gap-sm", children: [
|
|
5077
|
-
/* @__PURE__ */ jsx29("div", { className: "w-full shrink-0 sm:w-64", children: /* @__PURE__ */ jsx29(
|
|
5078
|
+
showToolRow ? /* @__PURE__ */ jsxs26("div", { className: "flex flex-wrap items-center gap-sm", children: [
|
|
5079
|
+
searchable ? /* @__PURE__ */ jsx29("div", { className: "w-full shrink-0 sm:w-64", children: /* @__PURE__ */ jsx29(
|
|
5078
5080
|
Input,
|
|
5079
5081
|
{
|
|
5080
5082
|
size: "md",
|
|
@@ -5085,7 +5087,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5085
5087
|
placeholder: searchPlaceholder ?? "Search",
|
|
5086
5088
|
leading: /* @__PURE__ */ jsx29(Icon, { name: "search", size: 16, strokeWidth: 2, tone: "default" })
|
|
5087
5089
|
}
|
|
5088
|
-
) }),
|
|
5090
|
+
) }) : null,
|
|
5089
5091
|
activeFilters.length > 0 ? /* @__PURE__ */ jsx29("div", { className: "flex flex-wrap items-center gap-xs", children: activeFilters.map((item) => /* @__PURE__ */ jsxs26(
|
|
5090
5092
|
Chip,
|
|
5091
5093
|
{
|
|
@@ -5141,7 +5143,7 @@ function DataTableDesktopInner(props, ref) {
|
|
|
5141
5143
|
}
|
|
5142
5144
|
) }) : null,
|
|
5143
5145
|
toolbar ? /* @__PURE__ */ jsx29("div", { className: "ml-auto flex shrink-0 items-center gap-xs", children: toolbar }) : null
|
|
5144
|
-
] })
|
|
5146
|
+
] }) : null
|
|
5145
5147
|
] }),
|
|
5146
5148
|
activeRange && typeof document !== "undefined" ? createPortal5(
|
|
5147
5149
|
/* @__PURE__ */ jsx29(
|
|
@@ -5567,6 +5569,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5567
5569
|
onFilterChange,
|
|
5568
5570
|
filterBy,
|
|
5569
5571
|
searchPlaceholder,
|
|
5572
|
+
searchable = true,
|
|
5570
5573
|
query,
|
|
5571
5574
|
onQueryChange,
|
|
5572
5575
|
searchBy,
|
|
@@ -5653,6 +5656,10 @@ function DataTableMobileInner(props, ref) {
|
|
|
5653
5656
|
const subtitleText = activeSpec?.subtitle;
|
|
5654
5657
|
const trailingText = activeSpec?.trailing;
|
|
5655
5658
|
const metaText = activeSpec?.meta;
|
|
5659
|
+
const hasRanges = Boolean(ranges && ranges.length > 0);
|
|
5660
|
+
const hasHeader = Boolean(
|
|
5661
|
+
title || description || searchable || activeFilters.length > 0 || hasRanges || toolbar
|
|
5662
|
+
);
|
|
5656
5663
|
return /* @__PURE__ */ jsxs27(
|
|
5657
5664
|
"div",
|
|
5658
5665
|
{
|
|
@@ -5663,12 +5670,12 @@ function DataTableMobileInner(props, ref) {
|
|
|
5663
5670
|
),
|
|
5664
5671
|
...rest,
|
|
5665
5672
|
children: [
|
|
5666
|
-
/* @__PURE__ */ jsxs27("div", { className: "flex w-full flex-col gap-base px-lg pt-lg pb-base", children: [
|
|
5673
|
+
hasHeader ? /* @__PURE__ */ jsxs27("div", { className: "flex w-full flex-col gap-base px-lg pt-lg pb-base", children: [
|
|
5667
5674
|
title || description ? /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-xs", children: [
|
|
5668
5675
|
title ? /* @__PURE__ */ jsx30("p", { className: "text-body-lg text-ink", children: title }) : null,
|
|
5669
5676
|
description ? /* @__PURE__ */ jsx30("p", { className: "text-body-sm text-muted", children: description }) : null
|
|
5670
5677
|
] }) : null,
|
|
5671
|
-
/* @__PURE__ */ jsx30(
|
|
5678
|
+
searchable ? /* @__PURE__ */ jsx30(
|
|
5672
5679
|
"div",
|
|
5673
5680
|
{
|
|
5674
5681
|
className: cn24(
|
|
@@ -5693,7 +5700,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5693
5700
|
}
|
|
5694
5701
|
)
|
|
5695
5702
|
}
|
|
5696
|
-
),
|
|
5703
|
+
) : null,
|
|
5697
5704
|
activeFilters.length > 0 ? /* @__PURE__ */ jsx30("div", { className: "flex flex-wrap items-center gap-xs", children: activeFilters.map((item) => /* @__PURE__ */ jsxs27(
|
|
5698
5705
|
Chip,
|
|
5699
5706
|
{
|
|
@@ -5708,7 +5715,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5708
5715
|
},
|
|
5709
5716
|
item.value
|
|
5710
5717
|
)) }) : null,
|
|
5711
|
-
|
|
5718
|
+
hasRanges && ranges ? /* @__PURE__ */ jsx30("div", { className: "flex flex-wrap items-center gap-base", children: /* @__PURE__ */ jsx30(
|
|
5712
5719
|
FilterPill,
|
|
5713
5720
|
{
|
|
5714
5721
|
menuLabel: "Date range",
|
|
@@ -5721,7 +5728,7 @@ function DataTableMobileInner(props, ref) {
|
|
|
5721
5728
|
}
|
|
5722
5729
|
) }) : null,
|
|
5723
5730
|
toolbar ? /* @__PURE__ */ jsx30("div", { className: "flex flex-wrap items-center gap-xs", children: toolbar }) : null
|
|
5724
|
-
] }),
|
|
5731
|
+
] }) : null,
|
|
5725
5732
|
bulkActions && selected.length > 0 ? /* @__PURE__ */ jsxs27("div", { className: "flex flex-wrap items-center gap-sm border-t border-hairline bg-surface-soft px-lg py-sm", children: [
|
|
5726
5733
|
/* @__PURE__ */ jsxs27("span", { className: "text-body-sm text-ink", children: [
|
|
5727
5734
|
selected.length,
|
|
@@ -5876,6 +5883,7 @@ function DataTableInner(props, ref) {
|
|
|
5876
5883
|
onFilterChange,
|
|
5877
5884
|
filterBy,
|
|
5878
5885
|
searchPlaceholder,
|
|
5886
|
+
searchable,
|
|
5879
5887
|
query,
|
|
5880
5888
|
onQueryChange,
|
|
5881
5889
|
searchBy,
|
|
@@ -5937,6 +5945,7 @@ function DataTableInner(props, ref) {
|
|
|
5937
5945
|
onFilterChange: handleFilter,
|
|
5938
5946
|
filterBy,
|
|
5939
5947
|
searchPlaceholder,
|
|
5948
|
+
searchable,
|
|
5940
5949
|
query: activeQuery,
|
|
5941
5950
|
onQueryChange: handleQuery,
|
|
5942
5951
|
searchBy,
|
package/dist/styles.css
CHANGED
|
@@ -3461,6 +3461,11 @@
|
|
|
3461
3461
|
width: calc(var(--spacing) * 64);
|
|
3462
3462
|
}
|
|
3463
3463
|
}
|
|
3464
|
+
.sm\:w-80 {
|
|
3465
|
+
@media (width >= 640px) {
|
|
3466
|
+
width: calc(var(--spacing) * 80);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3464
3469
|
.sm\:grid-cols-2 {
|
|
3465
3470
|
@media (width >= 640px) {
|
|
3466
3471
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
package/package.json
CHANGED
|
@@ -38,6 +38,12 @@ export interface DataTableProps<T extends DataTableBaseRow = DataTableBaseRow>
|
|
|
38
38
|
onFilterChange?: (value: string) => void;
|
|
39
39
|
filterBy?: (row: T, value: string) => boolean;
|
|
40
40
|
searchPlaceholder?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Renders the built-in search field in both layouts. Defaults to true. Set
|
|
43
|
+
* it to false when the page already owns a global search bar: `query` and
|
|
44
|
+
* `searchBy` keep filtering exactly the same, only the field is left out.
|
|
45
|
+
*/
|
|
46
|
+
searchable?: boolean;
|
|
41
47
|
query?: string;
|
|
42
48
|
onQueryChange?: (value: string) => void;
|
|
43
49
|
searchBy?: (row: T) => string;
|
|
@@ -96,6 +102,7 @@ function DataTableInner<T extends DataTableBaseRow>(
|
|
|
96
102
|
onFilterChange,
|
|
97
103
|
filterBy,
|
|
98
104
|
searchPlaceholder,
|
|
105
|
+
searchable,
|
|
99
106
|
query,
|
|
100
107
|
onQueryChange,
|
|
101
108
|
searchBy,
|
|
@@ -167,6 +174,7 @@ function DataTableInner<T extends DataTableBaseRow>(
|
|
|
167
174
|
onFilterChange: handleFilter,
|
|
168
175
|
filterBy,
|
|
169
176
|
searchPlaceholder,
|
|
177
|
+
searchable,
|
|
170
178
|
query: activeQuery,
|
|
171
179
|
onQueryChange: handleQuery,
|
|
172
180
|
searchBy,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Badge } from "../Badge";
|
|
3
3
|
import { Button } from "../Button";
|
|
4
|
+
import { Icon } from "../Icon";
|
|
5
|
+
import { Input } from "../Input";
|
|
4
6
|
import { DataTable } from "./DataTable";
|
|
5
7
|
import type { DataTableColumn, DataTableFilter, DataTableRange } from "./DataTable";
|
|
6
8
|
|
|
@@ -148,6 +150,7 @@ export default function Preview() {
|
|
|
148
150
|
const [selected, setSelected] = React.useState<string[]>([]);
|
|
149
151
|
const [filter, setFilter] = React.useState("all");
|
|
150
152
|
const [query, setQuery] = React.useState("");
|
|
153
|
+
const [pageQuery, setPageQuery] = React.useState("");
|
|
151
154
|
|
|
152
155
|
return (
|
|
153
156
|
<div className="w-full max-w-5xl mx-auto p-lg space-y-lg bg-canvas">
|
|
@@ -221,6 +224,46 @@ export default function Preview() {
|
|
|
221
224
|
selectable={false}
|
|
222
225
|
mobileRow={mobileRow}
|
|
223
226
|
/>
|
|
227
|
+
|
|
228
|
+
<div className="flex w-full flex-col gap-md">
|
|
229
|
+
<div className="flex w-full flex-col gap-xs">
|
|
230
|
+
<p className="text-body-lg text-ink">Page-level search</p>
|
|
231
|
+
<p className="text-body-sm text-muted">
|
|
232
|
+
The table renders no field of its own, but the controlled query
|
|
233
|
+
still filters both layouts.
|
|
234
|
+
</p>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div className="w-full sm:w-80">
|
|
238
|
+
<Input
|
|
239
|
+
size="md"
|
|
240
|
+
type="search"
|
|
241
|
+
value={pageQuery}
|
|
242
|
+
onChange={(event) => setPageQuery(event.target.value)}
|
|
243
|
+
ariaLabel="Search all invoices"
|
|
244
|
+
placeholder="Search all invoices"
|
|
245
|
+
leading={<Icon name="search" size={16} strokeWidth={2} tone="default" />}
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<DataTable<Invoice>
|
|
250
|
+
columns={columns}
|
|
251
|
+
rows={invoices}
|
|
252
|
+
searchable={false}
|
|
253
|
+
query={pageQuery}
|
|
254
|
+
onQueryChange={setPageQuery}
|
|
255
|
+
searchBy={(row) => `${row.id} ${row.customer}`}
|
|
256
|
+
filters={filters.slice(0, 1)}
|
|
257
|
+
pageSize={5}
|
|
258
|
+
mobilePageSize={4}
|
|
259
|
+
itemLabel="invoices"
|
|
260
|
+
dense
|
|
261
|
+
selectable={false}
|
|
262
|
+
mobileRow={mobileRow}
|
|
263
|
+
emptyTitle="Nothing matches that search"
|
|
264
|
+
emptyDescription="Try a customer name or an invoice number."
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
224
267
|
</div>
|
|
225
268
|
);
|
|
226
269
|
}
|
|
@@ -226,6 +226,12 @@ export interface DataTableDesktopProps<
|
|
|
226
226
|
/** Row predicate for the active filter. `"all"` always shows everything. */
|
|
227
227
|
filterBy?: (row: T, value: string) => boolean;
|
|
228
228
|
searchPlaceholder?: string;
|
|
229
|
+
/**
|
|
230
|
+
* Renders the built-in search field. Defaults to true. Set it to false when
|
|
231
|
+
* the page already owns a global search bar: `query` and `searchBy` keep
|
|
232
|
+
* filtering exactly the same, only the field is left out.
|
|
233
|
+
*/
|
|
234
|
+
searchable?: boolean;
|
|
229
235
|
/** Controlled search text. */
|
|
230
236
|
query?: string;
|
|
231
237
|
onQueryChange?: (value: string) => void;
|
|
@@ -277,6 +283,7 @@ function DataTableDesktopInner<T extends DataTableBaseRow>(
|
|
|
277
283
|
onFilterChange,
|
|
278
284
|
filterBy,
|
|
279
285
|
searchPlaceholder,
|
|
286
|
+
searchable = true,
|
|
280
287
|
query,
|
|
281
288
|
onQueryChange,
|
|
282
289
|
searchBy,
|
|
@@ -505,6 +512,10 @@ function DataTableDesktopInner<T extends DataTableBaseRow>(
|
|
|
505
512
|
|
|
506
513
|
const rangeTriggerLabel = activeRange?.detail ?? activeRange?.label ?? "";
|
|
507
514
|
|
|
515
|
+
/** The tool row is only worth its vertical space when it holds something. */
|
|
516
|
+
const showToolRow =
|
|
517
|
+
searchable || activeFilters.length > 0 || Boolean(activeRange) || Boolean(toolbar);
|
|
518
|
+
|
|
508
519
|
return (
|
|
509
520
|
<div
|
|
510
521
|
ref={ref}
|
|
@@ -526,89 +537,93 @@ function DataTableDesktopInner<T extends DataTableBaseRow>(
|
|
|
526
537
|
</div>
|
|
527
538
|
) : null}
|
|
528
539
|
|
|
529
|
-
|
|
530
|
-
<div className="
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
value={activeQuery}
|
|
535
|
-
onChange={(event) => applyQuery(event.target.value)}
|
|
536
|
-
ariaLabel={searchPlaceholder ?? "Search table"}
|
|
537
|
-
placeholder={searchPlaceholder ?? "Search"}
|
|
538
|
-
leading={
|
|
539
|
-
<Icon name="search" size={16} strokeWidth={2} tone="default" />
|
|
540
|
-
}
|
|
541
|
-
/>
|
|
542
|
-
</div>
|
|
543
|
-
|
|
544
|
-
{activeFilters.length > 0 ? (
|
|
545
|
-
<div className="flex flex-wrap items-center gap-xs">
|
|
546
|
-
{activeFilters.map((item) => (
|
|
547
|
-
<Chip
|
|
548
|
-
key={item.value}
|
|
549
|
-
appearance="outline"
|
|
540
|
+
{showToolRow ? (
|
|
541
|
+
<div className="flex flex-wrap items-center gap-sm">
|
|
542
|
+
{searchable ? (
|
|
543
|
+
<div className="w-full shrink-0 sm:w-64">
|
|
544
|
+
<Input
|
|
550
545
|
size="md"
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
{
|
|
555
|
-
{
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
546
|
+
type="search"
|
|
547
|
+
value={activeQuery}
|
|
548
|
+
onChange={(event) => applyQuery(event.target.value)}
|
|
549
|
+
ariaLabel={searchPlaceholder ?? "Search table"}
|
|
550
|
+
placeholder={searchPlaceholder ?? "Search"}
|
|
551
|
+
leading={
|
|
552
|
+
<Icon name="search" size={16} strokeWidth={2} tone="default" />
|
|
553
|
+
}
|
|
554
|
+
/>
|
|
555
|
+
</div>
|
|
556
|
+
) : null}
|
|
562
557
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
size={16}
|
|
582
|
-
strokeWidth={1.75}
|
|
583
|
-
tone="default"
|
|
584
|
-
/>
|
|
585
|
-
}
|
|
586
|
-
trailingIcon={
|
|
587
|
-
<Icon
|
|
588
|
-
name="chevron-down"
|
|
589
|
-
size={16}
|
|
590
|
-
strokeWidth={2}
|
|
591
|
-
tone="muted"
|
|
592
|
-
className={cn(
|
|
593
|
-
"transition-transform duration-200 ease-out",
|
|
594
|
-
rangeOpen && "rotate-180",
|
|
595
|
-
)}
|
|
596
|
-
/>
|
|
597
|
-
}
|
|
598
|
-
>
|
|
599
|
-
<span className="whitespace-nowrap text-body-sm text-ink">
|
|
600
|
-
{rangeTriggerLabel}
|
|
601
|
-
</span>
|
|
602
|
-
</Button>
|
|
603
|
-
</div>
|
|
604
|
-
) : null}
|
|
558
|
+
{activeFilters.length > 0 ? (
|
|
559
|
+
<div className="flex flex-wrap items-center gap-xs">
|
|
560
|
+
{activeFilters.map((item) => (
|
|
561
|
+
<Chip
|
|
562
|
+
key={item.value}
|
|
563
|
+
appearance="outline"
|
|
564
|
+
size="md"
|
|
565
|
+
selected={activeFilter === item.value}
|
|
566
|
+
onClick={() => applyFilter(item.value)}
|
|
567
|
+
>
|
|
568
|
+
{item.label}
|
|
569
|
+
{typeof item.count === "number" ? (
|
|
570
|
+
<span className="ml-xs text-muted-soft">{item.count}</span>
|
|
571
|
+
) : null}
|
|
572
|
+
</Chip>
|
|
573
|
+
))}
|
|
574
|
+
</div>
|
|
575
|
+
) : null}
|
|
605
576
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
577
|
+
{activeRange ? (
|
|
578
|
+
<div className="relative shrink-0">
|
|
579
|
+
<Button
|
|
580
|
+
ref={triggerRef}
|
|
581
|
+
variant="field-soft"
|
|
582
|
+
size="field"
|
|
583
|
+
className="w-auto"
|
|
584
|
+
aria-expanded={rangeOpen}
|
|
585
|
+
aria-controls={panelId}
|
|
586
|
+
aria-haspopup="menu"
|
|
587
|
+
aria-label={`Date range: ${rangeTriggerLabel}`}
|
|
588
|
+
onClick={() => {
|
|
589
|
+
placePanel();
|
|
590
|
+
setRangeOpen((value) => !value);
|
|
591
|
+
}}
|
|
592
|
+
leadingIcon={
|
|
593
|
+
<Icon
|
|
594
|
+
name="calendar"
|
|
595
|
+
size={16}
|
|
596
|
+
strokeWidth={1.75}
|
|
597
|
+
tone="default"
|
|
598
|
+
/>
|
|
599
|
+
}
|
|
600
|
+
trailingIcon={
|
|
601
|
+
<Icon
|
|
602
|
+
name="chevron-down"
|
|
603
|
+
size={16}
|
|
604
|
+
strokeWidth={2}
|
|
605
|
+
tone="muted"
|
|
606
|
+
className={cn(
|
|
607
|
+
"transition-transform duration-200 ease-out",
|
|
608
|
+
rangeOpen && "rotate-180",
|
|
609
|
+
)}
|
|
610
|
+
/>
|
|
611
|
+
}
|
|
612
|
+
>
|
|
613
|
+
<span className="whitespace-nowrap text-body-sm text-ink">
|
|
614
|
+
{rangeTriggerLabel}
|
|
615
|
+
</span>
|
|
616
|
+
</Button>
|
|
617
|
+
</div>
|
|
618
|
+
) : null}
|
|
619
|
+
|
|
620
|
+
{toolbar ? (
|
|
621
|
+
<div className="ml-auto flex shrink-0 items-center gap-xs">
|
|
622
|
+
{toolbar}
|
|
623
|
+
</div>
|
|
624
|
+
) : null}
|
|
625
|
+
</div>
|
|
626
|
+
) : null}
|
|
612
627
|
</div>
|
|
613
628
|
|
|
614
629
|
{activeRange && typeof document !== "undefined"
|
|
@@ -121,6 +121,12 @@ export interface DataTableMobileProps<
|
|
|
121
121
|
onFilterChange?: (value: string) => void;
|
|
122
122
|
filterBy?: (row: T, value: string) => boolean;
|
|
123
123
|
searchPlaceholder?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Renders the built-in search field. Defaults to true. Set it to false when
|
|
126
|
+
* the page already owns a global search bar: `query` and `searchBy` keep
|
|
127
|
+
* filtering exactly the same, only the field is left out.
|
|
128
|
+
*/
|
|
129
|
+
searchable?: boolean;
|
|
124
130
|
query?: string;
|
|
125
131
|
onQueryChange?: (value: string) => void;
|
|
126
132
|
searchBy?: (row: T) => string;
|
|
@@ -310,6 +316,7 @@ function DataTableMobileInner<T extends DataTableMobileBaseRow>(
|
|
|
310
316
|
onFilterChange,
|
|
311
317
|
filterBy,
|
|
312
318
|
searchPlaceholder,
|
|
319
|
+
searchable = true,
|
|
313
320
|
query,
|
|
314
321
|
onQueryChange,
|
|
315
322
|
searchBy,
|
|
@@ -441,6 +448,13 @@ function DataTableMobileInner<T extends DataTableMobileBaseRow>(
|
|
|
441
448
|
const trailingText = activeSpec?.trailing;
|
|
442
449
|
const metaText = activeSpec?.meta;
|
|
443
450
|
|
|
451
|
+
const hasRanges = Boolean(ranges && ranges.length > 0);
|
|
452
|
+
|
|
453
|
+
/** Nothing to show above the list means no header block, and no dead space. */
|
|
454
|
+
const hasHeader = Boolean(
|
|
455
|
+
title || description || searchable || activeFilters.length > 0 || hasRanges || toolbar,
|
|
456
|
+
);
|
|
457
|
+
|
|
444
458
|
return (
|
|
445
459
|
<div
|
|
446
460
|
ref={ref}
|
|
@@ -450,77 +464,83 @@ function DataTableMobileInner<T extends DataTableMobileBaseRow>(
|
|
|
450
464
|
)}
|
|
451
465
|
{...rest}
|
|
452
466
|
>
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
467
|
+
{hasHeader ? (
|
|
468
|
+
<div className="flex w-full flex-col gap-base px-lg pt-lg pb-base">
|
|
469
|
+
{title || description ? (
|
|
470
|
+
<div className="flex flex-col gap-xs">
|
|
471
|
+
{title ? <p className="text-body-lg text-ink">{title}</p> : null}
|
|
472
|
+
{description ? (
|
|
473
|
+
<p className="text-body-sm text-muted">{description}</p>
|
|
474
|
+
) : null}
|
|
475
|
+
</div>
|
|
476
|
+
) : null}
|
|
477
|
+
|
|
478
|
+
{searchable ? (
|
|
479
|
+
<div
|
|
480
|
+
className={cn(
|
|
481
|
+
"flex min-h-12 w-full items-center gap-sm rounded-xl border px-base",
|
|
482
|
+
"border-hairline bg-canvas",
|
|
483
|
+
"transition duration-150 ease-out",
|
|
484
|
+
"focus-within:border-primary focus-within:ring-1 focus-within:ring-primary",
|
|
485
|
+
)}
|
|
486
|
+
>
|
|
487
|
+
<Input
|
|
488
|
+
chrome="embedded"
|
|
489
|
+
type="search"
|
|
490
|
+
value={activeQuery}
|
|
491
|
+
onChange={(event) => applyQuery(event.target.value)}
|
|
492
|
+
placeholder={searchPlaceholder ?? "Search"}
|
|
493
|
+
ariaLabel={searchPlaceholder ?? "Search table"}
|
|
494
|
+
autoComplete="off"
|
|
495
|
+
className="flex-1"
|
|
496
|
+
inputClassName="[&::-webkit-search-cancel-button]:appearance-none"
|
|
497
|
+
leading={
|
|
498
|
+
<Icon name="search" size={18} strokeWidth={2} tone="default" />
|
|
499
|
+
}
|
|
500
|
+
/>
|
|
501
|
+
</div>
|
|
502
|
+
) : null}
|
|
503
|
+
|
|
504
|
+
{activeFilters.length > 0 ? (
|
|
505
|
+
<div className="flex flex-wrap items-center gap-xs">
|
|
506
|
+
{activeFilters.map((item) => (
|
|
507
|
+
<Chip
|
|
508
|
+
key={item.value}
|
|
509
|
+
appearance="outline"
|
|
510
|
+
size="md"
|
|
511
|
+
selected={activeFilter === item.value}
|
|
512
|
+
onClick={() => applyFilter(item.value)}
|
|
513
|
+
>
|
|
514
|
+
{item.label}
|
|
515
|
+
{typeof item.count === "number" ? (
|
|
516
|
+
<span className="ml-xs text-muted-soft">{item.count}</span>
|
|
517
|
+
) : null}
|
|
518
|
+
</Chip>
|
|
519
|
+
))}
|
|
520
|
+
</div>
|
|
521
|
+
) : null}
|
|
522
|
+
|
|
523
|
+
{hasRanges && ranges ? (
|
|
524
|
+
<div className="flex flex-wrap items-center gap-base">
|
|
525
|
+
<FilterPill
|
|
526
|
+
menuLabel="Date range"
|
|
527
|
+
options={ranges.map((item) => ({
|
|
528
|
+
value: item.value,
|
|
529
|
+
label: item.detail
|
|
530
|
+
? `${item.label} · ${item.detail}`
|
|
531
|
+
: item.label,
|
|
532
|
+
}))}
|
|
533
|
+
value={activeRange?.value ?? ranges[0].value}
|
|
534
|
+
onValueChange={applyRange}
|
|
535
|
+
/>
|
|
536
|
+
</div>
|
|
537
|
+
) : null}
|
|
538
|
+
|
|
539
|
+
{toolbar ? (
|
|
540
|
+
<div className="flex flex-wrap items-center gap-xs">{toolbar}</div>
|
|
541
|
+
) : null}
|
|
485
542
|
</div>
|
|
486
|
-
|
|
487
|
-
{activeFilters.length > 0 ? (
|
|
488
|
-
<div className="flex flex-wrap items-center gap-xs">
|
|
489
|
-
{activeFilters.map((item) => (
|
|
490
|
-
<Chip
|
|
491
|
-
key={item.value}
|
|
492
|
-
appearance="outline"
|
|
493
|
-
size="md"
|
|
494
|
-
selected={activeFilter === item.value}
|
|
495
|
-
onClick={() => applyFilter(item.value)}
|
|
496
|
-
>
|
|
497
|
-
{item.label}
|
|
498
|
-
{typeof item.count === "number" ? (
|
|
499
|
-
<span className="ml-xs text-muted-soft">{item.count}</span>
|
|
500
|
-
) : null}
|
|
501
|
-
</Chip>
|
|
502
|
-
))}
|
|
503
|
-
</div>
|
|
504
|
-
) : null}
|
|
505
|
-
|
|
506
|
-
{ranges && ranges.length > 0 ? (
|
|
507
|
-
<div className="flex flex-wrap items-center gap-base">
|
|
508
|
-
<FilterPill
|
|
509
|
-
menuLabel="Date range"
|
|
510
|
-
options={ranges.map((item) => ({
|
|
511
|
-
value: item.value,
|
|
512
|
-
label: item.detail ? `${item.label} · ${item.detail}` : item.label,
|
|
513
|
-
}))}
|
|
514
|
-
value={activeRange?.value ?? ranges[0].value}
|
|
515
|
-
onValueChange={applyRange}
|
|
516
|
-
/>
|
|
517
|
-
</div>
|
|
518
|
-
) : null}
|
|
519
|
-
|
|
520
|
-
{toolbar ? (
|
|
521
|
-
<div className="flex flex-wrap items-center gap-xs">{toolbar}</div>
|
|
522
|
-
) : null}
|
|
523
|
-
</div>
|
|
543
|
+
) : null}
|
|
524
544
|
|
|
525
545
|
{bulkActions && selected.length > 0 ? (
|
|
526
546
|
<div className="flex flex-wrap items-center gap-sm border-t border-hairline bg-surface-soft px-lg py-sm">
|