@apliteni/apliteni-ui 0.56.1 → 0.57.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.56.1",
3
+ "version": "0.57.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
package/react/README.md CHANGED
@@ -28,7 +28,7 @@ import '@apliteni/apliteni-ui/react/css'; // React components' shell styles (mo
28
28
  import { DataTable, Modal, Button } from '@apliteni/apliteni-ui/react';
29
29
  ```
30
30
 
31
- Components: `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Tabs`, `Button`, `Badge`, `Card`, `Callout`, `Icon`.
31
+ Components: `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Card`, `Callout`, `Icon`.
32
32
 
33
33
  `Snippet` accepts `label`, `code`, `reveal`, and `copyLabel` props. It treats `code`
34
34
  as plain text and copies it exactly as provided. After a successful clipboard write,
@@ -501,3 +501,11 @@ AppShell includes the kit’s rail and top band. Pass the current router pathnam
501
501
  <Reports />
502
502
  </AppShell>
503
503
  ```
504
+
505
+ ## Tooltip
506
+
507
+ Tooltip accepts text and inline children, without nested controls. Hovering or focusing opens it; pressing Escape, moving focus away, or moving the mouse away closes it. On touch devices, tapping toggles it. It uses the kit’s tooltip styles and moves below the trigger when there is not enough space above. Keep the same information elsewhere on the page.
508
+
509
+ ```tsx
510
+ <Tooltip text="Updated daily">Balance</Tooltip>
511
+ ```
@@ -670,3 +670,54 @@
670
670
  align-items: center;
671
671
  gap: var(--space-3);
672
672
  }
673
+
674
+ /* ../src/styles/tooltip.css */
675
+ .ui-tip-host {
676
+ position: relative;
677
+ }
678
+ .ui-tip {
679
+ --ui-tip-gap: 8px;
680
+ --ui-tip-bg: var(--bg-elevated);
681
+ position: absolute;
682
+ top: var(--ui-tip-y, 0px);
683
+ left: var(--ui-tip-x, 50%);
684
+ z-index: var(--z-dropdown);
685
+ translate: calc(-50% + var(--ui-tip-shift, 0px)) calc(-100% - var(--ui-tip-gap));
686
+ display: flex;
687
+ flex-direction: column;
688
+ width: max-content;
689
+ max-width: var(--prose-caption);
690
+ padding: var(--space-2) var(--space-3);
691
+ background: var(--ui-tip-bg);
692
+ border: 1px solid var(--border-strong);
693
+ box-shadow: inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop);
694
+ border-radius: var(--radius-sm);
695
+ font-family: var(--font-sans);
696
+ font-size: var(--text-xs);
697
+ line-height: 1.45;
698
+ color: var(--text);
699
+ pointer-events: none;
700
+ opacity: 0;
701
+ visibility: hidden;
702
+ transition: opacity var(--dur-fast) var(--ease), visibility var(--dur-fast) linear;
703
+ }
704
+ .ui-tip.is-below {
705
+ translate: calc(-50% + var(--ui-tip-shift, 0px)) var(--ui-tip-gap);
706
+ }
707
+ .ui-tip.is-open {
708
+ opacity: 1;
709
+ visibility: visible;
710
+ }
711
+ .ui-tip__label {
712
+ color: var(--text);
713
+ }
714
+ .ui-tip__value {
715
+ font-size: var(--text-sm);
716
+ font-weight: var(--weight-semibold);
717
+ color: var(--strong);
718
+ font-variant-numeric: tabular-nums;
719
+ }
720
+ .ui-tip__detail {
721
+ color: var(--text);
722
+ font-variant-numeric: tabular-nums;
723
+ }
@@ -612,7 +612,14 @@ type ThemeToggleProps = {
612
612
  };
613
613
  declare function ThemeToggle({ labelled }: ThemeToggleProps): react.JSX.Element;
614
614
 
615
+ type TooltipProps = {
616
+ text: string;
617
+ /** Inline content without nested controls. */
618
+ children: ReactNode;
619
+ };
620
+ declare function Tooltip({ text, children }: TooltipProps): react.JSX.Element;
621
+
615
622
  declare const PAGE_SIZES: readonly number[];
616
623
  declare const DEFAULT_PAGE_SIZE: number;
617
624
 
618
- export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, sortTableRows, useToast };
625
+ export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
@@ -2467,6 +2467,106 @@ function ThemeToggle({ labelled = false }) {
2467
2467
  ] }) : button;
2468
2468
  }
2469
2469
 
