geoiq-frontend-ui-kit
Version:
This project is a UI kit for GeoIQ's frontend. It's built with React, TypeScript, and Vite.
278 lines (277 loc) • 9.36 kB
JavaScript
import n, { useState as b, useRef as ue, useCallback as pe, useEffect as he, useMemo as D } from "react";
import { X as ge, ChevronUp as ye, ChevronDown as be, Plus as we, Check as Ee } from "lucide-react";
import { Command as Ce, CommandInput as Ne, CommandEmpty as xe, CommandGroup as ve, CommandList as je, CommandItem as K } from "./index.es56.js";
import { Popover as ke, PopoverTrigger as Se, PopoverContent as Le } from "./index.es86.js";
import { Button as Ae } from "./index.es9.js";
import { Typography as w } from "./index.es19.js";
import { Badge as Pe } from "./index.es8.js";
import { cn as R } from "./index.es85.js";
import { ComboBoxStates as Oe } from "./index.es104.js";
import { interpolateUrl as Q } from "./index.es105.js";
function Me(o) {
const c = (a) => typeof a == "string" ? { id: a, name: a } : {
id: String(a.id ?? a.value ?? a.email ?? a),
name: String(a.name ?? a.label ?? a.id ?? a.value ?? a.email ?? a)
};
if (Array.isArray(o))
return o.map(c);
if (o && typeof o == "object") {
const a = o.data ?? o.options;
if (Array.isArray(a))
return a.map(c);
}
return [];
}
function _e(o, c) {
if (o && typeof o == "object") {
const a = o.data ?? o;
if (a && typeof a == "object") {
const m = a;
return {
id: String(m.id ?? m.value ?? m.email ?? c),
name: String(m.name ?? m.label ?? m.id ?? m.value ?? m.email ?? c)
};
}
}
return { id: c, name: c };
}
async function U(o) {
if (!o) return {};
const c = typeof o == "function" ? o() : o;
return await Promise.resolve(c) ?? {};
}
function Xe({
optionsUrl: o,
createOptionUrl: c,
fetchOptions: a,
createOption: m,
requestHeaders: P,
optionsRequestBody: V,
createOptionRequestBody: Y,
createOptionValueKey: Z = "value",
createOptionExtraBody: O,
skipCreateOptionExtraBody: I,
urlTemplateData: v = {},
value: f,
fieldId: j,
placeholder: H = "Select or type...",
searchPlaceholder: q = "Search or type a value...",
emptyMessage: ee = "No options",
addCustomMessage: te = (p) => `Add "${p}"`,
minSelected: M = 0,
maxSelected: l,
onChange: k,
container: ne
}) {
const [p, _] = b(!1), [d, S] = b(""), [T, h] = b([]), [ae, L] = b(!0), [$, A] = b(!1), [B, g] = b(null), z = ue(null), G = pe(async () => {
if (a) {
L(!0);
try {
const t = await a();
h(t);
} catch {
h([]);
} finally {
L(!1);
}
return;
}
const e = o && Q(o, { ...v, fieldId: j });
if (e) {
L(!0);
try {
const t = await U(P), s = V ?? { ...v, fieldId: j }, r = await (await fetch(e, {
method: "POST",
headers: { "Content-Type": "application/json", ...t },
body: JSON.stringify(s)
})).json();
h(Me(r));
} catch {
h([]);
} finally {
L(!1);
}
}
}, [
o,
v,
j,
a,
P,
V
]);
he(() => {
G();
}, [G]);
const E = D(() => {
const e = f ?? [], t = T.map((r) => ({
id: r.id,
name: r.name
})), s = e.filter(
(r) => r?.trim() && !t.some((u) => u.id === r.trim())
).map((r) => ({ id: r.trim(), name: r.trim() })), i = [...t];
for (const r of s)
i.some((u) => u.id === r.id) || i.push(r);
return i;
}, [T, f]), re = D(() => {
if (!d.trim()) return E;
const e = d.toLowerCase();
return E.filter(
(t) => t.name.toLowerCase().includes(e) || t.id.toLowerCase().includes(e)
);
}, [E, d]), F = l != null && (f ?? []).length >= l, oe = !F && d.trim() && !E.some(
(e) => e.id.toLowerCase() === d.trim().toLowerCase() || e.name.toLowerCase() === d.trim().toLowerCase()
), se = (e) => {
const t = f ?? [], i = t.includes(e) ? t.filter((r) => r !== e) : [...t, e];
l != null && i.length > l || k(i);
}, ie = async () => {
const e = d.trim();
if (!e || F) return;
if (g(null), m) {
A(!0);
try {
const s = await m(e);
h((r) => r.some((u) => u.id === s.id) ? r : [...r, s]);
const i = [...f ?? [], s.id];
(l == null || i.length <= l) && k(i), S(""), _(!1);
} catch (s) {
const i = s instanceof Error ? s.message : String(s);
g(i || "Failed to add option");
} finally {
A(!1);
}
return;
}
const t = c && Q(c, {
...v,
fieldId: j,
value: e
});
if (t) {
A(!0);
try {
const s = await U(P), i = I ? {} : typeof O == "function" ? await Promise.resolve(O()) : O ?? {}, r = {
...Y,
[Z]: e,
...i
}, u = await fetch(t, {
method: "POST",
headers: { "Content-Type": "application/json", ...s },
body: JSON.stringify(r)
}), N = await u.text();
if (!u.ok) {
let y = N;
try {
const x = JSON.parse(N);
y = String(x.message ?? x.error ?? x.msg ?? N);
} catch {
}
g(y || "Failed to add option");
return;
}
const fe = N ? JSON.parse(N) : {}, J = _e(fe, e);
h((y) => y.some((x) => x.id === J.id) ? y : [...y, J]);
const X = [...f ?? [], J.id];
(l == null || X.length <= l) && k(X), S(""), _(!1);
} catch {
g("Failed to add option");
} finally {
A(!1);
}
}
}, le = (e) => {
const t = (f ?? []).filter((s) => s !== e);
k(t);
}, ce = (e) => {
_(e), e || (S(""), g(null));
}, C = f ?? [], W = C.map((e) => E.find((t) => t.id === e)).filter(Boolean), me = M > 0 && C.length < M, de = l != null && C.length > l;
return /* @__PURE__ */ n.createElement(ke, { open: p, onOpenChange: ce }, /* @__PURE__ */ n.createElement(Se, { ref: z, asChild: !0 }, /* @__PURE__ */ n.createElement(
Ae,
{
variant: "secondary",
role: "combobox",
"aria-expanded": p,
className: R(
Oe({
isOpen: p,
isValue: C.length > 0
}),
"w-full justify-between px-3 py-2 min-h-[40px] h-fit"
)
},
/* @__PURE__ */ n.createElement("div", { className: "flex flex-wrap gap-1 flex-1 min-w-0" }, W.length > 0 ? W.map((e) => /* @__PURE__ */ n.createElement(
Pe,
{
key: e.id,
variant: "default",
size: "sm",
showBorder: !0,
className: "gap-1"
},
/* @__PURE__ */ n.createElement("span", { className: "truncate max-w-[120px]" }, e.name),
/* @__PURE__ */ n.createElement(
"button",
{
type: "button",
className: "ml-0.5 rounded-full outline-none hover:bg-light-3",
onMouseDown: (t) => t.preventDefault(),
onClick: (t) => {
t.stopPropagation(), le(e.id);
}
},
/* @__PURE__ */ n.createElement(ge, { className: "w-3 h-3" })
)
)) : /* @__PURE__ */ n.createElement(w, { variant: "body2", className: "text-light-4" }, H)),
p ? /* @__PURE__ */ n.createElement(ye, { className: "w-4 h-4 ml-2 stroke-light-2 shrink-0" }) : /* @__PURE__ */ n.createElement(be, { className: "w-4 h-4 ml-2 stroke-light-2 shrink-0" })
)), /* @__PURE__ */ n.createElement(
Le,
{
style: { width: z.current?.clientWidth },
className: "p-0 mt-1 rounded-lg bg-light-1",
container: ne
},
/* @__PURE__ */ n.createElement(Ce, { shouldFilter: !1 }, /* @__PURE__ */ n.createElement(
Ne,
{
placeholder: q,
value: d,
onValueChange: (e) => {
S(e), g(null);
}
}
), /* @__PURE__ */ n.createElement(xe, null, /* @__PURE__ */ n.createElement(w, { variant: "body3", className: "text-light-4" }, ae ? "Loading..." : ee)), /* @__PURE__ */ n.createElement(ve, null, /* @__PURE__ */ n.createElement(je, null, oe && /* @__PURE__ */ n.createElement(
K,
{
value: `__add__${d.trim()}`,
className: "text-brand hover:bg-light-2 focus:bg-light-2 gap-2",
onSelect: ie,
disabled: $
},
/* @__PURE__ */ n.createElement(we, { className: "h-4 w-4" }),
$ ? "Adding..." : te(d.trim())
), re.map((e) => {
const t = C.includes(e.id), s = !t && F;
return /* @__PURE__ */ n.createElement(
K,
{
key: e.id,
value: e.id,
disabled: s,
className: R(
"justify-between gap-2 text-light-2 hover:bg-light-2 hover:text-light-1 focus:bg-light-2 focus:text-accent-foreground",
t ? "bg-light-2" : "",
s ? "opacity-50" : ""
),
onSelect: () => se(e.id)
},
/* @__PURE__ */ n.createElement(w, { variant: "body2" }, e.name),
t ? /* @__PURE__ */ n.createElement(Ee, { className: R("h-4 w-4 stroke-brand") }) : /* @__PURE__ */ n.createElement("div", null)
);
})))),
B && /* @__PURE__ */ n.createElement(w, { variant: "body5", className: "text-danger-1 px-3 py-2" }, B)
), me && /* @__PURE__ */ n.createElement(w, { variant: "body5", className: "text-danger-1 mt-1" }, "Select at least ", M, " option(s)", l != null && ` (max ${l})`), de && /* @__PURE__ */ n.createElement(w, { variant: "body5", className: "text-danger-1 mt-1" }, "Select at most ", l, " option(s)"));
}
export {
Xe as CreatableMultiCombobox
};
//# sourceMappingURL=index.es28.js.map