@memelabui/ui 0.7.0 → 0.8.1

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
@@ -139,10 +139,12 @@ function matchModifiers(e, mods) {
139
139
  }
140
140
  function useHotkeys(bindings, options = {}) {
141
141
  const { enabled = true } = options;
142
+ const bindingsRef = React.useRef(bindings);
143
+ bindingsRef.current = bindings;
142
144
  React.useEffect(() => {
143
145
  if (!enabled) return;
144
146
  const onKeyDown = (e) => {
145
- for (const binding of bindings) {
147
+ for (const binding of bindingsRef.current) {
146
148
  if (e.key === binding.key && matchModifiers(e, binding.modifiers)) {
147
149
  binding.handler(e);
148
150
  }
@@ -150,7 +152,7 @@ function useHotkeys(bindings, options = {}) {
150
152
  };
151
153
  document.addEventListener("keydown", onKeyDown);
152
154
  return () => document.removeEventListener("keydown", onKeyDown);
153
- }, [enabled, ...bindings]);
155
+ }, [enabled]);
154
156
  }
155
157
  function useIntersectionObserver(options = {}) {
156
158
  const { root = null, rootMargin = "0px", threshold = 0, enabled = true } = options;
@@ -206,6 +208,30 @@ function useSharedNow(options = {}) {
206
208
  }, [interval, untilMs, enabled]);
207
209
  return now;
208
210
  }
211
+ var lockCount = 0;
212
+ var savedOverflow = "";
213
+ function lockScroll() {
214
+ if (typeof document === "undefined") return;
215
+ if (lockCount === 0) {
216
+ savedOverflow = document.body.style.overflow;
217
+ document.body.style.overflow = "hidden";
218
+ }
219
+ lockCount++;
220
+ }
221
+ function unlockScroll() {
222
+ if (typeof document === "undefined") return;
223
+ lockCount = Math.max(0, lockCount - 1);
224
+ if (lockCount === 0) {
225
+ document.body.style.overflow = savedOverflow;
226
+ }
227
+ }
228
+ function useScrollLock(active) {
229
+ React.useEffect(() => {
230
+ if (!active) return;
231
+ lockScroll();
232
+ return () => unlockScroll();
233
+ }, [active]);
234
+ }
209
235
 
210
236
  // src/tokens/colors.ts
211
237
  var colors = {
@@ -487,126 +513,129 @@ var Textarea = React.forwardRef(function Textarea2({ hasError, label, error, hel
487
513
  helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
488
514
  ] });
489
515
  });
