Initial commit from ZIP

This commit is contained in:
kusz committed 2024-11-27 13:18:16 +01:00
commit 378d4692a9
454 files changed
+40367

No files matched your search

+160
View File
@@ -0,0 +1,160 @@
import React, { useState, useCallback, useRef, useContext, useEffect } from 'react';
import { Caret } from '../assets/index';
import cx from 'classnames';
import useOnClickOutside from 'use-onclickoutside';
export type CurrentValue = {
value: number;
label: string;
};
type ContextValues = {
open: boolean;
disabled: boolean;
fullwidth: boolean;
current: CurrentValue | undefined;
onChange: (value: CurrentValue) => void;
toggleOpen: () => void;
};
type ItemProps = {
label: string;
value: number;
};
const DropdownContext = React.createContext<ContextValues>({
open: false,
fullwidth: false,
disabled: false,
current: undefined,
onChange: () => {},
toggleOpen: () => {},
});
const useDropdownContext = (): ContextValues => {
const context = useContext(DropdownContext);
if (!context) {
throw new Error(`NOPE`);
}
return context;
};
const Item: React.FC<ItemProps> = ({ label, value }) => {
const { onChange } = useDropdownContext();
return (
<a className="dropdown-item" onClick={(): void => onChange({ label, value })}>
{label}
</a>
);
};
const Label: React.FC<{ valueDataTestid?: string }> = ({ children, valueDataTestid }) => {
const { current, fullwidth, disabled } = useDropdownContext();
return (
<div className="dropdown-trigger">
<button
className={cx(['button', 'is-outlined', { 'is-fullwidth': fullwidth }])}
aria-haspopup="true"
aria-controls="dropdown-menu"
type="button"
disabled={disabled}
>
<span className={cx({ 'is-dimmed': !current })} data-testid={valueDataTestid}>
{current ? current.label : children}
</span>
<span className={cx('icon is-small', { 'is-dimmed': !current })}>
<Caret className={cx('caret--opened')} />
</span>
</button>
</div>
);
};
const Menu: React.FC = ({ children }) => {
return (
<div className="dropdown-menu" id="dropdown-menu" role="menu">
<div className="dropdown-content">{children}</div>
</div>
);
};
type CompoundComponents = {
Item: React.FC<ItemProps>;
Menu: React.FC;
Label: React.FC<{ valueDataTestid?: string }>;
};
type DropdownProps = {
fullwidth: boolean;
disabled: boolean;
onChange: (value: number) => void;
initial?: CurrentValue;
dataTestid?: string;
title?: string;
};
const Dropdown: React.FC<DropdownProps> & CompoundComponents = ({
onChange,
title,
disabled,
fullwidth,
children,
initial,
dataTestid,
}) => {
const [current, setCurrent] = useState<CurrentValue | undefined>(initial);
const [open, setOpen] = useState(false);
useEffect(() => {
setCurrent(initial);
}, [initial]);
const handleChange = useCallback(
({ value, label }) => {
if (!disabled) {
setCurrent({ value, label });
onChange && onChange(value);
}
},
[disabled, onChange]
);
const toggleOpen = useCallback(() => {
if (!disabled) {
setOpen((open) => !open);
}
}, [disabled]);
const node = useRef<HTMLHeadingElement>(null);
useOnClickOutside(node, () => setOpen(false));
return (
<DropdownContext.Provider
value={{
open,
fullwidth,
disabled,
current,
onChange: handleChange,
toggleOpen,
}}
>
<div
className={cx(['dropdown', open && 'is-active', { 'is-fullwidth': fullwidth }])}
onClick={toggleOpen}
ref={node}
data-testid={dataTestid}
title={title}
>
{children}
</div>
</DropdownContext.Provider>
);
};
Dropdown.Item = Item;
Dropdown.Menu = Menu;
Dropdown.Label = Label;
export default Dropdown;