@apotech-os/ui-sdk 0.9.0 → 0.10.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
@@ -3,6 +3,11 @@
3
3
  var clsx = require('clsx');
4
4
  var tailwindMerge = require('tailwind-merge');
5
5
  var React = require('react');
6
+ var dialog = require('@base-ui/react/dialog');
7
+ var menu = require('@base-ui/react/menu');
8
+ var popover = require('@base-ui/react/popover');
9
+ var _switch = require('@base-ui/react/switch');
10
+ var tabs = require('@base-ui/react/tabs');
6
11
  var classVarianceAuthority = require('class-variance-authority');
7
12
  var jsxRuntime = require('react/jsx-runtime');
8
13
 
@@ -310,6 +315,12 @@ function downloadCsv(rows, filename) {
310
315
  const blob = new Blob([`${BOM}${toCsv(rows)}`], { type: "text/csv;charset=utf-8" });
311
316
  downloadBlob(blob, filename);
312
317
  }
318
+ function triggerProps(trigger) {
319
+ if (React__namespace.isValidElement(trigger)) {
320
+ return { render: trigger, nativeButton: trigger.type === "button" || trigger.type === Button };
321
+ }
322
+ return { render: /* @__PURE__ */ jsxRuntime.jsx("span", { children: trigger }), nativeButton: false };
323
+ }
313
324
  var buttonVariants = classVarianceAuthority.cva(
314
325
  "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
315
326
  {
@@ -373,9 +384,18 @@ function CardHeader({ className, ...props }) {
373
384
  function CardTitle({ className, ...props }) {
374
385
  return /* @__PURE__ */ jsxRuntime.jsx("h3", { className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props });
375
386
  }
387
+ function CardDescription({
388
+ className,
389
+ ...props
390
+ }) {
391
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-sm text-muted-foreground", className), ...props });
392
+ }
376
393
  function CardContent({ className, ...props }) {
377
394
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("p-6 pt-0", className), ...props });
378
395
  }
396
+ function CardFooter({ className, ...props }) {
397
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-center p-6 pt-0", className), ...props });
398
+ }
379
399
  var Input = React__namespace.forwardRef(({ className, type, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
380
400
  "input",
381
401
  {
@@ -396,7 +416,13 @@ function TableHeader({
396
416
  className,
397
417
  ...props
398
418
  }) {
399
- return /* @__PURE__ */ jsxRuntime.jsx("thead", { className: cn("[&_tr]:border-b", className), ...props });
419
+ return /* @__PURE__ */ jsxRuntime.jsx(
420
+ "thead",
421
+ {
422
+ className: cn("[&_tr]:border-b [&_tr]:hover:bg-transparent", className),
423
+ ...props
424
+ }
425
+ );
400
426
  }
401
427
  function TableBody({ className, ...props }) {
402
428
  return /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: cn("[&_tr:last-child]:border-0", className), ...props });
@@ -418,7 +444,7 @@ function TableHead({ className, ...props }) {
418
444
  "th",
419
445
  {
420
446
  className: cn(
421
- "h-10 px-4 text-left align-middle text-xs font-medium text-muted-foreground",
447
+ "h-10 px-4 text-left align-middle text-xs font-medium uppercase tracking-wide text-muted-foreground",
422
448
  className
423
449
  ),
424
450
  ...props
@@ -443,42 +469,43 @@ var Select = React__namespace.forwardRef(
443
469
  )
444
470
  );
445
471
  Select.displayName = "Select";
446
- var TabsContext = React__namespace.createContext(null);
447
- function useTabsContext() {
448
- const ctx = React__namespace.useContext(TabsContext);
449
- if (!ctx) throw new Error("Tabs sub-component must be used within <Tabs>");
450
- return ctx;
451
- }
452
472
  function Tabs({ defaultValue, value, onValueChange, className, children }) {
453
- const [internal, setInternal] = React__namespace.useState(defaultValue ?? "");
454
- const current = value ?? internal;
455
- const setValue = (v) => {
456
- setInternal(v);
457
- onValueChange?.(v);
458
- };
459
- return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: { value: current, setValue }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className, children }) });
473
+ return /* @__PURE__ */ jsxRuntime.jsx(
474
+ tabs.Tabs.Root,
475
+ {
476
+ className,
477
+ defaultValue,
478
+ value,
479
+ onValueChange: (next) => onValueChange?.(String(next)),
480
+ children
481
+ }
482
+ );
460
483
  }
461
484
  function TabsList({
462
485
  className,
463
486
  children
464
487
  }) {
465
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("inline-flex items-center gap-1 rounded-lg bg-muted p-1", className), children });
488
+ return /* @__PURE__ */ jsxRuntime.jsx(
489
+ tabs.Tabs.List,
490
+ {
491
+ className: cn("inline-flex items-center gap-1 rounded-lg bg-muted p-1", className),
492
+ children
493
+ }
494
+ );
466
495
  }