490
- function TagInput({
491
- value,
492
- onChange,
493
- placeholder,
494
- disabled = false,
495
- label,
496
- error,
497
- maxTags,
498
- className,
499
- id: externalId
500
- }) {
501
- const generatedId = React.useId();
502
- const inputId = externalId ?? generatedId;
503
- const errorId = error ? `${inputId}-error` : void 0;
504
- const inputRef = React.useRef(null);
505
- const atMax = maxTags !== void 0 && value.length >= maxTags;
506
- function addTag(raw) {
507
- const tag = raw.trim();
508
- if (!tag || value.includes(tag) || atMax) return;
509
- onChange([...value, tag]);
510
- }
511
- function removeTag(index) {
512
- onChange(value.filter((_, i) => i !== index));
513
- }
514
- function handleKeyDown(e) {
515
- const input = inputRef.current;
516
- if (!input) return;
517
- if (e.key === "Enter" || e.key === "," || e.key === "Tab") {
518
- if (input.value) {
519
- e.preventDefault();
520
- addTag(input.value);
521
- input.value = "";
522
- }
523
- return;
516
+ var TagInput = React.forwardRef(
517
+ function TagInput2({
518
+ value,
519
+ onChange,
520
+ placeholder,
521
+ disabled = false,
522
+ label,
523
+ error,
524
+ maxTags,
525
+ className,
526
+ id: externalId
527
+ }, ref) {
528
+ const generatedId = React.useId();
529
+ const inputId = externalId ?? generatedId;
530
+ const errorId = error ? `${inputId}-error` : void 0;
531
+ const internalRef = React.useRef(null);
532
+ const inputRef = ref ?? internalRef;
533
+ const atMax = maxTags !== void 0 && value.length >= maxTags;
534
+ function addTag(raw) {
535
+ const tag = raw.trim();
536
+ if (!tag || value.includes(tag) || atMax) return;
537
+ onChange([...value, tag]);
524
538
  }
525
- if (e.key === "Backspace" && !input.value && value.length > 0) {
526
- removeTag(value.length - 1);
539
+ function removeTag(index) {
540
+ onChange(value.filter((_, i) => i !== index));
527
541
  }
528
- }
529
- function handlePaste(e) {
530
- const pasted = e.clipboardData.getData("text");
531
- if (!pasted.includes(",")) return;
532
- e.preventDefault();
533
- const parts = pasted.split(",");
534
- const next = [...value];
535
- for (const part of parts) {
536
- const tag = part.trim();
537
- if (tag && !next.includes(tag)) {
538
- if (maxTags !== void 0 && next.length >= maxTags) break;
539
- next.push(tag);
542
+ function handleKeyDown(e) {
543
+ const input = inputRef.current;
544
+ if (!input) return;
545
+ if (e.key === "Enter" || e.key === "," || e.key === "Tab") {
546
+ if (input.value) {
547
+ e.preventDefault();
548
+ addTag(input.value);
549
+ input.value = "";
550
+ }
551
+ return;
552
+ }
553
+ if (e.key === "Backspace" && !input.value && value.length > 0) {
554
+ removeTag(value.length - 1);
540
555
  }
541
556
  }
542
- onChange(next);
543
- if (inputRef.current) inputRef.current.value = "";
544
- }
545
- const wrapper = /* @__PURE__ */ jsxRuntime.jsxs(
546
- "div",
547
- {
548
- className: cn(
549
- "flex flex-wrap items-center gap-1.5 min-h-[42px] w-full rounded-xl px-3 py-2 bg-white/10 ring-1 ring-white/10 transition-shadow focus-within:ring-2 focus-within:ring-primary/40",
550
- error && "ring-2 ring-rose-500/40 focus-within:ring-rose-500/40",
551
- disabled && "opacity-50 cursor-not-allowed",
552
- className
553
- ),
554
- onClick: () => inputRef.current?.focus(),
555
- children: [
556
- value.map((tag, i) => /* @__PURE__ */ jsxRuntime.jsxs(
557
- "span",
558
- {
559
- className: "inline-flex items-center gap-1 bg-white/10 text-white/90 rounded-full text-xs px-2.5 py-1 ring-1 ring-white/10",
560
- children: [
561
- tag,
562
- !disabled && /* @__PURE__ */ jsxRuntime.jsx(
563
- "button",
564
- {
565
- type: "button",
566
- "aria-label": `Remove ${tag}`,
567
- onClick: (e) => {
568
- e.stopPropagation();
569
- removeTag(i);
570
- },
571
- className: "text-white/50 hover:text-white/90 transition-colors leading-none",
572
- children: "\u2715"
573
- }
574
- )
575
- ]
576
- },
577
- `${tag}-${i}`
578
- )),
579
- /* @__PURE__ */ jsxRuntime.jsx(
580
- "input",
581
- {
582
- ref: inputRef,
583
- id: inputId,
584
- type: "text",
585
- disabled: disabled || atMax,
586
- placeholder: value.length === 0 ? placeholder : void 0,
587
- "aria-invalid": !!error || void 0,
588
- "aria-describedby": errorId,
589
- onKeyDown: handleKeyDown,
590
- onPaste: handlePaste,
591
- onBlur: (e) => {
592
- if (e.target.value) {
593
- addTag(e.target.value);
594
- e.target.value = "";
595
- }
596
- },
597
- className: "flex-1 min-w-[120px] bg-transparent text-sm text-white outline-none placeholder-white/30 disabled:cursor-not-allowed"
598
- }
599
- )
600
- ]
557
+ function handlePaste(e) {
558
+ const pasted = e.clipboardData.getData("text");
559
+ if (!pasted.includes(",")) return;
560
+ e.preventDefault();
561
+ const parts = pasted.split(",");
562
+ const next = [...value];
563
+ for (const part of parts) {
564
+ const tag = part.trim();
565
+ if (tag && !next.includes(tag)) {
566
+ if (maxTags !== void 0 && next.length >= maxTags) break;
567
+ next.push(tag);
568
+ }
569
+ }
570
+ onChange(next);
571
+ if (inputRef.current) inputRef.current.value = "";
601
572
  }
602
- );
603
- if (!label && !error) return wrapper;
604
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
605
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
606
- wrapper,
607
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error })
608
- ] });
609
- }
573
+ const wrapper = /* @__PURE__ */ jsxRuntime.jsxs(
574
+ "div",
575
+ {
576
+ className: cn(
577
+ "flex flex-wrap items-center gap-1.5 min-h-[42px] w-full rounded-xl px-3 py-2 bg-white/10 ring-1 ring-white/10 transition-shadow focus-within:ring-2 focus-within:ring-primary/40",
578
+ error && "ring-2 ring-rose-500/40 focus-within:ring-rose-500/40",
579
+ disabled && "opacity-50 cursor-not-allowed",
580
+ className
581
+ ),
582
+ onClick: () => inputRef.current?.focus(),
583
+ children: [
584
+ value.map((tag, i) => /* @__PURE__ */ jsxRuntime.jsxs(
585
+ "span",
586
+ {
587
+ className: "inline-flex items-center gap-1 bg-white/10 text-white/90 rounded-full text-xs px-2.5 py-1 ring-1 ring-white/10",
588
+ children: [
589
+ tag,
590
+ !disabled && /* @__PURE__ */ jsxRuntime.jsx(
591
+ "button",
592
+ {
593
+ type: "button",
594
+ "aria-label": `Remove ${tag}`,
595
+ onClick: (e) => {
596
+ e.stopPropagation();
597
+ removeTag(i);
598
+ },
599
+ className: "text-white/50 hover:text-white/90 transition-colors leading-none",
600
+ children: "\u2715"
601
+ }
602
+ )
603
+ ]
604
+ },
605
+ `${tag}-${i}`
606
+ )),
607
+ /* @__PURE__ */ jsxRuntime.jsx(
608
+ "input",
609
+ {
610
+ ref: inputRef,
611
+ id: inputId,
612
+ type: "text",
613
+ disabled: disabled || atMax,
614
+ placeholder: value.length === 0 ? placeholder : void 0,
615
+ "aria-invalid": !!error || void 0,
616
+ "aria-describedby": errorId,
617
+ onKeyDown: handleKeyDown,
618
+ onPaste: handlePaste,
619
+ onBlur: (e) => {
620
+ if (e.target.value) {
621
+ addTag(e.target.value);
622
+ e.target.value = "";
623
+ }
624
+ },
625
+ className: "flex-1 min-w-[120px] bg-transparent text-sm text-white outline-none placeholder-white/30 disabled:cursor-not-allowed"
626
+ }
627
+ )
628
+ ]
629
+ }
630
+ );
631
+ if (!label && !error) return wrapper;
632
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
633
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
634
+ wrapper,
635
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error })
636
+ ] });
637
+ }
638
+ );
610
639
  var base3 = "inline-flex items-center justify-center rounded-full font-semibold ring-1 ring-white/10";