2470
+ // src/Tooltip.tsx
2471
+ import { useEffect as useEffect11, useId as useId10, useLayoutEffect as useLayoutEffect5, useRef as useRef15, useState as useState10 } from "react";
2472
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
2473
+ function Tooltip({ text: text2, children }) {
2474
+ const id = useId10();
2475
+ const host = useRef15(null);
2476
+ const trigger = useRef15(null);
2477
+ const tip = useRef15(null);
2478
+ const ignoreMouseUntil = useRef15(0);
2479
+ const touchMoved = useRef15(false);
2480
+ const [open, setOpen] = useState10(false);
2481
+ function place() {
2482
+ const h = host.current;
2483
+ const t = tip.current;
2484
+ const mark = trigger.current.getBoundingClientRect();
2485
+ const rect = h.getBoundingClientRect();
2486
+ const view = h.ownerDocument.documentElement;
2487
+ const clip = { top: 0, left: 0, right: view.clientWidth, bottom: view.clientHeight };
2488
+ for (let el = h; el && el !== h.ownerDocument.body; el = el.parentElement) {
2489
+ const style = getComputedStyle(el);
2490
+ if (![style.overflow, style.overflowX, style.overflowY].some((v) => v && v !== "visible")) continue;
2491
+ const bounds = el.getBoundingClientRect();
2492
+ clip.top = Math.max(clip.top, bounds.top);
2493
+ clip.left = Math.max(clip.left, bounds.left);
2494
+ clip.right = Math.min(clip.right, bounds.right);
2495
+ clip.bottom = Math.min(clip.bottom, bounds.bottom);
2496
+ }
2497
+ const gap = parseFloat(getComputedStyle(t).getPropertyValue("--ui-tip-gap")) || 8;
2498
+ const above = mark.top - clip.top;
2499
+ const below = clip.bottom - mark.bottom;
2500
+ const isBelow = above < t.offsetHeight + gap && below > above;
2501
+ t.classList.toggle("is-below", isBelow);
2502
+ const centre = mark.left + mark.width / 2;
2503
+ const ideal = centre - t.offsetWidth / 2;
2504
+ const left = Math.max(clip.left, Math.min(ideal, clip.right - t.offsetWidth));
2505
+ t.style.setProperty("--ui-tip-x", `${centre - rect.left - h.clientLeft + h.scrollLeft}px`);
2506
+ t.style.setProperty("--ui-tip-y", `${(isBelow ? mark.bottom : mark.top) - rect.top - h.clientTop + h.scrollTop}px`);
2507
+ t.style.setProperty("--ui-tip-shift", `${left - ideal}px`);
2508
+ }
2509
+ useLayoutEffect5(() => {
2510
+ if (open) place();
2511
+ }, [open, text2]);
2512
+ useEffect11(() => {
2513
+ if (!open) return;
2514
+ const doc = host.current.ownerDocument;
2515
+ const view = doc.defaultView;
2516
+ const escape = (event) => {
2517
+ if (event.key === "Escape") setOpen(false);
2518
+ };
2519
+ doc.addEventListener("keydown", escape);
2520
+ view.addEventListener("scroll", place, true);
2521
+ view.addEventListener("resize", place);
2522
+ return () => {
2523
+ doc.removeEventListener("keydown", escape);
2524
+ view.removeEventListener("scroll", place, true);
2525
+ view.removeEventListener("resize", place);
2526
+ };
2527
+ }, [open]);
2528
+ return /* @__PURE__ */ jsxs24("span", { ref: host, className: "ui-tip-host", style: { display: "inline-block" }, children: [
2529
+ /* @__PURE__ */ jsx28(
2530
+ "span",
2531
+ {
2532
+ ref: trigger,
2533
+ className: "ui-focusable",
2534
+ tabIndex: 0,
2535
+ "aria-describedby": id,
2536
+ onMouseEnter: () => {
2537
+ if (Date.now() > ignoreMouseUntil.current) setOpen(true);
2538
+ },
2539
+ onMouseLeave: () => {
2540
+ if (Date.now() > ignoreMouseUntil.current) setOpen(false);
2541
+ },
2542
+ onFocus: () => {
2543
+ if (Date.now() > ignoreMouseUntil.current) setOpen(true);
2544
+ },
2545
+ onBlur: () => setOpen(false),
2546
+ onKeyDown: (event) => {
2547
+ if (event.key === "Escape") setOpen(false);
2548
+ },
2549
+ onTouchStart: () => {
2550
+ ignoreMouseUntil.current = Date.now() + 800;
2551
+ touchMoved.current = false;
2552
+ },
2553
+ onTouchMove: () => {
2554
+ touchMoved.current = true;
2555
+ },
2556
+ onTouchCancel: () => {
2557
+ touchMoved.current = true;
2558
+ },
2559
+ onTouchEnd: () => {
2560
+ ignoreMouseUntil.current = Date.now() + 800;
2561
+ if (!touchMoved.current) setOpen((value) => !value);
2562
+ },
2563
+ children
2564
+ }
2565
+ ),
2566
+ /* @__PURE__ */ jsx28("span", { ref: tip, id, role: "tooltip", className: `ui-tip${open ? " is-open" : ""}`, children: /* @__PURE__ */ jsx28("span", { className: "ui-tip__label", children: text2 }) })
2567
+ ] });
2568
+ }
2569
+
2470
2570
  // src/index.ts
2471
2571
  var PAGE_SIZES = KIT_PAGE_SIZES;
2472
2572
  var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
@@ -2510,6 +2610,7 @@ export {
2510
2610
  ThemeToggle,
2511
2611
  Timeline,
2512
2612
  Toast,
2613
+ Tooltip,
2513
2614
  sortTableRows,
2514
2615
  useToast
2515
2616
  };