@bmsuisse/ui 0.7.0 → 0.8.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/dist/index.d.ts +62 -1
- package/dist/index.js +326 -81
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -880,7 +880,8 @@ function FormField({
|
|
|
880
880
|
// src/patterns/combobox/Combobox.tsx
|
|
881
881
|
import { Check as Check4, ChevronsUpDown, X as X3 } from "lucide-react";
|
|
882
882
|
import { useEffect, useMemo, useRef, useState as useState3 } from "react";
|
|
883
|
-
|
|
883
|
+
|
|
884
|
+
// src/lib/optionGrouping.ts
|
|
884
885
|
function groupMembers(options, group) {
|
|
885
886
|
return options.filter((o) => o.group === group && !o.disabled).map((o) => o.value);
|
|
886
887
|
}
|
|
@@ -890,6 +891,24 @@ function groupCheckState(options, selectedValues, group) {
|
|
|
890
891
|
if (selectedCount === 0) return "unchecked";
|
|
891
892
|
return selectedCount === members.length ? "checked" : "indeterminate";
|
|
892
893
|
}
|
|
894
|
+
function buildRenderChunks(visibleOptions) {
|
|
895
|
+
const chunks = [];
|
|
896
|
+
visibleOptions.forEach((option, index) => {
|
|
897
|
+
const row = { option, index };
|
|
898
|
+
const last = chunks[chunks.length - 1];
|
|
899
|
+
if (option.group && last?.kind === "group" && last.group === option.group) {
|
|
900
|
+
last.rows.push(row);
|
|
901
|
+
} else if (option.group) {
|
|
902
|
+
chunks.push({ kind: "group", group: option.group, rows: [row] });
|
|
903
|
+
} else {
|
|
904
|
+
chunks.push({ kind: "single", row });
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
return chunks;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// src/patterns/combobox/Combobox.tsx
|
|
911
|
+
import { jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
893
912
|
function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
|
|
894
913
|
const selectedSet = new Set(selectedValues);
|
|
895
914
|
const groupsPresent = Array.from(
|
|
@@ -909,21 +928,6 @@ function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
|
|
|
909
928
|
if (fullyCoveredGroups.length === 0 || hasLeftover) return null;
|
|
910
929
|
return fullyCoveredGroups.map(groupLabel).join(", ");
|
|
911
930
|
}
|
|
912
|
-
function buildRenderChunks(visibleOptions) {
|
|
913
|
-
const chunks = [];
|
|
914
|
-
visibleOptions.forEach((option, index) => {
|
|
915
|
-
const row = { option, index };
|
|
916
|
-
const last = chunks[chunks.length - 1];
|
|
917
|
-
if (option.group && last?.kind === "group" && last.group === option.group) {
|
|
918
|
-
last.rows.push(row);
|
|
919
|
-
} else if (option.group) {
|
|
920
|
-
chunks.push({ kind: "group", group: option.group, rows: [row] });
|
|
921
|
-
} else {
|
|
922
|
-
chunks.push({ kind: "single", row });
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
return chunks;
|
|
926
|
-
}
|
|
927
931
|
function Combobox(props) {
|
|
928
932
|
const {
|
|
929
933
|
options,
|
|
@@ -1149,9 +1153,249 @@ function Combobox(props) {
|
|
|
1149
1153
|
] });
|
|
1150
1154
|
}
|
|
1151
1155
|
|
|
1156
|
+
// src/patterns/tag-combobox/TagCombobox.tsx
|
|
1157
|
+
import { X as X4 } from "lucide-react";
|
|
1158
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState4 } from "react";
|
|
1159
|
+
import { jsx as jsx25, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1160
|
+
function TagCombobox({
|
|
1161
|
+
options,
|
|
1162
|
+
value,
|
|
1163
|
+
onChange,
|
|
1164
|
+
placeholder = "Select\u2026",
|
|
1165
|
+
emptyMessage = "No matches.",
|
|
1166
|
+
disabled,
|
|
1167
|
+
loading = false,
|
|
1168
|
+
loadingMessage = "Loading\u2026",
|
|
1169
|
+
className,
|
|
1170
|
+
id,
|
|
1171
|
+
onSearchChange,
|
|
1172
|
+
container,
|
|
1173
|
+
groupLabels,
|
|
1174
|
+
...rest
|
|
1175
|
+
}) {
|
|
1176
|
+
const testId = rest["data-testid"];
|
|
1177
|
+
const [open, setOpen] = useState4(false);
|
|
1178
|
+
const [search, setSearch] = useState4("");
|
|
1179
|
+
const [activeIndex, setActiveIndex] = useState4(0);
|
|
1180
|
+
const inputRef = useRef2(null);
|
|
1181
|
+
const fieldRef = useRef2(null);
|
|
1182
|
+
const selectedOptions = useMemo2(
|
|
1183
|
+
() => value.map((v) => options.find((o) => o.value === v) ?? { value: v, label: v }),
|
|
1184
|
+
[options, value]
|
|
1185
|
+
);
|
|
1186
|
+
const visibleOptions = useMemo2(() => {
|
|
1187
|
+
if (onSearchChange) return options;
|
|
1188
|
+
const term = search.trim().toLowerCase();
|
|
1189
|
+
if (!term) return options;
|
|
1190
|
+
return options.filter((option) => option.label.toLowerCase().includes(term));
|
|
1191
|
+
}, [options, search, onSearchChange]);
|
|
1192
|
+
useEffect2(() => {
|
|
1193
|
+
setActiveIndex((i) => Math.min(Math.max(i, 0), Math.max(visibleOptions.length - 1, 0)));
|
|
1194
|
+
}, [visibleOptions.length]);
|
|
1195
|
+
const renderChunks = useMemo2(() => buildRenderChunks(visibleOptions), [visibleOptions]);
|
|
1196
|
+
useEffect2(() => {
|
|
1197
|
+
if (!open) return;
|
|
1198
|
+
setSearch("");
|
|
1199
|
+
onSearchChange?.("");
|
|
1200
|
+
const firstSelected = value[0];
|
|
1201
|
+
const selectedIdx = firstSelected ? options.findIndex((o) => o.value === firstSelected) : -1;
|
|
1202
|
+
setActiveIndex(selectedIdx >= 0 ? selectedIdx : 0);
|
|
1203
|
+
}, [open]);
|
|
1204
|
+
function updateSearch(next) {
|
|
1205
|
+
setSearch(next);
|
|
1206
|
+
onSearchChange?.(next);
|
|
1207
|
+
if (!open) setOpen(true);
|
|
1208
|
+
}
|
|
1209
|
+
function toggleOption(option) {
|
|
1210
|
+
if (option.disabled) return;
|
|
1211
|
+
const next = value.includes(option.value) ? value.filter((v) => v !== option.value) : [...value, option.value];
|
|
1212
|
+
onChange(next);
|
|
1213
|
+
updateSearch("");
|
|
1214
|
+
inputRef.current?.focus();
|
|
1215
|
+
}
|
|
1216
|
+
function removeValue(v) {
|
|
1217
|
+
if (options.find((o) => o.value === v)?.disabled) return;
|
|
1218
|
+
onChange(value.filter((existing) => existing !== v));
|
|
1219
|
+
}
|
|
1220
|
+
const groupLabel = (group) => groupLabels?.[group] ?? group;
|
|
1221
|
+
function toggleGroup(group) {
|
|
1222
|
+
const members = groupMembers(options, group);
|
|
1223
|
+
const next = groupCheckState(options, value, group) === "checked" ? value.filter((v) => !members.includes(v)) : Array.from(/* @__PURE__ */ new Set([...value, ...members]));
|
|
1224
|
+
onChange(next);
|
|
1225
|
+
inputRef.current?.focus();
|
|
1226
|
+
}
|
|
1227
|
+
function handleKeyDown(event) {
|
|
1228
|
+
if (event.key === "ArrowDown") {
|
|
1229
|
+
event.preventDefault();
|
|
1230
|
+
setOpen(true);
|
|
1231
|
+
setActiveIndex((i) => Math.min(i + 1, Math.max(visibleOptions.length - 1, 0)));
|
|
1232
|
+
} else if (event.key === "ArrowUp") {
|
|
1233
|
+
event.preventDefault();
|
|
1234
|
+
setActiveIndex((i) => Math.max(i - 1, 0));
|
|
1235
|
+
} else if (event.key === "Enter") {
|
|
1236
|
+
event.preventDefault();
|
|
1237
|
+
const option = visibleOptions[activeIndex];
|
|
1238
|
+
if (option) toggleOption(option);
|
|
1239
|
+
} else if (event.key === "Escape") {
|
|
1240
|
+
setOpen(false);
|
|
1241
|
+
} else if (event.key === "Backspace" && search === "") {
|
|
1242
|
+
const last = value[value.length - 1];
|
|
1243
|
+
if (last !== void 0) removeValue(last);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function renderOption({ option, index }) {
|
|
1247
|
+
const isSelected = value.includes(option.value);
|
|
1248
|
+
const isActive = index === activeIndex;
|
|
1249
|
+
return /* @__PURE__ */ jsxs11(
|
|
1250
|
+
"button",
|
|
1251
|
+
{
|
|
1252
|
+
type: "button",
|
|
1253
|
+
role: "option",
|
|
1254
|
+
"aria-selected": isSelected,
|
|
1255
|
+
disabled: option.disabled,
|
|
1256
|
+
"data-testid": testId ? `${testId}-option` : void 0,
|
|
1257
|
+
"data-option-value": option.value,
|
|
1258
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
1259
|
+
onClick: () => toggleOption(option),
|
|
1260
|
+
className: cn(
|
|
1261
|
+
"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none disabled:pointer-events-none disabled:opacity-50",
|
|
1262
|
+
isActive && "bg-accent text-accent-foreground"
|
|
1263
|
+
),
|
|
1264
|
+
children: [
|
|
1265
|
+
/* @__PURE__ */ jsx25(
|
|
1266
|
+
"input",
|
|
1267
|
+
{
|
|
1268
|
+
type: "checkbox",
|
|
1269
|
+
checked: isSelected,
|
|
1270
|
+
readOnly: true,
|
|
1271
|
+
tabIndex: -1,
|
|
1272
|
+
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1273
|
+
}
|
|
1274
|
+
),
|
|
1275
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate", children: option.label })
|
|
1276
|
+
]
|
|
1277
|
+
},
|
|
1278
|
+
option.value
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
return /* @__PURE__ */ jsxs11(Popover, { open, onOpenChange: setOpen, children: [
|
|
1282
|
+
/* @__PURE__ */ jsx25(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs11(
|
|
1283
|
+
"div",
|
|
1284
|
+
{
|
|
1285
|
+
ref: fieldRef,
|
|
1286
|
+
"data-testid": testId,
|
|
1287
|
+
role: "combobox",
|
|
1288
|
+
"aria-expanded": open,
|
|
1289
|
+
"aria-disabled": disabled,
|
|
1290
|
+
className: cn(
|
|
1291
|
+
"flex min-h-9 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent px-2 py-1.5 text-sm",
|
|
1292
|
+
"focus-within:outline-none focus-within:ring-2 focus-within:ring-ring",
|
|
1293
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
1294
|
+
className
|
|
1295
|
+
),
|
|
1296
|
+
onClick: () => {
|
|
1297
|
+
if (!disabled) inputRef.current?.focus();
|
|
1298
|
+
},
|
|
1299
|
+
children: [
|
|
1300
|
+
selectedOptions.map((option) => /* @__PURE__ */ jsxs11(
|
|
1301
|
+
"span",
|
|
1302
|
+
{
|
|
1303
|
+
className: "inline-flex items-center gap-1 rounded-full bg-muted py-0.5 pr-1 pl-2.5 text-xs font-medium text-muted-foreground",
|
|
1304
|
+
children: [
|
|
1305
|
+
/* @__PURE__ */ jsx25("span", { className: "max-w-48 truncate", children: option.label }),
|
|
1306
|
+
!disabled && /* @__PURE__ */ jsx25(
|
|
1307
|
+
"button",
|
|
1308
|
+
{
|
|
1309
|
+
type: "button",
|
|
1310
|
+
"aria-label": `Remove ${option.label}`,
|
|
1311
|
+
className: "rounded-full p-0.5 hover:bg-accent hover:text-accent-foreground",
|
|
1312
|
+
onClick: (event) => {
|
|
1313
|
+
event.stopPropagation();
|
|
1314
|
+
removeValue(option.value);
|
|
1315
|
+
},
|
|
1316
|
+
children: /* @__PURE__ */ jsx25(X4, { className: "h-3 w-3" })
|
|
1317
|
+
}
|
|
1318
|
+
)
|
|
1319
|
+
]
|
|
1320
|
+
},
|
|
1321
|
+
option.value
|
|
1322
|
+
)),
|
|
1323
|
+
/* @__PURE__ */ jsx25(
|
|
1324
|
+
"input",
|
|
1325
|
+
{
|
|
1326
|
+
ref: inputRef,
|
|
1327
|
+
id,
|
|
1328
|
+
value: search,
|
|
1329
|
+
disabled,
|
|
1330
|
+
placeholder: selectedOptions.length === 0 ? placeholder : void 0,
|
|
1331
|
+
"aria-label": placeholder,
|
|
1332
|
+
className: "min-w-24 flex-1 bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
|
|
1333
|
+
onFocus: () => setOpen(true),
|
|
1334
|
+
onChange: (event) => updateSearch(event.target.value),
|
|
1335
|
+
onKeyDown: handleKeyDown
|
|
1336
|
+
}
|
|
1337
|
+
)
|
|
1338
|
+
]
|
|
1339
|
+
}
|
|
1340
|
+
) }),
|
|
1341
|
+
/* @__PURE__ */ jsx25(
|
|
1342
|
+
PopoverContent,
|
|
1343
|
+
{
|
|
1344
|
+
align: "start",
|
|
1345
|
+
container,
|
|
1346
|
+
className: "w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
|
|
1347
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1348
|
+
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
1349
|
+
onInteractOutside: (event) => {
|
|
1350
|
+
if (fieldRef.current?.contains(event.target)) event.preventDefault();
|
|
1351
|
+
},
|
|
1352
|
+
children: /* @__PURE__ */ jsx25("div", { role: "listbox", className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: loading ? /* @__PURE__ */ jsx25("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : renderChunks.length === 0 ? /* @__PURE__ */ jsx25("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : renderChunks.map((chunk) => {
|
|
1353
|
+
if (chunk.kind === "single") return renderOption(chunk.row);
|
|
1354
|
+
const headerState = groupCheckState(options, value, chunk.group);
|
|
1355
|
+
return (
|
|
1356
|
+
// The header and every one of its group's rows share this wrapper -- its
|
|
1357
|
+
// bounds are the sticky header's containing block, so `sticky top-0` keeps
|
|
1358
|
+
// the header pinned for the group's whole scroll extent instead of just
|
|
1359
|
+
// past its first row. Spacing above a group lives on this wrapper's
|
|
1360
|
+
// `pt-1`/`first:pt-0`, not on the header itself -- see `Combobox`'s own
|
|
1361
|
+
// identical structure for why both of these matter.
|
|
1362
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
1363
|
+
/* @__PURE__ */ jsxs11(
|
|
1364
|
+
"div",
|
|
1365
|
+
{
|
|
1366
|
+
"data-group-header": true,
|
|
1367
|
+
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1368
|
+
children: [
|
|
1369
|
+
/* @__PURE__ */ jsx25(
|
|
1370
|
+
"input",
|
|
1371
|
+
{
|
|
1372
|
+
type: "checkbox",
|
|
1373
|
+
checked: headerState === "checked",
|
|
1374
|
+
ref: (el) => {
|
|
1375
|
+
if (el) el.indeterminate = headerState === "indeterminate";
|
|
1376
|
+
},
|
|
1377
|
+
onChange: () => toggleGroup(chunk.group),
|
|
1378
|
+
onClick: (event) => event.stopPropagation(),
|
|
1379
|
+
className: "h-3.5 w-3.5 shrink-0 accent-primary",
|
|
1380
|
+
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1381
|
+
}
|
|
1382
|
+
),
|
|
1383
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1384
|
+
]
|
|
1385
|
+
}
|
|
1386
|
+
),
|
|
1387
|
+
chunk.rows.map(renderOption)
|
|
1388
|
+
] }, `group-${chunk.group}`)
|
|
1389
|
+
);
|
|
1390
|
+
}) })
|
|
1391
|
+
}
|
|
1392
|
+
)
|
|
1393
|
+
] });
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1152
1396
|
// src/patterns/alert-box/AlertBox.tsx
|
|
1153
1397
|
import { AlertCircle, AlertTriangle, CheckCircle2, Info } from "lucide-react";
|
|
1154
|
-
import { jsx as
|
|
1398
|
+
import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1155
1399
|
var variantStyles = {
|
|
1156
1400
|
error: "border-destructive bg-destructive/10 text-destructive",
|
|
1157
1401
|
warning: "border-amber-500 bg-amber-500/10 text-amber-700 dark:text-amber-300",
|
|
@@ -1159,24 +1403,24 @@ var variantStyles = {
|
|
|
1159
1403
|
success: "border-emerald-500 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
|
|
1160
1404
|
};
|
|
1161
1405
|
var defaultIcons = {
|
|
1162
|
-
error: /* @__PURE__ */
|
|
1163
|
-
warning: /* @__PURE__ */
|
|
1164
|
-
info: /* @__PURE__ */
|
|
1165
|
-
success: /* @__PURE__ */
|
|
1406
|
+
error: /* @__PURE__ */ jsx26(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1407
|
+
warning: /* @__PURE__ */ jsx26(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1408
|
+
info: /* @__PURE__ */ jsx26(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1409
|
+
success: /* @__PURE__ */ jsx26(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
|
|
1166
1410
|
};
|
|
1167
1411
|
var AlertBox = ({ variant, title, children, className, icon }) => {
|
|
1168
1412
|
const resolvedIcon = icon === void 0 ? defaultIcons[variant] : icon;
|
|
1169
|
-
return /* @__PURE__ */
|
|
1170
|
-
resolvedIcon != null && /* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
title && /* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1413
|
+
return /* @__PURE__ */ jsx26("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs12("div", { className: "flex flex-row items-start gap-2", children: [
|
|
1414
|
+
resolvedIcon != null && /* @__PURE__ */ jsx26("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
|
|
1415
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1", children: [
|
|
1416
|
+
title && /* @__PURE__ */ jsx26("p", { className: "font-bold", children: title }),
|
|
1417
|
+
/* @__PURE__ */ jsx26("div", { children })
|
|
1174
1418
|
] })
|
|
1175
1419
|
] }) });
|
|
1176
1420
|
};
|
|
1177
1421
|
|
|
1178
1422
|
// src/patterns/status-badge/StatusBadge.tsx
|
|
1179
|
-
import { jsx as
|
|
1423
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
1180
1424
|
var TONE_CLASSES = {
|
|
1181
1425
|
success: "border-transparent bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300",
|
|
1182
1426
|
warning: "border-transparent bg-amber-500/15 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300",
|
|
@@ -1223,11 +1467,11 @@ function resolveTone(status, tone, toneMap) {
|
|
|
1223
1467
|
var StatusBadge = ({ status, label, tone, toneMap, className, ...props }) => {
|
|
1224
1468
|
const resolvedTone = resolveTone(status, tone, toneMap);
|
|
1225
1469
|
const displayLabel = label ?? deriveLabel(status);
|
|
1226
|
-
return /* @__PURE__ */
|
|
1470
|
+
return /* @__PURE__ */ jsx27(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
|
|
1227
1471
|
};
|
|
1228
1472
|
|
|
1229
1473
|
// src/patterns/button-group/ButtonGroup.tsx
|
|
1230
|
-
import { jsx as
|
|
1474
|
+
import { jsx as jsx28, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1231
1475
|
function ButtonGroup({
|
|
1232
1476
|
options,
|
|
1233
1477
|
value,
|
|
@@ -1237,10 +1481,10 @@ function ButtonGroup({
|
|
|
1237
1481
|
className,
|
|
1238
1482
|
...props
|
|
1239
1483
|
}) {
|
|
1240
|
-
return /* @__PURE__ */
|
|
1484
|
+
return /* @__PURE__ */ jsx28("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
|
|
1241
1485
|
const selected = option.value === value;
|
|
1242
1486
|
const Icon2 = option.icon;
|
|
1243
|
-
return /* @__PURE__ */
|
|
1487
|
+
return /* @__PURE__ */ jsxs13(
|
|
1244
1488
|
Button,
|
|
1245
1489
|
{
|
|
1246
1490
|
type: "button",
|
|
@@ -1258,7 +1502,7 @@ function ButtonGroup({
|
|
|
1258
1502
|
index > 0 && "-ml-px"
|
|
1259
1503
|
),
|
|
1260
1504
|
children: [
|
|
1261
|
-
Icon2 && /* @__PURE__ */
|
|
1505
|
+
Icon2 && /* @__PURE__ */ jsx28(Icon2, { className: "h-4 w-4" }),
|
|
1262
1506
|
option.label
|
|
1263
1507
|
]
|
|
1264
1508
|
},
|
|
@@ -1270,7 +1514,7 @@ function ButtonGroup({
|
|
|
1270
1514
|
// src/patterns/loading-spinner/LoadingSpinner.tsx
|
|
1271
1515
|
import { cva as cva4 } from "class-variance-authority";
|
|
1272
1516
|
import { Loader2 } from "lucide-react";
|
|
1273
|
-
import { jsx as
|
|
1517
|
+
import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1274
1518
|
var loadingSpinnerIconVariants = cva4("animate-spin text-muted-foreground", {
|
|
1275
1519
|
variants: {
|
|
1276
1520
|
size: {
|
|
@@ -1288,27 +1532,27 @@ var LoadingSpinner = ({
|
|
|
1288
1532
|
label,
|
|
1289
1533
|
className,
|
|
1290
1534
|
...props
|
|
1291
|
-
}) => /* @__PURE__ */
|
|
1292
|
-
/* @__PURE__ */
|
|
1293
|
-
label ? /* @__PURE__ */
|
|
1535
|
+
}) => /* @__PURE__ */ jsxs14("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
|
|
1536
|
+
/* @__PURE__ */ jsx29(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
|
|
1537
|
+
label ? /* @__PURE__ */ jsx29("span", { children: label }) : null
|
|
1294
1538
|
] });
|
|
1295
1539
|
var LoadingOverlay = ({
|
|
1296
1540
|
size = "lg",
|
|
1297
1541
|
label,
|
|
1298
1542
|
className,
|
|
1299
1543
|
...props
|
|
1300
|
-
}) => /* @__PURE__ */
|
|
1544
|
+
}) => /* @__PURE__ */ jsx29(
|
|
1301
1545
|
"div",
|
|
1302
1546
|
{
|
|
1303
1547
|
className: cn("flex h-full min-h-32 w-full items-center justify-center", className),
|
|
1304
1548
|
...props,
|
|
1305
|
-
children: /* @__PURE__ */
|
|
1549
|
+
children: /* @__PURE__ */ jsx29(LoadingSpinner, { size, label })
|
|
1306
1550
|
}
|
|
1307
1551
|
);
|
|
1308
1552
|
|
|
1309
1553
|
// src/patterns/search-bar/SearchBar.tsx
|
|
1310
|
-
import { Search, X as
|
|
1311
|
-
import { jsx as
|
|
1554
|
+
import { Search, X as X5 } from "lucide-react";
|
|
1555
|
+
import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1312
1556
|
function SearchBar({
|
|
1313
1557
|
value,
|
|
1314
1558
|
onChange,
|
|
@@ -1320,9 +1564,9 @@ function SearchBar({
|
|
|
1320
1564
|
...props
|
|
1321
1565
|
}) {
|
|
1322
1566
|
const showClear = onClear !== false && value.length > 0;
|
|
1323
|
-
return /* @__PURE__ */
|
|
1324
|
-
/* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
1567
|
+
return /* @__PURE__ */ jsxs15("div", { className: cn("relative", className), children: [
|
|
1568
|
+
/* @__PURE__ */ jsx30("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: isLoading ? /* @__PURE__ */ jsx30(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx30(Search, { className: "h-4 w-4", "aria-hidden": "true" }) }),
|
|
1569
|
+
/* @__PURE__ */ jsx30(
|
|
1326
1570
|
"input",
|
|
1327
1571
|
{
|
|
1328
1572
|
ref: inputRef,
|
|
@@ -1341,14 +1585,14 @@ function SearchBar({
|
|
|
1341
1585
|
...props
|
|
1342
1586
|
}
|
|
1343
1587
|
),
|
|
1344
|
-
showClear && /* @__PURE__ */
|
|
1588
|
+
showClear && /* @__PURE__ */ jsx30(
|
|
1345
1589
|
"button",
|
|
1346
1590
|
{
|
|
1347
1591
|
type: "button",
|
|
1348
1592
|
onClick: () => onClear ? onClear() : onChange(""),
|
|
1349
1593
|
"aria-label": clearLabel,
|
|
1350
1594
|
className: "absolute top-1/2 right-2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
1351
|
-
children: /* @__PURE__ */
|
|
1595
|
+
children: /* @__PURE__ */ jsx30(X5, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1352
1596
|
}
|
|
1353
1597
|
)
|
|
1354
1598
|
] });
|
|
@@ -1363,7 +1607,7 @@ function useSidebarCollapsed() {
|
|
|
1363
1607
|
}
|
|
1364
1608
|
|
|
1365
1609
|
// src/patterns/sidebar/NavItem.tsx
|
|
1366
|
-
import { jsx as
|
|
1610
|
+
import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1367
1611
|
function NavItem({
|
|
1368
1612
|
as,
|
|
1369
1613
|
icon: Icon2,
|
|
@@ -1375,7 +1619,7 @@ function NavItem({
|
|
|
1375
1619
|
}) {
|
|
1376
1620
|
const collapsed = useSidebarCollapsed();
|
|
1377
1621
|
const Comp = as ?? "a";
|
|
1378
|
-
const row = /* @__PURE__ */
|
|
1622
|
+
const row = /* @__PURE__ */ jsxs16(
|
|
1379
1623
|
Comp,
|
|
1380
1624
|
{
|
|
1381
1625
|
className: cn(
|
|
@@ -1387,7 +1631,7 @@ function NavItem({
|
|
|
1387
1631
|
),
|
|
1388
1632
|
...rest,
|
|
1389
1633
|
children: [
|
|
1390
|
-
Icon2 && /* @__PURE__ */
|
|
1634
|
+
Icon2 && /* @__PURE__ */ jsx31(
|
|
1391
1635
|
Icon2,
|
|
1392
1636
|
{
|
|
1393
1637
|
className: cn("h-[18px] w-[18px] shrink-0", active ? "text-nav-primary" : "text-muted-foreground"),
|
|
@@ -1395,7 +1639,7 @@ function NavItem({
|
|
|
1395
1639
|
"aria-hidden": true
|
|
1396
1640
|
}
|
|
1397
1641
|
),
|
|
1398
|
-
/* @__PURE__ */
|
|
1642
|
+
/* @__PURE__ */ jsx31(
|
|
1399
1643
|
"span",
|
|
1400
1644
|
{
|
|
1401
1645
|
className: cn(
|
|
@@ -1410,9 +1654,9 @@ function NavItem({
|
|
|
1410
1654
|
}
|
|
1411
1655
|
);
|
|
1412
1656
|
if (!collapsed) return row;
|
|
1413
|
-
return /* @__PURE__ */
|
|
1414
|
-
/* @__PURE__ */
|
|
1415
|
-
/* @__PURE__ */
|
|
1657
|
+
return /* @__PURE__ */ jsxs16(Tooltip, { children: [
|
|
1658
|
+
/* @__PURE__ */ jsx31(TooltipTrigger, { asChild: true, children: row }),
|
|
1659
|
+
/* @__PURE__ */ jsxs16(TooltipContent, { side: "right", className: "flex items-center gap-1.5", children: [
|
|
1416
1660
|
label,
|
|
1417
1661
|
badge
|
|
1418
1662
|
] })
|
|
@@ -1421,8 +1665,8 @@ function NavItem({
|
|
|
1421
1665
|
|
|
1422
1666
|
// src/patterns/sidebar/NavGroup.tsx
|
|
1423
1667
|
import { ChevronDown as ChevronDown2 } from "lucide-react";
|
|
1424
|
-
import { useState as
|
|
1425
|
-
import { jsx as
|
|
1668
|
+
import { useState as useState5 } from "react";
|
|
1669
|
+
import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1426
1670
|
function NavGroup({
|
|
1427
1671
|
label,
|
|
1428
1672
|
defaultCollapsed = false,
|
|
@@ -1431,15 +1675,15 @@ function NavGroup({
|
|
|
1431
1675
|
children,
|
|
1432
1676
|
className
|
|
1433
1677
|
}) {
|
|
1434
|
-
const [internalCollapsed, setInternalCollapsed] =
|
|
1678
|
+
const [internalCollapsed, setInternalCollapsed] = useState5(defaultCollapsed);
|
|
1435
1679
|
const groupCollapsed = collapsed ?? internalCollapsed;
|
|
1436
1680
|
const railCollapsed = useSidebarCollapsed();
|
|
1437
1681
|
const setGroupCollapsed = (next) => {
|
|
1438
1682
|
onCollapsedChange?.(next);
|
|
1439
1683
|
if (collapsed === void 0) setInternalCollapsed(next);
|
|
1440
1684
|
};
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
label && (railCollapsed ? /* @__PURE__ */
|
|
1685
|
+
return /* @__PURE__ */ jsxs17("div", { className: cn("flex flex-col gap-0.5", className), children: [
|
|
1686
|
+
label && (railCollapsed ? /* @__PURE__ */ jsx32("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs17(
|
|
1443
1687
|
"button",
|
|
1444
1688
|
{
|
|
1445
1689
|
type: "button",
|
|
@@ -1447,8 +1691,8 @@ function NavGroup({
|
|
|
1447
1691
|
"aria-expanded": !groupCollapsed,
|
|
1448
1692
|
className: "group flex w-full items-center justify-between px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground/80 hover:text-foreground",
|
|
1449
1693
|
children: [
|
|
1450
|
-
/* @__PURE__ */
|
|
1451
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsx32("span", { children: label }),
|
|
1695
|
+
/* @__PURE__ */ jsx32(
|
|
1452
1696
|
ChevronDown2,
|
|
1453
1697
|
{
|
|
1454
1698
|
className: cn(
|
|
@@ -1461,7 +1705,7 @@ function NavGroup({
|
|
|
1461
1705
|
]
|
|
1462
1706
|
}
|
|
1463
1707
|
)),
|
|
1464
|
-
!groupCollapsed && /* @__PURE__ */
|
|
1708
|
+
!groupCollapsed && /* @__PURE__ */ jsx32("div", { className: "flex flex-col gap-0.5", children })
|
|
1465
1709
|
] });
|
|
1466
1710
|
}
|
|
1467
1711
|
|
|
@@ -1469,11 +1713,11 @@ function NavGroup({
|
|
|
1469
1713
|
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
1470
1714
|
import {
|
|
1471
1715
|
useCallback,
|
|
1472
|
-
useEffect as
|
|
1473
|
-
useRef as
|
|
1474
|
-
useState as
|
|
1716
|
+
useEffect as useEffect3,
|
|
1717
|
+
useRef as useRef3,
|
|
1718
|
+
useState as useState6
|
|
1475
1719
|
} from "react";
|
|
1476
|
-
import { jsx as
|
|
1720
|
+
import { jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1477
1721
|
function resolve(value, collapsed) {
|
|
1478
1722
|
return typeof value === "function" ? value(collapsed) : value;
|
|
1479
1723
|
}
|
|
@@ -1492,9 +1736,9 @@ function Sidebar({
|
|
|
1492
1736
|
children,
|
|
1493
1737
|
className
|
|
1494
1738
|
}) {
|
|
1495
|
-
const [internalWidth, setInternalWidth] =
|
|
1739
|
+
const [internalWidth, setInternalWidth] = useState6(defaultWidth);
|
|
1496
1740
|
const currentWidth = width ?? internalWidth;
|
|
1497
|
-
const [isDragging, setIsDragging] =
|
|
1741
|
+
const [isDragging, setIsDragging] = useState6(false);
|
|
1498
1742
|
const setWidth = useCallback(
|
|
1499
1743
|
(next) => {
|
|
1500
1744
|
const clamped = Math.min(maxWidth, Math.max(minWidth, next));
|
|
@@ -1503,7 +1747,7 @@ function Sidebar({
|
|
|
1503
1747
|
},
|
|
1504
1748
|
[maxWidth, minWidth, onWidthChange, width]
|
|
1505
1749
|
);
|
|
1506
|
-
const dragStartRef =
|
|
1750
|
+
const dragStartRef = useRef3(null);
|
|
1507
1751
|
const handlePointerDown = (event) => {
|
|
1508
1752
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1509
1753
|
dragStartRef.current = { x: event.clientX, width: currentWidth };
|
|
@@ -1520,7 +1764,7 @@ function Sidebar({
|
|
|
1520
1764
|
dragStartRef.current = null;
|
|
1521
1765
|
setIsDragging(false);
|
|
1522
1766
|
};
|
|
1523
|
-
return /* @__PURE__ */
|
|
1767
|
+
return /* @__PURE__ */ jsx33(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx33(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs18(
|
|
1524
1768
|
"aside",
|
|
1525
1769
|
{
|
|
1526
1770
|
style: { width: collapsed ? railWidth : currentWidth },
|
|
@@ -1530,22 +1774,22 @@ function Sidebar({
|
|
|
1530
1774
|
className
|
|
1531
1775
|
),
|
|
1532
1776
|
children: [
|
|
1533
|
-
(header || onCollapsedChange) && /* @__PURE__ */
|
|
1534
|
-
header && /* @__PURE__ */
|
|
1535
|
-
onCollapsedChange && /* @__PURE__ */
|
|
1777
|
+
(header || onCollapsedChange) && /* @__PURE__ */ jsxs18("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: [
|
|
1778
|
+
header && /* @__PURE__ */ jsx33("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
|
|
1779
|
+
onCollapsedChange && /* @__PURE__ */ jsx33(
|
|
1536
1780
|
"button",
|
|
1537
1781
|
{
|
|
1538
1782
|
type: "button",
|
|
1539
1783
|
onClick: () => onCollapsedChange(!collapsed),
|
|
1540
1784
|
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1541
1785
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1542
|
-
children: collapsed ? /* @__PURE__ */
|
|
1786
|
+
children: collapsed ? /* @__PURE__ */ jsx33(PanelLeftOpen, { className: "h-4 w-4", "aria-hidden": true }) : /* @__PURE__ */ jsx33(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
|
|
1543
1787
|
}
|
|
1544
1788
|
)
|
|
1545
1789
|
] }),
|
|
1546
|
-
/* @__PURE__ */
|
|
1547
|
-
footer && /* @__PURE__ */
|
|
1548
|
-
resizable && !collapsed && /* @__PURE__ */
|
|
1790
|
+
/* @__PURE__ */ jsx33(SidebarNav, { children }),
|
|
1791
|
+
footer && /* @__PURE__ */ jsx33("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
|
|
1792
|
+
resizable && !collapsed && /* @__PURE__ */ jsx33(
|
|
1549
1793
|
"div",
|
|
1550
1794
|
{
|
|
1551
1795
|
onPointerDown: handlePointerDown,
|
|
@@ -1560,9 +1804,9 @@ function Sidebar({
|
|
|
1560
1804
|
) }) });
|
|
1561
1805
|
}
|
|
1562
1806
|
function SidebarNav({ children, className, ...rest }) {
|
|
1563
|
-
const ref =
|
|
1564
|
-
const [showFade, setShowFade] =
|
|
1565
|
-
|
|
1807
|
+
const ref = useRef3(null);
|
|
1808
|
+
const [showFade, setShowFade] = useState6(false);
|
|
1809
|
+
useEffect3(() => {
|
|
1566
1810
|
const el = ref.current;
|
|
1567
1811
|
if (!el) return;
|
|
1568
1812
|
const update = () => setShowFade(el.scrollHeight - el.scrollTop - el.clientHeight > 1);
|
|
@@ -1575,8 +1819,8 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
1575
1819
|
observer.disconnect();
|
|
1576
1820
|
};
|
|
1577
1821
|
}, []);
|
|
1578
|
-
return /* @__PURE__ */
|
|
1579
|
-
/* @__PURE__ */
|
|
1822
|
+
return /* @__PURE__ */ jsxs18("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
|
|
1823
|
+
/* @__PURE__ */ jsx33(
|
|
1580
1824
|
"div",
|
|
1581
1825
|
{
|
|
1582
1826
|
ref,
|
|
@@ -1585,7 +1829,7 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
1585
1829
|
children
|
|
1586
1830
|
}
|
|
1587
1831
|
),
|
|
1588
|
-
showFade && /* @__PURE__ */
|
|
1832
|
+
showFade && /* @__PURE__ */ jsx33(
|
|
1589
1833
|
"div",
|
|
1590
1834
|
{
|
|
1591
1835
|
className: "pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-gradient-to-t from-sidebar to-transparent",
|
|
@@ -1679,6 +1923,7 @@ export {
|
|
|
1679
1923
|
TabsContent,
|
|
1680
1924
|
TabsList,
|
|
1681
1925
|
TabsTrigger,
|
|
1926
|
+
TagCombobox,
|
|
1682
1927
|
Textarea,
|
|
1683
1928
|
Tooltip,
|
|
1684
1929
|
TooltipContent,
|