611
640
  var sizeClass4 = {
612
641
  sm: "text-xs px-2.5 py-1",
@@ -1257,23 +1286,6 @@ var Card = React.forwardRef(function Card2({ hoverable, variant = "surface", pad
1257
1286
  }
1258
1287
  );
1259
1288
  });
1260
- var scrollLockCount = 0;
1261
- var savedOverflow = "";
1262
- function lockScroll() {
1263
- if (typeof document === "undefined") return;
1264
- if (scrollLockCount === 0) {
1265
- savedOverflow = document.body.style.overflow;
1266
- document.body.style.overflow = "hidden";
1267
- }
1268
- scrollLockCount++;
1269
- }
1270
- function unlockScroll() {
1271
- if (typeof document === "undefined") return;
1272
- scrollLockCount = Math.max(0, scrollLockCount - 1);
1273
- if (scrollLockCount === 0) {
1274
- document.body.style.overflow = savedOverflow;
1275
- }
1276
- }
1277
1289
  function Modal({
1278
1290
  isOpen,
1279
1291
  onClose,
@@ -1305,11 +1317,7 @@ function Modal({
1305
1317
  if (lastActive?.isConnected) focusSafely(lastActive);
1306
1318
  };
1307
1319
  }, [isOpen]);
1308
- React.useEffect(() => {
1309
- if (!isOpen) return;
1310
- lockScroll();
1311
- return () => unlockScroll();
1312
- }, [isOpen]);
1320
+ useScrollLock(isOpen);
1313
1321
  if (!isOpen) return null;
1314
1322
  return /* @__PURE__ */ jsxRuntime.jsx(
1315
1323
  "div",
@@ -1458,17 +1466,32 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
1458
1466
  const el = anchorRef.current;
1459
1467
  if (!el) return;
1460
1468
  const r = el.getBoundingClientRect();
1469
+ const vw = window.innerWidth;
1461
1470
  const centerX = r.left + r.width / 2;
1462
- const preferTop = placement === "top";
1471
+ const centerY = r.top + r.height / 2;
1463
1472
  const topY = r.top - 10;
1464
1473
  const bottomY = r.bottom + 10;
1465
- const canTop = topY > 8;
1466
- const effPlacement = preferTop ? canTop ? "top" : "bottom" : "bottom";
1467
- setPos({
1468
- left: Math.round(centerX),
1469
- top: Math.round(effPlacement === "top" ? topY : bottomY),
1470
- placement: effPlacement
1471
- });
1474
+ const leftX = r.left - 10;
1475
+ const rightX = r.right + 10;
1476
+ let effPlacement = placement;
1477
+ if (placement === "top" || placement === "bottom") {
1478
+ const canTop = topY > 8;
1479
+ effPlacement = placement === "top" ? canTop ? "top" : "bottom" : "bottom";
1480
+ setPos({
1481
+ left: Math.round(centerX),
1482
+ top: Math.round(effPlacement === "top" ? topY : bottomY),
1483
+ placement: effPlacement
1484
+ });
1485
+ } else {
1486
+ const canLeft = leftX > 8;
1487
+ const canRight = rightX < vw - 8;
1488
+ effPlacement = placement === "left" ? canLeft ? "left" : "right" : canRight ? "right" : "left";
1489
+ setPos({
1490
+ left: Math.round(effPlacement === "left" ? leftX : rightX),
1491
+ top: Math.round(centerY),
1492
+ placement: effPlacement
1493
+ });
1494
+ }
1472
1495
  }, [placement]);
