@lateralus-ai/shipping-ui 2.0.0-dev.22 → 2.0.0-dev.24
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/domain/Filters/FilterDropdown.d.ts +33 -7
- package/dist/domain/Filters/FilterPill.d.ts +13 -3
- package/dist/domain/Filters/index.d.ts +2 -2
- package/dist/index.cjs +23 -23
- package/dist/index.esm.js +3302 -3270
- package/dist/primitives/Switch.d.ts +4 -0
- package/package.json +1 -1
- package/src/domain/Filters/FilterDropdown.tsx +540 -438
- package/src/domain/Filters/FilterPill.tsx +74 -61
- package/src/domain/Filters/FilterPills.tsx +34 -34
- package/src/domain/Filters/FilteredPill.tsx +59 -59
- package/src/domain/Filters/FiltersBar.tsx +72 -72
- package/src/domain/Filters/formatActiveFilterChipLabel.ts +23 -23
- package/src/domain/Filters/index.ts +37 -35
- package/src/primitives/Switch.tsx +16 -6
- package/src/stories/canvases/FiltersCanvas.tsx +171 -167
|
@@ -1,167 +1,171 @@
|
|
|
1
|
-
import { useMemo, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
FilterDropdown,
|
|
4
|
-
FilterPill,
|
|
5
|
-
FilterPills,
|
|
6
|
-
FilteredPill,
|
|
7
|
-
FiltersBar,
|
|
8
|
-
formatActiveFilterChipLabel,
|
|
9
|
-
type FilterCategoryRow,
|
|
10
|
-
} from "../../domain/Filters";
|
|
11
|
-
import {
|
|
12
|
-
FigmaContent,
|
|
13
|
-
FigmaGrid,
|
|
14
|
-
FigmaPage,
|
|
15
|
-
FigmaSection,
|
|
16
|
-
} from "../_layout";
|
|
17
|
-
import { FIGMA_WIDTHS } from "./figma-widths";
|
|
18
|
-
|
|
19
|
-
const demoOptions = {
|
|
20
|
-
status: [
|
|
21
|
-
{ value: "approved", label: "Approved" },
|
|
22
|
-
{ value: "reviewed", label: "Reviewed" },
|
|
23
|
-
{ value: "pending", label: "Pending" },
|
|
24
|
-
],
|
|
25
|
-
vessel: [
|
|
26
|
-
{ value: "aurora", label: "Aurora" },
|
|
27
|
-
{ value: "north", label: "North Star" },
|
|
28
|
-
],
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const FiltersCanvas = () => {
|
|
32
|
-
const [status, setStatus] = useState<string[]>([]);
|
|
33
|
-
const [vessel, setVessel] = useState<string[]>([]);
|
|
34
|
-
const [groupByVessel, setGroupByVessel] = useState(false);
|
|
35
|
-
|
|
36
|
-
const selectedValuesBySelectionKey = useMemo(
|
|
37
|
-
() => ({ status, vessel }),
|
|
38
|
-
[status, vessel],
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
const categoryRows: FilterCategoryRow[] = useMemo(
|
|
42
|
-
() => [
|
|
43
|
-
{
|
|
44
|
-
id: "status",
|
|
45
|
-
label: "Status",
|
|
46
|
-
content: {
|
|
47
|
-
type: "options",
|
|
48
|
-
selectionKey: "status",
|
|
49
|
-
selectionMode: "multi",
|
|
50
|
-
options: demoOptions.status,
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: "vessel",
|
|
55
|
-
label: "Vessel",
|
|
56
|
-
content: {
|
|
57
|
-
type: "options",
|
|
58
|
-
selectionKey: "vessel",
|
|
59
|
-
selectionMode: "multi",
|
|
60
|
-
options: demoOptions.vessel,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
kind: "toggle",
|
|
65
|
-
id: "group",
|
|
66
|
-
label: "Group by Vessel",
|
|
67
|
-
checked: groupByVessel,
|
|
68
|
-
onCheckedChange: setGroupByVessel,
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
[groupByVessel],
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const chips = useMemo(() => {
|
|
75
|
-
const next: {
|
|
76
|
-
key: string;
|
|
77
|
-
label: string;
|
|
78
|
-
onRemove: () => void;
|
|
79
|
-
}[] = [];
|
|
80
|
-
if (status.length > 0) {
|
|
81
|
-
const labels = status.map(
|
|
82
|
-
(v) => demoOptions.status.find((o) => o.value === v)?.label ?? v,
|
|
83
|
-
);
|
|
84
|
-
next.push({
|
|
85
|
-
key: "status",
|
|
86
|
-
label: formatActiveFilterChipLabel("Status", labels),
|
|
87
|
-
onRemove: () => setStatus([]),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (vessel.length > 0) {
|
|
91
|
-
const labels = vessel.map(
|
|
92
|
-
(v) => demoOptions.vessel.find((o) => o.value === v)?.label ?? v,
|
|
93
|
-
);
|
|
94
|
-
next.push({
|
|
95
|
-
key: "vessel",
|
|
96
|
-
label: formatActiveFilterChipLabel("Vessel", labels),
|
|
97
|
-
onRemove: () => setVessel([]),
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return next;
|
|
101
|
-
}, [status, vessel]);
|
|
102
|
-
|
|
103
|
-
const activeFilterCount = chips.length;
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<FigmaPage title="Filters" width={FIGMA_WIDTHS.filters}>
|
|
107
|
-
<FigmaContent>
|
|
108
|
-
<FigmaSection label="Filter Pill (trigger)">
|
|
109
|
-
<FigmaGrid gap={16}>
|
|
110
|
-
<FilterPill />
|
|
111
|
-
<FilterPill activeFilterCount={2} />
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
FilterDropdown,
|
|
4
|
+
FilterPill,
|
|
5
|
+
FilterPills,
|
|
6
|
+
FilteredPill,
|
|
7
|
+
FiltersBar,
|
|
8
|
+
formatActiveFilterChipLabel,
|
|
9
|
+
type FilterCategoryRow,
|
|
10
|
+
} from "../../domain/Filters";
|
|
11
|
+
import {
|
|
12
|
+
FigmaContent,
|
|
13
|
+
FigmaGrid,
|
|
14
|
+
FigmaPage,
|
|
15
|
+
FigmaSection,
|
|
16
|
+
} from "../_layout";
|
|
17
|
+
import { FIGMA_WIDTHS } from "./figma-widths";
|
|
18
|
+
|
|
19
|
+
const demoOptions = {
|
|
20
|
+
status: [
|
|
21
|
+
{ value: "approved", label: "Approved" },
|
|
22
|
+
{ value: "reviewed", label: "Reviewed" },
|
|
23
|
+
{ value: "pending", label: "Pending" },
|
|
24
|
+
],
|
|
25
|
+
vessel: [
|
|
26
|
+
{ value: "aurora", label: "Aurora" },
|
|
27
|
+
{ value: "north", label: "North Star" },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const FiltersCanvas = () => {
|
|
32
|
+
const [status, setStatus] = useState<string[]>([]);
|
|
33
|
+
const [vessel, setVessel] = useState<string[]>([]);
|
|
34
|
+
const [groupByVessel, setGroupByVessel] = useState(false);
|
|
35
|
+
|
|
36
|
+
const selectedValuesBySelectionKey = useMemo(
|
|
37
|
+
() => ({ status, vessel }),
|
|
38
|
+
[status, vessel],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const categoryRows: FilterCategoryRow[] = useMemo(
|
|
42
|
+
() => [
|
|
43
|
+
{
|
|
44
|
+
id: "status",
|
|
45
|
+
label: "Status",
|
|
46
|
+
content: {
|
|
47
|
+
type: "options",
|
|
48
|
+
selectionKey: "status",
|
|
49
|
+
selectionMode: "multi",
|
|
50
|
+
options: demoOptions.status,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "vessel",
|
|
55
|
+
label: "Vessel",
|
|
56
|
+
content: {
|
|
57
|
+
type: "options",
|
|
58
|
+
selectionKey: "vessel",
|
|
59
|
+
selectionMode: "multi",
|
|
60
|
+
options: demoOptions.vessel,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
kind: "toggle",
|
|
65
|
+
id: "group",
|
|
66
|
+
label: "Group by Vessel",
|
|
67
|
+
checked: groupByVessel,
|
|
68
|
+
onCheckedChange: setGroupByVessel,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
[groupByVessel],
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const chips = useMemo(() => {
|
|
75
|
+
const next: {
|
|
76
|
+
key: string;
|
|
77
|
+
label: string;
|
|
78
|
+
onRemove: () => void;
|
|
79
|
+
}[] = [];
|
|
80
|
+
if (status.length > 0) {
|
|
81
|
+
const labels = status.map(
|
|
82
|
+
(v) => demoOptions.status.find((o) => o.value === v)?.label ?? v,
|
|
83
|
+
);
|
|
84
|
+
next.push({
|
|
85
|
+
key: "status",
|
|
86
|
+
label: formatActiveFilterChipLabel("Status", labels),
|
|
87
|
+
onRemove: () => setStatus([]),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (vessel.length > 0) {
|
|
91
|
+
const labels = vessel.map(
|
|
92
|
+
(v) => demoOptions.vessel.find((o) => o.value === v)?.label ?? v,
|
|
93
|
+
);
|
|
94
|
+
next.push({
|
|
95
|
+
key: "vessel",
|
|
96
|
+
label: formatActiveFilterChipLabel("Vessel", labels),
|
|
97
|
+
onRemove: () => setVessel([]),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return next;
|
|
101
|
+
}, [status, vessel]);
|
|
102
|
+
|
|
103
|
+
const activeFilterCount = chips.length;
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<FigmaPage title="Filters" width={FIGMA_WIDTHS.filters}>
|
|
107
|
+
<FigmaContent>
|
|
108
|
+
<FigmaSection label="Filter Pill (trigger)">
|
|
109
|
+
<FigmaGrid gap={16}>
|
|
110
|
+
<FilterPill />
|
|
111
|
+
<FilterPill activeFilterCount={2} />
|
|
112
|
+
<FilterPill appearance="ghost" />
|
|
113
|
+
<FilterPill appearance="ghost" activeFilterCount={2} />
|
|
114
|
+
</FigmaGrid>
|
|
115
|
+
</FigmaSection>
|
|
116
|
+
|
|
117
|
+
<FigmaSection label="Filtered Pill">
|
|
118
|
+
<FigmaGrid gap={16}>
|
|
119
|
+
<FilteredPill label="Approved" onRemove={() => undefined} />
|
|
120
|
+
<FilteredPill label="Reviewed" onRemove={() => undefined} />
|
|
121
|
+
</FigmaGrid>
|
|
122
|
+
</FigmaSection>
|
|
123
|
+
|
|
124
|
+
<FigmaSection label="FilterDropdown + FilterPills (interactive)">
|
|
125
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
126
|
+
<FilterPills chips={chips} />
|
|
127
|
+
<div className="flex shrink-0 items-center gap-4">
|
|
128
|
+
<FilterDropdown
|
|
129
|
+
align="end"
|
|
130
|
+
submenuSide="left"
|
|
131
|
+
triggerAppearance="ghost"
|
|
132
|
+
categoryRows={categoryRows}
|
|
133
|
+
selectedValuesBySelectionKey={selectedValuesBySelectionKey}
|
|
134
|
+
activeFilterCount={activeFilterCount}
|
|
135
|
+
onSelectOption={(key, value) => {
|
|
136
|
+
if (key === "status") {
|
|
137
|
+
setStatus((prev) =>
|
|
138
|
+
prev.includes(value)
|
|
139
|
+
? prev.filter((v) => v !== value)
|
|
140
|
+
: [...prev, value],
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (key === "vessel") {
|
|
144
|
+
setVessel((prev) =>
|
|
145
|
+
prev.includes(value)
|
|
146
|
+
? prev.filter((v) => v !== value)
|
|
147
|
+
: [...prev, value],
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}}
|
|
151
|
+
onResetAll={() => {
|
|
152
|
+
setStatus([]);
|
|
153
|
+
setVessel([]);
|
|
154
|
+
setGroupByVessel(false);
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</FigmaSection>
|
|
160
|
+
|
|
161
|
+
<FigmaSection label="Filters Bar (layout stub)">
|
|
162
|
+
<div className="space-y-6">
|
|
163
|
+
<FiltersBar />
|
|
164
|
+
<FiltersBar filters="on" activeTab="Reports" />
|
|
165
|
+
<FiltersBar tabs="off" searchPlaceholder="Search workflows..." />
|
|
166
|
+
</div>
|
|
167
|
+
</FigmaSection>
|
|
168
|
+
</FigmaContent>
|
|
169
|
+
</FigmaPage>
|
|
170
|
+
);
|
|
171
|
+
};
|