update Field.tsx, TypeDropdown.tsx, Typeahead.tsx and typeahead-opt.tsx
This commit is contained in:
parent
624df96b1e
commit
4460c2bfcb
|
|
@ -14,6 +14,7 @@ export const Field: React.FC<{
|
||||||
fm: any;
|
fm: any;
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
isBetter?: boolean;
|
||||||
onLoad?: () => Promise<any> | any;
|
onLoad?: () => Promise<any> | any;
|
||||||
type?:
|
type?:
|
||||||
| "rating"
|
| "rating"
|
||||||
|
|
@ -40,7 +41,6 @@ export const Field: React.FC<{
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
hidden_label?: boolean;
|
hidden_label?: boolean;
|
||||||
|
|
||||||
onChange?: ({ data }: any) => Promise<void> | void;
|
onChange?: ({ data }: any) => Promise<void> | void;
|
||||||
className?: string;
|
className?: string;
|
||||||
classField?: string;
|
classField?: string;
|
||||||
|
|
@ -48,9 +48,11 @@ export const Field: React.FC<{
|
||||||
prefix?: string | any | (() => any);
|
prefix?: string | any | (() => any);
|
||||||
suffix?: string | any | (() => any);
|
suffix?: string | any | (() => any);
|
||||||
allowNew?: boolean;
|
allowNew?: boolean;
|
||||||
|
unique?: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
fm,
|
fm,
|
||||||
label,
|
label,
|
||||||
|
isBetter,
|
||||||
name,
|
name,
|
||||||
onLoad,
|
onLoad,
|
||||||
type = "text",
|
type = "text",
|
||||||
|
|
@ -65,6 +67,7 @@ export const Field: React.FC<{
|
||||||
prefix,
|
prefix,
|
||||||
suffix,
|
suffix,
|
||||||
allowNew,
|
allowNew,
|
||||||
|
unique = true,
|
||||||
}) => {
|
}) => {
|
||||||
let result = null;
|
let result = null;
|
||||||
const field = useLocal({
|
const field = useLocal({
|
||||||
|
|
@ -247,6 +250,8 @@ export const Field: React.FC<{
|
||||||
disabled={is_disable}
|
disabled={is_disable}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
mode="multi"
|
mode="multi"
|
||||||
|
unique={unique}
|
||||||
|
isBetter={isBetter}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : ["checkbox"].includes(type) ? (
|
) : ["checkbox"].includes(type) ? (
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ export const TypeDropdown: React.FC<any> = ({
|
||||||
disabled,
|
disabled,
|
||||||
mode,
|
mode,
|
||||||
allowNew = false,
|
allowNew = false,
|
||||||
|
unique = true,
|
||||||
|
isBetter = false,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -22,7 +24,7 @@ export const TypeDropdown: React.FC<any> = ({
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
allowNew={allowNew}
|
allowNew={allowNew}
|
||||||
unique={mode === "multi" ? true : false}
|
unique={mode === "multi" ? (isBetter ? false : true) : false}
|
||||||
disabledSearch={false}
|
disabledSearch={false}
|
||||||
// popupClassName={}
|
// popupClassName={}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ export const Typeahead: FC<{
|
||||||
note?: string;
|
note?: string;
|
||||||
disabledSearch?: boolean;
|
disabledSearch?: boolean;
|
||||||
onInit?: (e: any) => void;
|
onInit?: (e: any) => void;
|
||||||
|
isBetter?: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
value,
|
value,
|
||||||
fitur,
|
fitur,
|
||||||
|
|
@ -59,7 +60,9 @@ export const Typeahead: FC<{
|
||||||
popupClassName,
|
popupClassName,
|
||||||
disabledSearch,
|
disabledSearch,
|
||||||
onInit,
|
onInit,
|
||||||
|
isBetter = false,
|
||||||
}) => {
|
}) => {
|
||||||
|
const maxLength = 4;
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [debouncedTerm, setDebouncedTerm] = useState("");
|
const [debouncedTerm, setDebouncedTerm] = useState("");
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
|
|
@ -68,6 +71,10 @@ export const Typeahead: FC<{
|
||||||
options: [] as OptItem[],
|
options: [] as OptItem[],
|
||||||
loaded: false,
|
loaded: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
selectBetter: {
|
||||||
|
all: false,
|
||||||
|
partial: [] as any[],
|
||||||
|
},
|
||||||
search: {
|
search: {
|
||||||
input: "",
|
input: "",
|
||||||
timeout: null as any,
|
timeout: null as any,
|
||||||
|
|
@ -102,9 +109,10 @@ export const Typeahead: FC<{
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
if (Array.isArray(value) && value?.length) {
|
||||||
if (!select_found) {
|
if (!select_found) {
|
||||||
local.select = options[0];
|
local.select = options[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -513,6 +521,25 @@ export const Typeahead: FC<{
|
||||||
resetSearch();
|
resetSearch();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onRemove={(data) => {
|
||||||
|
local.value = local.value.filter((val) => data?.value !== val);
|
||||||
|
local.render();
|
||||||
|
input.current?.focus();
|
||||||
|
|
||||||
|
if (typeof onChange === "function") {
|
||||||
|
onChange(local.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onSelectAll={(data: boolean) => {
|
||||||
|
local.value = data ? options.map((e) => e?.value) : [];
|
||||||
|
local.render();
|
||||||
|
input.current?.focus();
|
||||||
|
if (typeof onChange === "function") {
|
||||||
|
onChange(local.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
init={local}
|
||||||
|
isBetter={isBetter}
|
||||||
loading={local.loading}
|
loading={local.loading}
|
||||||
showEmpty={!allow_new}
|
showEmpty={!allow_new}
|
||||||
className={popupClassName}
|
className={popupClassName}
|
||||||
|
|
@ -520,8 +547,89 @@ export const Typeahead: FC<{
|
||||||
options={options}
|
options={options}
|
||||||
searching={local.search.searching}
|
searching={local.search.searching}
|
||||||
searchText={local.search.input}
|
searchText={local.search.input}
|
||||||
|
onSearch={async (e) => {
|
||||||
|
const val = e.currentTarget.value;
|
||||||
|
if (!local.open) {
|
||||||
|
local.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
local.search.input = val;
|
||||||
|
local.render();
|
||||||
|
|
||||||
|
if (local.search.promise) {
|
||||||
|
await local.search.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
local.search.searching = true;
|
||||||
|
local.render();
|
||||||
|
if (allow_new) {
|
||||||
|
setSearchTerm(val);
|
||||||
|
}
|
||||||
|
if (local.search.searching) {
|
||||||
|
if (local.local_search) {
|
||||||
|
if (!local.loaded) {
|
||||||
|
await loadOptions();
|
||||||
|
}
|
||||||
|
const search = local.search.input.toLowerCase();
|
||||||
|
if (search) {
|
||||||
|
local.search.result = options.filter((e) =>
|
||||||
|
e.label.toLowerCase().includes(search)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
local.search.result.length > 0 &&
|
||||||
|
!local.search.result.find(
|
||||||
|
(e) => e.value === local.select?.value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
local.search.result = null;
|
||||||
|
}
|
||||||
|
local.search.searching = false;
|
||||||
|
local.render();
|
||||||
|
} else {
|
||||||
|
clearTimeout(local.search.timeout);
|
||||||
|
local.search.timeout = setTimeout(async () => {
|
||||||
|
const result = options_fn?.({
|
||||||
|
search: local.search.input,
|
||||||
|
existing: options,
|
||||||
|
});
|
||||||
|
if (result) {
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
local.search.promise = result;
|
||||||
|
local.search.result = (await result).map((item) => {
|
||||||
|
if (typeof item === "string")
|
||||||
|
return { value: item, label: item };
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
local.search.searching = false;
|
||||||
|
local.search.promise = null;
|
||||||
|
} else {
|
||||||
|
local.search.result = result.map((item) => {
|
||||||
|
if (typeof item === "string")
|
||||||
|
return { value: item, label: item };
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
local.search.searching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
local.search.result.length > 0 &&
|
||||||
|
!local.search.result.find(
|
||||||
|
(e) => e.value === local.select?.value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
local.open = false;
|
if (!isBetter) local.open = false;
|
||||||
resetSearch();
|
resetSearch();
|
||||||
const item = options.find((item) => item.value === value);
|
const item = options.find((item) => item.value === value);
|
||||||
if (item) {
|
if (item) {
|
||||||
|
|
@ -545,9 +653,20 @@ export const Typeahead: FC<{
|
||||||
}
|
}
|
||||||
isMulti={local.mode === "multi"}
|
isMulti={local.mode === "multi"}
|
||||||
selected={({ item, options, idx }) => {
|
selected={({ item, options, idx }) => {
|
||||||
if (item.value === local.select?.value) {
|
// console.log(local.select);
|
||||||
|
if (isBetter) {
|
||||||
|
const val = local.value?.length ? local.value : [];
|
||||||
|
let isSelect = options.find((e) => {
|
||||||
|
return (
|
||||||
|
e?.value === item?.value &&
|
||||||
|
val.find((ex) => ex === item?.value)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return isSelect ? true : false;
|
||||||
|
} else if (item.value === local.select?.value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -576,82 +695,51 @@ export const Typeahead: FC<{
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<input
|
{isBetter ? (
|
||||||
placeholder={
|
<div className="h-9 flex-grow flex flex-row items-start">
|
||||||
local.mode === "multi"
|
<div className="flex flex-grow"></div>
|
||||||
? placeholder
|
<div className="h-9 flex flex-row items-center px-2">
|
||||||
: valueLabel[0]?.label || placeholder
|
<GoChevronDown size={14} />
|
||||||
}
|
</div>
|
||||||
type="text"
|
</div>
|
||||||
ref={input}
|
) : (
|
||||||
value={inputval}
|
<input
|
||||||
onChange={async (e) => {
|
placeholder={
|
||||||
const val = e.currentTarget.value;
|
local.mode === "multi"
|
||||||
if (!local.open) {
|
? placeholder
|
||||||
local.open = true;
|
: valueLabel[0]?.label || placeholder
|
||||||
}
|
}
|
||||||
|
type="text"
|
||||||
|
ref={input}
|
||||||
|
value={inputval}
|
||||||
|
onChange={async (e) => {
|
||||||
|
const val = e.currentTarget.value;
|
||||||
|
if (!local.open) {
|
||||||
|
local.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
local.search.input = val;
|
local.search.input = val;
|
||||||
local.render();
|
local.render();
|
||||||
|
|
||||||
if (local.search.promise) {
|
if (local.search.promise) {
|
||||||
await local.search.promise;
|
await local.search.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
local.search.searching = true;
|
local.search.searching = true;
|
||||||
local.render();
|
local.render();
|
||||||
if (allow_new) {
|
if (allow_new) {
|
||||||
setSearchTerm(val);
|
setSearchTerm(val);
|
||||||
}
|
}
|
||||||
if (local.search.searching) {
|
if (local.search.searching) {
|
||||||
if (local.local_search) {
|
if (local.local_search) {
|
||||||
if (!local.loaded) {
|
if (!local.loaded) {
|
||||||
await loadOptions();
|
await loadOptions();
|
||||||
}
|
|
||||||
const search = local.search.input.toLowerCase();
|
|
||||||
if (search) {
|
|
||||||
local.search.result = options.filter((e) =>
|
|
||||||
e.label.toLowerCase().includes(search)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
local.search.result.length > 0 &&
|
|
||||||
!local.search.result.find(
|
|
||||||
(e) => e.value === local.select?.value
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
local.select = local.search.result[0];
|
|
||||||
}
|
}
|
||||||
} else {
|
const search = local.search.input.toLowerCase();
|
||||||
local.search.result = null;
|
if (search) {
|
||||||
}
|
local.search.result = options.filter((e) =>
|
||||||
local.search.searching = false;
|
e.label.toLowerCase().includes(search)
|
||||||
local.render();
|
);
|
||||||
} else {
|
|
||||||
clearTimeout(local.search.timeout);
|
|
||||||
local.search.timeout = setTimeout(async () => {
|
|
||||||
const result = options_fn?.({
|
|
||||||
search: local.search.input,
|
|
||||||
existing: options,
|
|
||||||
});
|
|
||||||
if (result) {
|
|
||||||
if (result instanceof Promise) {
|
|
||||||
local.search.promise = result;
|
|
||||||
local.search.result = (await result).map((item) => {
|
|
||||||
if (typeof item === "string")
|
|
||||||
return { value: item, label: item };
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
local.search.searching = false;
|
|
||||||
local.search.promise = null;
|
|
||||||
} else {
|
|
||||||
local.search.result = result.map((item) => {
|
|
||||||
if (typeof item === "string")
|
|
||||||
return { value: item, label: item };
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
local.search.searching = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
local.search.result.length > 0 &&
|
local.search.result.length > 0 &&
|
||||||
|
|
@ -661,24 +749,64 @@ export const Typeahead: FC<{
|
||||||
) {
|
) {
|
||||||
local.select = local.search.result[0];
|
local.select = local.search.result[0];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
local.render();
|
local.search.result = null;
|
||||||
}
|
}
|
||||||
}, 100);
|
local.search.searching = false;
|
||||||
|
local.render();
|
||||||
|
} else {
|
||||||
|
clearTimeout(local.search.timeout);
|
||||||
|
local.search.timeout = setTimeout(async () => {
|
||||||
|
const result = options_fn?.({
|
||||||
|
search: local.search.input,
|
||||||
|
existing: options,
|
||||||
|
});
|
||||||
|
if (result) {
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
local.search.promise = result;
|
||||||
|
local.search.result = (await result).map((item) => {
|
||||||
|
if (typeof item === "string")
|
||||||
|
return { value: item, label: item };
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
local.search.searching = false;
|
||||||
|
local.search.promise = null;
|
||||||
|
} else {
|
||||||
|
local.search.result = result.map((item) => {
|
||||||
|
if (typeof item === "string")
|
||||||
|
return { value: item, label: item };
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
local.search.searching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
local.search.result.length > 0 &&
|
||||||
|
!local.search.result.find(
|
||||||
|
(e) => e.value === local.select?.value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
local.select = local.search.result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
disabled={!disabled ? disabledSearch : disabled}
|
||||||
disabled={!disabled ? disabledSearch : disabled}
|
spellCheck={false}
|
||||||
spellCheck={false}
|
className={cx(
|
||||||
className={cx(
|
"text-black flex h-9 w-full border-input bg-transparent px-3 py-1 text-base border-none shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground md:text-sm focus:outline-none focus:ring-0",
|
||||||
"text-black flex h-9 w-full border-input bg-transparent px-3 py-1 text-base border-none shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground md:text-sm focus:outline-none focus:ring-0",
|
local.mode === "single" ? "cursor-pointer" : ""
|
||||||
local.mode === "single" ? "cursor-pointer" : ""
|
)}
|
||||||
)}
|
style={{
|
||||||
style={{
|
pointerEvents: disabledSearch ? "none" : "auto", // Mencegah input menangkap klik saat disabled
|
||||||
pointerEvents: disabledSearch ? "none" : "auto", // Mencegah input menangkap klik saat disabled
|
}}
|
||||||
}}
|
onKeyDown={keydown}
|
||||||
onKeyDown={keydown}
|
/>
|
||||||
/>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</TypeaheadOptions>
|
</TypeaheadOptions>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,16 @@ import { FC } from "react";
|
||||||
import { useLocal } from "@/lib/utils/use-local";
|
import { useLocal } from "@/lib/utils/use-local";
|
||||||
import { Popover } from "../../Popover/Popover";
|
import { Popover } from "../../Popover/Popover";
|
||||||
import { ButtonBetter } from "../../ui/button";
|
import { ButtonBetter } from "../../ui/button";
|
||||||
|
import { Checkbox } from "../../ui/checkbox";
|
||||||
|
import { ScrollArea } from "../../ui/scroll-area";
|
||||||
|
import { IoCheckmark, IoSearchOutline } from "react-icons/io5";
|
||||||
|
|
||||||
export type OptionItem = { value: string; label: string };
|
export type OptionItem = { value: string; label: string };
|
||||||
export const TypeaheadOptions: FC<{
|
export const TypeaheadOptions: FC<{
|
||||||
popup?: boolean;
|
popup?: boolean;
|
||||||
|
onRemove?: (data: any) => void;
|
||||||
|
onSelectAll?: (data: boolean) => void;
|
||||||
|
init?: any;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
children: any;
|
children: any;
|
||||||
|
|
@ -24,6 +30,9 @@ export const TypeaheadOptions: FC<{
|
||||||
width?: number;
|
width?: number;
|
||||||
isMulti?: boolean;
|
isMulti?: boolean;
|
||||||
fitur?: "search-add";
|
fitur?: "search-add";
|
||||||
|
isBetter?: boolean;
|
||||||
|
onSearch?: (event: any) => void;
|
||||||
|
search?: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
popup,
|
popup,
|
||||||
loading,
|
loading,
|
||||||
|
|
@ -40,6 +49,12 @@ export const TypeaheadOptions: FC<{
|
||||||
width,
|
width,
|
||||||
isMulti,
|
isMulti,
|
||||||
fitur,
|
fitur,
|
||||||
|
isBetter,
|
||||||
|
init,
|
||||||
|
onSearch,
|
||||||
|
search,
|
||||||
|
onRemove,
|
||||||
|
onSelectAll,
|
||||||
}) => {
|
}) => {
|
||||||
if (!popup) return children;
|
if (!popup) return children;
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
|
|
@ -50,12 +65,20 @@ export const TypeaheadOptions: FC<{
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
className,
|
className,
|
||||||
width
|
"flex flex-col",
|
||||||
|
isBetter
|
||||||
|
? css`
|
||||||
|
min-width: 350px;
|
||||||
|
height: 450px;
|
||||||
|
`
|
||||||
|
: width
|
||||||
? css`
|
? css`
|
||||||
min-width: ${width}px;
|
min-width: ${width}px;
|
||||||
|
height: 450px;
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
|
height: 450px;
|
||||||
`,
|
`,
|
||||||
css`
|
css`
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
|
|
@ -63,45 +86,74 @@ export const TypeaheadOptions: FC<{
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!loading ? (
|
{isBetter ? (
|
||||||
<>
|
<>
|
||||||
{options.map((item, idx) => {
|
<div className="flex flex-row w-full p-1">
|
||||||
const is_selected = selected?.({ item, options, idx });
|
<div className="flex-grow flex flex-row relative">
|
||||||
|
|
||||||
if (is_selected) {
|
|
||||||
local.selectedIdx = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
|
||||||
key={item.value + "_" + idx}
|
|
||||||
className={cx(
|
className={cx(
|
||||||
"opt-item px-3 py-1 cursor-pointer option-item text-sm",
|
"absolute left-0 px-1.5",
|
||||||
is_selected ? "bg-blue-600 text-white" : "hover:bg-blue-50",
|
css`
|
||||||
idx > 0 && "border-t"
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
`
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
|
||||||
onSelect?.(item.value);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{item.label || <> </>}
|
<IoSearchOutline />
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
})}
|
<input
|
||||||
|
placeholder={"Search"}
|
||||||
|
type="text"
|
||||||
|
spellCheck={false}
|
||||||
|
onChange={onSearch}
|
||||||
|
className={cx(
|
||||||
|
"pl-6 pr-3 py-1 rounded-md text-black flex h-9 flex-grow border border-gray-200 bg-white text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground md:text-sm focus:outline-none focus:ring-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{init.search.input === "" || !init.search.input ? (
|
||||||
|
<div className="flex flex-row px-3 py-1 gap-x-2 items-center cursor-pointer">
|
||||||
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
className="border border-primary"
|
||||||
|
checked={init?.selectBetter?.all ? true : false}
|
||||||
|
onClick={(e) => {
|
||||||
|
init.selectBetter.all = !init.selectBetter.all;
|
||||||
|
init.render();
|
||||||
|
if (typeof onSelectAll === "function")
|
||||||
|
onSelectAll(init.selectBetter.all);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Select All
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading || searching ? (
|
{loading || searching ? (
|
||||||
<div className="px-4 w-full text-slate-400 text-sm py-2">
|
<div
|
||||||
|
className={cx(
|
||||||
|
isBetter && "flex-grow flex flex-row items-center justify-center",
|
||||||
|
"px-4 w-full text-slate-400 text-sm py-2"
|
||||||
|
)}
|
||||||
|
>
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{options.length === 0 && (
|
{options.length === 0 ? (
|
||||||
<div className="p-4 w-full text-center text-md text-slate-400">
|
<div
|
||||||
|
className={cx(
|
||||||
|
isBetter &&
|
||||||
|
"flex-grow flex flex-row items-center justify-center",
|
||||||
|
"p-4 w-full text-center text-md text-slate-400"
|
||||||
|
)}
|
||||||
|
>
|
||||||
{fitur === "search-add" ? (
|
{fitur === "search-add" ? (
|
||||||
<ButtonBetter
|
<ButtonBetter
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
|
|
@ -155,9 +207,73 @@ export const TypeaheadOptions: FC<{
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<ScrollArea className="w-full flex-grow flex flex-col gap-y-2">
|
||||||
|
{options.map((item, idx) => {
|
||||||
|
const is_selected = isBetter
|
||||||
|
? init.selectBetter.all
|
||||||
|
? true
|
||||||
|
: selected?.({ item, options, idx })
|
||||||
|
: selected?.({ item, options, idx });
|
||||||
|
|
||||||
|
if (is_selected) {
|
||||||
|
local.selectedIdx = idx;
|
||||||
|
}
|
||||||
|
if (isBetter) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex flex-row px-3 py-1 gap-x-2 items-center cursor-pointer"
|
||||||
|
key={item.value + "_" + idx}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id="terms"
|
||||||
|
className="border border-primary"
|
||||||
|
checked={is_selected}
|
||||||
|
onClick={() => {
|
||||||
|
if (is_selected) {
|
||||||
|
if (typeof onRemove === "function") onRemove(item);
|
||||||
|
} else {
|
||||||
|
onSelect?.(item.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{item.label || <> </>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
key={item.value + "_" + idx}
|
||||||
|
className={cx(
|
||||||
|
"opt-item px-3 py-1 cursor-pointer option-item text-sm",
|
||||||
|
is_selected
|
||||||
|
? "bg-blue-600 text-white"
|
||||||
|
: "hover:bg-blue-50",
|
||||||
|
idx > 0 && "border-t"
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
onSelect?.(item.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.label || <> </>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ScrollArea>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{isBetter ? (
|
||||||
|
<div className="w-full flex flex-row items-center justify-end p-1 border-t border-gray-200">
|
||||||
|
<ButtonBetter className="rounded-md text-xs flex flex-row items-center gap-x-1">
|
||||||
|
<IoCheckmark />
|
||||||
|
OK
|
||||||
|
</ButtonBetter>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -169,7 +285,7 @@ export const TypeaheadOptions: FC<{
|
||||||
arrow={false}
|
arrow={false}
|
||||||
onOpenChange={onOpenChange}
|
onOpenChange={onOpenChange}
|
||||||
backdrop={false}
|
backdrop={false}
|
||||||
classNameTrigger={!isMulti ? "w-full" : ""}
|
classNameTrigger={!isMulti ? "w-full" : "flex-grow"}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
className="flex-1 rounded-md overflow-hidden"
|
className="flex-1 rounded-md overflow-hidden"
|
||||||
content={content}
|
content={content}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue