@apliteni/apliteni-ui 0.43.1 → 0.44.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.43.1",
3
+ "version": "0.44.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: `DataTable`, `Pagination`, `StatBand`, `Modal`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Button`, `Badge`, `Card`, `Icon`.
31
+ Components: `DataTable`, `Pagination`, `StatBand`, `Modal`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Tabs`, `Button`, `Badge`, `Card`, `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,
@@ -332,3 +332,9 @@ import '@apliteni/apliteni-ui/react/css';
332
332
  ]} />
333
333
  </DrawerSection>
334
334
  ```
335
+
336
+ ## Tabs
337
+
338
+ Tabs is controlled through `value` and `onChange`. Pass items with unique values, labels, optional counts, and React panel content. Provide a label for the tablist and a value that matches one item. Inactive panel content is unmounted.
339
+
340
+ Arrow keys move between tabs and wrap from the last tab to the first. They also activate the selected tab. Home and End select the first and last tabs. Tab moves to the selected panel. New panels use the kit’s fade animation, which is shortened when reduced motion is enabled.
@@ -450,7 +450,21 @@ type SnippetProps = {
450
450
  };
451
451
  declare function Snippet({ label, code, reveal, copyLabel }: SnippetProps): react.JSX.Element;
452
452
 
453
+ type TabItem = {
454
+ value: string;
455
+ label: string;
456
+ count?: number;
457
+ panel: ReactNode;
458
+ };
459
+ type TabsProps = {
460
+ items: TabItem[];
461
+ value: string;
462
+ onChange: (value: string) => void;
463
+ label: string;
464
+ };
465
+ declare function Tabs({ items, value, onChange, label }: TabsProps): react.JSX.Element;
466
+
453
467
  declare const PAGE_SIZES: readonly number[];
454
468
  declare const DEFAULT_PAGE_SIZE: number;
455
469
 
456
- export { BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, 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, 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, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TableSort, sortTableRows };
470
+ export { BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, 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, 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, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TabItem, type TableSort, Tabs, type TabsProps, sortTableRows };
@@ -1748,6 +1748,83 @@ function Snippet({ label = "shell", code = "", reveal = false, copyLabel = "Copy
1748
1748
  ] });
1749
1749
  }
1750
1750
 
1751
+ // src/Tabs.tsx
1752
+ import { useEffect as useEffect6, useId as useId6, useRef as useRef10 } from "react";
1753
+ import { playEntrance } from "@apliteni/apliteni-ui/motion";
1754
+ import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
1755
+ function Tabs({ items, value, onChange, label }) {
1756
+ const id = useId6();
1757
+ const previous = useRef10(value);
1758
+ const activePanel = useRef10(null);
1759
+ const buttons = useRef10([]);
1760
+ useEffect6(() => {
1761
+ if (previous.current !== value) playEntrance(activePanel.current);
1762
+ previous.current = value;
1763
+ }, [value]);
1764
+ return /* @__PURE__ */ jsxs15("div", { className: "ui-tabs", children: [
1765
+ /* @__PURE__ */ jsx19("div", { className: "ui-tabs__list", role: "tablist", "aria-label": label, children: items.map((item, i) => /* @__PURE__ */ jsxs15(
1766
+ "button",
1767
+ {
1768
+ type: "button",
1769
+ role: "tab",
1770
+ ref: (el) => {
1771
+ buttons.current[i] = el;
1772
+ },
1773
+ className: `ui-tabs__tab${item.value === value ? " is-active" : ""}`,
1774
+ id: `${id}-tab-${i}`,
1775
+ "aria-controls": `${id}-panel-${i}`,
1776
+ "aria-selected": item.value === value,
1777
+ tabIndex: item.value === value ? 0 : -1,
1778
+ onClick: () => onChange(item.value),
1779
+ onKeyDown: (event) => {
1780
+ let next;
1781
+ switch (event.key) {
1782
+ case "ArrowRight":
1783
+ next = (i + 1) % items.length;
1784
+ break;
1785
+ case "ArrowLeft":
1786
+ next = (i - 1 + items.length) % items.length;
1787
+ break;
1788
+ case "Home":
1789
+ next = 0;
1790
+ break;
1791
+ case "End":
1792
+ next = items.length - 1;
1793
+ break;
1794
+ default:
1795
+ return;
1796
+ }
1797
+ event.preventDefault();
1798
+ buttons.current[next]?.focus();
1799
+ onChange(items[next].value);
1800
+ },
1801
+ children: [
1802
+ item.label,
1803
+ item.count != null && /* @__PURE__ */ jsxs15(Fragment5, { children: [
1804
+ " ",
1805
+ item.count
1806
+ ] })
1807
+ ]
1808
+ },
1809
+ item.value
1810
+ )) }),
1811
+ /* @__PURE__ */ jsx19("div", { className: "ui-tabs__panels", children: items.map((item, i) => /* @__PURE__ */ jsx19(
1812
+ "div",
1813
+ {
1814
+ className: "ui-tabs__panel",
1815
+ role: "tabpanel",
1816
+ ref: item.value === value ? activePanel : void 0,
1817
+ id: `${id}-panel-${i}`,
1818
+ "aria-labelledby": `${id}-tab-${i}`,
1819
+ tabIndex: 0,
1820
+ hidden: item.value !== value,
1821
+ children: item.value === value ? item.panel : null
1822
+ },
1823
+ item.value
1824
+ )) })
1825
+ ] });
1826
+ }
1827
+
1751
1828
  // src/index.ts
1752
1829
  var PAGE_SIZES = KIT_PAGE_SIZES;
1753
1830
  var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
@@ -1778,5 +1855,6 @@ export {
1778
1855
  SkeletonTable,
1779
1856
  Snippet,
1780
1857
  StatBand,
1858
+ Tabs,
1781
1859
  sortTableRows
1782
1860
  };