Unfinished solution

This commit is contained in:
decamel 2022-08-30 15:41:21 +03:00
parent 0364c20240
commit 62c1cfd697
10 changed files with 1507 additions and 1213 deletions

2375
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,13 +19,13 @@
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-custom-scrollbars": "^4.2.1",
"react-dom": "^18.2.0",
"react-hotkeys": "^2.0.0",
"react-i18next": "^11.18.3",
"react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.0",
"storybook-addon-pseudo-states": "^1.15.1",
"tailwindcss": "^3.1.7",
"tsconfig-paths-webpack-plugin": "^4.0.0",
@ -96,6 +96,7 @@
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/react-custom-scrollbars": "^4.0.10",
"autoprefixer": "^10.4.8",
"babel-plugin-named-exports-order": "^0.0.2",
"jest": "^28.1.3",

View File

@ -1,7 +1,28 @@
/* -------------------------------------------------------------------------- */
/* Libraries */
/* -------------------------------------------------------------------------- */
import React from "react";
import Searchbar from "components/Searchbar/Searchbar";
import React, { useState } from "react";
import { Scrollbars } from "react-custom-scrollbars";
const hintsValues = [
{ id: "1", caption: "Wade Cooper" },
{ id: "2", caption: "Arlene Mccoy" },
{ id: "3", caption: "Devon Webb" },
{ id: "4", caption: "Tom Cook" },
{ id: "5", caption: "Tanya Fox" },
{ id: "6", caption: "Hellen Schmidt" },
];
const hintsValues2 = [
{ id: "1", caption: "Wade Cooper2" },
{ id: "2", caption: "Arlene Mccoy2" },
{ id: "3", caption: "Devon Webb2" },
{ id: "4", caption: "Tom Cook2" },
{ id: "5", caption: "Tanya Fox2" },
{ id: "6", caption: "Hellen Schmidt2" },
];
/* -------------------------------------------------------------------------- */
/* Application root component */
/* -------------------------------------------------------------------------- */
@ -10,9 +31,29 @@ import React from "react";
* @return {JSX.Element}
*/
function App() {
return <Scrollbar style={{ width: 250, height: 250 }}>
<p>Hello world!</p>
</Scrollbar>;
return <Page />;
}
function Page() {
const [hints, setHints] = useState<any[]>([]);
function delayedMock(hints: any[]): Promise<any> {
return new Promise((resolve) => setTimeout(() => resolve(hints), 2000));
}
const onChange = async (query: string) => {
console.log(query);
if (query === "ok") {
await delayedMock(hintsValues2).then((v) => setHints(v));
return;
}
console.log("touched");
await delayedMock([]).then((v) => setHints(v));
};
return (
<>
<Searchbar hints={hints} onChange={onChange} onSelected={console.log} />
</>
);
}
export default App;

View File

@ -1,6 +1,6 @@
import classNames from "classnames";
import React, { useEffect, useLayoutEffect } from "react";
import Scrollbar from "react-scrollbars-custom";
import { Scrollbars } from "react-custom-scrollbars";
import { WithRouteProps } from "routes";
import { withRouteParams } from "routes/withRoute";
import { useUIViewModel } from "ui/controller/uiViewModel";
@ -34,19 +34,19 @@ const Page = ({ title, withOutlet, children, activePath }: Props) => {
isDrawerCollapsed ? "w-24" : "1.5xl:w-80 2xl:w-96"
)}
>
<Scrollbar>
<Scrollbars>
<div className="px-6 py-1">
<SideNav collapsed={isDrawerCollapsed} />
</div>
</Scrollbar>
</Scrollbars>
</aside>
<main className="relative rounded-lg m-2 pl-24 bg-main shadow-lg shadow-serv-900/20">
<div className="absolute -left-5 top-6">
<DrawerController />
</div>
<Scrollbar>
<Scrollbars>
<div className="px-8 pt-20">{children}</div>
</Scrollbar>
</Scrollbars>
</main>
</div>
);

View File

