@apliteni/apliteni-ui 0.41.5 → 0.43.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.41.5",
3
+ "version": "0.43.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
package/react/README.md CHANGED
@@ -28,7 +28,19 @@ 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`, `Button`, `Badge`, `Card`, `Icon`.
31
+ Components: `DataTable`, `Pagination`, `StatBand`, `Modal`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Button`, `Badge`, `Card`, `Icon`.
32
+
33
+ `Snippet` accepts `label`, `code`, `reveal`, and `copyLabel` props. It treats `code`
34
+ as plain text and copies it exactly as provided. After a successful clipboard write,
35
+ it shows “Copied” for 1.4 seconds. If copying fails, it shows “Copy failed” so readers
36
+ can try again or select the text manually.
37
+
38
+ ```tsx
39
+ <Snippet label="Terminal" code="npm install @apliteni/apliteni-ui" />
40
+ ```
41
+
42
+ Before a reveal snippet, explain that the secret is stored hashed and will not be
43
+ shown again, and ask the reader to copy it now. The page decides when to remove it.
32
44
 
33
45
  `Button` accepts `size="xs" | "sm" | "md" | "lg"` (default `md`). Use `xs`
34
46
  for a small inline control: `<Button size="xs" variant="ghost" icon="copy" iconOnly>Copy</Button>`.
@@ -73,8 +85,7 @@ focus is already back on the opener and the dialog takes no clicks.
73
85
  A panel that slides in from an edge of the screen over a scrim. It renders the vanilla
74
86
  `drawer()` markup, class for class, so it looks and moves like the kit's drawer, and like
75
87
  `Pagination` its styles come from `@apliteni/apliteni-ui/css` rather than from this bundle.
76
- Group what goes inside it the way `drawerSection()` does — a heading over a `<dl>` of label
77
- and value rows — rather than in cards; the rules are on Guidelines / Drawers.
88
+ Group its contents with `DrawerSection` and `KeyValueList`; see Guidelines / Drawers.
78
89
 
