Skip to content

Commit

Permalink
Feat/integrate slots front (#92)
Browse files Browse the repository at this point in the history
* refactor: init new base input elements

* chore: configuration changes

* feat: slots implement + modals rework init

* chore: icons fixes + modals rework fixes + sidebar&utils improvements

* fix: slots filling and saving fixes

* chore: add slot condition settings & rework condition modal & add shadcn/ui

* chore: add quiet save handlers

* chore: slots components refactor & add condition icons
  • Loading branch information
mxerf authored Oct 1, 2024
1 parent 7013d0a commit 1de852f
Show file tree
Hide file tree
Showing 57 changed files with 2,467 additions and 425 deletions.
12 changes: 8 additions & 4 deletions backend/chatsky_ui/services/json_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@

from omegaconf.dictconfig import DictConfig

from chatsky_ui.core.logger_config import get_logger
from chatsky_ui.core.config import settings
from chatsky_ui.core.logger_config import get_logger
from chatsky_ui.db.base import read_conf, write_conf
from chatsky_ui.services.condition_finder import ServiceReplacer


logger = get_logger(__name__)

PRE_TRANSITIONS_PROCESSING = "PRE_TRANSITIONS_PROCESSING"


PRE_TRANSITION = "PRE_TRANSITION"


PRE_TRANSITION = "PRE_TRANSITION"

Expand Down Expand Up @@ -147,7 +151,7 @@ def _get_slot(slots, id_):
def _fill_nodes_into_script(nodes: dict, script: dict) -> None:
"""Fill nodes into chatsky script dictunary."""
for _, node in nodes.items():
if node["info"].type == "link_node":
if node["info"].type in ["link_node", "slots_node"]:
continue
if node["flow"] not in script["script"]:
script["script"][node["flow"]] = {}
Expand All @@ -171,7 +175,7 @@ async def update_responses_lines(nodes: dict) -> Tuple[dict, List[str]]:
"""
responses_list = []
for node in nodes.values():
if node["info"].type == "link_node":
if node["info"].type in ["link_node", "slots_node"]:
continue
response = node["info"].data.response
logger.debug("response type: %s", response.type)
Expand Down
Binary file modified frontend/bun.lockb
Binary file not shown.
20 changes: 20 additions & 0 deletions frontend/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
1 change: 1 addition & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</head>
<body>
<div id="root"></div>
<!-- <div id="modal_root"></div> -->
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
16 changes: 15 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@
"@babel/preset-react": "^7.24.1",
"@babel/preset-typescript": "^7.24.1",
"@codemirror/lang-python": "^6.1.5",
"@headlessui/react": "^2.1.8",
"@jest/globals": "^29.7.0",
"@nextui-org/react": "^2.2.9",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@react-spring/web": "^9.7.3",
"@rollup/rollup-linux-arm64-gnu": "4.13.0",
"@testing-library/jest-dom": "^6.4.5",
Expand All @@ -42,7 +48,10 @@
"@xyflow/react": "^12.2.0",
"axios": "^1.6.7",
"babel-jest": "^29.7.0",
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"esbuild": "^0.21.4",
"esbuild-wasm": "0.20.2",
"framer-motion": "^11.0.6",
Expand All @@ -52,7 +61,7 @@
"jest-fetch-mock": "^3.0.3",
"jsdom": "^24.0.0",
"lodash": "^4.17.21",
"lucide-react": "^0.343.0",
"lucide-react": "^0.445.0",
"random-words": "^2.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -61,15 +70,20 @@
"react-router-dom": "^6.22.2",
"react-test-renderer": "^18.3.1",
"react-xarrows": "^2.0.2",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"tailwindcss-children": "^2.1.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"uuid": "^9.0.1",
"vaul": "^0.9.4",
"yaml": "^2.4.1"
},
"devDependencies": {
"@types/bun": "latest",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.0",
"@types/node": "^22.5.5",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^9.0.8",
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { ReactFlowProvider } from "@xyflow/react"
import { RouterProvider, createBrowserRouter } from "react-router-dom"
import { Preloader } from "./UI/Preloader/Preloader"
import ContextWrapper from "./contexts"
import PopUpProvider from "./contexts/popUpContext"
import { UndoRedoProvider } from "./contexts/undoRedoContext"
import Fallback from "./pages/Fallback"
import Flow from "./pages/Flow"
import Home from "./pages/Home"
import Index from "./pages/Index"

const App = () => {

const router = createBrowserRouter([
{
path: "/",
Expand All @@ -26,9 +26,11 @@ const App = () => {
path: "app/flow/:flowId",
element: (
<ReactFlowProvider>
<UndoRedoProvider>
<Flow />
</UndoRedoProvider>
<PopUpProvider>
<UndoRedoProvider>
<Flow />
</UndoRedoProvider>
</PopUpProvider>
</ReactFlowProvider>
),
loader: Preloader,
Expand Down
132 changes: 132 additions & 0 deletions frontend/src/UI/Input/DefCombobox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import * as Popover from "@radix-ui/react-popover"
import classNames from "classnames"
import { CheckIcon } from "lucide-react"
import React, { ReactNode, useCallback, useEffect, useRef, useState } from "react"

interface ComboboxProps {
items: string[]
placeholder?: string
selected: string
setSelected: (value: string) => void
startContent?: ReactNode // Дополнительный контент в начале input
endContent?: ReactNode // Дополнительный контент в конце input
}

const DefCombobox: React.FC<ComboboxProps> = ({
selected,
setSelected,
items,
placeholder = "Select an option",
endContent,
startContent,
}) => {
const [inputValue, setInputValue] = useState("")
const [isOpen, setIsOpen] = useState(false)
const [filteredItems, setFilteredItems] = useState(items)
const [highlightedIndex, setHighlightedIndex] = useState(-1)
const containerRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setInputValue(value)
setFilteredItems(items.filter((item) => item.toLowerCase().includes(value.toLowerCase())))
setHighlightedIndex(-1) // Сбрасываем выделение
setIsOpen(true)
}

const handleSelectItem = useCallback(
(item: string) => {
setInputValue(item)
setSelected(item)
setIsOpen(false)
},
[setSelected]
)

useEffect(() => {
if (isOpen && inputRef.current) {
inputRef.current.focus() // Ставим фокус обратно на input при открытии
}
}, [isOpen])

useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (isOpen) {
if (e.key === "ArrowDown") {
setHighlightedIndex((prev) => Math.min(prev + 1, filteredItems.length - 1))
e.preventDefault() // Предотвращаем прокрутку страницы
} else if (e.key === "ArrowUp") {
setHighlightedIndex((prev) => Math.max(prev - 1, 0))
e.preventDefault() // Предотвращаем прокрутку страницы
} else if (e.key === "Enter" && highlightedIndex >= 0) {
handleSelectItem(filteredItems[highlightedIndex])
e.preventDefault() // Предотвращаем отправку формы, если она есть
}
}
}
window.addEventListener("keydown", handleKeyDown)

return () => {
window.removeEventListener("keydown", handleKeyDown)
}
}, [isOpen, highlightedIndex, filteredItems, handleSelectItem])

return (
<div className='combobox-container'>
<div
ref={containerRef}
className='w-full flex items-center justify-between bg-background p-2 rounded-lg border border-input-border hover:bg-bg-secondary transition-colors'>
{startContent && <span style={{ marginRight: "8px" }}>{startContent}</span>}
<input
ref={inputRef}
type='text'
value={inputValue}
onChange={handleInputChange}
placeholder={placeholder}
className='w-full bg-transparent outline-none placeholder:text-input-border text-sm'
/>
{endContent && <span style={{ marginLeft: "8px" }}>{endContent}</span>}
</div>

{/* Popover for dropdown menu */}
<Popover.Root
open={isOpen}
onOpenChange={setIsOpen}>
<Popover.Trigger asChild>
<div />
</Popover.Trigger>

<Popover.Content
onOpenAutoFocus={(e) => e.preventDefault()}
align='start'
side='bottom'
style={{
width: containerRef.current?.offsetWidth ?? "320px",
}}
className={`mt-2 bg-background border border-input-border rounded-lg py-1 z-[9999] overflow-x-hidden *:text-sm`}>
{filteredItems.length ? (
filteredItems.map((item, index) => (
<div
key={item}
className={classNames(
"flex items-center justify-between hover:bg-bg-secondary py-1 px-3 cursor-pointer transition-colors",
highlightedIndex === index && "bg-bg-secondary"
)}
onClick={() => handleSelectItem(item)}>
{item}
{selected === item && <CheckIcon className='size-4' />}
</div>
))
) : (
<div className='p-2'>No items found</div>
)}
</Popover.Content>
</Popover.Root>

{/* Стили для компонента */}
</div>
)
}

export default DefCombobox
52 changes: 52 additions & 0 deletions frontend/src/UI/Input/DefInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Input, InputProps, InputSlots, SlotsToClasses } from "@nextui-org/react"
import classNames from "classnames"


// const DefInput = ({ className, label, labelClassName, wrapperClassName, ...props }: DefInputType) => {
// return (
// <div className={classNames("flex flex-col w-full", wrapperClassName)}>
// <label className={classNames("text-sm text-input-border-focus font-medium", labelClassName)}>{label}</label>
// <input
// className={classNames(
// "min-h-10 h-10 bg-input-background text-input-foreground border border-input-border focus:border-input-border-focus focus:outline-input-border-focus placeholder:text-input-border-focus rounded-[8px] px-3.5",
// className
// )}
// {...props}
// />
// </div>
// )
// }

const defInputStyles: SlotsToClasses<InputSlots> = {
label: "text-black/50 dark:text-white/90",
input: [
"bg-transparent",
"placeholder:text-input-border-focus",
],
innerWrapper: "bg-transparent",
inputWrapper: [
"min-h-10 h-10",
"px-3.5",
"rounded-[8px]",
"shadow-none",
"bg-input-background",
"border border-input-border",
"hover:bg-transparent",
"group-data-[focus=true]:bg-input-background",
"group-data-[hover=true]:bg-input-background-disabled",
"!cursor-text",
],
}

const DefInput = ({ className, ...props }: InputProps) => {
return (
<Input
labelPlacement="outside"
classNames={defInputStyles}
className={classNames("w-full", className)}
{...props}
/>
)
}

export default DefInput
Loading

0 comments on commit 1de852f

Please sign in to comment.