1473
1496
  const scheduleOpen = React.useCallback(() => {
1474
1497
  clearTimer();
@@ -1542,7 +1565,7 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
1542
1565
  style: pos ? {
1543
1566
  left: pos.left,
1544
1567
  top: pos.top,
1545
- transform: pos.placement === "top" ? "translate(-50%, -100%)" : "translate(-50%, 0%)"
1568
+ transform: pos.placement === "top" ? "translate(-50%, -100%)" : pos.placement === "bottom" ? "translate(-50%, 0%)" : pos.placement === "left" ? "translate(-100%, -50%)" : "translate(0%, -50%)"
1546
1569
  } : { left: 0, top: 0, transform: "translate(-9999px, -9999px)" },
1547
1570
  children: content
1548
1571
  }
@@ -2085,7 +2108,7 @@ function StatCard({ value, label, icon, trend, className }) {
2085
2108
  "div",
2086
2109
  {
2087
2110
  className: cn(
2088
- "bg-white/5 ring-1 ring-white/10 rounded-xl p-4 flex items-start gap-3",
2111
+ "glass rounded-xl p-4 flex items-start gap-3",
2089
2112
  className
2090
2113
  ),
2091
2114
  children: [
@@ -3818,23 +3841,6 @@ function Popover({
3818
3841
  ) : null
3819
3842
  ] });
3820
3843
  }
3821
- var scrollLockCount2 = 0;
3822
- var savedOverflow2 = "";
3823
- function lockScroll2() {
3824
- if (typeof document === "undefined") return;
3825
- if (scrollLockCount2 === 0) {
3826
- savedOverflow2 = document.body.style.overflow;
3827
- document.body.style.overflow = "hidden";
3828
- }
3829
- scrollLockCount2++;
3830
- }
3831
- function unlockScroll2() {
3832
- if (typeof document === "undefined") return;
3833
- scrollLockCount2 = Math.max(0, scrollLockCount2 - 1);
3834
- if (scrollLockCount2 === 0) {
3835
- document.body.style.overflow = savedOverflow2;
3836
- }
3837
- }
3838
3844
  var sizeClass8 = {
3839
3845
  left: { sm: "w-64", md: "w-80", lg: "w-96", full: "w-screen" },
3840
3846
  right: { sm: "w-64", md: "w-80", lg: "w-96", full: "w-screen" },
@@ -3884,11 +3890,7 @@ function Drawer({
3884
3890
  if (lastActive?.isConnected) focusSafely(lastActive);
3885
3891
  };
3886
3892
  }, [isOpen]);
3887
- React.useEffect(() => {
3888
- if (!isOpen) return;
3889
- lockScroll2();
3890
- return () => unlockScroll2();
3891
- }, [isOpen]);
3893
+ useScrollLock(isOpen);
3892
3894
  if (!isOpen) return null;
3893
3895
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50", role: "presentation", children: [
3894
3896
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4271,6 +4273,61 @@ function VisuallyHidden({ children, as: Tag = "span", style, ...props }) {
4271
4273
  }
4272
4274
  );
4273
4275
  }
4276
+ var ErrorBoundary = class extends React.Component {
4277
+ constructor() {
4278
+ super(...arguments);
4279
+ this.state = { error: null };
4280
+ this.reset = () => {
4281
+ this.setState({ error: null });
4282
+ };
4283
+ }
4284
+ static getDerivedStateFromError(error) {
4285
+ return { error };
4286
+ }
4287
+ componentDidCatch(error, errorInfo) {
4288
+ this.props.onError?.(error, errorInfo);
4289
+ }
4290
+ render() {
4291
+ const { error } = this.state;
4292
+ if (!error) return this.props.children;
4293
+ const { fallback } = this.props;
4294
+ if (typeof fallback === "function") {
4295
+ return fallback(error, this.reset);
4296
+ }
4297
+ if (fallback !== void 0) {
4298
+ return fallback;
4299
+ }
4300
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: cn("glass rounded-xl p-6 text-center"), children: [
4301
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-white mb-2", children: "Something went wrong" }),
4302
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/60 mb-4", children: error.message }),
4303
+ /* @__PURE__ */ jsxRuntime.jsx(
4304
+ "button",
4305
+ {
4306
+ type: "button",
4307
+ onClick: this.reset,
4308
+ 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]",
4309
+ children: "Try again"
4310
+ }
4311
+ )
4312
+ ] });
4313
+ }
4314
+ };
4315
+ function LoadingScreen({ message, size = "lg", className }) {
4316
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4317
+ "div",
4318
+ {
4319
+ role: "status",
4320
+ className: cn(
4321
+ "flex flex-col items-center justify-center min-h-screen bg-ml-bg gap-4",
4322
+ className
4323
+ ),
4324
+ children: [
4325
+ /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size }),
4326
+ message && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/60", children: message })
4327
+ ]
4328
+ }
4329
+ );
4330
+ }
4274
4331
 
