Skip to content

Commit

Permalink
Merge pull request #8 from mezh-hq/feature/ui-revamp
Browse files Browse the repository at this point in the history
UI Revamp
  • Loading branch information
miyurugunarathna committed Aug 11, 2024
2 parents 8d914f7 + 948aa91 commit fad9b58
Show file tree
Hide file tree
Showing 65 changed files with 921 additions and 713 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
Changelog

# v2.1.1 [2024-08-07]

## Patch Release

### Fixes
- Fixes an issue where the styles of the category and section selector were not being applied correctly.

---

# v2.1.0 [2024-08-07]

## Minor Release

### Features
- Adds 2 new actions, `actions.selectElement` and `actions.getState` to select an element and retrieve the updated state respectively.
- Exposes the `store` object which is the Redux store used by the toolkit to manage its state.

---

# v2.0.0 [2024-07-31]

## Patch with Breaking Changes
Expand Down
Binary file modified bun.lockb
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mezh-hq/react-seat-toolkit",
"version": "2.0.0-blizzard.2",
"version": "2.1.1",
"description": "React UI library to design and render seat layouts",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
Expand Down Expand Up @@ -68,6 +68,7 @@
"@radix-ui/react-popover": "1.0.7",
"@radix-ui/react-radio-group": "1.1.3",
"@radix-ui/react-select": "2.0.0",
"@radix-ui/react-switch": "1.1.0",
"@radix-ui/react-tooltip": "1.0.6",
"@reduxjs/toolkit": "2.1.0",
"class-variance-authority": "0.7.0",
Expand Down
11 changes: 8 additions & 3 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { panDown, panLeft, panRight, panUp } from "@/components/workspace/zoom";
import { panDown, panLeft, panRight, panUp } from "@/components/workspace/dock";
import { store } from "@/store";
import { clearElements, deselectElement } from "@/store/reducers/editor";
import { clearElements, deselectElement, selectElement } from "@/store/reducers/editor";
import { stateToJSON } from "@/utils";

export const actions = {
const actions = {
selectElement: (elementId: string) => store.dispatch(selectElement(elementId)),
deselectElement: (elementId: string) => store.dispatch(deselectElement(elementId)),
deselectAllElements: () => store.dispatch(clearElements(false)),
getState: stateToJSON,
panLeft,
panDown,
panRight,
panUp
};

export { store, actions };

export default actions;
2 changes: 1 addition & 1 deletion src/components/controls/control-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ControlInput = ({ id, label, ...props }: ControlInputProps) => {
return (
<>
<Label htmlFor={id}>{label}</Label>
<Input id={id} className="col-span-2 h-8" {...props} />
<Input id={id} className="col-span-2" {...props} />
</>
);
};
Expand Down
12 changes: 8 additions & 4 deletions src/components/controls/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ const ImageControls = ({ options: { maxImageSize = 1024000 } = {} }: IImageContr
}, [file]);