79
90
  ```tsx
80
91
  <Drawer open={open} title="Transaction" onClose={() => setOpen(false)}
@@ -292,3 +303,32 @@ Clearing the page-jump box, or typing into it and then pressing a step, does not
292
303
  page.
293
304
 
294
305
  `pageSize` defaults to `DEFAULT_PAGE_SIZE` (100). **Breaking:** it used to default to 4.
306
+
307
+ ## KeyValueList and DrawerSection
308
+
309
+ KeyValueList displays one record’s facts with labels beside their values. Pass rows
310
+ with a label and a React value. Set `columns={2}` to show two label-value pairs on
311
+ each line. Null and undefined values are displayed as an em dash. Boolean values
312
+ are displayed as `false` or `true`, zero remains visible, and strings are preserved.
313
+ Set `redacted` on a row to display “Hidden” with an eye-off icon without rendering
314
+ the value. Below 560px, each label appears above its value, and the layout
315
+ changes to one column.
316
+
317
+ DrawerSection groups content under a heading and adds a hairline between adjacent
318
+ sections. Its `headingLevel` defaults to 3. Import both the kit CSS and the React
319
+ CSS. Use a table for figures that readers need to compare down a column.
320
+
321
+ ```tsx
322
+ import { DrawerSection, KeyValueList } from '@apliteni/apliteni-ui/react';
323
+ import '@apliteni/apliteni-ui/css';
324
+ import '@apliteni/apliteni-ui/react/css';
325
+
326
+ <DrawerSection title="Transaction">
327
+ <KeyValueList rows={[
328
+ { label: 'Reference', value: <a href="/invoices/1001">INV-1001</a> },
329
+ { label: 'Amount', value: '€ 1,240.00' },
330
+ { label: 'Account', redacted: true },
331
+ { label: 'Note' },
332
+ ]} />
333
+ </DrawerSection>
334
+ ```
@@ -114,3 +114,31 @@
114
114
  transition: none;
115
115
  }
116
116
  }
117
+
118
+ /* src/KeyValueList.css */
119
+ .ui-drawer__rows.ui-kv {
120
+ grid-template-columns: calc(var(--space-6) * 5) minmax(0, 1fr);
121
+ }
122
+ .ui-drawer__rows.ui-kv--two-columns {
123
+ grid-template-columns: repeat(2, calc(var(--space-6) * 5) minmax(0, 1fr));
124
+ }
125
+ .ui-kv__hidden {
126
+ display: inline-flex;
127
+ align-items: center;
128
+ gap: var(--space-1);
129
+ color: var(--text);
130
+ }
131
+ .ui-kv__hidden svg {
132
+ width: var(--space-4);
133
+ height: var(--space-4);
134
+ stroke-width: 2.25;
135
+ }
136
+ @media (max-width: 560px) {
137
+ .ui-drawer__rows.ui-kv {
138
+ grid-template-columns: minmax(0, 1fr);
139
+ row-gap: var(--space-3);
140
+ }
141
+ .ui-kv > .ui-drawer__row {
142
+ display: block;
143
+ }
144
+ }
@@ -425,7 +425,32 @@ type FilterBarProps = {
425
425
  };
426
426
  declare function FilterBar(props: FilterBarProps): react.JSX.Element;
427
427
 
428
+ type KeyValueRow = {
429
+ label: string;
430
+ value?: ReactNode;
431
+ redacted?: boolean;
432
+ };
433
+ type KeyValueListProps = Omit<ComponentPropsWithoutRef<'dl'>, 'children'> & {
434
+ rows: readonly KeyValueRow[];
435
+ columns?: 1 | 2;
436
+ };
437
+ declare function KeyValueList({ rows, columns, className, ...rest }: KeyValueListProps): react.JSX.Element;
438
+ type DrawerSectionProps = Omit<ComponentPropsWithoutRef<'section'>, 'title'> & {
439
+ title?: ReactNode;
440
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
441
+ };
442
+ declare function DrawerSection({ title, headingLevel, className, children, ...rest }: DrawerSectionProps): react.JSX.Element;
443
+
444
+ type SnippetProps = {
445
+ label?: string;
446
+ /** Plain text, displayed and copied without HTML parsing. */
447
+ code?: string;
448
+ reveal?: boolean;
449
+ copyLabel?: string;
450
+ };
451
+ declare function Snippet({ label, code, reveal, copyLabel }: SnippetProps): react.JSX.Element;
452
+
428
453
  declare const PAGE_SIZES: readonly number[];
429
454
  declare const DEFAULT_PAGE_SIZE: number;
430
455
 
431
- 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, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, type Filter, FilterBar, type FilterBarProps, Icon, 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, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TableSort, sortTableRows };
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 };
@@ -1685,6 +1685,69 @@ function FilterBar(props) {
1685
1685
  return /* @__PURE__ */ jsx16("div", { ref: host, dangerouslySetInnerHTML: initial.current });
1686
1686
  }
1687
1687
 
1688
+ // src/KeyValueList.tsx
1689
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1690
+ function detail({ value, redacted }) {
1691
+ if (redacted) return /* @__PURE__ */ jsxs13("span", { className: "ui-kv__hidden", children: [
1692
+ /* @__PURE__ */ jsx17(Icon, { name: "eyeOff" }),
1693
+ "Hidden"
1694
+ ] });
1695
+ if (value == null) return "\u2014";
1696
+ return typeof value === "boolean" ? String(value) : value;
1697
+ }
1698
+ function KeyValueList({ rows, columns = 1, className, ...rest }) {
1699
+ return /* @__PURE__ */ jsx17("dl", { ...rest, className: ["ui-drawer__rows", "ui-kv", columns === 2 && "ui-kv--two-columns", className].filter(Boolean).join(" "), children: rows.map((row, index) => /* @__PURE__ */ jsxs13("div", { className: "ui-drawer__row", children: [
1700
+ /* @__PURE__ */ jsx17("dt", { children: row.label }),
1701
+ /* @__PURE__ */ jsx17("dd", { children: detail(row) })
1702
+ ] }, index)) });
1703
+ }
1704
+ function DrawerSection({ title, headingLevel = 3, className, children, ...rest }) {
1705
+ const Heading = `h${headingLevel}`;
1706
+ return /* @__PURE__ */ jsxs13("section", { ...rest, className: ["ui-drawer__section", className].filter(Boolean).join(" "), children: [
1707
+ title != null && /* @__PURE__ */ jsx17(Heading, { className: "ui-drawer__section-title", children: title }),
1708
+ children
1709
+ ] });
1710
+ }
1711
+
1712
+ // src/Snippet.tsx
1713
+ import { useEffect as useEffect5, useRef as useRef9, useState as useState6 } from "react";
1714
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1715
+ function Snippet({ label = "shell", code = "", reveal = false, copyLabel = "Copy" }) {
1716
+ const [status, setStatus] = useState6("idle");
1717
+ const request = useRef9(0);
1718
+ const timer = useRef9(void 0);
1719
+ useEffect5(() => {
1720
+ setStatus("idle");
1721
+ return () => {
1722
+ request.current++;
1723
+ clearTimeout(timer.current);
1724
+ };
1725
+ }, [code]);
1726
+ async function copy() {
1727
+ const current = ++request.current;
1728
+ clearTimeout(timer.current);
1729
+ setStatus("idle");
1730
+ try {
1731
+ await navigator.clipboard.writeText(code);
1732
+ if (current !== request.current) return;
1733
+ setStatus("copied");
1734
+ timer.current = setTimeout(() => setStatus("idle"), 1400);
1735
+ } catch {
1736
+ if (current === request.current) setStatus("failed");
1737
+ }
1738
+ }
1739
+ return /* @__PURE__ */ jsxs14("div", { className: reveal ? "ui-snippet ui-snippet--reveal" : "ui-snippet", children: [
1740
+ /* @__PURE__ */ jsxs14("div", { className: "ui-snippet__bar", children: [
1741
+ /* @__PURE__ */ jsx18("span", { children: label }),
1742
+ /* @__PURE__ */ jsxs14("button", { type: "button", className: "ui-snippet__copy", "aria-live": "polite", "aria-atomic": "true", onClick: copy, children: [
1743
+ /* @__PURE__ */ jsx18(Icon, { name: status === "copied" ? "check" : "copy" }),
1744
+ status === "copied" ? "Copied" : status === "failed" ? "Copy failed" : copyLabel
1745
+ ] })
1746
+ ] }),
1747
+ /* @__PURE__ */ jsx18("pre", { children: code })
1748
+ ] });
1749
+ }
1750
+
1688
1751
  // src/index.ts
1689
1752
  var PAGE_SIZES = KIT_PAGE_SIZES;
1690
1753
  var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
@@ -1700,9 +1763,11 @@ export {
1700
1763
  DeltaValue,
1701
1764
  Denied,
1702
1765
  Drawer,
1766
+ DrawerSection,
1703
1767
  Dropdown,
1704
1768
  FilterBar,
1705
1769
  Icon,
1770
+ KeyValueList,
1706
1771
  Modal,
1707
1772
  NumericValue,
1708
1773
  PAGE_SIZES,
@@ -1711,6 +1776,7 @@ export {
1711
1776
  Segmented,
1712
1777
  Skeleton,
1713
1778
  SkeletonTable,
1779
+ Snippet,
1714
1780
  StatBand,
1715
1781
  sortTableRows
1716
1782
  };
@@ -14,7 +14,7 @@
14
14
  align-items: center;
15
15
  padding: 10px 15px;
16
16
  }
17
- .ui-snippet__bar span {
17
+ .ui-snippet__bar > span {
18
18
  /* rank: label */
19
19
  font-size: var(--text-sm);
20
20
  font-weight: var(--weight-medium);
@@ -50,6 +50,7 @@
50
50
  transition: color var(--dur-fast) var(--ease);
51
51
  }
52
52
  .ui-snippet__copy:hover { color: var(--accent); }
53
+ .ui-snippet--reveal .ui-snippet__copy:hover { color: var(--strong); }
53
54
  .ui-snippet__copy svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2.8; }
54
55
 
55
56
  /* Shell token highlighting (matches viz/account.mjs classes) */
@@ -63,8 +64,8 @@
63
64
  .ui-snippet--reveal {
64
65
  background: color-mix(in srgb, var(--green) 10%, var(--surface)); --ring-gap: color-mix(in srgb, var(--green) 10%, var(--surface));
65
66
  }
66
- .ui-snippet--reveal .ui-snippet__bar span { color: var(--green); }
67
- .ui-snippet--reveal pre { color: var(--green); word-break: break-all; white-space: pre-wrap; }
67
+ .ui-snippet--reveal .ui-snippet__bar > span { color: var(--chip-success-ink); }
68
+ .ui-snippet--reveal pre { color: var(--chip-success-ink); word-break: break-all; white-space: pre-wrap; }
68
69
 
69
70
  /* Inline code */
70
71
  .ui-code {