4275
4332
  exports.ActiveFilterPills = ActiveFilterPills;
4276
4333
  exports.Alert = Alert;
@@ -4297,10 +4354,12 @@ exports.DropdownMenu = DropdownMenu;
4297
4354
  exports.DropdownSeparator = DropdownSeparator;
4298
4355
  exports.DropdownTrigger = DropdownTrigger;
4299
4356
  exports.EmptyState = EmptyState;
4357
+ exports.ErrorBoundary = ErrorBoundary;
4300
4358
  exports.FormField = FormField;
4301
4359
  exports.Heading = Heading;
4302
4360
  exports.IconButton = IconButton;
4303
4361
  exports.Input = Input;
4362
+ exports.LoadingScreen = LoadingScreen;
4304
4363
  exports.Modal = Modal;
4305
4364
  exports.MutationOverlay = MutationOverlay;
4306
4365
  exports.Navbar = Navbar;
@@ -4353,5 +4412,6 @@ exports.useDisclosure = useDisclosure;
4353
4412
  exports.useHotkeys = useHotkeys;
4354
4413
  exports.useIntersectionObserver = useIntersectionObserver;
4355
4414
  exports.useMediaQuery = useMediaQuery;
4415
+ exports.useScrollLock = useScrollLock;
4356
4416
  exports.useSharedNow = useSharedNow;
4357
4417
  exports.useToast = useToast;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { MemelabColors, colors } from './tokens/index.cjs';
2
2
  import * as react from 'react';
3
- import { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, ReactElement, ComponentType } from 'react';
3
+ import { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, ReactElement, ComponentType, Component, ErrorInfo } from 'react';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
6
  type ClassValue = string | number | null | undefined | boolean | Record<string, boolean | null | undefined> | ClassValue[];