return (
<div className="w-full h-full flex flex-col justify-between gap-5">
<div className="w-full flex flex-col gap-5">
<div
className="h-full w-full flex justify-center items-center rounded-md cursor-pointer bg-gray-100 hover:bg-gray-200 transition-all duration-medium"
className="w-full aspect-square flex justify-center items-center rounded-md overflow-clip cursor-pointer bg-slate-100 border border-border"
onClick={onUploadClick}
>
{file ? <img src={file} alt="uploaded image" className="h-full w-full" /> : <Image size={30} />}
{file ? (
<img src={file} alt="uploaded image" className="h-full w-full object-cover" />
) : (
<Image size={24} className="text-slate-500" />
)}
</div>
<Button className="py-2.5" onClick={onAddToWorkspace} disabled={!file}>
<Button className="py-2.5" variant="secondary" onClick={onAddToWorkspace} disabled={!file}>
Add to Workspace
</Button>
<input id="image-input" type="file" accept="image/*" className="hidden" onInput={onUpload} />
Expand Down
48 changes: 30 additions & 18 deletions src/components/controls/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { useMemo } from "react";
import { X } from "lucide-react";
import { useSelector } from "react-redux";
import { twMerge } from "tailwind-merge";
import { dataAttributes, ids } from "@/constants";
import { store } from "@/store";
import { toggleControls } from "@/store/reducers/editor";
import { ISTKProps } from "@/types";
import { AnimatedSwitcher } from "../core";
import { AnimatedSwitcher, IconButton } from "../core";
import { Tool } from "../toolbar/data";
import { ElementType } from "../workspace/elements";
import { default as ImageControls } from "./image";
Expand All @@ -15,7 +18,9 @@ import { default as SeatControls } from "./seat";
import { default as SelectControls } from "./select";
import { default as ShapeControls } from "./shapes";

const transition = "transition-all duration-500";
const onCogClick = () => store.dispatch(toggleControls());

const transition = "transition-all duration-500 ease-in-out";

const width = "w-[22rem]";

Expand All @@ -32,7 +37,7 @@ const Controls = ({ options, styles }: IControlProps) => {
const firstElementType = document
.getElementById(selectedElementIds[0])
?.getAttribute?.(dataAttributes.elementType);
if (firstElementType === ElementType.Booth) return NoSelectionControls;
if (firstElementType === ElementType.Booth) return SelectControls;
if (selectedElementIds.length > 1) {
const same = selectedElementIds.every((id) => {
return document.getElementById(id)?.getAttribute?.(dataAttributes.elementType) === firstElementType;
Expand All @@ -51,23 +56,30 @@ const Controls = ({ options, styles }: IControlProps) => {
}, [selectedTool, selectedElementIds]);

return (
<>
<div className={twMerge("pointer-events-none grow-0 shrink-0", transition, open ? width : "w-0")} />
<div
id={ids.controls}
className={twMerge(
"py-5 px-6 h-[calc(100%-32px)] absolute top-0 border-t border-black overflow-y-auto",
transition,
width,
open ? "right-0" : "-right-[22rem]"
)}
>
<AnimatedSwitcher
key={ControlComponent.name}
component={<ControlComponent options={options} styles={styles} />}
<div
id={ids.controls}
className={twMerge(
"h-full bg-white border-l shadow-lg border-border absolute top-0 overflow-y-auto z-10",
transition,
width,
open ? "right-0" : "-right-[22rem]"
)}
>
<div className="flex justify-between items-center gap-4 h-14 border-b border-border box-content px-5 sticky top-0 bg-white">
<h5>Settings</h5>
<IconButton
className="w-6 h-6 p-0 shrink-0"
variant="secondary"
icon={<X className="w-4 h-4" />}
onClick={onCogClick}
/>
</div>
</>
<AnimatedSwitcher
key={ControlComponent.name}
component={<ControlComponent options={options} styles={styles} />}
className="py-4 px-5"
/>
</div>
);
};

Expand Down
8 changes: 4 additions & 4 deletions src/components/controls/no-controls.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CircleSlash } from "lucide-react";
import { Frame } from "lucide-react";

const NoControls = () => {
return (
<div className="h-full w-full flex flex-col justify-center items-center gap-6">
<span className="text-lg font-medium">No controls available for tool</span>
<CircleSlash size={40} strokeWidth={1} />
<div className="h-full w-full flex flex-col justify-center items-center gap-2 text-slate-400">
<Frame />
<p className="text-sm">No controls available for tool</p>
</div>
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/components/controls/no-selection-controls.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CircleSlash } from "lucide-react";
import { FolderCog } from "lucide-react";

const NoSelectionControls = () => {
return (
<div className="h-full w-full flex flex-col justify-center items-center gap-6">
<span className="text-lg font-medium">No controls available for selection</span>
<CircleSlash size={40} strokeWidth={1} />
<div className="h-full w-full flex flex-col justify-center items-center gap-2 text-slate-400">
<FolderCog />
<p className="text-sm">No controls available for selection</p>
</div>
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/components/controls/no-selection.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CircleSlash } from "lucide-react";
import { MousePointerSquareDashed } from "lucide-react";

const NoSelectedElement = () => {
return (
<div className="h-full w-full flex flex-col justify-center items-center gap-6">
<span className="text-lg font-medium">No element selected</span>
<CircleSlash size={40} strokeWidth={1} />
<div className="h-full w-full flex flex-col justify-center items-center gap-2 text-slate-400">
<MousePointerSquareDashed />
<p className="text-sm">No element selected</p>
</div>
);
};
Expand Down
23 changes: 20 additions & 3 deletions src/components/controls/select/general.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import { useSelector } from "react-redux";
import { Button } from "@/components/core";
import { Button, Input, Label } from "@/components/core";
import { d3Extended } from "@/utils";

const GeneralSelectControls = () => {
const selectedElementIds = useSelector((state: any) => state.editor.selectedElementIds);

const firstElement = d3Extended.selectById(selectedElementIds[0]);
return (
<div className="flex flex-col gap-4 py-1 mt-1">
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-3">
<Label htmlFor="rotation-input">Rotation (deg)</Label>
<Input
id="rotation-input"
key={firstElement?.rotation()}
defaultValue={firstElement?.rotation()}
type="number"
min={-360}
max={360}
onChange={(e) => {
selectedElementIds.forEach((id) => {
d3Extended.selectById(id).style("transform", `rotate(${e.target.value}deg)`);
});
}}
/>
</div>
<Button
className="py-[0.35rem]"
variant="secondary"
Expand All @@ -20,6 +36,7 @@ const GeneralSelectControls = () => {
</Button>
<Button
className="py-[0.35rem]"
variant="secondary"
onClick={() => {
selectedElementIds.forEach((id) => {
d3Extended.selectById(id).moveToBack();
Expand Down
15 changes: 6 additions & 9 deletions src/components/controls/select/image.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from "react";
import { useSelector } from "react-redux";
import { Checkbox } from "@/components/core";
import { Label, Switch } from "@/components/core";
import { dataAttributes } from "@/constants";

const ImageSelectControls = () => {
Expand All @@ -18,14 +18,11 @@ const ImageSelectControls = () => {
};

return (
<div className="flex justify-end gap-4 py-1">
<Checkbox id="stk-lock-position" checked={locked} onCheckedChange={onCheckedChange} />
<label
htmlFor="stk-lock-position"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Object Lock
</label>
<div className="flex flex-col gap-5">
<div className="flex justify-between items-center gap-2">
<Label htmlFor="stk-lock-position">Object Lock</Label>
<Switch id="stk-lock-position" checked={locked} onCheckedChange={onCheckedChange} />
</div>
</div>
);
};
Expand Down
5 changes: 3 additions & 2 deletions src/components/controls/select/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo } from "react";
import { Fragment, useMemo } from "react";
import { useSelector } from "react-redux";
import { ElementType } from "@/components/workspace/elements";
import { dataAttributes } from "@/constants";
Expand All @@ -21,11 +21,12 @@ const SelectControls = ({ options, styles }: IControlProps) => {
if (firstElementType === ElementType.Shape) return ShapeSelectControls;
if (firstElementType === ElementType.Polyline) return PolylineSelectControls;
if (firstElementType === ElementType.Image) return ImageSelectControls;
if (firstElementType === ElementType.Booth) return Fragment;
return SeatSelectControls;
}, [selectedElementIds]);

return (
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-5">
<ControlComponent options={options} styles={styles} />
<GeneralSelectControls />
</div>
Expand Down
46 changes: 22 additions & 24 deletions src/components/controls/select/polyline/section-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Percent, Trash2 } from "lucide-react";
import { DollarSign, Plus, X } from "lucide-react";
import { useSelector } from "react-redux";
import { default as debounce } from "lodash/debounce";
import { twMerge } from "tailwind-merge";
import { Input, Popover, PopoverContent, PopoverTrigger } from "@/components/core";
import { IconButton, Input, Popover, PopoverContent, PopoverTrigger } from "@/components/core";
import { dataAttributes } from "@/constants";
import { store } from "@/store";
import { addSection, deleteSection, updatePolylines, updateSection } from "@/store/reducers/editor";
Expand All @@ -21,56 +21,54 @@ export const SectionManager = ({ options }: IControlProps) => {
const sections = useSelector((state: any) => state.editor.sections);
return (
<div className="grid gap-4">
<div className="flex flex-col gap-2">
<h4 className="font-bold leading-none pb-1">Manage Sections</h4>
<hr />
<span className="hover:text-gray-500 cursor-pointer transition-all duration-medium" onClick={onAddSection}>
+ Add Section
</span>
<hr />
<div className="flex gap-2 justify-between items-center">
<h6 className="font-medium text-sm">Sections</h6>
<IconButton
className="w-6 h-6 p-0"
variant="secondary"
icon={<Plus className="w-4 h-4" />}
onClick={onAddSection}
/>
</div>
<div className="flex flex-col gap-4">
{sections.map(
(section, index) =>
section.id !== "0" && (
<div key={`category-${section.id}`} className="flex justify-start items-center gap-4">
<div key={`category-${section.id}`} className="flex justify-start items-center gap-2">
<input
defaultValue={section.color}
type="color"
className="flex-shrink-0 w-6 h-6 p-0 bg-white rounded-color-input"
className="flex-shrink-0 w-6 h-6 p-0 bg-white square-color-input"
onChange={(e) => onUpdateSection({ ...section, color: e.target.value })}
/>
<input
defaultValue={section.stroke}
type="color"
className="flex-shrink-0 w-6 h-6 p-0 bg-white rounded-color-input"
className="flex-shrink-0 w-6 h-6 p-0 bg-white square-color-input"
onChange={(e) => onUpdateSection({ ...section, stroke: e.target.value })}
/>
<Input
defaultValue={section.name}
className="h-8"
onChange={(e) => onUpdateSection({ ...section, name: e.target.value })}
/>
<Percent
size={22}
className={twMerge(
"flex-shrink-0 cursor-pointer transition-all duration-medium ",
section?.freeSeating ? "text-blue-600 hover:text-blue-500" : "hover:text-gray-500"
)}
<IconButton
className={twMerge("w-6 h-6 p-0 shrink-0", section?.freeSeating && "text-gray-400")}
variant="secondary"
icon={<DollarSign className="w-4 h-4" />}
onClick={() =>
section?.freeSeating
? onUpdateSection({ ...section, freeSeating: false })
: onUpdateSection({ ...section, freeSeating: true })
}
/>
{!options?.disableSectionDelete && (
<Trash2
size={22}
className={twMerge(
"hover:text-gray-500 flex-shrink-0 cursor-pointer transition-all duration-medium",
index === 0 && "opacity-0 pointer-events-none"
)}
<IconButton
className="w-6 h-6 p-0 shrink-0"
variant="secondary"
icon={<X className="w-4 h-4" />}
onClick={() => onDeleteSection(section.id)}
disabled={index === 0}
/>
)}
</div>
Expand Down
Loading

0 comments on commit fad9b58

Please sign in to comment.