@memelabui/ui 0.6.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.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useState, useId, useMemo, useRef, useEffect, createContext, useCallback, isValidElement, cloneElement, useReducer, useContext } from 'react';
1
+ import React, { forwardRef, useState, useId, useMemo, useRef, useEffect, createContext, useCallback, isValidElement, cloneElement, useReducer, useContext, Component } from 'react';
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { createPortal } from 'react-dom';
4
4
 
@@ -133,10 +133,12 @@ function matchModifiers(e, mods) {
133
133
  }
134
134
  function useHotkeys(bindings, options = {}) {
135
135
  const { enabled = true } = options;
136
+ const bindingsRef = useRef(bindings);
137
+ bindingsRef.current = bindings;
136
138
  useEffect(() => {
137
139
  if (!enabled) return;
138
140
  const onKeyDown = (e) => {
139
- for (const binding of bindings) {
141
+ for (const binding of bindingsRef.current) {
140
142
  if (e.key === binding.key && matchModifiers(e, binding.modifiers)) {
141
143
  binding.handler(e);
142
144
  }
@@ -144,7 +146,7 @@ function useHotkeys(bindings, options = {}) {
144
146
  };
145
147
  document.addEventListener("keydown", onKeyDown);
146
148
  return () => document.removeEventListener("keydown", onKeyDown);
147
- }, [enabled, ...bindings]);
149
+ }, [enabled]);
148
150
  }
149
151
  function useIntersectionObserver(options = {}) {
150
152
  const { root = null, rootMargin = "0px", threshold = 0, enabled = true } = options;
@@ -200,6 +202,30 @@ function useSharedNow(options = {}) {
200
202
  }, [interval, untilMs, enabled]);
201
203
  return now;
202
204
  }
205
+ var lockCount = 0;
206
+ var savedOverflow = "";
207
+ function lockScroll() {
208
+ if (typeof document === "undefined") return;
209
+ if (lockCount === 0) {
210
+ savedOverflow = document.body.style.overflow;
211
+ document.body.style.overflow = "hidden";
212
+ }
213
+ lockCount++;
214
+ }
215
+ function unlockScroll() {
216
+ if (typeof document === "undefined") return;
217
+ lockCount = Math.max(0, lockCount - 1);
218
+ if (lockCount === 0) {
219
+ document.body.style.overflow = savedOverflow;
220
+ }
221
+ }
222
+ function useScrollLock(active) {
223
+ useEffect(() => {
224
+ if (!active) return;
225
+ lockScroll();
226
+ return () => unlockScroll();
227
+ }, [active]);
228
+ }
203
229
 
204
230
  // src/tokens/colors.ts
205
231
  var colors = {
@@ -1230,33 +1256,27 @@ function TabPanel({ value, children, className }) {
1230
1256
  }
1231
1257
  );
1232
1258
  }
