@kjaniec-dev/ui 0.7.1 → 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.cjs CHANGED
@@ -540,6 +540,7 @@ function useTabs() {
540
540
  }
541
541
  function Tabs({ value, defaultValue, onValueChange, className, children, ...props }) {
542
542
  const [internal, setInternal] = React18__namespace.useState(defaultValue ?? "");
543
+ const uid = React18__namespace.useId();
543
544
  const current = value ?? internal;
544
545
  const setValue = React18__namespace.useCallback(
545
546
  (v) => {
@@ -548,7 +549,7 @@ function Tabs({ value, defaultValue, onValueChange, className, children, ...prop
548
549
  },
549
550
  [value, onValueChange]
550
551
  );
551
- return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: { value: current, setValue }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className, ...props, children }) });
552
+ return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: { value: current, setValue, uid }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className, ...props, children }) });
552
553
  }
553
554
  var TabsList = React18__namespace.forwardRef(
554
555
  ({ className, onKeyDown, ...props }, ref) => {
@@ -599,7 +600,7 @@ var TabsList = React18__namespace.forwardRef(
599
600
  TabsList.displayName = "TabsList";
600
601
  var TabsTrigger = React18__namespace.forwardRef(
601
602
  ({ className, value, children, ...props }, ref) => {
602
- const { value: current, setValue } = useTabs();
603
+ const { value: current, setValue, uid } = useTabs();
603
604
  const active = current === value;
604
605
  return /* @__PURE__ */ jsxRuntime.jsx(
605
606
  "button",
@@ -607,8 +608,8 @@ var TabsTrigger = React18__namespace.forwardRef(
607
608
  ref,
608
609
  type: "button",
609
610
  role: "tab",
610
- id: `tabs-trigger-${value}`,
611
- "aria-controls": `tabs-panel-${value}`,
611
+ id: `tabs-trigger-${uid}-${value}`,
612
+ "aria-controls": `tabs-panel-${uid}-${value}`,
612
613
  "aria-selected": active,
613
614
  tabIndex: active ? 0 : -1,
614
615
  onClick: () => setValue(value),
@@ -628,7 +629,7 @@ var TabsTrigger = React18__namespace.forwardRef(
628
629
  TabsTrigger.displayName = "TabsTrigger";
629
630
  var TabsContent = React18__namespace.forwardRef(
630
631
  ({ className, value, ...props }, ref) => {
631
- const { value: current } = useTabs();
632
+ const { value: current, uid } = useTabs();
632
633
  const active = current === value;
633
634
  if (!active) return null;
634
635
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -636,8 +637,8 @@ var TabsContent = React18__namespace.forwardRef(
636
637
  {
637
638
  ref,
638
639
  role: "tabpanel",
639
- id: `tabs-panel-${value}`,
640
- "aria-labelledby": `tabs-trigger-${value}`,
640
+ id: `tabs-panel-${uid}-${value}`,
641
+ "aria-labelledby": `tabs-trigger-${uid}-${value}`,
641
642
  tabIndex: 0,
642
643
  className: cn("pt-5 text-[0.9rem] text-muted-foreground outline-none", className),
643
644
  ...props
@@ -1366,23 +1367,39 @@ function DataTable({
1366
1367
  }[col.align || "left"];
1367
1368
  const isSortable = col.sortable && onSort && col.sortKey;
1368
1369
  const isSortedActive = sortBy && col.sortKey === sortBy;
1370
+ const ariaSort = isSortable ? isSortedActive ? sortDirection === "asc" ? "ascending" : "descending" : "none" : void 0;
1369
1371
  return /* @__PURE__ */ jsxRuntime.jsx(
1370
1372
  TableHead,
1371
1373
  {
1372
1374
  className: cn(
1373
1375
  alignClass,
1374
- isSortable && "cursor-pointer select-none hover:bg-muted/30 transition-colors",
1376
+ isSortable && "p-0 hover:bg-muted/30 transition-colors",
1375
1377
  col.className
1376
1378
  ),
1377
- onClick: () => {
1378
- if (!isSortable || !col.sortKey) return;
1379
- const nextDirection = isSortedActive && sortDirection === "asc" ? "desc" : "asc";
1380
- onSort(col.sortKey, nextDirection);
1381
- },
1382
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("inline-flex items-center gap-1.5", alignClass === "text-right" && "justify-end w-full"), children: [
1383
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: col.header }),
1384
- isSortable && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground shrink-0 select-none", children: isSortedActive ? sortDirection === "asc" ? "\u25B2" : "\u25BC" : "\u21C5" })
1385
- ] })
1379
+ "aria-sort": ariaSort,
1380
+ children: isSortable ? /* @__PURE__ */ jsxRuntime.jsxs(
1381
+ "button",
1382
+ {
1383
+ type: "button",
1384
+ onClick: () => {
1385
+ if (!col.sortKey) return;
1386
+ const nextDirection = isSortedActive && sortDirection === "asc" ? "desc" : "asc";
1387
+ onSort(col.sortKey, nextDirection);
1388
+ },
1389
+ className: cn(
1390
+ "w-full px-4 py-3 flex items-center gap-1.5 font-semibold text-[0.72rem] uppercase tracking-[0.05em] text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 transition-colors cursor-pointer",
1391
+ {
1392
+ "justify-start text-left": col.align === "left" || !col.align,
1393
+ "justify-center text-center": col.align === "center",
1394
+ "justify-end text-right": col.align === "right"
1395
+ }
1396
+ ),
1397
+ children: [
1398
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: col.header }),
1399
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground shrink-0 select-none", children: isSortedActive ? sortDirection === "asc" ? "\u25B2" : "\u25BC" : "\u21C5" })
1400
+ ]
1401
+ }
1402
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("inline-flex items-center gap-1.5", alignClass === "text-right" && "justify-end w-full"), children: col.header })
1386
1403
  },
1387
1404
  idx
1388
1405
  );
@@ -1895,7 +1912,7 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
1895
1912
  }, [open, filteredItems, activeIndex, onClose]);
1896
1913
  React18__namespace.useEffect(() => {
1897
1914
  if (listRef.current) {
1898
- const activeEl = listRef.current.children[activeIndex];
1915
+ const activeEl = listRef.current.querySelector('[aria-selected="true"]');
1899
1916
  if (activeEl) {
1900
1917
  activeEl.scrollIntoView({ block: "nearest" });
1901
1918
  }
@@ -1918,6 +1935,9 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
1918
1935
  "div",
1919
1936
  {
1920
1937
  ref: containerRef,
1938
+ role: "dialog",
1939
+ "aria-modal": "true",
1940
+ "aria-label": "Command palette",
1921
1941
  className: "w-[95vw] max-w-lg bg-surface border border-border rounded-kj-xl shadow-kj-lg flex flex-col overflow-hidden max-h-[500px]",
1922
1942
  children: [
1923
1943
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border-b border-border flex items-center gap-2", children: [
@@ -1926,6 +1946,11 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
1926
1946
  "input",
1927
1947
  {
1928
1948
  type: "text",
1949
+ role: "combobox",
1950
+ "aria-expanded": open,
1951
+ "aria-autocomplete": "list",
1952
+ "aria-controls": "command-palette-listbox",
1953
+ "aria-activedescendant": filteredItems.length > 0 ? `cmd-item-${activeIndex}` : void 0,
1929
1954
  placeholder,
1930
1955
  value: search,
1931
1956
  onChange: (e) => setSearch(e.target.value),
@@ -1938,44 +1963,64 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
1938
1963
  'No results found for "',
1939
1964
  search,
1940
1965
  '"'
1941
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: listRef, className: "space-y-4", children: Object.entries(groups).map(([cat, itemsInCat]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
1942
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1 text-[0.65rem] font-bold uppercase tracking-wider text-muted-foreground", children: cat }),
1943
- itemsInCat.map((item) => {
1944
- const currentIdx = absoluteItemIndex++;
1945
- const isActive = currentIdx === activeIndex;
1946
- return /* @__PURE__ */ jsxRuntime.jsxs(
1947
- "div",
1948
- {
1949
- onClick: () => {
1950
- item.action();
1951
- onClose();
1952
- },
1953
- className: cn(
1954
- "flex items-center justify-between gap-3 px-3 py-2.5 rounded-kj-md cursor-pointer select-none transition-colors",
1955
- isActive ? "bg-primary/10 text-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"
1956
- ),
1957
- children: [
1958
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
1959
- item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground [&_svg]:h-[1.1rem] [&_svg]:w-[1.1rem]", children: item.icon }),
1960
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex flex-col", children: [
1961
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[0.85rem] font-semibold", isActive ? "text-primary" : "text-foreground"), children: item.title }),
1962
- item.subtitle && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.75rem] text-muted-foreground truncate", children: item.subtitle })
1963
- ] })
1964
- ] }),
1965
- item.shortcut && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: item.shortcut.map((sc, scIdx) => /* @__PURE__ */ jsxRuntime.jsx(
1966
- "kbd",
1967
- {
1968
- className: "px-1.5 py-0.5 rounded border border-border bg-subtle text-[10px] font-mono leading-none shadow-kj-xs text-muted-foreground",
1969
- children: sc
1966
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1967
+ "div",
1968
+ {
1969
+ ref: listRef,
1970
+ id: "command-palette-listbox",
1971
+ role: "listbox",
1972
+ "aria-label": "Commands",
1973
+ className: "space-y-4",
1974
+ children: Object.entries(groups).map(([cat, itemsInCat]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-labelledby": `cmd-cat-${cat}`, className: "space-y-1", children: [
1975
+ /* @__PURE__ */ jsxRuntime.jsx(
1976
+ "div",
1977
+ {
1978
+ id: `cmd-cat-${cat}`,
1979
+ className: "px-3 py-1 text-[0.65rem] font-bold uppercase tracking-wider text-muted-foreground",
1980
+ children: cat
1981
+ }
1982
+ ),
1983
+ itemsInCat.map((item) => {
1984
+ const currentIdx = absoluteItemIndex++;
1985
+ const isActive = currentIdx === activeIndex;
1986
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1987
+ "div",
1988
+ {
1989
+ id: `cmd-item-${currentIdx}`,
1990
+ role: "option",
1991
+ "aria-selected": isActive,
1992
+ onClick: () => {
1993
+ item.action();
1994
+ onClose();
1970
1995
  },
1971
- scIdx
1972
- )) })
1973
- ]
1974
- },
1975
- item.id
1976
- );
1977
- })
1978
- ] }, cat)) }) }),
1996
+ className: cn(
1997
+ "flex items-center justify-between gap-3 px-3 py-2.5 rounded-kj-md cursor-pointer select-none transition-colors",
1998
+ isActive ? "bg-primary/10 text-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"
1999
+ ),
2000
+ children: [
2001
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
2002
+ item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground [&_svg]:h-[1.1rem] [&_svg]:w-[1.1rem]", children: item.icon }),
2003
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex flex-col", children: [
2004
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[0.85rem] font-semibold", isActive ? "text-primary" : "text-foreground"), children: item.title }),
2005
+ item.subtitle && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.75rem] text-muted-foreground truncate", children: item.subtitle })
2006
+ ] })
2007
+ ] }),
2008
+ item.shortcut && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: item.shortcut.map((sc, scIdx) => /* @__PURE__ */ jsxRuntime.jsx(
2009
+ "kbd",
2010
+ {
2011
+ className: "px-1.5 py-0.5 rounded border border-border bg-subtle text-[10px] font-mono leading-none shadow-kj-xs text-muted-foreground",
2012
+ children: sc
2013
+ },
2014
+ scIdx
2015
+ )) })
2016
+ ]
2017
+ },
2018
+ item.id
2019
+ );
2020
+ })
2021
+ ] }, cat))
2022
+ }
2023
+ ) }),
1979
2024
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2.5 border-t border-border bg-subtle flex items-center justify-between text-[11px] text-muted-foreground font-medium", children: [
1980
2025
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1981
2026
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [
@@ -2035,6 +2080,422 @@ function SidebarNav({ className, groups, currentHref, ...props }) {
2035
2080
  ] }, groupIdx)) });
