Skip to content

Commit 88b8b6f

Browse files
committed
Feat: added section selector dropdown
1 parent bef7313 commit 88b8b6f

File tree

6 files changed

+144
-9
lines changed

6 files changed

+144
-9
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "react-seat-toolkit",
2+
"name": "@mezh-hq/react-seat-toolkit",
33
"version": "0.0.0",
44
"description": "React UI library to design and render seat layouts",
55
"main": "src/index.jsx",

src/components/controls/select/polyline.jsx renamed to src/components/controls/select/polyline/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { useSelector } from "react-redux";
22
import { d3Extended, rgbToHex } from "@/utils";
3-
import { default as ControlInput } from "../control-input";
3+
import { default as ControlInput } from "../../control-input";
4+
import { default as SectionSelector } from "./section-selector";
45

56
const PolylineSelectControls = () => {
6-
const selectedElementIds = useSelector((state) => state.editor.selectedElementIds);
7-
7+
const selectedElementIds = useSelector((state: any) => state.editor.selectedElementIds);
88
const firstElement = document.getElementById(selectedElementIds[0]);
9-
109
return (
1110
<div className="flex flex-col gap-4 py-1">
11+
<SectionSelector firstElement={firstElement} selectedElementIds={selectedElementIds} />
1212
<div className="grid grid-cols-3 items-center gap-4">
1313
<ControlInput
1414
id="polyline-stroke-input"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Trash2 } from "lucide-react";
2+
import { useSelector } from "react-redux";
3+
import { debounce } from "lodash";
4+
import { twMerge } from "tailwind-merge";
5+
import { Input, Popover, PopoverContent, PopoverTrigger } from "@/components/core";
6+
import { dataAttributes } from "@/constants";
7+
import { store } from "@/store";
8+
import { addSection, deleteSection, updatePolyline, updateSection } from "@/store/reducers/editor";
9+
import { Callout, Caption, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../core";
10+
11+
const onAddSection = () => store.dispatch(addSection(undefined));
12+
13+
const onDeleteSection = (id: string) => store.dispatch(deleteSection(id));
14+
15+
const onUpdateSection = debounce((section) => store.dispatch(updateSection(section)), 150);
16+
17+
const SectionSelector = ({ firstElement, selectedElementIds }) => {
18+
const sections = useSelector((state: any) => state.editor.sections);
19+
return (
20+
<>
21+
<div className="w-full flex justify-between items-center gap-12">
22+
<Callout className="font-semibold">Sections</Callout>
23+
<Popover>
24+
<PopoverTrigger>
25+
<Caption className="text-blue-500 hover:text-blue-600 transform translate-y-0.5 cursor-pointer transition-all duration-medium">
26+
Manage
27+
</Caption>
28+
</PopoverTrigger>
29+
<PopoverContent className="bg-white w-80 py-4 mr-4">
30+
<div className="grid gap-4">
31+
<div className="flex flex-col gap-2">
32+
<h4 className="font-bold leading-none pb-1">Manage Sections</h4>
33+
<hr />
34+
<span
35+
className="hover:text-gray-500 cursor-pointer transition-all duration-medium"
36+
onClick={onAddSection}
37+
>
38+
+ Add Section
39+
</span>
40+
<hr />
41+
</div>
42+
<div className="flex flex-col gap-4">
43+
{sections.map((section, index) => (
44+
<div key={`category-${section.id}`} className="flex justify-start items-center gap-4">
45+
<input
46+
defaultValue={section.color}
47+
type="color"
48+
className="flex-shrink-0 w-6 h-6 p-0 bg-white rounded-color-input"
49+
onChange={(e) => onUpdateSection({ ...section, color: e.target.value })}
50+
/>
51+
<Input
52+
defaultValue={section.name}
53+
className="h-8"
54+
onChange={(e) => onUpdateSection({ ...section, name: e.target.value })}
55+
/>
56+
<Trash2
57+
size={22}
58+
className={twMerge(
59+
"hover:text-gray-500 flex-shrink-0 cursor-pointer transition-all duration-medium",
60+
index === 0 && "opacity-0 pointer-events-none"
61+
)}
62+
onClick={() => onDeleteSection(section.id)}
63+
/>
64+
</div>
65+
))}
66+
</div>
67+
</div>
68+
</PopoverContent>
69+
</Popover>
70+
</div>
71+
<Select
72+
onValueChange={(value) => {
73+
selectedElementIds.forEach((id: string) =>
74+
store.dispatch(updatePolyline({ id, section: value === "0" ? null : value }))
75+
);
76+
}}
77+
defaultValue={firstElement?.getAttribute?.(dataAttributes.section) || undefined}
78+
>
79+
<SelectTrigger className="w-full">
80+
<SelectValue placeholder="Select Section" />
81+
</SelectTrigger>
82+
<SelectContent className="bg-white">
83+
{sections.map((section) => (
84+
<SelectItem key={section.id} value={section.id}>
85+
<div className="flex gap-3 items-center">
86+
{section.id === 0 ? (
87+
<div className="w-4 h-0.5 bg-black" />
88+
) : (
89+
<div className="h-4 w-4 rounded-full" style={{ backgroundColor: section.color }} />
90+
)}{" "}
91+
{section.name}
92+
</div>
93+
</SelectItem>
94+
))}
95+
</SelectContent>
96+
</Select>
97+
</>
98+
);
99+
};
100+
101+
export default SectionSelector;

src/components/workspace/elements/polyline.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { forwardRef } from "react";
22
import { twMerge } from "tailwind-merge";
3+
import { dataAttributes } from "@/constants";
34

4-
const Polyline = forwardRef(({ id, points, color, stroke, ...props }, ref) => {
5+
const Polyline = forwardRef(({ id, points, color, stroke, section, ...props }, ref) => {
56
return (
67
<polyline
78
ref={ref}
89
id={id}
910
points={points.map((p) => `${p.x},${p.y}`).join(" ")}
1011
{...props}
1112
style={{ color: color ?? "transparent", stroke }}
13+
{...{ [dataAttributes.section]: section }}
1214
className={twMerge(props.className)}
1315
/>
1416
);

src/constants/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export const dataAttributes = {
2222
elementType: "data-element-type",
2323
shape: "data-shape",
2424
category: "data-category",
25-
status: "data-status"
25+
status: "data-status",
26+
section: "data-section"
2627
};
2728

2829
export enum SeatStatus {

src/store/reducers/editor/index.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,29 @@ const initialState = {
3838
}
3939
],
4040
sections: [
41+
{
42+
id: 0,
43+
name: "No Section",
44+
color: "#ffffff"
45+
},
46+
{
47+
id: uuidv4(),
48+
name: "Section 1",
49+
color: "#000000",
50+
freeSeating: true,
51+
capacity: 100
52+
},
53+
{
54+
id: uuidv4(),
55+
name: "Section 2",
56+
color: "#FF0000",
57+
freeSeating: false
58+
},
4159
{
4260
id: uuidv4(),
43-
name: "Section 1"
61+
name: "Section 3",
62+
color: "#0000FF",
63+
freeSeating: false
4464
}
4565
],
4666
selectedPolylineId: null,
@@ -132,6 +152,10 @@ export const slice = createSlice({
132152
addPolyline: (state, action) => {
133153
state.polylines.push(action.payload);
134154
},
155+
updatePolyline(state, action) {
156+
const index = state.polylines.findIndex((polyline) => polyline.id === action.payload.id);
157+
state.polylines[index] = { ...state.polylines[index], ...action.payload };
158+
},
135159
deletePolyline: (state, action) => {
136160
state.polylines = state.polylines.filter((polyline) => polyline.id !== action.payload);
137161
},
@@ -165,13 +189,18 @@ export const slice = createSlice({
165189
addSection: (state) => {
166190
state.sections.push({
167191
id: uuidv4(),
168-
name: `Section ${state.sections.length + 1}`
192+
name: `Section ${state.sections.length + 1}`,
193+
color: "#000000",
194+
freeSeating: false
169195
});
170196
},
171197
updateSection: (state, action) => {
172198
const index = state.sections.findIndex((section) => section.id === action.payload.id);
173199
state.sections[index] = action.payload;
174200
},
201+
deleteSection: (state, action) => {
202+
state.sections = state.sections.filter((section) => section.id !== action.payload);
203+
},
175204
setSelectedPolylineId: (state, action) => {
176205
state.selectedPolylineId = action.payload;
177206
}
@@ -201,6 +230,7 @@ export const {
201230
addShape,
202231
deleteShape,
203232
addPolyline,
233+
updatePolyline,
204234
deletePolyline,
205235
addPolylinePoint,
206236
addImage,
@@ -210,6 +240,7 @@ export const {
210240
updateCategory,
211241
addSection,
212242
updateSection,
243+
deleteSection,
213244
setSelectedPolylineId
214245
} = slice.actions;
215246

0 commit comments

Comments
 (0)