467
496
  function TabsTrigger({
468
497
  value,
469
498
  className,
470
499
  children
471
500
  }) {
472
- const { value: current, setValue } = useTabsContext();
473
- const active = current === value;
474
501
  return /* @__PURE__ */ jsxRuntime.jsx(
475
- "button",
502
+ tabs.Tabs.Tab,
476
503
  {
477
- type: "button",
478
- onClick: () => setValue(value),
504
+ value,
479
505
  className: cn(
480
506
  "rounded-md px-3 py-1.5 text-sm font-medium transition-colors",
481
- active ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground",
507
+ "text-muted-foreground hover:text-foreground",
508
+ "data-[active]:bg-card data-[active]:text-foreground data-[active]:shadow-sm",
482
509
  className
483
510
  ),
484
511
  children
@@ -490,38 +517,25 @@ function TabsContent({
490
517
  className,
491
518
  children
492
519
  }) {
493
- const { value: current } = useTabsContext();
494
- if (current !== value) return null;
495
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children });
520
+ return /* @__PURE__ */ jsxRuntime.jsx(tabs.Tabs.Panel, { value, className, children });
496
521
  }
497
522
  function Modal({ open, onClose, title, children, className }) {
498
- if (!open) return null;
499
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
500
- /* @__PURE__ */ jsxRuntime.jsx(
501
- "button",
502
- {
503
- type: "button",
504
- "aria-label": "Close",
505
- className: "absolute inset-0 cursor-default bg-foreground/40",
506
- onClick: onClose
507
- }
508
- ),
523
+ return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Root, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Portal, { children: [
524
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Backdrop, { "aria-label": "Close", className: "fixed inset-0 z-50 bg-foreground/40" }),
509
525
  /* @__PURE__ */ jsxRuntime.jsxs(
510
- "div",
526
+ dialog.Dialog.Popup,
511
527
  {
512
- role: "dialog",
513
- "aria-modal": true,
514
528
  className: cn(
515
- "relative z-10 w-full max-w-md rounded-xl border bg-card p-5 shadow-lg",
529
+ "fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-xl border bg-card p-5 shadow-lg",
516
530
  className
517
531
  ),
518
532
  children: [
519
- title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-3 text-base font-semibold", children: title }),
533
+ title && /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Title, { className: "mb-3 text-base font-semibold", children: title }),
520
534
  children
521
535
  ]
522
536
  }
523
537
  )
524
- ] });
538
+ ] }) });
525
539
  }
526
540
  var spinnerSizes = { sm: "size-4", md: "size-6", lg: "size-8" };
