@apliteni/apliteni-ui 0.49.2 → 0.51.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.49.2",
3
+ "version": "0.51.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`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Tabs`, `Button`, `Badge`, `Card`, `Callout`, `Icon`.
31
+ Components: `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `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,
@@ -77,6 +77,13 @@ vanilla factory where the steps have to be real links. `PAGE_SIZES` and
77
77
  either number. They are declared in this package's own types, and `PAGE_SIZES` is a
78
78
  `readonly number[]`: pass it to `pageSizes`, but do not add sizes to it.
79
79
 
80
+ ## EmptyState
81
+
82
+ `EmptyState` uses the vanilla layout. `variant` selects `first-run`, `no-matches`,
83
+ `not-found`, or `not-yet-built` copy; `title`, `sub`, and `icon` override it.
84
+ `art` accepts a vanilla illustration name, trusted SVG string, or React node; `actions` accepts React nodes. Use at most one primary action and one ghost
85
+ action. The page supplies its own h1; EmptyState uses vanilla’s div title.
86
+
80
87
  ## What the Modal does with focus
81
88
 
82
89
  Opening moves focus to the first eligible control in the body, in DOM order, or to
@@ -433,3 +440,20 @@ function SaveButton() {
433
440
 
434
441
  <Toast><SaveButton /></Toast>
435
442
  ```
443
+
444
+ ## AppShell
445
+
446
+ AppShell includes the kit’s rail and top band. Pass the current router pathname so the longest matching section path becomes active. Spread the props from `renderLink` onto your router link. The rail’s folded state is saved in a shared cookie. On phones, the bar shows up to four sections. If there are more, it shows three sections and a More sheet. Provide no more than one primary page action. The palette defaults to section links; pass `palette` to add custom commands. Import both the kit CSS and React CSS.
447
+
448
+ ```tsx
449
+ <AppShell
450
+ pathname={location.pathname}
451
+ sections={[{ href: '/reports', label: 'Reports', icon: 'chart' }]}
452
+ title="Reports"
453
+ account={{ name: 'Demo User', email: 'demo@example.com' }}
454
+ onSignOut={signOut}
455
+ renderLink={(section, { href, ...props }) => <Link to={section.href} {...props} />}
456
+ >
457
+ <Reports />
458
+ </AppShell>
459
+ ```
@@ -525,3 +525,71 @@
525
525
  .rx-toast-stack > .ui-toast {
526
526
  pointer-events: auto;
527
527
  }
528
+
529
+ /* src/AppShell.css */
530
+ .ui-react-app__bottom {
531
+ display: none;
532
+ }
533
+ .ui-react-app__actions {
534
+ display: flex;
535
+ flex-wrap: wrap;
536
+ gap: var(--space-3);
537
+ margin-bottom: var(--space-5);
538
+ }
539
+ .ui-react-app .ui-app__brand .ui-react-app__mark {
540
+ display: inline-flex;
541
+ opacity: 1;
542
+ }
543
+ .ui-react-app__bottom .ui-nav__item {
544
+ min-height: 48px;
545
+ }
546
+ @media (max-width: 560px) {
547
+ .ui-react-app {
548
+ display: block;
549
+ }
550
+ .ui-react-app > .ui-app__rail {
551
+ display: none;
552
+ }
553
+ .ui-react-app .ui-app__main {
554
+ padding-bottom: calc(96px + env(safe-area-inset-bottom));
555
+ }
556
+ .ui-react-app .ui-app__bar {
557
+ padding-inline: var(--space-4);
558
+ }
559
+ .ui-react-app .ui-app__search {
560
+ min-width: 0;
561
+ }
562
+ .ui-react-app .ui-app__search kbd {
563
+ display: none;
564
+ }
565
+ .ui-react-app__bottom {
566
+ display: block;
567
+ position: fixed;
568
+ inset: auto 0 0;
569
+ z-index: var(--z-sticky);
570
+ padding: var(--space-2) var(--space-2) calc(var(--space-2) + env(safe-area-inset-bottom));
571
+ background: var(--bg);
572
+ border-top: 1px solid var(--border);
573
+ }
574
+ .ui-react-app__bottom .ui-nav__list {
575
+ display: flex;
576
+ flex-direction: row;
577
+ }
578
+ .ui-react-app__bottom li {
579
+ flex: 1;
580
+ min-width: 0;
581
+ }
582
+ .ui-react-app__bottom .ui-nav__item {
583
+ justify-content: center;
584
+ flex-direction: column;
585
+ gap: var(--space-1);
586
+ padding-inline: var(--space-1);
587
+ }
588
+ .ui-react-app__bottom .ui-nav__label {
589
+ flex: none;
590
+ font-size: var(--text-xs);
591
+ }
592
+ .ui-react-app__bottom .ui-nav__badge {
593
+ display: none;
594
+ }
595
+ }
@@ -550,7 +550,54 @@ type ToastProps = {
550
550
  declare function useToast(): (notice: ToastNotice) => void;
551
551
  declare function Toast({ children }: ToastProps): react.JSX.Element;
552
552
 
553
+ type AppShellSection = {
554
+ href: string;
555
+ label: string;
556
+ icon: string;
557
+ count?: number;
558
+ };
559
+ type AppShellProps = {
560
+ sections: AppShellSection[];
561
+ /** Current router pathname; the longest matching section wins. */
562
+ pathname: string;
563
+ title: string;
564
+ children?: ReactNode;
565
+ word?: string;
566
+ brand?: ReactNode;
567
+ brandHref?: string;
568
+ width?: 'centered' | 'wide';
569
+ back?: {
570
+ href: string;
571
+ label: string;
572
+ };
573
+ lede?: string;
574
+ actions?: ReactNode;
575
+ bandControl?: ReactNode;
576
+ account: {
577
+ name: string;
578
+ email: string;
579
+ };
580
+ onSignOut: () => void;
581
+ /** Server-read fold preference. The browser restores the shared rail cookie. */
582
+ defaultCollapsed?: boolean;
583
+ palette?: Omit<CommandPaletteProps, 'open' | 'onClose'>;
584
+ /** Spread linkProps onto the router link, including children and onClick. */
585
+ renderLink?: (section: AppShellSection, linkProps: ComponentPropsWithoutRef<'a'>) => ReactNode;
586
+ };
587
+ declare function AppShell({ sections, pathname, title, children, word, brand, brandHref, width, back, lede, actions, bandControl, account, onSignOut, defaultCollapsed, palette, renderLink }: AppShellProps): react.JSX.Element;
588
+
589
+ type EmptyStateVariant = 'first-run' | 'no-matches' | 'not-found' | 'not-yet-built';
590
+ type EmptyStateProps = {
591
+ variant?: EmptyStateVariant;
592
+ title?: string;
593
+ sub?: string;
594
+ icon?: string;
595
+ art?: ReactNode;
596
+ actions?: ReactNode;
597
+ };
598
+ declare function EmptyState({ variant, title, sub, icon, art, actions }: EmptyStateProps): react.JSX.Element;
599
+
553
600
  declare const PAGE_SIZES: readonly number[];
554
601
  declare const DEFAULT_PAGE_SIZE: number;
555
602
 
556
- export { 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, 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, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, Timeline, type TimelineEvent, type TimelineProps, Toast, type ToastNotice, type ToastProps, sortTableRows, useToast };
603
+ 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, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, Timeline, type TimelineEvent, type TimelineProps, Toast, type ToastNotice, type ToastProps, sortTableRows, useToast };
@@ -2152,10 +2152,193 @@ function Toast({ children }) {
2152
2152
  ] });
2153
2153
  }
2154
2154
 
2155
+ // src/AppShell.tsx
2156
+ import { useEffect as useEffect9, useId as useId9, useState as useState9 } from "react";
2157
+ import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
2158
+ import { Fragment as Fragment7, jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
2159
+ var path = (value) => value.split(/[?#]/)[0].replace(/\/+$/, "") || "/";
2160
+ function AppShell({
2161
+ sections,
2162
+ pathname,
2163
+ title,
2164
+ children,
2165
+ word = "apliteni-ui",
2166
+ brand,
2167
+ brandHref = "/",
2168
+ width = "centered",
2169
+ back,
2170
+ lede,
2171
+ actions,
2172
+ bandControl,
2173
+ account,
2174
+ onSignOut,
2175
+ defaultCollapsed = false,
2176
+ palette,
2177
+ renderLink
2178
+ }) {
2179
+ const uid = useId9();
2180
+ const [collapsed, setCollapsed] = useState9(defaultCollapsed);
2181
+ const [search, setSearch] = useState9(false);
2182
+ const [more, setMore] = useState9(false);
2183
+ const [theme, setTheme] = useState9("dark");
2184
+ const current = path(pathname);
2185
+ const active = sections.filter((section) => current === path(section.href) || path(section.href) !== "/" && current.startsWith(`${path(section.href)}/`)).sort((a, b) => path(b.href).length - path(a.href).length)[0];
2186
+ useEffect9(() => {
2187
+ setCollapsed(railCollapsed() ?? defaultCollapsed);
2188
+ }, [defaultCollapsed]);
2189
+ useEffect9(() => {
2190
+ setMore(false);
2191
+ setSearch(false);
2192
+ }, [pathname]);
2193
+ useEffect9(() => {
2194
+ const root = document.documentElement;
2195
+ const sync2 = () => setTheme(root.getAttribute("data-theme") === "light" ? "light" : "dark");
2196
+ sync2();
2197
+ const observer = new MutationObserver(sync2);
2198
+ observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
2199
+ return () => observer.disconnect();
2200
+ }, []);
2201
+ useEffect9(() => {
2202
+ const hotkey = (event) => {
2203
+ if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== "k") return;
2204
+ const target = event.target;
2205
+ if (target?.closest?.('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="dialog"]')) return;
2206
+ event.preventDefault();
2207
+ setSearch(true);
2208
+ };
2209
+ document.addEventListener("keydown", hotkey);
2210
+ return () => document.removeEventListener("keydown", hotkey);
2211
+ }, []);
2212
+ const fold = () => {
2213
+ const next = !collapsed;
2214
+ setCollapsed(next);
2215
+ try {
2216
+ document.cookie = `apliteni-ui-rail=${next ? "collapsed" : "expanded"}; Max-Age=31536000; Path=/; SameSite=Lax`;
2217
+ } catch {
2218
+ }
2219
+ };
2220
+ const links = (items) => items.map((section) => {
2221
+ const linkProps = {
2222
+ href: section.href,
2223
+ className: `ui-nav__item${section === active ? " is-active" : ""}`,
2224
+ "aria-current": section === active ? "page" : void 0,
2225
+ "aria-label": `${section.label}${section.count == null ? "" : ` ${section.count}`}`,
2226
+ onClick: (event) => {
2227
+ if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
2228
+ },
2229
+ children: /* @__PURE__ */ jsxs21(Fragment7, { children: [
2230
+ /* @__PURE__ */ jsx25("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx25(Icon, { name: section.icon }) }),
2231
+ /* @__PURE__ */ jsx25("span", { className: "ui-nav__label", children: section.label }),
2232
+ section.count != null && /* @__PURE__ */ jsx25("span", { className: "ui-nav__badge", children: section.count })
2233
+ ] })
2234
+ };
2235
+ return /* @__PURE__ */ jsx25("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx25("a", { ...linkProps }) }, section.href);
2236
+ });
2237
+ const foldLabel = collapsed ? "Expand sidebar" : "Collapse sidebar";
2238
+ const visible = sections.length > 4 ? sections.slice(0, 3) : sections;
2239
+ const remaining = sections.slice(visible.length);
2240
+ const initials = account.name.trim().split(/\s+/).map((part) => part[0]).slice(0, 2).join("");
2241
+ return /* @__PURE__ */ jsxs21("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
2242
+ /* @__PURE__ */ jsxs21("div", { className: "ui-app__rail", children: [
2243
+ /* @__PURE__ */ jsx25("div", { className: "ui-app__head", children: /* @__PURE__ */ jsxs21("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
2244
+ brand ?? /* @__PURE__ */ jsx25("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
2245
+ /* @__PURE__ */ jsx25("span", { children: word })
2246
+ ] }) }),
2247
+ /* @__PURE__ */ jsx25("nav", { className: "ui-nav ui-nav--side", "aria-label": "Sections", children: /* @__PURE__ */ jsx25("ul", { className: "ui-nav__list", children: links(sections) }) }),
2248
+ /* @__PURE__ */ jsx25("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx25("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs21("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
2249
+ /* @__PURE__ */ jsx25("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs21("svg", { className: "ui-icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
2250
+ /* @__PURE__ */ jsx25("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2251
+ /* @__PURE__ */ jsx25("path", { className: "ui-app__fold-seam", d: "M9 3v18" })
2252
+ ] }) }),
2253
+ /* @__PURE__ */ jsx25("span", { className: "ui-nav__label", children: foldLabel })
2254
+ ] }) }) })
2255
+ ] }),
2256
+ /* @__PURE__ */ jsxs21("div", { className: "ui-app__well", children: [
2257
+ /* @__PURE__ */ jsxs21("header", { className: "ui-app__bar", children: [
2258
+ /* @__PURE__ */ jsxs21("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
2259
+ /* @__PURE__ */ jsx25("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx25(Icon, { name: "search" }) }),
2260
+ /* @__PURE__ */ jsx25("span", { className: "ui-app__search-txt", children: "Search or run a command\u2026" }),
2261
+ /* @__PURE__ */ jsx25("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
2262
+ ] }),
2263
+ /* @__PURE__ */ jsx25("span", { className: "ui-app__bar-gap" }),
2264
+ bandControl,
2265
+ /* @__PURE__ */ jsx25("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx25("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
2266
+ /* @__PURE__ */ jsx25("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx25(
2267
+ Dropdown,
2268
+ {
2269
+ variant: "menu",
2270
+ align: "end",
2271
+ chevron: false,
2272
+ ariaLabel: `Signed in as ${account.name}, ${account.email}`,
2273
+ triggerClass: "ui-app__user-trigger",
2274
+ panelClass: "ui-app__user-panel",
2275
+ triggerContent: /* @__PURE__ */ jsx25("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
2276
+ header: /* @__PURE__ */ jsxs21("div", { className: "ui-dropdown__head", children: [
2277
+ /* @__PURE__ */ jsx25("b", { children: account.name }),
2278
+ /* @__PURE__ */ jsx25("span", { children: account.email })
2279
+ ] }),
2280
+ items: [{ label: "Sign out", icon: "logout", danger: true }],
2281
+ onSelect: onSignOut
2282
+ }
2283
+ ) })
2284
+ ] }),
2285
+ /* @__PURE__ */ jsxs21("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
2286
+ back && /* @__PURE__ */ jsx25(BackLink, { ...back }),
2287
+ /* @__PURE__ */ jsx25("h1", { children: title }),
2288
+ lede && /* @__PURE__ */ jsx25("p", { className: "ui-app__sub", children: lede }),
2289
+ actions && /* @__PURE__ */ jsx25("div", { className: "ui-react-app__actions", children: actions }),
2290
+ /* @__PURE__ */ jsx25("div", { className: "ui-app__body", children })
2291
+ ] })
2292
+ ] }),
2293
+ /* @__PURE__ */ jsx25("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs21("ul", { className: "ui-nav__list", children: [
2294
+ links(visible),
2295
+ remaining.length > 0 && /* @__PURE__ */ jsx25("li", { children: /* @__PURE__ */ jsxs21(
2296
+ "button",
2297
+ {
2298
+ type: "button",
2299
+ className: `ui-nav__item${active && remaining.includes(active) ? " is-active" : ""}`,
2300
+ "aria-haspopup": "dialog",
2301
+ "aria-expanded": more,
2302
+ onClick: () => setMore(true),
2303
+ children: [
2304
+ /* @__PURE__ */ jsx25("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx25(Icon, { name: "moreHorizontal" }) }),
2305
+ /* @__PURE__ */ jsx25("span", { className: "ui-nav__label", children: "More" })
2306
+ ]
2307
+ }
2308
+ ) })
2309
+ ] }) }),
2310
+ /* @__PURE__ */ jsx25(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: /* @__PURE__ */ jsx25("nav", { className: "ui-nav ui-nav--side", "aria-label": "More sections", children: /* @__PURE__ */ jsx25("ul", { className: "ui-nav__list", children: links(remaining) }) }) }),
2311
+ /* @__PURE__ */ jsx25(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
2312
+ ] });
2313
+ }
2314
+
2315
+ // src/EmptyState.tsx
2316
+ import { icon as kitIcon2, illo } from "@apliteni/apliteni-ui";
2317
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
2318
+ var cases = {
2319
+ "first-run": { title: "Nothing needs you yet", sub: "New items will appear here when they need your attention.", icon: "check" },
2320
+ "no-matches": { title: "No results in this view", sub: "Try another search or clear your filters.", icon: "search" },
2321
+ "not-found": { title: "We can't find that page", sub: "Check the address or return to the home page.", icon: "folder" },
2322
+ "not-yet-built": { title: "This ships later", sub: "This screen is not available yet. Check back later.", icon: "clock" }
2323
+ };
2324
+ function EmptyState({ variant = "first-run", title, sub, icon: icon5, art, actions }) {
2325
+ const preset = cases[variant];
2326
+ const heading = title ?? preset.title;
2327
+ const guidance = sub ?? preset.sub;
2328
+ const glyph = icon5 ?? preset.icon;
2329
+ return /* @__PURE__ */ jsxs22("div", { className: "ui-empty", children: [
2330
+ art ? typeof art === "string" ? /* @__PURE__ */ jsx26("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx26("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx26("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
2331
+ heading && /* @__PURE__ */ jsx26("div", { className: "ui-empty__title", children: heading }),
2332
+ guidance && /* @__PURE__ */ jsx26("div", { className: "ui-empty__sub", children: guidance }),
2333
+ actions && /* @__PURE__ */ jsx26("div", { className: "ui-empty__actions", children: actions })
2334
+ ] });
2335
+ }
2336
+
2155
2337
  // src/index.ts
2156
2338
  var PAGE_SIZES = KIT_PAGE_SIZES;
2157
2339
  var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
2158
2340
  export {
2341
+ AppShell,
2159
2342
  BackLink,
2160
2343
  Badge,
2161
2344
  BusyRegion,
@@ -2171,6 +2354,7 @@ export {
2171
2354
  Drawer,
2172
2355
  DrawerSection,
2173
2356
  Dropdown,
2357
+ EmptyState,
2174
2358
  FileField,
2175
2359
  FilterBar,
2176
2360
  Icon,