1233
- var Card = forwardRef(function Card2({ hoverable, variant = "surface", className, ...props }, ref) {
1259
+ var padClass = {
1260
+ none: "",
1261
+ sm: "p-3",
1262
+ md: "p-5",
1263
+ lg: "p-6"
1264
+ };
1265
+ var Card = forwardRef(function Card2({ hoverable, variant = "surface", padding = "md", className, ...props }, ref) {
1234
1266
  return /* @__PURE__ */ jsx(
1235
1267
  "div",
1236
1268
  {
1237
1269
  ref,
1238
1270
  ...props,
1239
- className: cn(variant === "glass" ? "glass" : "surface", hoverable && "surface-hover", className)
1271
+ className: cn(
1272
+ variant === "glass" ? "glass" : "surface",
1273
+ hoverable && "surface-hover",
1274
+ padClass[padding],
1275
+ className
1276
+ )
1240
1277
  }
1241
1278
  );
1242
1279
  });
1243
- var scrollLockCount = 0;
1244
- var savedOverflow = "";
1245
- function lockScroll() {
1246
- if (typeof document === "undefined") return;
1247
- if (scrollLockCount === 0) {
1248
- savedOverflow = document.body.style.overflow;
1249
- document.body.style.overflow = "hidden";
1250
- }
1251
- scrollLockCount++;
1252
- }
1253
- function unlockScroll() {
1254
- if (typeof document === "undefined") return;
1255
- scrollLockCount = Math.max(0, scrollLockCount - 1);
1256
- if (scrollLockCount === 0) {
1257
- document.body.style.overflow = savedOverflow;
1258
- }
1259
- }
1260
1280
  function Modal({
1261
1281
  isOpen,
1262
1282
  onClose,
@@ -1288,11 +1308,7 @@ function Modal({
1288
1308
  if (lastActive?.isConnected) focusSafely(lastActive);
1289
1309
  };
1290
1310
  }, [isOpen]);
1291
- useEffect(() => {
1292
- if (!isOpen) return;
1293
- lockScroll();
1294
- return () => unlockScroll();
1295
- }, [isOpen]);
1311
+ useScrollLock(isOpen);
1296
1312
  if (!isOpen) return null;
1297
1313
  return /* @__PURE__ */ jsx(
1298
1314
  "div",
@@ -1441,17 +1457,32 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
1441
1457
  const el = anchorRef.current;
1442
1458
  if (!el) return;
1443
1459
  const r = el.getBoundingClientRect();
1460
+ const vw = window.innerWidth;
1444
1461
  const centerX = r.left + r.width / 2;
1445
- const preferTop = placement === "top";
1462
+ const centerY = r.top + r.height / 2;
1446
1463
  const topY = r.top - 10;
1447
1464
  const bottomY = r.bottom + 10;
1448
- const canTop = topY > 8;
1449
- const effPlacement = preferTop ? canTop ? "top" : "bottom" : "bottom";
1450
- setPos({
1451
- left: Math.round(centerX),
1452
- top: Math.round(effPlacement === "top" ? topY : bottomY),
1453
- placement: effPlacement
1454
- });
1465
+ const leftX = r.left - 10;
1466
+ const rightX = r.right + 10;
1467
+ let effPlacement = placement;
1468
+ if (placement === "top" || placement === "bottom") {
1469
+ const canTop = topY > 8;
1470
+ effPlacement = placement === "top" ? canTop ? "top" : "bottom" : "bottom";
1471
+ setPos({
1472
+ left: Math.round(centerX),
1473
+ top: Math.round(effPlacement === "top" ? topY : bottomY),
1474
+ placement: effPlacement
1475
+ });
1476
+ } else {
1477
+ const canLeft = leftX > 8;
1478
+ const canRight = rightX < vw - 8;
1479
+ effPlacement = placement === "left" ? canLeft ? "left" : "right" : canRight ? "right" : "left";
1480
+ setPos({
1481
+ left: Math.round(effPlacement === "left" ? leftX : rightX),
1482
+ top: Math.round(centerY),
1483
+ placement: effPlacement
1484
+ });
1485
+ }
1455
1486
  }, [placement]);
1456
1487
  const scheduleOpen = useCallback(() => {
1457
1488
  clearTimer();
@@ -1525,7 +1556,7 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
1525
1556
  style: pos ? {
1526
1557
  left: pos.left,
1527
1558
  top: pos.top,
1528
- transform: pos.placement === "top" ? "translate(-50%, -100%)" : "translate(-50%, 0%)"
1559
+ transform: pos.placement === "top" ? "translate(-50%, -100%)" : pos.placement === "bottom" ? "translate(-50%, 0%)" : pos.placement === "left" ? "translate(-100%, -50%)" : "translate(0%, -50%)"
1529
1560
  } : { left: 0, top: 0, transform: "translate(-9999px, -9999px)" },
1530
1561
  children: content
1531
1562
  }
@@ -2068,7 +2099,7 @@ function StatCard({ value, label, icon, trend, className }) {
2068
2099
  "div",
2069
2100
  {
2070
2101
  className: cn(
2071
- "bg-white/5 ring-1 ring-white/10 rounded-xl p-4 flex items-start gap-3",
2102
+ "glass rounded-xl p-4 flex items-start gap-3",
2072
2103
  className
2073
2104
  ),
2074
2105
  children: [
@@ -3801,23 +3832,6 @@ function Popover({
3801
3832
  ) : null
3802
3833
  ] });
3803
3834
  }
3804
- var scrollLockCount2 = 0;
3805
- var savedOverflow2 = "";
3806
- function lockScroll2() {
3807
- if (typeof document === "undefined") return;
3808
- if (scrollLockCount2 === 0) {
3809
- savedOverflow2 = document.body.style.overflow;
3810
- document.body.style.overflow = "hidden";
3811
- }
3812
- scrollLockCount2++;
3813
- }
3814
- function unlockScroll2() {
3815
- if (typeof document === "undefined") return;
3816
- scrollLockCount2 = Math.max(0, scrollLockCount2 - 1);
3817
- if (scrollLockCount2 === 0) {
3818
- document.body.style.overflow = savedOverflow2;
3819
- }
3820
- }
3821
3835
  var sizeClass8 = {
3822
3836
  left: { sm: "w-64", md: "w-80", lg: "w-96", full: "w-screen" },
3823
3837
  right: { sm: "w-64", md: "w-80", lg: "w-96", full: "w-screen" },
@@ -3867,11 +3881,7 @@ function Drawer({
3867
3881
  if (lastActive?.isConnected) focusSafely(lastActive);
3868
3882
  };
3869
3883
  }, [isOpen]);
3870
- useEffect(() => {
3871
- if (!isOpen) return;
3872
- lockScroll2();
3873
- return () => unlockScroll2();
3874
- }, [isOpen]);
3884
+ useScrollLock(isOpen);
3875
3885
  if (!isOpen) return null;
3876
3886
  return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50", role: "presentation", children: [
3877
3887
  /* @__PURE__ */ jsx(
@@ -4254,5 +4264,60 @@ function VisuallyHidden({ children, as: Tag = "span", style, ...props }) {
4254
4264
  }
4255
4265
  );
4256
4266
  }
4267
+ var ErrorBoundary = class extends Component {
4268
+ constructor() {
4269
+ super(...arguments);
4270
+ this.state = { error: null };
4271
+ this.reset = () => {
4272
+ this.setState({ error: null });
4273
+ };
4274
+ }
4275
+ static getDerivedStateFromError(error) {
4276
+ return { error };
4277
+ }
4278
+ componentDidCatch(error, errorInfo) {
4279
+ this.props.onError?.(error, errorInfo);
4280
+ }
4281
+ render() {
4282
+ const { error } = this.state;
4283
+ if (!error) return this.props.children;
4284
+ const { fallback } = this.props;
4285
+ if (typeof fallback === "function") {
4286
+ return fallback(error, this.reset);
4287
+ }
4288
+ if (fallback !== void 0) {
4289
+ return fallback;
4290
+ }
4291
+ return /* @__PURE__ */ jsxs("div", { role: "alert", className: cn("glass rounded-xl p-6 text-center"), children: [
4292
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-white mb-2", children: "Something went wrong" }),
4293
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60 mb-4", children: error.message }),
4294
+ /* @__PURE__ */ jsx(
4295
+ "button",
4296
+ {
4297
+ type: "button",
4298
+ onClick: this.reset,
4299
+ className: "inline-flex items-center justify-center gap-2 rounded-xl font-semibold leading-none px-3.5 py-2.5 text-sm bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-glow hover:shadow-glow-lg hover:scale-[1.02] transition-[transform,background-color,box-shadow,opacity] active:translate-y-[0.5px]",
4300
+ children: "Try again"
4301
+ }
4302
+ )
4303
+ ] });
4304
+ }
4305
+ };
4306
+ function LoadingScreen({ message, size = "lg", className }) {
4307
+ return /* @__PURE__ */ jsxs(
4308
+ "div",
4309
+ {
4310
+ role: "status",
4311
+ className: cn(
4312
+ "flex flex-col items-center justify-center min-h-screen bg-ml-bg gap-4",
4313
+ className
4314
+ ),
4315
+ children: [
4316
+ /* @__PURE__ */ jsx(Spinner, { size }),
4317
+ message && /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60", children: message })
4318
+ ]
4319
+ }
4320
+ );
4321
+ }
4257
4322
 
4258
- export { ActiveFilterPills, Alert, Avatar, Badge, Breadcrumbs, Button, Card, Checkbox, CollapsibleSection, ColorInput, Combobox, ConfirmDialog, CooldownRing, CopyField, DashboardLayout, Divider, DotIndicator, Drawer, DropZone, Dropdown, DropdownItem, DropdownMenu, DropdownSeparator, DropdownTrigger, EmptyState, FormField, Heading, IconButton, Input, Modal, MutationOverlay, Navbar, NotificationBell, PageShell, Pagination, Pill, Popover, ProgressBar, ProgressButton, RadioGroup, RadioItem, ScrollArea, SearchInput, SectionCard, Select, Sidebar, Skeleton, Slider, Spinner, Stack, StageProgress, StatCard, Stepper, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TagInput, Text, Textarea, ToastProvider, Toggle, Tooltip, Transition, VisuallyHidden, cn, colors, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useSharedNow, useToast };
4323
+ export { ActiveFilterPills, Alert, Avatar, Badge, Breadcrumbs, Button, Card, Checkbox, CollapsibleSection, ColorInput, Combobox, ConfirmDialog, CooldownRing, CopyField, DashboardLayout, Divider, DotIndicator, Drawer, DropZone, Dropdown, DropdownItem, DropdownMenu, DropdownSeparator, DropdownTrigger, EmptyState, ErrorBoundary, FormField, Heading, IconButton, Input, LoadingScreen, Modal, MutationOverlay, Navbar, NotificationBell, PageShell, Pagination, Pill, Popover, ProgressBar, ProgressButton, RadioGroup, RadioItem, ScrollArea, SearchInput, SectionCard, Select, Sidebar, Skeleton, Slider, Spinner, Stack, StageProgress, StatCard, Stepper, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TagInput, Text, Textarea, ToastProvider, Toggle, Tooltip, Transition, VisuallyHidden, cn, colors, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useScrollLock, useSharedNow, useToast };