527
541
  function Spinner({ className, size = "md" }) {
@@ -571,28 +585,19 @@ function Switch({
571
585
  ...props
572
586
  }) {
573
587
  return /* @__PURE__ */ jsxRuntime.jsx(
574
- "button",
588
+ _switch.Switch.Root,
575
589
  {
576
- type: "button",
577
- role: "switch",
578
- "aria-checked": checked,
590
+ checked,
591
+ onCheckedChange: (next) => onCheckedChange?.(next),
579
592
  disabled,
580
- onClick: () => onCheckedChange?.(!checked),
581
593
  className: cn(
582
- "relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
583
- checked ? "bg-primary" : "bg-input",
594
+ // `data-[disabled]`, not `disabled:` the root is a span, so `:disabled`
595
+ // never matches it and the dimming would silently stop happening.
596
+ "relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full bg-input transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[checked]:bg-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
584
597
  className
585
598
  ),
586
599
  ...props,
587
- children: /* @__PURE__ */ jsxRuntime.jsx(
588
- "span",
589
- {
590
- className: cn(
591
- "inline-block size-4 transform rounded-full bg-white shadow transition-transform",
592
- checked ? "translate-x-4" : "translate-x-0.5"
593
- )
594
- }
595
- )
600
+ children: /* @__PURE__ */ jsxRuntime.jsx(_switch.Switch.Thumb, { className: "inline-block size-4 translate-x-0.5 rounded-full bg-white shadow transition-transform data-[checked]:translate-x-4" })
596
601
  }
597
602
  );
598
603
  }
@@ -721,47 +726,26 @@ function SidePanel({
721
726
  children,
722
727
  className
723
728
  }) {
724
- React__namespace.useEffect(() => {
725
- if (!open) return;
726
- const onKeyDown = (e) => {
727
- if (e.key === "Escape") onClose();
728
- };
729
- window.addEventListener("keydown", onKeyDown);
730
- return () => window.removeEventListener("keydown", onKeyDown);
731
- }, [open, onClose]);
732
- if (!open) return null;
733
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50", children: [
734
- /* @__PURE__ */ jsxRuntime.jsx(
735
- "button",
736
- {
737
- type: "button",
738
- "aria-label": "Fermer",
739
- className: "absolute inset-0 cursor-default bg-foreground/40",
740
- onClick: onClose
741
- }
742
- ),
729
+ return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Root, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Portal, { children: [
730
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Backdrop, { "aria-label": "Fermer", className: "fixed inset-0 z-50 bg-foreground/40" }),
743
731
  /* @__PURE__ */ jsxRuntime.jsxs(
744
- "aside",
732
+ dialog.Dialog.Popup,
745
733
  {
746
- role: "dialog",
747
- "aria-modal": true,
748
734
  className: cn(
749
- "absolute inset-y-0 right-0 flex w-full max-w-lg flex-col border-l bg-card shadow-xl",
735
+ "fixed inset-y-0 right-0 z-50 flex w-full max-w-lg flex-col border-l bg-card shadow-xl",
750
736
  className
751
737
  ),
752
738
  children: [
753
739
  /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex shrink-0 items-start gap-3 border-b px-5 py-4", children: [
754
740
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
755
- title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold", children: title }),
756
- description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description })
741
+ title && /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Title, { className: "text-base font-semibold", children: title }),
742
+ description && /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Description, { className: "mt-0.5 text-xs text-muted-foreground", children: description })
757
743
  ] }),
758
744
  /* @__PURE__ */ jsxRuntime.jsx(
759
- "button",
745
+ dialog.Dialog.Close,
760
746
  {
761
- type: "button",
762
747
  "aria-label": "Fermer",
763
748
  className: "-mr-1 shrink-0 rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-foreground",
764
- onClick: onClose,
765
749
  children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { className: "size-4" })
766
750
  }
767
751
  )
@@ -770,7 +754,7 @@ function SidePanel({
770
754
  ]
771
755
  }
772
756
  )
773
- ] });
757
+ ] }) });
774
758
  }
775
759
  var COPY_FEEDBACK_MS = 2e3;