2036
2081
  }
2037
2082
  SidebarNav.displayName = "SidebarNav";
2083
+ var fabVariants = classVarianceAuthority.cva(
2084
+ [
2085
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap select-none",
2086
+ "font-sans font-semibold leading-none",
2087
+ "border border-transparent rounded-full cursor-pointer shadow-lg",
2088
+ "transition-[background-color,color,border-color,box-shadow,transform] duration-150",
2089
+ "active:translate-y-px active:shadow-md",
2090
+ "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/45",
2091
+ "disabled:opacity-50 disabled:pointer-events-none"
2092
+ ],
2093
+ {
2094
+ variants: {
2095
+ variant: {
2096
+ primary: "bg-primary text-primary-foreground hover:bg-primary-hover",
2097
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover",
2098
+ outline: "bg-transparent text-foreground border-border hover:bg-muted hover:border-muted-foreground",
2099
+ danger: "bg-danger text-white hover:brightness-95"
2100
+ },
2101
+ size: {
2102
+ sm: "h-10 w-10 p-0 text-sm",
2103
+ md: "h-14 w-14 p-0 text-base",
2104
+ lg: "h-16 w-16 p-0 text-lg"
2105
+ },
2106
+ position: {
2107
+ "bottom-right": "fixed bottom-6 right-6 z-50",
2108
+ "bottom-left": "fixed bottom-6 left-6 z-50",
2109
+ "bottom-center": "fixed bottom-6 left-1/2 -translate-x-1/2 z-50",
2110
+ none: "relative"
2111
+ }
2112
+ },
2113
+ defaultVariants: { variant: "primary", size: "md", position: "bottom-right" }
2114
+ }
2115
+ );
2116
+ var Fab = React18__namespace.forwardRef(
2117
+ ({ className, variant, size, position, mobileOnly, icon, label, loading, disabled, ...props }, ref) => {
2118
+ return /* @__PURE__ */ jsxRuntime.jsx(
2119
+ "button",
2120
+ {
2121
+ ref,
2122
+ className: cn(
2123
+ fabVariants({ variant, size, position }),
2124
+ mobileOnly && "md:hidden",
2125
+ className
2126
+ ),
2127
+ ...props,
2128
+ disabled: disabled || loading,
2129
+ "aria-label": label,
2130
+ "aria-busy": loading || void 0,
2131
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx(
2132
+ "span",
2133
+ {
2134
+ "aria-hidden": true,
2135
+ className: "h-[1.2em] w-[1.2em] rounded-full border-2 border-current border-t-transparent animate-spin"
2136
+ }
2137
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "[&_svg]:h-[1.35em] [&_svg]:w-[1.35em] flex items-center justify-center", children: icon })
2138
+ }
2139
+ );
2140
+ }
2141
+ );
2142
+ Fab.displayName = "Fab";
2143
+ var BottomSheetContext = React18__namespace.createContext(null);
2144
+ function BottomSheet({
2145
+ open,
2146
+ onClose,
2147
+ children,
2148
+ maxWidth = "max-w-lg",
2149
+ className,
2150
+ showClose = true
2151
+ }) {
2152
+ const titleId = React18__namespace.useId();
2153
+ const descId = React18__namespace.useId();
2154
+ const containerRef = React18__namespace.useRef(null);
2155
+ const lastActiveElement = React18__namespace.useRef(null);
2156
+ const [mounted, setMounted] = React18__namespace.useState(open);
2157
+ React18__namespace.useEffect(() => {
2158
+ if (open) {
2159
+ setMounted(true);
2160
+ } else {
2161
+ const timer = setTimeout(() => setMounted(false), 200);
2162
+ return () => clearTimeout(timer);
2163
+ }
2164
+ }, [open]);
2165
+ React18__namespace.useEffect(() => {
2166
+ if (!open) return;
2167
+ lastActiveElement.current = document.activeElement;
2168
+ const container = containerRef.current;
2169
+ if (container) {
2170
+ const focusables = container.querySelectorAll(
2171
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
2172
+ );
2173
+ if (focusables.length > 0) {
2174
+ focusables[0].focus();
2175
+ setTimeout(() => {
2176
+ if (container.contains(document.activeElement)) return;
2177
+ focusables[0].focus();
2178
+ }, 50);
2179
+ } else {
2180
+ container.focus();
2181
+ }
2182
+ }
2183
+ const handleKeyDown = (e) => {
2184
+ if (e.key === "Escape") {
2185
+ onClose();
2186
+ return;
2187
+ }
2188
+ if (e.key === "Tab") {
2189
+ const container2 = containerRef.current;
2190
+ if (!container2) return;
2191
+ const focusables = Array.from(
2192
+ container2.querySelectorAll(
2193
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
2194
+ )
2195
+ );
2196
+ if (focusables.length === 0) {
2197
+ e.preventDefault();
2198
+ return;
2199
+ }
2200
+ const first = focusables[0];
2201
+ const last = focusables[focusables.length - 1];
2202
+ if (!container2.contains(document.activeElement)) {
2203
+ e.preventDefault();
2204
+ if (e.shiftKey) {
2205
+ last.focus();
2206
+ } else {
2207
+ first.focus();
2208
+ }
2209
+ return;
2210
+ }
2211
+ if (e.shiftKey) {
2212
+ if (document.activeElement === first || document.activeElement === container2) {
2213
+ last.focus();
2214
+ e.preventDefault();
2215
+ }
2216
+ } else {
2217
+ if (document.activeElement === last) {
2218
+ first.focus();
2219
+ e.preventDefault();
2220
+ }
2221
+ }
2222
+ }
2223
+ };
2224
+ window.addEventListener("keydown", handleKeyDown);
2225
+ document.body.style.overflow = "hidden";
2226
+ return () => {
2227
+ window.removeEventListener("keydown", handleKeyDown);
2228
+ document.body.style.overflow = "";
2229
+ if (lastActiveElement.current) {
2230
+ lastActiveElement.current.focus();
2231
+ lastActiveElement.current = null;
2232
+ }
2233
+ };
2234
+ }, [open, onClose]);
2235
+ if (!open && !mounted) return null;
2236
+ return /* @__PURE__ */ jsxRuntime.jsx(BottomSheetContext.Provider, { value: { titleId, descId }, children: /* @__PURE__ */ jsxRuntime.jsx(
2237
+ "div",
2238
+ {
2239
+ role: "presentation",
2240
+ onClick: (e) => e.target === e.currentTarget && onClose(),
2241
+ className: cn(
2242
+ "fixed inset-0 z-[100] grid items-end sm:place-items-center p-0 sm:p-6 backdrop-blur-[3px] transition-all duration-200",
2243
+ "bg-[color-mix(in_oklch,#09090b_55%,transparent)]",
2244
+ open ? "opacity-100 pointer-events-auto visible" : "opacity-0 pointer-events-none invisible"
2245
+ ),
2246
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2247
+ "div",
2248
+ {
2249
+ ref: containerRef,
2250
+ role: "dialog",
2251
+ "aria-modal": "true",
2252
+ "aria-labelledby": titleId,
2253
+ "aria-describedby": descId,
2254
+ tabIndex: -1,
2255
+ className: cn(
2256
+ "relative w-full bg-surface border-t sm:border border-border shadow-kj-lg transition-all duration-200 outline-none",
2257
+ "rounded-t-kj-2xl sm:rounded-kj-2xl flex flex-col max-h-[85vh] sm:max-h-[90vh]",
2258
+ // Mobile sliding & Desktop scale/opacity transitions
2259
+ open ? "translate-y-0 sm:scale-100 sm:opacity-100" : "translate-y-full sm:scale-95 sm:opacity-0",
2260
+ // Mobile styling vs Desktop styling
2261
+ "bottom-0 sm:bottom-auto",
2262
+ maxWidth,
2263
+ className
2264
+ ),
2265
+ children: [
2266
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2 sm:hidden cursor-grab", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-1.5 rounded-full bg-muted-foreground/20" }) }),
2267
+ showClose && /* @__PURE__ */ jsxRuntime.jsx(
2268
+ "button",
2269
+ {
2270
+ type: "button",
2271
+ onClick: onClose,
2272
+ "aria-label": "Close",
2273
+ className: "absolute top-4 right-4 p-1.5 rounded-kj-sm text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer z-10",
2274
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
2275
+ }
2276
+ ),
2277
+ children
2278
+ ]
2279
+ }
2280
+ )
2281
+ }
2282
+ ) });
2283
+ }
2284
+ function BottomSheetHeader({ className, ...props }) {
2285
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("p-6 flex flex-col gap-1.5 border-b border-border", className), ...props });
2286
+ }
2287
+ function BottomSheetTitle({ className, id, ...props }) {
2288
+ const ctx = React18__namespace.useContext(BottomSheetContext);
2289
+ return /* @__PURE__ */ jsxRuntime.jsx("h2", { id: id ?? ctx?.titleId, className: cn("m-0 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
2290
+ }
2291
+ function BottomSheetDescription({ className, id, ...props }) {
2292
+ const ctx = React18__namespace.useContext(BottomSheetContext);
2293
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { id: id ?? ctx?.descId, className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
2294
+ }
2295
+ function BottomSheetContent({ className, ...props }) {
2296
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("p-6 overflow-y-auto flex-1", className), ...props });
2297
+ }
2298
+ function BottomSheetFooter({ className, ...props }) {
2299
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("p-6 border-t border-border flex gap-2.5 justify-end mt-auto", className), ...props });
2300
+ }
2301
+ var kbdVariants = classVarianceAuthority.cva(
2302
+ "inline-flex items-center justify-center font-mono font-medium text-muted-foreground bg-muted border border-border border-b-2 rounded-kj-sm select-none",
2303
+ {
2304
+ variants: {
2305
+ size: {
2306
+ sm: "h-5 min-w-5 px-1 text-[0.68rem]",
2307
+ md: "h-6 min-w-6 px-1.5 text-[0.78rem]"
2308
+ }
2309
+ },
2310
+ defaultVariants: { size: "sm" }
2311
+ }
2312
+ );
2313
+ var Kbd = React18__namespace.forwardRef(
2314
+ ({ className, size, keys, children, ...props }, ref) => {
2315
+ if (keys && keys.length > 0) {
2316
+ return /* @__PURE__ */ jsxRuntime.jsx(
2317
+ "span",
2318
+ {
2319
+ ref,
2320
+ className: cn("inline-flex items-center gap-1", className),
2321
+ ...props,
2322
+ children: keys.map((k, i) => /* @__PURE__ */ jsxRuntime.jsx("kbd", { className: kbdVariants({ size }), children: k }, i))
2323
+ }
2324
+ );
2325
+ }
2326
+ return /* @__PURE__ */ jsxRuntime.jsx("kbd", { ref, className: cn(kbdVariants({ size }), className), ...props, children });
2327
+ }
2328
+ );
2329
+ Kbd.displayName = "Kbd";
2330
+ var CodeBlock = React18__namespace.forwardRef(
2331
+ ({ className, code, language, filename, copyable = true, maxHeight, ...props }, ref) => {
2332
+ const [copied, setCopied] = React18__namespace.useState(false);
2333
+ const canCopy = copyable && typeof navigator !== "undefined" && !!navigator.clipboard;
2334
+ const onCopy = () => {
2335
+ navigator.clipboard.writeText(code).then(
2336
+ () => {
2337
+ setCopied(true);
2338
+ window.setTimeout(() => setCopied(false), 1500);
2339
+ },
2340
+ () => {
2341
+ }
2342
+ );
2343
+ };
2344
+ const label = filename ?? language;
2345
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2346
+ "div",
2347
+ {
2348
+ ref,
2349
+ className: cn("rounded-kj-md border border-border bg-muted/50 overflow-hidden", className),
2350
+ ...props,
2351
+ children: [
2352
+ (label || canCopy) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 px-3.5 py-2 border-b border-border", children: [
2353
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.72rem] font-mono text-muted-foreground", children: label }),
2354
+ canCopy && /* @__PURE__ */ jsxRuntime.jsx(
2355
+ "button",
2356
+ {
2357
+ type: "button",
2358
+ onClick: onCopy,
2359
+ className: "text-[0.72rem] font-medium text-muted-foreground hover:text-foreground transition-colors cursor-pointer bg-transparent border-0 p-0",
2360
+ children: copied ? "Copied" : "Copy"
2361
+ }
2362
+ )
2363
+ ] }),
2364
+ /* @__PURE__ */ jsxRuntime.jsx(
2365
+ "pre",
2366
+ {
2367
+ className: "m-0 px-3.5 py-3 overflow-x-auto text-[0.8rem] leading-relaxed font-mono text-foreground",
2368
+ style: maxHeight ? { maxHeight, overflowY: "auto" } : void 0,
2369
+ children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code })
2370
+ }
2371
+ )
2372
+ ]
2373
+ }
2374
+ );
2375
+ }
2376
+ );
2377
+ CodeBlock.displayName = "CodeBlock";
2378
+ var PopoverContext = React18__namespace.createContext(null);
2379
+ function usePopoverContext(part) {
2380
+ const ctx = React18__namespace.useContext(PopoverContext);
2381
+ if (!ctx) throw new Error(`${part} must be used inside <Popover>`);
2382
+ return ctx;
2383
+ }
2384
+ function Popover({ children, className, open: openProp, onOpenChange }) {
2385
+ const [openState, setOpenState] = React18__namespace.useState(false);
2386
+ const controlled = openProp !== void 0;
2387
+ const open = controlled ? openProp : openState;
2388
+ const triggerRef = React18__namespace.useRef(null);
2389
+ const ref = React18__namespace.useRef(null);
2390
+ const setOpen = React18__namespace.useCallback(
2391
+ (v) => {
2392
+ if (!controlled) setOpenState(v);
2393
+ onOpenChange?.(v);
2394
+ },
2395
+ [controlled, onOpenChange]
2396
+ );
2397
+ React18__namespace.useEffect(() => {
2398
+ if (!open) return;
2399
+ const onDoc = (e) => {
2400
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
2401
+ };
2402
+ const onEsc = (e) => {
2403
+ if (e.key === "Escape") {
2404
+ setOpen(false);
2405
+ triggerRef.current?.focus();
2406
+ }
2407
+ };
2408
+ document.addEventListener("mousedown", onDoc);
2409
+ document.addEventListener("keydown", onEsc);
2410
+ return () => {
2411
+ document.removeEventListener("mousedown", onDoc);
2412
+ document.removeEventListener("keydown", onEsc);
2413
+ };
2414
+ }, [open, setOpen]);
2415
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverContext.Provider, { value: { open, setOpen, triggerRef }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block", className), children }) });
2416
+ }
2417
+ var PopoverTrigger = React18__namespace.forwardRef(({ onClick, asChild, ...props }, ref) => {
2418
+ const ctx = usePopoverContext("PopoverTrigger");
2419
+ const setRefs = (node) => {
2420
+ ctx.triggerRef.current = node;
2421
+ if (typeof ref === "function") ref(node);
2422
+ else if (ref) ref.current = node;
2423
+ };
2424
+ if (asChild) {
2425
+ const child = React18__namespace.Children.only(props.children);
2426
+ return React18__namespace.cloneElement(child, {
2427
+ ref: setRefs,
2428
+ ...props,
2429
+ ...child.props,
2430
+ onClick: (e) => {
2431
+ ctx.setOpen(!ctx.open);
2432
+ onClick?.(e);
2433
+ child.props.onClick?.(e);
2434
+ },
2435
+ "aria-haspopup": "dialog",
2436
+ "aria-expanded": ctx.open
2437
+ });
2438
+ }
2439
+ return /* @__PURE__ */ jsxRuntime.jsx(
2440
+ "button",
2441
+ {
2442
+ ref: setRefs,
2443
+ ...props,
2444
+ type: "button",
2445
+ "aria-haspopup": "dialog",
2446
+ "aria-expanded": ctx.open,
2447
+ onClick: (e) => {
2448
+ ctx.setOpen(!ctx.open);
2449
+ onClick?.(e);
2450
+ }
2451
+ }
2452
+ );
2453
+ });
2454
+ PopoverTrigger.displayName = "PopoverTrigger";
2455
+ var sideAlignClasses = {
2456
+ "bottom-start": "top-[calc(100%+6px)] left-0",
2457
+ "bottom-center": "top-[calc(100%+6px)] left-1/2 -translate-x-1/2",
2458
+ "bottom-end": "top-[calc(100%+6px)] right-0",
2459
+ "top-start": "bottom-[calc(100%+6px)] left-0",
2460
+ "top-center": "bottom-[calc(100%+6px)] left-1/2 -translate-x-1/2",
2461
+ "top-end": "bottom-[calc(100%+6px)] right-0",
2462
+ "left-start": "right-[calc(100%+6px)] top-0",
2463
+ "left-center": "right-[calc(100%+6px)] top-1/2 -translate-y-1/2",
2464
+ "left-end": "right-[calc(100%+6px)] bottom-0",
2465
+ "right-start": "left-[calc(100%+6px)] top-0",
2466
+ "right-center": "left-[calc(100%+6px)] top-1/2 -translate-y-1/2",
2467
+ "right-end": "left-[calc(100%+6px)] bottom-0"
2468
+ };
2469
+ function PopoverContent({
2470
+ className,
2471
+ side = "bottom",
2472
+ align = "center",
2473
+ children,
2474
+ ...props
2475
+ }) {
2476
+ const ctx = usePopoverContext("PopoverContent");
2477
+ const ref = React18__namespace.useRef(null);
2478
+ React18__namespace.useEffect(() => {
2479
+ if (ctx.open) ref.current?.focus();
2480
+ }, [ctx.open]);
2481
+ if (!ctx.open) return null;
2482
+ return /* @__PURE__ */ jsxRuntime.jsx(
2483
+ "div",
2484
+ {
2485
+ ref,
2486
+ role: "dialog",
2487
+ tabIndex: -1,
2488
+ className: cn(
2489
+ "absolute z-40 min-w-[200px] p-4 rounded-kj-md bg-surface border border-border shadow-kj-lg outline-none",
2490
+ "animate-[kjpop_.12s_ease]",
2491
+ sideAlignClasses[`${side}-${align}`],
2492
+ className
2493
+ ),
2494
+ ...props,
2495
+ children
2496
+ }
2497
+ );
2498
+ }
2038
2499
 
2039
2500
  exports.Accordion = Accordion;
2040
2501
  exports.AccordionContent = AccordionContent;
@@ -2045,6 +2506,12 @@ exports.Avatar = Avatar;
2045
2506
  exports.AvatarGroup = AvatarGroup;
2046
2507
  exports.Badge = Badge;
2047
2508
  exports.BottomNavigation = BottomNavigation;
2509
+ exports.BottomSheet = BottomSheet;
2510
+ exports.BottomSheetContent = BottomSheetContent;
2511
+ exports.BottomSheetDescription = BottomSheetDescription;
2512
+ exports.BottomSheetFooter = BottomSheetFooter;
2513
+ exports.BottomSheetHeader = BottomSheetHeader;
2514
+ exports.BottomSheetTitle = BottomSheetTitle;
2048
2515
  exports.Breadcrumb = Breadcrumb;
2049
2516
  exports.BreadcrumbItem = BreadcrumbItem;
2050
2517
  exports.BreadcrumbSeparator = BreadcrumbSeparator;
@@ -2057,6 +2524,7 @@ exports.CardHeader = CardHeader;
2057
2524
  exports.CardTitle = CardTitle;
2058
2525
  exports.Checkbox = Checkbox;
2059
2526
  exports.CheckboxField = CheckboxField;
2527
+ exports.CodeBlock = CodeBlock;
2060
2528
  exports.CommandPalette = CommandPalette;
2061
2529
  exports.ConfirmDialog = ConfirmDialog;
2062
2530
  exports.DashboardShell = DashboardShell;
@@ -2070,10 +2538,12 @@ exports.DropdownMenuSeparator = DropdownMenuSeparator;
2070
2538
  exports.DropdownMenuTrigger = DropdownMenuTrigger;
2071
2539
  exports.EmptyState = EmptyState;
2072
2540
  exports.ErrorState = ErrorState;
2541
+ exports.Fab = Fab;
2073
2542
  exports.Field = Field;
2074
2543
  exports.FormField = FormField;
2075
2544
  exports.Hint = Hint;
2076
2545
  exports.Input = Input;
2546
+ exports.Kbd = Kbd;
2077
2547
  exports.Label = Label;
2078
2548
  exports.MetricCard = MetricCard;
2079
2549
  exports.Modal = Modal;
@@ -2082,6 +2552,9 @@ exports.ModalDescription = ModalDescription;
2082
2552
  exports.ModalTitle = ModalTitle;
2083
2553
  exports.PageHeader = PageHeader;
2084
2554
  exports.Pagination = Pagination;
2555
+ exports.Popover = Popover;
2556
+ exports.PopoverContent = PopoverContent;
2557
+ exports.PopoverTrigger = PopoverTrigger;
2085
2558
  exports.Progress = Progress;
2086
2559
  exports.Radio = Radio;
2087
2560
  exports.Segmented = Segmented;