@@ -95,6 +95,9 @@ type UseSharedNowOptions = {
95
95
  */
96
96
  declare function useSharedNow(options?: UseSharedNowOptions): number;
97
97
 
98
+ /** Ref-counted body scroll lock. Safe for nested overlays (Modal + Drawer). */
99
+ declare function useScrollLock(active: boolean): void;
100
+
98
101
  type Size = 'sm' | 'md' | 'lg';
99
102
 
100
103
  type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
@@ -196,7 +199,7 @@ type TagInputProps = {
196
199
  className?: string;
197
200
  id?: string;
198
201
  };
199
- declare function TagInput({ value, onChange, placeholder, disabled, label, error, maxTags, className, id: externalId, }: TagInputProps): react_jsx_runtime.JSX.Element;
202
+ declare const TagInput: react.ForwardRefExoticComponent<TagInputProps & react.RefAttributes<HTMLInputElement>>;
200
203
 
201
204
  type BadgeVariant = 'neutral' | 'primary' | 'success' | 'successSolid' | 'warning' | 'danger' | 'dangerSolid' | 'accent';
202
205
  type BadgeSize = 'sm' | 'md';
@@ -376,7 +379,7 @@ type ConfirmDialogProps = {
376
379
  };
377
380
  declare function ConfirmDialog({ isOpen, onClose, onConfirm, title, message, confirmText, cancelText, loadingText, variant, isLoading, }: ConfirmDialogProps): react_jsx_runtime.JSX.Element;
378
381
 
379
- type TooltipPlacement = 'top' | 'bottom';
382
+ type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
380
383
  type TooltipProps = {
381
384
  content: ReactNode;
382
385
  delayMs?: number;
@@ -919,4 +922,27 @@ type VisuallyHiddenProps = HTMLAttributes<HTMLSpanElement> & {
919
922
  */
920
923
  declare function VisuallyHidden({ children, as: Tag, style, ...props }: VisuallyHiddenProps): react_jsx_runtime.JSX.Element;
921
924
 
922
- export { ActiveFilterPills, type ActiveFilterPillsProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, type CardPadding, type CardProps, type CardVariant, Checkbox, type CheckboxProps, CollapsibleSection, type CollapsibleSectionProps, ColorInput, type ColorInputProps, Combobox, type ComboboxOption, type ComboboxProps, ConfirmDialog, type ConfirmDialogProps, type ConfirmDialogVariant, CooldownRing, type CooldownRingProps, type CooldownRingSize, CopyField, type CopyFieldProps, DashboardLayout, type DashboardLayoutProps, Divider, type DividerProps, DotIndicator, type DotIndicatorProps, Drawer, type DrawerProps, type DrawerSide, type DrawerSize, DropZone, type DropZoneProps, Dropdown, DropdownItem, type DropdownItemProps, DropdownMenu, type DropdownMenuProps, type DropdownProps, DropdownSeparator, type DropdownSeparatorProps, DropdownTrigger, type DropdownTriggerProps, EmptyState, type EmptyStateProps, type FilterPill, FormField, type FormFieldProps, Heading, type HeadingLevel, type HeadingProps, type HotkeyBinding, type HotkeyModifiers, IconButton, type IconButtonProps, Input, type InputProps, Modal, type ModalProps, MutationOverlay, type MutationOverlayProps, type MutationOverlayStatus, Navbar, type NavbarProps, NotificationBell, type NotificationBellProps, PageShell, type PageShellProps, type PageShellVariant, Pagination, type PaginationProps, Pill, Popover, type PopoverPlacement, type PopoverProps, ProgressBar, type ProgressBarProps, type ProgressBarVariant, ProgressButton, type ProgressButtonProps, RadioGroup, type RadioGroupProps, RadioItem, type RadioItemProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, SectionCard, type SectionCardProps, Select, type SelectProps, Sidebar, type SidebarProps, type Size, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, type SpinnerSize, Stack, type StackProps, StageProgress, type StageProgressProps, StatCard, type StatCardProps, type StatCardTrend, type Step, Stepper, type StepperProps, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, TableBody, type TableBodyProps, TableCell, type TableCellProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, Tabs, type TabsProps, type TabsVariant, TagInput, type TagInputProps, Text, type TextColor, type TextProps, type TextSize, Textarea, type TextareaProps, type ToastData, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, Toggle, type ToggleProps, type ToggleSize, Tooltip, type TooltipPlacement, type TooltipProps, Transition, type TransitionPreset, type TransitionProps, type UseClipboardReturn, type UseDisclosureReturn, type UseHotkeysOptions, type UseIntersectionObserverOptions, type UseIntersectionObserverReturn, type UseSharedNowOptions, VisuallyHidden, type VisuallyHiddenProps, cn, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useSharedNow, useToast };
925
+ type ErrorBoundaryProps = {
926
+ children: ReactNode;
927
+ fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
928
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
929
+ };
930
+ type State = {
931
+ error: Error | null;
932
+ };
933
+ declare class ErrorBoundary extends Component<ErrorBoundaryProps, State> {
934
+ state: State;
935
+ static getDerivedStateFromError(error: Error): State;
936
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
937
+ private reset;
938
+ render(): string | number | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | null | undefined;
939
+ }
940
+
941
+ type LoadingScreenProps = {
942
+ message?: string;
943
+ size?: SpinnerSize;
944
+ className?: string;
945
+ };
946
+ declare function LoadingScreen({ message, size, className }: LoadingScreenProps): react_jsx_runtime.JSX.Element;
947
+
948
+ export { ActiveFilterPills, type ActiveFilterPillsProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, type CardPadding, type CardProps, type CardVariant, Checkbox, type CheckboxProps, CollapsibleSection, type CollapsibleSectionProps, ColorInput, type ColorInputProps, Combobox, type ComboboxOption, type ComboboxProps, ConfirmDialog, type ConfirmDialogProps, type ConfirmDialogVariant, CooldownRing, type CooldownRingProps, type CooldownRingSize, CopyField, type CopyFieldProps, DashboardLayout, type DashboardLayoutProps, Divider, type DividerProps, DotIndicator, type DotIndicatorProps, Drawer, type DrawerProps, type DrawerSide, type DrawerSize, DropZone, type DropZoneProps, Dropdown, DropdownItem, type DropdownItemProps, DropdownMenu, type DropdownMenuProps, type DropdownProps, DropdownSeparator, type DropdownSeparatorProps, DropdownTrigger, type DropdownTriggerProps, EmptyState, type EmptyStateProps, ErrorBoundary, type ErrorBoundaryProps, type FilterPill, FormField, type FormFieldProps, Heading, type HeadingLevel, type HeadingProps, type HotkeyBinding, type HotkeyModifiers, IconButton, type IconButtonProps, Input, type InputProps, LoadingScreen, type LoadingScreenProps, Modal, type ModalProps, MutationOverlay, type MutationOverlayProps, type MutationOverlayStatus, Navbar, type NavbarProps, NotificationBell, type NotificationBellProps, PageShell, type PageShellProps, type PageShellVariant, Pagination, type PaginationProps, Pill, Popover, type PopoverPlacement, type PopoverProps, ProgressBar, type ProgressBarProps, type ProgressBarVariant, ProgressButton, type ProgressButtonProps, RadioGroup, type RadioGroupProps, RadioItem, type RadioItemProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, SectionCard, type SectionCardProps, Select, type SelectProps, Sidebar, type SidebarProps, type Size, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, type SpinnerSize, Stack, type StackProps, StageProgress, type StageProgressProps, StatCard, type StatCardProps, type StatCardTrend, type Step, Stepper, type StepperProps, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, TableBody, type TableBodyProps, TableCell, type TableCellProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, Tabs, type TabsProps, type TabsVariant, TagInput, type TagInputProps, Text, type TextColor, type TextProps, type TextSize, Textarea, type TextareaProps, type ToastData, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, Toggle, type ToggleProps, type ToggleSize, Tooltip, type TooltipPlacement, type TooltipProps, Transition, type TransitionPreset, type TransitionProps, type UseClipboardReturn, type UseDisclosureReturn, type UseHotkeysOptions, type UseIntersectionObserverOptions, type UseIntersectionObserverReturn, type UseSharedNowOptions, VisuallyHidden, type VisuallyHiddenProps, cn, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useScrollLock, useSharedNow, useToast };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { MemelabColors, colors } from './tokens/index.js';
2
2
  import * as react from 'react';
3
- import { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, ReactElement, ComponentType } from 'react';
3
+ import { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, ReactElement, ComponentType, Component, ErrorInfo } from 'react';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
6
  type ClassValue = string | number | null | undefined | boolean | Record<string, boolean | null | undefined> | ClassValue[];
@@ -95,6 +95,9 @@ type UseSharedNowOptions = {
95
95
  */
96
96
  declare function useSharedNow(options?: UseSharedNowOptions): number;
97
97
 
98
+ /** Ref-counted body scroll lock. Safe for nested overlays (Modal + Drawer). */
99
+ declare function useScrollLock(active: boolean): void;
100
+
98
101
  type Size = 'sm' | 'md' | 'lg';
99
102
 
100
103
  type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
@@ -196,7 +199,7 @@ type TagInputProps = {
196
199
  className?: string;
197
200
  id?: string;
198
201
  };
199
- declare function TagInput({ value, onChange, placeholder, disabled, label, error, maxTags, className, id: externalId, }: TagInputProps): react_jsx_runtime.JSX.Element;
202
+ declare const TagInput: react.ForwardRefExoticComponent<TagInputProps & react.RefAttributes<HTMLInputElement>>;
200
203
 
201
204
  type BadgeVariant = 'neutral' | 'primary' | 'success' | 'successSolid' | 'warning' | 'danger' | 'dangerSolid' | 'accent';
202
205
  type BadgeSize = 'sm' | 'md';
@@ -376,7 +379,7 @@ type ConfirmDialogProps = {
376
379
  };
377
380
  declare function ConfirmDialog({ isOpen, onClose, onConfirm, title, message, confirmText, cancelText, loadingText, variant, isLoading, }: ConfirmDialogProps): react_jsx_runtime.JSX.Element;
378
381
 
379
- type TooltipPlacement = 'top' | 'bottom';
382
+ type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
380
383
  type TooltipProps = {
381
384
  content: ReactNode;
382
385
  delayMs?: number;
@@ -919,4 +922,27 @@ type VisuallyHiddenProps = HTMLAttributes<HTMLSpanElement> & {
919
922
  */
920
923
  declare function VisuallyHidden({ children, as: Tag, style, ...props }: VisuallyHiddenProps): react_jsx_runtime.JSX.Element;
921
924
 
922
- export { ActiveFilterPills, type ActiveFilterPillsProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, type CardPadding, type CardProps, type CardVariant, Checkbox, type CheckboxProps, CollapsibleSection, type CollapsibleSectionProps, ColorInput, type ColorInputProps, Combobox, type ComboboxOption, type ComboboxProps, ConfirmDialog, type ConfirmDialogProps, type ConfirmDialogVariant, CooldownRing, type CooldownRingProps, type CooldownRingSize, CopyField, type CopyFieldProps, DashboardLayout, type DashboardLayoutProps, Divider, type DividerProps, DotIndicator, type DotIndicatorProps, Drawer, type DrawerProps, type DrawerSide, type DrawerSize, DropZone, type DropZoneProps, Dropdown, DropdownItem, type DropdownItemProps, DropdownMenu, type DropdownMenuProps, type DropdownProps, DropdownSeparator, type DropdownSeparatorProps, DropdownTrigger, type DropdownTriggerProps, EmptyState, type EmptyStateProps, type FilterPill, FormField, type FormFieldProps, Heading, type HeadingLevel, type HeadingProps, type HotkeyBinding, type HotkeyModifiers, IconButton, type IconButtonProps, Input, type InputProps, Modal, type ModalProps, MutationOverlay, type MutationOverlayProps, type MutationOverlayStatus, Navbar, type NavbarProps, NotificationBell, type NotificationBellProps, PageShell, type PageShellProps, type PageShellVariant, Pagination, type PaginationProps, Pill, Popover, type PopoverPlacement, type PopoverProps, ProgressBar, type ProgressBarProps, type ProgressBarVariant, ProgressButton, type ProgressButtonProps, RadioGroup, type RadioGroupProps, RadioItem, type RadioItemProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, SectionCard, type SectionCardProps, Select, type SelectProps, Sidebar, type SidebarProps, type Size, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, type SpinnerSize, Stack, type StackProps, StageProgress, type StageProgressProps, StatCard, type StatCardProps, type StatCardTrend, type Step, Stepper, type StepperProps, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, TableBody, type TableBodyProps, TableCell, type TableCellProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, Tabs, type TabsProps, type TabsVariant, TagInput, type TagInputProps, Text, type TextColor, type TextProps, type TextSize, Textarea, type TextareaProps, type ToastData, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, Toggle, type ToggleProps, type ToggleSize, Tooltip, type TooltipPlacement, type TooltipProps, Transition, type TransitionPreset, type TransitionProps, type UseClipboardReturn, type UseDisclosureReturn, type UseHotkeysOptions, type UseIntersectionObserverOptions, type UseIntersectionObserverReturn, type UseSharedNowOptions, VisuallyHidden, type VisuallyHiddenProps, cn, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useSharedNow, useToast };
925
+ type ErrorBoundaryProps = {
926
+ children: ReactNode;
927
+ fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
928
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
929
+ };
930
+ type State = {
931
+ error: Error | null;
932
+ };
933
+ declare class ErrorBoundary extends Component<ErrorBoundaryProps, State> {
934
+ state: State;
935
+ static getDerivedStateFromError(error: Error): State;
936
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
937
+ private reset;
938
+ render(): string | number | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | null | undefined;
939
+ }
940
+
941
+ type LoadingScreenProps = {
942
+ message?: string;
943
+ size?: SpinnerSize;
944
+ className?: string;
945
+ };
946
+ declare function LoadingScreen({ message, size, className }: LoadingScreenProps): react_jsx_runtime.JSX.Element;
947
+
948
+ export { ActiveFilterPills, type ActiveFilterPillsProps, Alert, type AlertProps, type AlertVariant, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, type CardPadding, type CardProps, type CardVariant, Checkbox, type CheckboxProps, CollapsibleSection, type CollapsibleSectionProps, ColorInput, type ColorInputProps, Combobox, type ComboboxOption, type ComboboxProps, ConfirmDialog, type ConfirmDialogProps, type ConfirmDialogVariant, CooldownRing, type CooldownRingProps, type CooldownRingSize, CopyField, type CopyFieldProps, DashboardLayout, type DashboardLayoutProps, Divider, type DividerProps, DotIndicator, type DotIndicatorProps, Drawer, type DrawerProps, type DrawerSide, type DrawerSize, DropZone, type DropZoneProps, Dropdown, DropdownItem, type DropdownItemProps, DropdownMenu, type DropdownMenuProps, type DropdownProps, DropdownSeparator, type DropdownSeparatorProps, DropdownTrigger, type DropdownTriggerProps, EmptyState, type EmptyStateProps, ErrorBoundary, type ErrorBoundaryProps, type FilterPill, FormField, type FormFieldProps, Heading, type HeadingLevel, type HeadingProps, type HotkeyBinding, type HotkeyModifiers, IconButton, type IconButtonProps, Input, type InputProps, LoadingScreen, type LoadingScreenProps, Modal, type ModalProps, MutationOverlay, type MutationOverlayProps, type MutationOverlayStatus, Navbar, type NavbarProps, NotificationBell, type NotificationBellProps, PageShell, type PageShellProps, type PageShellVariant, Pagination, type PaginationProps, Pill, Popover, type PopoverPlacement, type PopoverProps, ProgressBar, type ProgressBarProps, type ProgressBarVariant, ProgressButton, type ProgressButtonProps, RadioGroup, type RadioGroupProps, RadioItem, type RadioItemProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, SectionCard, type SectionCardProps, Select, type SelectProps, Sidebar, type SidebarProps, type Size, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, type SpinnerSize, Stack, type StackProps, StageProgress, type StageProgressProps, StatCard, type StatCardProps, type StatCardTrend, type Step, Stepper, type StepperProps, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, TableBody, type TableBodyProps, TableCell, type TableCellProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, Tabs, type TabsProps, type TabsVariant, TagInput, type TagInputProps, Text, type TextColor, type TextProps, type TextSize, Textarea, type TextareaProps, type ToastData, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, Toggle, type ToggleProps, type ToggleSize, Tooltip, type TooltipPlacement, type TooltipProps, Transition, type TransitionPreset, type TransitionProps, type UseClipboardReturn, type UseDisclosureReturn, type UseHotkeysOptions, type UseIntersectionObserverOptions, type UseIntersectionObserverReturn, type UseSharedNowOptions, VisuallyHidden, type VisuallyHiddenProps, cn, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useScrollLock, useSharedNow, useToast };