776
760
  function CopyButton({
@@ -813,68 +797,110 @@ function Popover({
813
797
  align = "start",
814
798
  className
815
799
  }) {
816
- const root = React__namespace.useRef(null);
817
- React__namespace.useEffect(() => {
818
- if (!open) return;
819
- const onPointerDown = (e) => {
820
- if (!root.current?.contains(e.target)) onOpenChange(false);
821
- };
822
- const onKeyDown = (e) => {
823
- if (e.key === "Escape") onOpenChange(false);
824
- };
825
- document.addEventListener("mousedown", onPointerDown);
826
- document.addEventListener("keydown", onKeyDown);
827
- return () => {
828
- document.removeEventListener("mousedown", onPointerDown);
829
- document.removeEventListener("keydown", onKeyDown);
830
- };
831
- }, [open, onOpenChange]);
832
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: root, className: "relative inline-block", children: [
833
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-expanded": open, onClick: () => onOpenChange(!open), children: trigger }),
834
- open && /* @__PURE__ */ jsxRuntime.jsx(
800
+ return (
801
+ // The callback is re-wrapped so Base UI's event-details argument stops at the
802
+ // SDK boundary: the engine is an implementation detail, not part of the API.
803
+ /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover.Root, { open, onOpenChange: (next) => onOpenChange(next), children: [
804
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block", children: /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Trigger, { ...triggerProps(trigger) }) }),
805
+ /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Positioner, { side: "bottom", align, sideOffset: 4, className: "z-50", children: /* @__PURE__ */ jsxRuntime.jsx(
806
+ popover.Popover.Popup,
807
+ {
808
+ className: cn("min-w-48 rounded-md border bg-card p-2 shadow-lg", className),
809
+ children
810
+ }
811
+ ) }) })
812
+ ] })
813
+ );
814
+ }
815
+ function DropdownMenu({ trigger, items, align = "end", className }) {
816
+ return /* @__PURE__ */ jsxRuntime.jsxs(menu.Menu.Root, { children: [
817
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block", children: /* @__PURE__ */ jsxRuntime.jsx(menu.Menu.Trigger, { ...triggerProps(trigger) }) }),
818
+ /* @__PURE__ */ jsxRuntime.jsx(menu.Menu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(menu.Menu.Positioner, { side: "bottom", align, sideOffset: 4, className: "z-50", children: /* @__PURE__ */ jsxRuntime.jsx(
819
+ menu.Menu.Popup,
820
+ {
821
+ className: cn(
822
+ "flex min-w-48 flex-col rounded-md border bg-card p-1 shadow-lg",
823
+ className
824
+ ),
825
+ children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(
826
+ menu.Menu.Item,
827
+ {
828
+ disabled: item.disabled,
829
+ onClick: item.onSelect,
830
+ className: cn(
831
+ "cursor-pointer rounded px-2 py-1.5 text-left text-sm outline-none data-[highlighted]:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
832
+ item.destructive && "text-destructive"
833
+ ),
834
+ children: item.label
835
+ },
836
+ i
837
+ ))
838
+ }
839
+ ) }) })
840
+ ] });
841
+ }
842
+ var ToastContext = React.createContext(null);
843
+ function useToast() {
844
+ const toast = React.useContext(ToastContext);
845
+ if (!toast) throw new Error("useToast must be used within ToastProvider");
846
+ return toast;
847
+ }
848
+ var TOAST_TTL_MS = 4e3;
849
+ var TONE = {
850
+ success: "border-l-status-success",
851
+ error: "border-l-status-blocked",
852
+ info: "border-l-status-info"
853
+ };
854
+ function ToastProvider({ children }) {
855
+ const [toasts, setToasts] = React.useState([]);
856
+ const nextId = React.useRef(0);
857
+ const toast = React.useCallback((input) => {
858
+ const id = nextId.current++;
859
+ setToasts((prev) => [...prev, { id, ...input }]);
860
+ setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), TOAST_TTL_MS);
861
+ }, []);
862
+ return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value: toast, children: [
863
+ children,
864
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none fixed bottom-4 right-4 z-[60] flex w-80 flex-col gap-2", children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsxs(
835
865
  "div",
836
866
  {
867
+ role: "status",
837
868
  className: cn(
838
- "absolute z-40 mt-1 min-w-48 rounded-md border bg-card p-2 shadow-lg",
839
- align === "end" ? "right-0" : "left-0",
869
+ "pointer-events-auto rounded-lg border border-l-4 bg-card px-4 py-3 text-foreground shadow-md",
870
+ TONE[t.variant ?? "success"]
871
+ ),
872
+ children: [
873
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: t.title }),
874
+ t.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 break-all text-xs text-muted-foreground", children: t.description })
875
+ ]
876
+ },
877
+ t.id
878
+ )) })
879
+ ] });
880
+ }
881
+ var SIDES = {
882
+ top: "bottom-full left-1/2 -translate-x-1/2 mb-1.5",
883
+ right: "left-full top-1/2 -translate-y-1/2 ml-1.5",
884
+ bottom: "top-full left-1/2 -translate-x-1/2 mt-1.5",
885
+ left: "right-full top-1/2 -translate-y-1/2 mr-1.5"
886
+ };
887
+ function Tooltip({ label, children, side = "top", className }) {
888
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "group/tt relative inline-flex", children: [
889
+ children,
890
+ /* @__PURE__ */ jsxRuntime.jsx(
891
+ "span",
892
+ {
893
+ role: "tooltip",
894
+ className: cn(
895
+ "pointer-events-none absolute z-50 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background opacity-0 shadow-md transition-opacity group-hover/tt:opacity-100",
896
+ SIDES[side],
840
897
  className
841
898
  ),
842
- children
899
+ children: label
843
900
  }
844
901
  )
845
902
  ] });
