@majapisoftwares/ui 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Badge/Badge.jsx +4 -3
- package/components/Input/MultiSelectInput.d.ts +3 -2
- package/components/Input/MultiSelectInput.jsx +31 -11
- package/components/Input/SelectInput.d.ts +12 -0
- package/components/Input/SelectInput.jsx +25 -0
- package/components/Input/useInputRefValue.d.ts +11 -0
- package/components/Input/useInputRefValue.jsx +51 -0
- package/components/Select/Select.d.ts +2 -1
- package/components/Select/Select.jsx +3 -3
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/preventDefault.d.ts +2 -1
- package/utils/stopPropagation.d.ts +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import clsx from "../../utils/clsx";
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { useCallback, } from "react";
|
|
4
|
+
import preventDefault from "../../utils/preventDefault";
|
|
4
5
|
const colorMap = {
|
|
5
6
|
default: {
|
|
6
7
|
badge: "bg-zinc-200 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-100",
|
|
@@ -39,12 +40,12 @@ function Badge({ color = "default", size = "md", className, children, onActionCl
|
|
|
39
40
|
}, [onActionClick]);
|
|
40
41
|
return (<Component className={clsx("inline-flex items-center py-0.5 font-medium", sizeMap.badge[size], colorMap[color].badge, className)} href={href} shallow={shallow} onClick={onClick} {...props}>
|
|
41
42
|
{children}
|
|
42
|
-
{onActionClick && (<
|
|
43
|
-
<span className="sr-only">
|
|
43
|
+
{onActionClick && (<div className={clsx("ml-1 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full focus:text-white focus:outline-hidden", sizeMap.button[size], colorMap[color].button)} onClick={handleActionClick} onPointerDown={preventDefault}>
|
|
44
|
+
<span className="sr-only">Remove</span>
|
|
44
45
|
<svg className="h-2 w-2" stroke="currentColor" fill="none" viewBox="0 0 8 8">
|
|
45
46
|
<path strokeLinecap="round" strokeWidth="1.5" d="M1 1l6 6m0-6L1 7"/>
|
|
46
47
|
</svg>
|
|
47
|
-
</
|
|
48
|
+
</div>)}
|
|
48
49
|
</Component>);
|
|
49
50
|
}
|
|
50
51
|
export default Badge;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ComponentProps } from "react";
|
|
2
2
|
import { type InputProps } from "./Input";
|
|
3
|
-
declare function MultiSelectInput({ options, value, onChange, emptyLabel, ref, ...props }: Omit<InputProps<undefined>, "value"
|
|
3
|
+
declare function MultiSelectInput({ options, value, onChange, emptyLabel, ref, badgeClassName, name, onValueChange, ...props }: Omit<InputProps<undefined>, "value"> & {
|
|
4
4
|
options: {
|
|
5
5
|
value: string;
|
|
6
6
|
name: string;
|
|
7
7
|
}[];
|
|
8
8
|
value?: string[];
|
|
9
|
-
|
|
9
|
+
onValueChange?: (value: string[]) => void;
|
|
10
10
|
emptyLabel?: string;
|
|
11
|
+
badgeClassName?: string;
|
|
11
12
|
}): import("react").JSX.Element;
|
|
12
13
|
export type MultiSelectInputProps = ComponentProps<typeof MultiSelectInput>;
|
|
13
14
|
export default MultiSelectInput;
|
|
@@ -3,24 +3,44 @@ import DropdownMenu from "../DropdownMenu";
|
|
|
3
3
|
import Button from "../Button";
|
|
4
4
|
import { ChevronDownIcon } from "@heroicons/react/16/solid";
|
|
5
5
|
import { xor } from "lodash-es";
|
|
6
|
-
|
|
6
|
+
import clsx from "../../utils/clsx";
|
|
7
|
+
import Badge from "../Badge";
|
|
8
|
+
import useInputRefValue from "./useInputRefValue";
|
|
9
|
+
import { useDeepCompareEffect } from "react-use";
|
|
10
|
+
function MultiSelectInput({ options, value, onChange, emptyLabel = "None", ref, badgeClassName, name, onValueChange, ...props }) {
|
|
11
|
+
const { innerRef, innerValue, setInnerValue } = useInputRefValue({
|
|
12
|
+
value,
|
|
13
|
+
ref,
|
|
14
|
+
name,
|
|
15
|
+
onChange,
|
|
16
|
+
});
|
|
17
|
+
const isEmpty = !innerValue?.length;
|
|
18
|
+
useDeepCompareEffect(() => {
|
|
19
|
+
if (onValueChange) {
|
|
20
|
+
onValueChange(innerValue || []);
|
|
21
|
+
}
|
|
22
|
+
}, [innerValue]);
|
|
7
23
|
return (<DropdownMenu.Root>
|
|
8
24
|
<DropdownMenu.Trigger asChild>
|
|
9
|
-
<Input as={Button} {...props} ref={
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
:
|
|
25
|
+
<Input as={Button} {...props} name={name} onChange={onChange} ref={innerRef} trailing={<ChevronDownIcon className="h-5 w-5 shrink-0"/>} inputClassName={clsx("text-left dark:hover:bg-zinc-700/70 bg-white gap-1.5 flex justify-start flex-wrap", {
|
|
26
|
+
"text-zinc-500 dark:text-zinc-500": isEmpty,
|
|
27
|
+
"p-1.5": !isEmpty,
|
|
28
|
+
})}>
|
|
29
|
+
{isEmpty
|
|
30
|
+
? emptyLabel
|
|
31
|
+
: options
|
|
32
|
+
.filter((option) => innerValue.includes(option.value))
|
|
33
|
+
.map((option, index) => (<Badge key={index} className={badgeClassName} onActionClick={() => setInnerValue(xor(innerValue, [option.value]))}>
|
|
34
|
+
{option.name}
|
|
35
|
+
</Badge>))}
|
|
16
36
|
</Input>
|
|
17
37
|
</DropdownMenu.Trigger>
|
|
18
38
|
<DropdownMenu.Content>
|
|
19
|
-
<DropdownMenu.Item onClick={() =>
|
|
39
|
+
<DropdownMenu.Item onClick={() => setInnerValue([])}>
|
|
20
40
|
{emptyLabel}
|
|
21
41
|
</DropdownMenu.Item>
|
|
22
|
-
{options.map((
|
|
23
|
-
{
|
|
42
|
+
{options.map((option) => (<DropdownMenu.CheckboxItem key={option.name} checked={innerValue?.includes(option.value)} onClick={() => setInnerValue(xor(value, [option.value]))}>
|
|
43
|
+
{option.name}
|
|
24
44
|
</DropdownMenu.CheckboxItem>))}
|
|
25
45
|
</DropdownMenu.Content>
|
|
26
46
|
</DropdownMenu.Root>);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
2
|
+
import { type InputProps } from "./index";
|
|
3
|
+
declare function SelectInput({ options, onChange, ref, placeholder, inputClassName, value, name, ...props }: InputProps<false> & {
|
|
4
|
+
options: {
|
|
5
|
+
value: string;
|
|
6
|
+
name: ReactNode;
|
|
7
|
+
}[];
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export type SelectInputProps = ComponentProps<typeof SelectInput>;
|
|
12
|
+
export default SelectInput;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {} from "react";
|
|
2
|
+
import Input, {} from "./index";
|
|
3
|
+
import Select from "../Select/Select";
|
|
4
|
+
import clsx from "../../utils/clsx";
|
|
5
|
+
import useInputRefValue from "./useInputRefValue";
|
|
6
|
+
function SelectInput({ options, onChange, ref, placeholder, inputClassName, value, name, ...props }) {
|
|
7
|
+
const { innerRef, innerValue, setInnerValue } = useInputRefValue({
|
|
8
|
+
value,
|
|
9
|
+
ref,
|
|
10
|
+
name,
|
|
11
|
+
onChange,
|
|
12
|
+
});
|
|
13
|
+
return (<Input as={Select.Root} {...props} ref={innerRef} {...{
|
|
14
|
+
value: innerValue,
|
|
15
|
+
onValueChange: setInnerValue,
|
|
16
|
+
}}>
|
|
17
|
+
<Select.Trigger placeholder={placeholder} buttonClassName={clsx("bg-white dark:hover:bg-zinc-700/70 dark:bg-zinc-800 w-full justify-between", inputClassName)}/>
|
|
18
|
+
<Select.Content>
|
|
19
|
+
{options?.map((option) => (<Select.Item key={option.value} value={option.value}>
|
|
20
|
+
{option.name}
|
|
21
|
+
</Select.Item>))}
|
|
22
|
+
</Select.Content>
|
|
23
|
+
</Input>);
|
|
24
|
+
}
|
|
25
|
+
export default SelectInput;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ChangeEvent, type Ref } from "react";
|
|
2
|
+
export default function useInputRefValue<T>({ value, ref, name, onChange, }: {
|
|
3
|
+
value?: T;
|
|
4
|
+
ref?: Ref<HTMLInputElement>;
|
|
5
|
+
name?: string;
|
|
6
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
+
}): {
|
|
8
|
+
innerRef: import("react").RefObject<HTMLInputElement>;
|
|
9
|
+
innerValue: T | undefined;
|
|
10
|
+
setInnerValue: import("react").Dispatch<import("react").SetStateAction<T | undefined>>;
|
|
11
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useDeepCompareEffect } from "react-use";
|
|
3
|
+
import { isEqual } from "lodash-es";
|
|
4
|
+
export default function useInputRefValue({ value, ref, name, onChange, }) {
|
|
5
|
+
const [innerValue, setInnerValue] = useState(value);
|
|
6
|
+
useDeepCompareEffect(() => {
|
|
7
|
+
if (value && !isEqual(value, innerValue)) {
|
|
8
|
+
setInnerValue(value);
|
|
9
|
+
}
|
|
10
|
+
}, [{ value }]);
|
|
11
|
+
const innerRef = useRef({
|
|
12
|
+
get value() {
|
|
13
|
+
return innerValue;
|
|
14
|
+
},
|
|
15
|
+
set value(value) {
|
|
16
|
+
setInnerValue(value);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (ref) {
|
|
21
|
+
if (typeof ref === "function") {
|
|
22
|
+
ref(innerRef.current);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
try {
|
|
26
|
+
ref.current = innerRef.current;
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
// do nothing
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}, [ref]);
|
|
35
|
+
useDeepCompareEffect(() => {
|
|
36
|
+
if (onChange) {
|
|
37
|
+
onChange({
|
|
38
|
+
target: {
|
|
39
|
+
name,
|
|
40
|
+
value: innerValue,
|
|
41
|
+
},
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}, [innerValue]);
|
|
46
|
+
return {
|
|
47
|
+
innerRef,
|
|
48
|
+
innerValue,
|
|
49
|
+
setInnerValue,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -6,8 +6,9 @@ declare function SelectSeparator({ className, ...props }: ComponentProps<typeof
|
|
|
6
6
|
declare function SelectItem({ children, className, indicatorClassName, ...props }: ComponentProps<typeof RSelect.Item> & {
|
|
7
7
|
indicatorClassName?: string;
|
|
8
8
|
}): import("react").JSX.Element;
|
|
9
|
-
declare function SelectTrigger({ className, placeholder, children, readOnly, ref, ...props }: ComponentProps<typeof RSelect.Trigger> & ComponentProps<typeof Button> & {
|
|
9
|
+
declare function SelectTrigger({ className, placeholder, children, readOnly, ref, buttonClassName, ...props }: ComponentProps<typeof RSelect.Trigger> & ComponentProps<typeof Button> & {
|
|
10
10
|
placeholder?: string;
|
|
11
|
+
buttonClassName?: string;
|
|
11
12
|
}): import("react").JSX.Element;
|
|
12
13
|
declare function SelectLabel({ className, ...props }: ComponentProps<typeof RSelect.Label>): import("react").JSX.Element;
|
|
13
14
|
declare const Select: {
|
|
@@ -29,11 +29,11 @@ function SelectItem({ children, className, indicatorClassName, ...props }) {
|
|
|
29
29
|
<RSelect.ItemText>{children}</RSelect.ItemText>
|
|
30
30
|
</RSelect.Item>);
|
|
31
31
|
}
|
|
32
|
-
function SelectTrigger({ className, placeholder, children, readOnly, ref, ...props }) {
|
|
33
|
-
return (<RSelect.Trigger asChild {...props} className={className} ref={children ? ref : undefined}>
|
|
32
|
+
function SelectTrigger({ className, placeholder, children, readOnly, ref, buttonClassName, ...props }) {
|
|
33
|
+
return (<RSelect.Trigger asChild {...props} className={clsx("data-[placeholder]:text-zinc-500", className)} ref={children ? ref : undefined}>
|
|
34
34
|
{children || (<Button trailing={!readOnly ? (<RSelect.Icon>
|
|
35
35
|
<ChevronDownIcon />
|
|
36
|
-
</RSelect.Icon>) : undefined} ref={ref} disabled={readOnly} className="opacity-100">
|
|
36
|
+
</RSelect.Icon>) : undefined} ref={ref} disabled={readOnly} className={clsx("rounded-md opacity-100", buttonClassName)}>
|
|
37
37
|
<RSelect.Value placeholder={placeholder}/>
|
|
38
38
|
</Button>)}
|
|
39
39
|
</RSelect.Trigger>);
|
package/package.json
CHANGED