diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d02352..9ef7b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/bun.lockb b/bun.lockb index 1d072a9..53d6f9c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index d59c05a..166527f 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/src/actions/index.ts b/src/actions/index.ts index 997dea4..231abfa 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -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; diff --git a/src/components/controls/control-input.tsx b/src/components/controls/control-input.tsx index 215b8da..c3717c3 100644 --- a/src/components/controls/control-input.tsx +++ b/src/components/controls/control-input.tsx @@ -9,7 +9,7 @@ const ControlInput = ({ id, label, ...props }: ControlInputProps) => { return ( <> - + ); }; diff --git a/src/components/controls/image.tsx b/src/components/controls/image.tsx index fba2afc..9096f13 100644 --- a/src/components/controls/image.tsx +++ b/src/components/controls/image.tsx @@ -49,14 +49,18 @@ const ImageControls = ({ options: { maxImageSize = 1024000 } = {} }: IImageContr }, [file]); return ( -
+
- {file ? uploaded image : } + {file ? ( + uploaded image + ) : ( + + )}
- diff --git a/src/components/controls/index.tsx b/src/components/controls/index.tsx index c0a8fd6..39840a9 100644 --- a/src/components/controls/index.tsx +++ b/src/components/controls/index.tsx @@ -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"; @@ -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]"; @@ -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; @@ -51,23 +56,30 @@ const Controls = ({ options, styles }: IControlProps) => { }, [selectedTool, selectedElementIds]); return ( - <> -
-
- } +
+
+
Settings
+ } + onClick={onCogClick} />
- + } + className="py-4 px-5" + /> +
); }; diff --git a/src/components/controls/no-controls.tsx b/src/components/controls/no-controls.tsx index 62dd924..771a3a4 100644 --- a/src/components/controls/no-controls.tsx +++ b/src/components/controls/no-controls.tsx @@ -1,10 +1,10 @@ -import { CircleSlash } from "lucide-react"; +import { Frame } from "lucide-react"; const NoControls = () => { return ( -
- No controls available for tool - +
+ +

No controls available for tool

); }; diff --git a/src/components/controls/no-selection-controls.tsx b/src/components/controls/no-selection-controls.tsx index a2ce90c..cab7566 100644 --- a/src/components/controls/no-selection-controls.tsx +++ b/src/components/controls/no-selection-controls.tsx @@ -1,10 +1,10 @@ -import { CircleSlash } from "lucide-react"; +import { FolderCog } from "lucide-react"; const NoSelectionControls = () => { return ( -
- No controls available for selection - +
+ +

No controls available for selection

); }; diff --git a/src/components/controls/no-selection.tsx b/src/components/controls/no-selection.tsx index c435d13..3570e65 100644 --- a/src/components/controls/no-selection.tsx +++ b/src/components/controls/no-selection.tsx @@ -1,10 +1,10 @@ -import { CircleSlash } from "lucide-react"; +import { MousePointerSquareDashed } from "lucide-react"; const NoSelectedElement = () => { return ( -
- No element selected - +
+ +

No element selected

); }; diff --git a/src/components/controls/select/general.tsx b/src/components/controls/select/general.tsx index 9e7d528..d708996 100644 --- a/src/components/controls/select/general.tsx +++ b/src/components/controls/select/general.tsx @@ -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 ( -
+
+
+ + { + selectedElementIds.forEach((id) => { + d3Extended.selectById(id).style("transform", `rotate(${e.target.value}deg)`); + }); + }} + /> +
)} - { - selectedElementIds.forEach((id: string) => { - const seat = d3Extended.selectById(id); - const seatLabel = d3Extended.selectById(`${id}-label`); - seat.attr(dataAttributes.status, value); - let color = seatStatusColors[value].background; - let textColor = seatStatusColors[value].label; - if (value === SeatStatus.Available) { - const category = store - .getState() - .editor.categories.find((c) => c.id === seat.attr(dataAttributes.category)); - if (category) { - color = category.color; - textColor = category.textColor; - } - } - seat.style("color", color); - seatLabel?.style("stroke", textColor); - }); - }} - className="w-full flex flex-wrap flex-row-reverse items-end gap-2 gap-y-4 my-1" - > +
{Object.values(SeatStatus).map((status) => { const id = `stk-seat-status-rg-${status}`; return ( -
- +
+ { + selectedElementIds.forEach((id: string) => { + const seat = d3Extended.selectById(id); + const seatLabel = d3Extended.selectById(`${id}-label`); + seat.attr(dataAttributes.status, status); + setState(status); + let color = seatStatusColors[status].background; + let textColor = seatStatusColors[status].label; + if (status === SeatStatus.Available) { + const category = store + .getState() + .editor.categories.find((c) => c.id === seat.attr(dataAttributes.category)); + if (category) { + color = category.color; + textColor = category.textColor; + } + } + seat.style("color", color); + seatLabel?.style("stroke", textColor); + }); + }} + />
); })} - +
); }; diff --git a/src/components/controls/select/shape.jsx b/src/components/controls/select/shape.jsx index f05536e..04ab139 100644 --- a/src/components/controls/select/shape.jsx +++ b/src/components/controls/select/shape.jsx @@ -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"; import { d3Extended, rgbToHex } from "@/utils"; import { default as ControlInput } from "../control-input"; @@ -13,8 +13,8 @@ const ShapeSelectControls = () => { const firstElement = document.getElementById(selectedElementIds[0]); return ( -
-
+
+
{ }); }} /> +
+
{ }); }} /> - -
- - -
- - {firstElement?.getAttribute?.(dataAttributes.shape) === "RectangleHorizontal" && ( +
+
+ + +
+ {firstElement?.getAttribute?.(dataAttributes.shape) === "RectangleHorizontal" && ( +
{ }); }} /> - )} +
+ )} +
{ d3Extended.selectById(id).style("stroke", e.target.value); }); }} + className="p-0 px-[.125rem]" /> +
+
{ d3Extended.selectById(id).style("color", e.target.value); }); }} + className="p-0 px-[.125rem]" />
diff --git a/src/components/controls/select/text.tsx b/src/components/controls/select/text.tsx index ae12ce0..1bbe998 100644 --- a/src/components/controls/select/text.tsx +++ b/src/components/controls/select/text.tsx @@ -1,6 +1,6 @@ import { useCallback } from "react"; import { useSelector } from "react-redux"; -import { Checkbox } from "@/components/core"; +import { Label, Switch } from "@/components/core"; import { store } from "@/store"; import { selectTextById, updateText } from "@/store/reducers/editor"; import { d3Extended, rgbToHex } from "@/utils"; @@ -19,8 +19,8 @@ const TextSelectControls = () => { ); return ( -
-
+
+
{ }); }} /> +
+
{ }); }} /> +
+
{ }); }} /> +
+
{ }); }} /> +
+
{ element.style("color", e.target.value); }); }} + className="p-0 px-[.125rem]" + /> +
+
+ + -
- - -
); diff --git a/src/components/controls/shapes/index.tsx b/src/components/controls/shapes/index.tsx index b6da557..6e6f2d7 100644 --- a/src/components/controls/shapes/index.tsx +++ b/src/components/controls/shapes/index.tsx @@ -32,17 +32,17 @@ const ShapeControls = () => { }, []); return ( -
+
{shapeList.map((Shape, i) => (
onShapeClick(CursorShape(Shape), i)} > - +
))}
diff --git a/src/components/controls/shapes/shape-list.ts b/src/components/controls/shapes/shape-list.ts index c740c87..778af31 100644 --- a/src/components/controls/shapes/shape-list.ts +++ b/src/components/controls/shapes/shape-list.ts @@ -3,47 +3,27 @@ import { ArrowBigLeft, ArrowBigRight, ArrowBigUp, - Boxes, - CircleDot, - CircleSlash, - Cone, Diamond, - Fence, - FireExtinguisher, Hexagon, - LandPlot, Octagon, Pentagon, - Power, - RectangleHorizontal, - Squircle, - Ticket, - Triangle, - TriangleRight + Play, + Square, + Squircle } from "lucide-react"; export const shapes = { - RectangleHorizontal: RectangleHorizontal, - Triangle: Triangle, - TriangleRight: TriangleRight, + ArrowBigLeft: ArrowBigLeft, + ArrowBigRight: ArrowBigRight, + ArrowBigDown: ArrowBigDown, + ArrowBigUp: ArrowBigUp, + Play: Play, Squircle: Squircle, Pentagon: Pentagon, + Square: Square, Hexagon: Hexagon, Octagon: Octagon, - CircleDot: CircleDot, - CircleSlash: CircleSlash, - Diamond: Diamond, - Cone: Cone, - ArrowBigDown: ArrowBigDown, - ArrowBigUp: ArrowBigUp, - ArrowBigLeft: ArrowBigLeft, - ArrowBigRight: ArrowBigRight, - Ticket: Ticket, - Power: Power, - Fence: Fence, - LandPlot: LandPlot, - Boxes: Boxes, - FireExtinguisher: FireExtinguisher + Diamond: Diamond }; export const shapeList = Object.values(shapes); diff --git a/src/components/core/button.tsx b/src/components/core/button.tsx index d5c5c8d..e2eabd2 100644 --- a/src/components/core/button.tsx +++ b/src/components/core/button.tsx @@ -2,16 +2,23 @@ import { cva } from "class-variance-authority"; import { twMerge } from "tailwind-merge"; const buttonVariants = cva( - "group flex justify-center items-center cursor-pointer rounded-md px-[1.15rem] py-[0.4rem] font-semibold outline-none transition-all duration-medium gap-2 splash", + "group flex justify-center items-center cursor-pointer rounded-md font-medium outline-none transition-all duration-medium splash", { variants: { variant: { primary: "bg-black text-white", - secondary: "text-black bg-white border border-black/20 after:bg-black/[0.15]" + secondary: "text-black bg-slate-100 hover:bg-slate-200/75 after:bg-black/[0.15]" + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10" } }, defaultVariants: { - variant: "primary" + variant: "primary", + size: "default" } } ); @@ -22,6 +29,7 @@ const Core = ({ variant = "primary", children, loading, className, ...props }: a role="button" className={twMerge( buttonVariants({ variant }), + loading ? "gap-2" : "gap-0", className, loading || props.disabled ? "opacity-80 pointer-events-none" : "" )} @@ -31,7 +39,7 @@ const Core = ({ variant = "primary", children, loading, className, ...props }: a {loading !== undefined && (
- + ); } diff --git a/src/components/core/input.tsx b/src/components/core/input.tsx index ef96caa..db2f418 100644 --- a/src/components/core/input.tsx +++ b/src/components/core/input.tsx @@ -8,7 +8,7 @@ const Input = React.forwardRef(({ className, type,
- - React Seat Toolkit - - {selectedTool && ( - {tools[selectedTool]?.description}} - duration={0.2} - /> - )} -
- ); -}; - -export default Footer; diff --git a/src/components/index.tsx b/src/components/index.tsx index d5f4937..5409d6c 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -2,7 +2,6 @@ import { twMerge } from "tailwind-merge"; import { useDesignerEvents, useInteractions, useToast, useUserEvents } from "@/hooks"; import { type ISTKProps } from "@/types"; import { default as Controls } from "./controls"; -import { default as Footer } from "./footer"; import { default as Operations } from "./operations"; import { default as Toolbar } from "./toolbar"; import { Cursor, default as Workspace } from "./workspace"; @@ -16,22 +15,21 @@ const Designer: React.FC = (props) => { <>
- +
- +
-