@opencxh/ui-kit 3.137.11 → 3.137.13

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.
@@ -0,0 +1,85 @@
1
+ import { default as React, ReactNode } from 'react';
2
+ import { ContentLoadingShape } from '../feedback/ContentLoading';
3
+ import { PageHeaderAction } from './PageHeader';
4
+ /**
5
+ * The `md` gutter matches `PageHeader`'s own horizontal padding, so the table
6
+ * or form below lines up with the title above it. The top value is small
7
+ * because the header already supplies most of that gap (`pt-4 pb-2.5`).
8
+ */
9
+ declare const paddingClasses: {
10
+ readonly none: "";
11
+ readonly sm: "px-3 pb-3 pt-1";
12
+ readonly md: "px-5 pb-5 pt-1.5";
13
+ };
14
+ export interface PageProps {
15
+ /** Page title, rendered by `PageHeader`. */
16
+ title: string;
17
+ /** Short description beside the title. */
18
+ subtitle?: string;
19
+ /** Line under the title — a count, a record, a timestamp. */
20
+ meta?: ReactNode;
21
+ /** Toolbar actions, right-aligned in the header. */
22
+ actions?: PageHeaderAction[];
23
+ /** Renders the back chevron beside the title. */
24
+ onBack?: () => void;
25
+ /**
26
+ * Draws the rule under the header. Defaults to `scroll`, because a header
27
+ * that stays put while the body scrolls needs an edge — without it the first
28
+ * row slides up against the title with nothing between them. A page that
29
+ * scrolls as a whole does not, so the rule would just be a stray line.
30
+ */
31
+ surface?: boolean;
32
+ /** Full-width row at the bottom of the header — a filter strip, a tab bar. */
33
+ headerContent?: ReactNode;
34
+ /** Swaps the body for a skeleton. */
35
+ loading?: boolean;
36
+ /**
37
+ * What the body is about to render, so the placeholder has the right shape.
38
+ * Most app pages are a table; a detail page is usually a form.
39
+ */
40
+ loadingShape?: ContentLoadingShape;
41
+ /**
42
+ * Rendered instead of `children` when there is nothing to show — pass an
43
+ * `EmptyState`. Ignored while `loading`, so an empty list does not flash its
44
+ * "nothing here" message during the first fetch.
45
+ */
46
+ empty?: ReactNode;
47
+ padding?: keyof typeof paddingClasses;
48
+ /**
49
+ * Whether the page owns its scroll. `true` (default) pins the header and
50
+ * scrolls the body — the right behaviour for a table you page through.
51
+ * Set `false` when the page is rendered inside something that already
52
+ * scrolls and should flow with it.
53
+ */
54
+ scroll?: boolean;
55
+ className?: string;
56
+ contentClassName?: string;
57
+ children?: ReactNode;
58
+ }
59
+ /**
60
+ * One app page: the header, the content gutter, the scroll container, and the
61
+ * loading and empty states.
62
+ *
63
+ * These four were re-invented per page — 24 times in `eylo-voip` alone, each
64
+ * with its own gutter — which is how `p-4` there and `px-5` everywhere else
65
+ * ended up side by side in the same product. Reach for this instead of pairing
66
+ * a `PageHeader` with a padded `div`.
67
+ *
68
+ * Settings panels are the exception: they live inside the settings frame, which
69
+ * draws its own title and scrolls itself, so they use `SettingsPage`.
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * <Page
74
+ * title={t("interactions")}
75
+ * subtitle={t("interactions_description")}
76
+ * actions={[{ id: "refresh", label: t("refresh"), onClick: reload }]}
77
+ * loading={loading}
78
+ * empty={!interactions.length && <EmptyState title={t("interaction_no_records_found")} />}
79
+ * >
80
+ * <InteractionsTable interactions={interactions} />
81
+ * </Page>
82
+ * ```
83
+ */
84
+ export declare const Page: React.FC<PageProps>;
85
+ export {};
@@ -2,6 +2,7 @@ export { cn, createSizes, createVariants } from './utils/cn';
2
2
  export { useAnchoredPosition, type AnchoredPositionOptions } from './utils/useAnchoredPosition';
3
3
  export { IDENTITY_TONE_COUNT, identityDotClass, identityPlateClass, identityTone, type IdentityTone } from './utils/identity';
4
4
  export { catTone, type CatTone } from './utils/catTone';
5
+ export { normalizeText, text } from './utils/text';
5
6
  export { MeetingGrid, type MeetingGridProps } from './meeting/MeetingGrid';
6
7
  export { ParticipantTile, type ParticipantTileProps } from './meeting/ParticipantTile';
7
8
  export { VideoSurface, type VideoSurfaceProps } from './meeting/VideoSurface';
@@ -44,6 +45,7 @@ export { Image, type ImageProps } from './content/Image';
44
45
  export { List, ListBulkAction, type ListGroup, type ListProps } from './content/List';
45
46
  export { MessageBubble, type MessageBubbleProps } from './content/MessageBubble';
46
47
  export { KpiCard, type KpiCardProps } from './content/KpiCard';
48
+ export { Page, type PageProps } from './content/Page';
47
49
  export { PageHeader, type PageHeaderAction, type PageHeaderProps } from './content/PageHeader';
48
50
  export { PageToolbar, PageToolbarActions, type PageToolbarActionsProps, type PageToolbarProps } from './content/PageToolbar';
49
51
  export { RichText, type RichTextProps } from './content/RichText';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Coerce an unknown value to text.
3
+ *
4
+ * Every input component in this kit declares its `value` as `string`, and every
5
+ * one of them is fed straight out of form data, which is `any`. A field whose
6
+ * entity type is numeric therefore arrives as a number — `eylo-voip`'s time
7
+ * rules do exactly that (`interval?: number`, `days?: number[]`) — and the two
8
+ * failure modes are both silent from the type system's side:
9
+ *
10
+ * - a hard `TypeError: (…).trim is not a function` the moment anything calls a
11
+ * string method on it;
12
+ * - a comparison that simply never matches, because `1 === "1"` is false, so a
13
+ * stored option renders as unselected with no error at all.
14
+ *
15
+ * `null` and `undefined` become `""` so callers can treat "absent" and "empty"
16
+ * alike, which is what a text input does anyway.
17
+ */
18
+ export declare const text: (value: unknown) => string;
19
+ /** {@link text} folded to a comparable key: trimmed and lower-cased. */
20
+ export declare const normalizeText: (value: unknown) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/ui-kit",
3
- "version": "3.137.11",
3
+ "version": "3.137.13",
4
4
  "description": "Theme-aware UI component library for OpenCXH platform",
5
5
  "type": "module",
6
6
  "exports": {