@ -0,0 +1,81 @@
import { Fragment, useEffect, useState } from "react";
import { Combobox, Transition } from "@headlessui/react";
type Hint = {
id: string;
caption: string;
};
type Props = {
onChange: (query: string) => void;
onSelected?: (value: Hint) => void;
hints: Hint[];
};
export default function Searchbar({ onChange, onSelected, hints }: Props) {
const [selected, setSelected] = useState(hints[0] ?? null);
const [query, setQuery] = useState("");
useEffect(() => {
onChange(query);
}, [query, onChange]);
const handleSelected = (value: Hint) => {
setSelected(value);
onSelected && onSelected(value);
};
return (
<div className="fixed top-16 w-72">
<Combobox value={selected} onChange={handleSelected}>
<div className="relative mt-1">
<div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm">
<Combobox.Input
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
displayValue={(hint: Hint | undefined) => hint?.caption ?? ""}
onChange={(event) => setQuery(event.target.value)}
/>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{hints.length === 0 && query !== "" ? (
<div className="relative cursor-default select-none py-2 px-4 text-gray-700">
Nothing found.
</div>
) : (
hints.map((hint) => (
<Combobox.Option
key={hint.id}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active ? "bg-teal-600 text-white" : "text-gray-900"
}`
}
value={hint}
>
{({ selected, active }) => (
<>
<span
className={`block truncate ${
selected ? "font-medium" : "font-normal"
}`}
>
{hint.caption}
</span>
</>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</div>
);
}

View File

@ -7,7 +7,7 @@ import { Listbox, Transition } from "@headlessui/react";
import classNames from "classnames";
import "../index.css";
import { ReactComponent as SelectIcon } from "../assets/svg/select-arrow.svg";
import { Scrollbar } from "react-scrollbars-custom";
import { Scrollbars } from "react-custom-scrollbars";
/* -------------------------------------------------------------------------- */
/* Component props */
@ -114,7 +114,7 @@ function Select<T>({
leaveTo="opacity-0"
>
<Listbox.Options className={`${SelectOptionsStyle}`}>
<Scrollbar
<Scrollbars
style={{
height: options.length * elementScrollSize,
maxHeight: maxScrollSize,
@ -139,7 +139,7 @@ function Select<T>({
}`}
</Listbox.Option>
))}
</Scrollbar>
</Scrollbars>
</Listbox.Options>
</Transition>
</div>

View File

@ -0,0 +1,114 @@
/* -------------------------------------------------------------------------- */
/* Imports */
/* -------------------------------------------------------------------------- */
import React, { Fragment } from "react";
import { Menu, Transition } from "@headlessui/react";
import { PropsPartion } from "./DropDownMenuItem";
import classNames from "classnames";
import { ReactComponent as SelectIcon } from "assets/svg/select-arrow.svg";
type ChildType = React.ReactElement<any & PropsPartion[]>;
type ChildrenType = ChildType[] | ChildType;
/* -------------------------------------------------------------------------- */
/* Component props */
/* -------------------------------------------------------------------------- */
type MenuProps = {
emphasis?: "high" | "low";
disabled?: boolean;
className?: string | undefined;
button: React.ReactNode;
children: ChildrenType;
};
/* -------------------------------------------------------------------------- */
/* Styles */
/* -------------------------------------------------------------------------- */
const MenuButtonStyle = `
inline-flex
justify-center w-full
cursor-default
rounded
border border-gray-100
outline-8
bg-white
py-2
pl-4
pr-1
text-base`;
const MenuItemStyle = `
absolute
left-0
mt-2 w-60
origin-top-left
rounded
bg-white
shadow-lg
focus:outline-none
py-2 px-4 sm:text-sm`;
/* -------------------------------------------------------------------------- */
/* Component implementation */
/* -------------------------------------------------------------------------- */
/**
* Component usage:
*
* ```
* <ContextMenu button={<ButtonComponent/>} emphasis="low|hight">
* <ContextMenuAction caption="Menu item action" icon={<IconComponent/>} action={()=>{}} />
* </ContextMenu>
* ```
*/
export default function DropDownMenu({
button,
children,
className,
emphasis = "low",
}: MenuProps) {
return (
<Menu as="div" className="relative inline-block text-right">
{({ open }) => (
<>
<Menu.Button
className={classNames([
`${MenuButtonStyle}`,
{ "bg-gray-100 font-bold uppercase": emphasis === "high" },
className,
])}
>
{button}
<SelectIcon
className={`${
open ? "rotate-180 transform transition-transform duration-100" : "font-normal"
} my-2 mx-3 h-2 w-3 flex-center`}
aria-hidden="true"
/>
</Menu.Button>
<Transition
as={Fragment}
show={open}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
static
className={classNames([
className,
`${MenuItemStyle}`,
{ "ml-2": emphasis === "high" },
])}
>
{children}
</Menu.Items>
</Transition>
</>
)}
</Menu>
);
}

View File

@ -0,0 +1,36 @@
import classNames from "classnames";
import React from "react";
type Props = {
action: Function;
caption: string;
disabled?: boolean;
icon?: React.ReactNode;
className?: string | undefined;
};
export default function DropDownMenuAction({
action,
caption,
disabled,
icon,
className,
}: Props) {
return (
<button
onClick={(e) => action(e)}
disabled={disabled}
className={classNames([
"group flex items-center text-base",
{ "opacity-50": disabled, "cursor-default": !disabled },
className,
])}
>
{icon && <div className="mr-2 h-5 w-5">{icon}</div>}
<span className="px-2 py-2">{caption}</span>
</button>
);
}

View File

@ -0,0 +1,27 @@
import {Menu} from '@headlessui/react';
import React from 'react';
export type PropsPartion = {
disabled?: boolean | undefined;
active?: boolean | undefined;
};
type Props = {
children: React.ReactElement<
any & PropsPartion
>;
} & PropsPartion;
/**
* Context menu item component
* @return {JSX.Element}
*/
export default function DropDownMenuItem({children, ...props}: Props) {
return (
<Menu.Item {...props}>
{(params) => {
return React.cloneElement(children, params);
}}
</Menu.Item>
);
}

View File

@ -1,18 +1,5 @@
import React, {
Fragment,
useState,
useRef,
useEffect,
FC,
isValidElement,
} from "react";
import { Dialog, Transition } from "@headlessui/react";
import { BottomSheetModal } from "components/containers/modal/BottomSheetModal";
import { BottomBarAcceptCookies } from "components/containers/modal/BottomBarAcceptCookies";
import { Button } from "components/Button/Button";
import classNames from "classnames";
import React, { useState, useRef, useEffect } from "react";
import "./ColumnLayout.css";
import { Children } from "react";
import MainColumn from "./Maincolumn";
import LeftColumn from "./LeftColumn";
import RightColumn from "./RightColumn";
@ -101,8 +88,8 @@ export const ColumnLayout: React.FC<ColumnLayoutProps> & ColumnExtentions = ({
React.Children.only(child).type === LeftColumn
) {
return React.cloneElement(child, {
openLeftBar: openLeftBar,
widthElement: widthElement,
// openLeftBar: openLeftBar,
// widthElement: widthElement,
});
} else {
return child;