846
903
  }
847
- function DropdownMenu({ trigger, items, align = "end", className }) {
848
- const [open, setOpen] = React__namespace.useState(false);
849
- return /* @__PURE__ */ jsxRuntime.jsx(
850
- Popover,
851
- {
852
- open,
853
- onOpenChange: setOpen,
854
- trigger,
855
- align,
856
- className: cn("p-1", className),
857
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { role: "menu", className: "flex flex-col", children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(
858
- "button",
859
- {
860
- type: "button",
861
- role: "menuitem",
862
- disabled: item.disabled,
863
- className: cn(
864
- "cursor-pointer rounded px-2 py-1.5 text-left text-sm hover:bg-accent disabled:pointer-events-none disabled:opacity-50",
865
- item.destructive && "text-destructive"
866
- ),
867
- onClick: () => {
868
- setOpen(false);
869
- item.onSelect();
870
- },
871
- children: item.label
872
- },
873
- i
874
- )) })
875
- }
876
- );
877
- }
878
904
 
879
905
  exports.AppApiProvider = AppApiProvider;
880
906
  exports.AppContextProvider = AppContextProvider;
@@ -883,6 +909,8 @@ exports.Badge = Badge;
883
909
  exports.Button = Button;
884
910
  exports.Card = Card;
885
911
  exports.CardContent = CardContent;
912
+ exports.CardDescription = CardDescription;
913
+ exports.CardFooter = CardFooter;
886
914
  exports.CardHeader = CardHeader;
887
915
  exports.CardTitle = CardTitle;
888
916
  exports.Checkbox = Checkbox;
@@ -908,6 +936,10 @@ exports.TabsContent = TabsContent;
908
936
  exports.TabsList = TabsList;
909
937
  exports.TabsTrigger = TabsTrigger;
910
938
  exports.Textarea = Textarea;
939
+ exports.ToastProvider = ToastProvider;
940
+ exports.Tooltip = Tooltip;
941
+ exports.badgeVariants = badgeVariants;
942
+ exports.buttonVariants = buttonVariants;
911
943
  exports.cn = cn;
912
944
  exports.copyToClipboard = copyToClipboard;
913
945
  exports.downloadBlob = downloadBlob;
@@ -918,5 +950,6 @@ exports.useAppContext = useAppContext;
918
950
  exports.useAppRun = useAppRun;
919
951
  exports.useAppRuns = useAppRuns;
920
952
  exports.useAppSettings = useAppSettings;
953
+ exports.useToast = useToast;
921
954
  //# sourceMappingURL=index.cjs.map
922
955
  //# sourceMappingURL=index.cjs.map