@opencxh/ui-kit 3.109.0 → 3.110.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.
@@ -3,11 +3,11 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
3
3
  /**
4
4
  * The visual style variant of the button
5
5
  */
6
- variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive';
6
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'success' | 'warning' | 'danger-solid';
7
7
  /**
8
8
  * The size of the button
9
9
  */
10
- size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
10
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
11
11
  /**
12
12
  * Whether the button should take the full width of its container
13
13
  */
@@ -28,6 +28,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
28
28
  * Whether the button should only display an icon (no text)
29
29
  */
30
30
  iconOnly?: boolean;
31
+ /** `circle` is for a control that is only ever an icon: call answer/reject. */
32
+ shape?: 'default' | 'circle';
31
33
  }
32
34
  /**
33
35
  * Button component with theme integration and multiple variants
@@ -2,12 +2,27 @@ import { default as React } from 'react';
2
2
  export interface FilterChipProps {
3
3
  /** Chip text (e.g. "Status", or "Status: Open" when a value is set). */
4
4
  label: React.ReactNode;
5
+ /** Leading icon, before the label. */
6
+ icon?: React.ReactNode;
7
+ /**
8
+ * Meaning carried by the chip's own value — a priority, a state. Applies to
9
+ * the resting chip; `active` still wins, because "you picked this" outranks
10
+ * "this is urgent".
11
+ */
12
+ tone?: "default" | "muted" | "warning" | "danger";
13
+ /**
14
+ * `pill` is the filter-bar shape. `rect` is for a chip that labels a property
15
+ * rather than a filter, where a softer corner sits better next to inputs.
16
+ */
17
+ shape?: "pill" | "rect";
5
18
  /** Selected/active — renders the filled dark treatment. */
6
19
  active?: boolean;
7
20
  /** Show a trailing caret (opens a menu the consumer wires with Dropdown/Popover). */
8
21
  caret?: boolean;
9
22
  /** Open a value clear affordance (✕) — only shown when `active`. */
10
23
  onClear?: () => void;
24
+ /** Accessible name for the clear affordance. */
25
+ clearLabel?: string;
11
26
  onClick?: () => void;
12
27
  className?: string;
13
28
  }
@@ -23,4 +38,4 @@ export interface FilterChipProps {
23
38
  * active={!!status} onClick={openMenu} onClear={() => setStatus(null)} />
24
39
  * ```
25
40
  */
26
- export declare function FilterChip({ label, active, caret, onClear, onClick, className, }: FilterChipProps): React.JSX.Element;
41
+ export declare function FilterChip({ label, icon, tone, shape, active, caret, onClear, clearLabel, onClick, className, }: FilterChipProps): React.JSX.Element;
@@ -14,7 +14,7 @@ export interface ChannelBadgeProps {
14
14
  /**
15
15
  * Coloured channel tile (the glyph-in-rounded-square used across inbox rows,
16
16
  * the focus queue, the attention list and Recent tabs). Colours come from the
17
- * `--ch-*` design-system tokens, so it is mode-aware.
17
+ * `--color-channel-*` design-system tokens, so it is mode-aware.
18
18
  *
19
19
  * @example
20
20
  * ```tsx
@@ -24,6 +24,12 @@ export interface ListProps<T> {
24
24
  trailing?: (item: T, index: number) => React.ReactNode;
25
25
  /** Row click */
26
26
  onSelect?: (item: T, index: number) => void;
27
+ /**
28
+ * Fired when keyboard focus lands on a row. Lets a consumer mirror the
29
+ * roving focus into its own "active" state (what a hand-rolled `useArrowNav`
30
+ * used to provide) without owning the key handling.
31
+ */
32
+ onActiveIndexChange?: (index: number) => void;
27
33
  /** Highlighted row (accent wash) */
28
34
  isActive?: (item: T, index: number) => boolean;
29
35
  /** Bold + accent dot */
@@ -102,4 +108,4 @@ export interface ListProps<T> {
102
108
  * />
103
109
  * ```
104
110
  */
105
- export declare function List<T>({ items, getRowKey, variant, leading, renderItem, trailing, onSelect, isActive, unread, dimmed, groupBy, groups, renderGroupHeader, stickyGroupHeaders, collapsibleGroups, hideGroupCount, selectable, selectedItems, onSelectionChange, bulkActions, header, stickyHeader, bordered, emptyContent, loading, loadingRows, className, "aria-label": ariaLabel, }: ListProps<T>): React.JSX.Element;
111
+ export declare function List<T>({ items, getRowKey, variant, leading, renderItem, trailing, onSelect, onActiveIndexChange, isActive, unread, dimmed, groupBy, groups, renderGroupHeader, stickyGroupHeaders, collapsibleGroups, hideGroupCount, selectable, selectedItems, onSelectionChange, bulkActions, header, stickyHeader, bordered, emptyContent, loading, loadingRows, className, "aria-label": ariaLabel, }: ListProps<T>): React.JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { default as React } from 'react';
2
+ export interface MessageBubbleProps {
3
+ /** Direction. `in` is received, `out` is sent by us. */
4
+ side: "in" | "out";
5
+ /**
6
+ * Part of a run from the same sender. The bubble keeps its shape; callers use
7
+ * this to drop the repeated header and avatar.
8
+ */
9
+ continued?: boolean;
10
+ /** Border emphasis for a message that is not settled yet, e.g. a pending draft. */
11
+ tone?: "default" | "warning";
12
+ /**
13
+ * Layout only — width and padding. Those differ per surface (a chat bubble
14
+ * hugs its text, an email card fills the column and pads its own sections),
15
+ * so the component owns the shape and the caller owns the box.
16
+ */
17
+ className?: string;
18
+ children: React.ReactNode;
19
+ }
20
+ /**
21
+ * A single message in a conversation: chat, email and call transcript all use
22
+ * the same shape.
23
+ *
24
+ * The corner facing the sender is tight (`rounded-sm`) and the other three are
25
+ * `rounded-lg`, which is what makes direction readable without a tail. Sent
26
+ * messages sit on `bg-bubble-out-soft`, a 4% ink wash — deliberately lighter than
27
+ * `accent-soft`, which reads as heavy on every message you have ever sent.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * <MessageBubble side="out" className="max-w-bubble px-4 py-3">
32
+ * <Text>{body}</Text>
33
+ * </MessageBubble>
34
+ * ```
35
+ */
36
+ export declare function MessageBubble({ side, continued, tone, className, children, }: MessageBubbleProps): React.JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { default as React } from 'react';
2
+ export interface SectionDividerProps {
3
+ /** Centred label. Without one the divider is a plain rule. */
4
+ label?: React.ReactNode;
5
+ /** Right-aligned slot after the rule, e.g. a count or a status. */
6
+ trailing?: React.ReactNode;
7
+ /** Push the label to the start instead of centring it. */
8
+ align?: "center" | "start";
9
+ className?: string;
10
+ }
11
+ /**
12
+ * A hairline that separates two runs of content, optionally naming the run that
13
+ * follows: day separators in a feed, group headings in search results.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <SectionDivider label="Gisteren" />
18
+ * <SectionDivider label="Gesprekken" align="start" trailing={<Badge>12</Badge>} />
19
+ * ```
20
+ */
21
+ export declare function SectionDivider({ label, trailing, align, className }: SectionDividerProps): React.JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { default as React } from 'react';
2
+ export interface SettingsRowProps {
3
+ /** What the setting is called. */
4
+ label: React.ReactNode;
5
+ /** One line on what it does or what turning it on means. */
6
+ description?: React.ReactNode;
7
+ /** The control itself — a `Switch`, `Select`, `Button`… */
8
+ control: React.ReactNode;
9
+ /**
10
+ * Draw the row as a bordered pill. Use it for a flat list of independent
11
+ * toggles; leave it off inside a `SettingsSection`, which already groups.
12
+ */
13
+ bordered?: boolean;
14
+ disabled?: boolean;
15
+ className?: string;
16
+ }
17
+ /**
18
+ * One setting: label (+ description) on the left, its control on the right.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <SettingsRow
23
+ * label="Agenda synchroniseren"
24
+ * description="Afspraken uit dit account verschijnen in je agenda."
25
+ * control={<Switch checked={on} onChange={setOn} aria-label="Agenda synchroniseren" />}
26
+ * />
27
+ * ```
28
+ */
29
+ export declare function SettingsRow({ label, description, control, bordered, disabled, className, }: SettingsRowProps): React.JSX.Element;
30
+ export interface SettingsSectionProps {
31
+ /** Lucide icon component, rendered in the section heading. */
32
+ icon?: React.ComponentType<Record<string, unknown>>;
33
+ title: React.ReactNode;
34
+ description?: React.ReactNode;
35
+ /** Right-aligned slot in the heading, e.g. a "Reset" button. */
36
+ actions?: React.ReactNode;
37
+ children: React.ReactNode;
38
+ className?: string;
39
+ }
40
+ /**
41
+ * A titled group of `SettingsRow`s. The body is indented to line up under the
42
+ * title text rather than the icon.
43
+ */
44
+ export declare function SettingsSection({ icon, title, description, actions, children, className, }: SettingsSectionProps): React.JSX.Element;
@@ -2,7 +2,7 @@ import { default as React } from 'react';
2
2
  export interface SourceChipProps {
3
3
  /** Label — the linked conversation subject. */
4
4
  label: React.ReactNode;
5
- /** Optional leading dot colour (a `--dot-*` / channel CSS var, or any colour). */
5
+ /** Optional leading dot colour (a `--color-cat-*` or channel CSS var). */
6
6
  dotColor?: string;
7
7
  /** Click opens the linked conversation. */
8
8
  onClick?: () => void;
@@ -14,7 +14,7 @@ export interface SourceChipProps {
14
14
  *
15
15
  * @example
16
16
  * ```tsx
17
- * <SourceChip label="Vraag over factuur maart" dotColor="var(--dot-finance)" onClick={openThread} />
17
+ * <SourceChip label="Vraag over factuur maart" dotColor="var(--color-cat-2)" onClick={openThread} />
18
18
  * ```
19
19
  */
20
20
  export declare function SourceChip({ label, dotColor, onClick, className }: SourceChipProps): React.JSX.Element;
@@ -1,5 +1,11 @@
1
1
  import { ReactNode } from 'react';
2
- interface ContentLoadingProps {
2
+ export interface ContentLoadingProps {
3
+ /**
4
+ * `page` (default) fills the route with its own `<main>` shell and padding.
5
+ * `inline` drops both, so the skeleton can sit inside an existing panel,
6
+ * card or scroll container without nesting a second `<main>` landmark.
7
+ */
8
+ variant?: "page" | "inline";
3
9
  /**
4
10
  * Optional custom skeleton content (overrides default)
5
11
  */
@@ -17,5 +23,4 @@ interface ContentLoadingProps {
17
23
  */
18
24
  tableRows?: number;
19
25
  }
20
- export declare function ContentLoading({ children, showTableSkeleton, tableColumns, tableRows, }: ContentLoadingProps): import("react").JSX.Element;
21
- export {};
26
+ export declare function ContentLoading({ variant, children, showTableSkeleton, tableColumns, tableRows, }: ContentLoadingProps): import("react").JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { default as React } from 'react';
2
+ export interface StatusDotProps {
3
+ /**
4
+ * Meaning, not colour. `neutral` is the off/idle state; `accent` marks
5
+ * unread; the rest follow the status tokens.
6
+ */
7
+ tone?: "neutral" | "accent" | "success" | "warning" | "danger" | "info" | "note"
8
+ /** Identity without status — an inbox, a speaker. See --color-cat-*. */
9
+ | "cat-1" | "cat-2" | "cat-3" | "cat-4" | "cat-5";
10
+ /** Square instead of round — the marker style used for inbox identity. */
11
+ shape?: "round" | "square";
12
+ /** Slow pulse for something live: an active call, a running recording. */
13
+ pulse?: boolean;
14
+ /**
15
+ * Accessible name. Without one the dot is decorative and hidden from
16
+ * assistive tech, which is right when adjacent text already says the state.
17
+ */
18
+ label?: string;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * Small state marker: presence, session state, unread, live recording.
23
+ *
24
+ * Replaces a scatter of hand-rolled `h-2 w-2 rounded-full bg-green-500` spans
25
+ * that used raw palette colours and disagreed on size (8px vs 10px).
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <StatusDot tone="success" label="Online" />
30
+ * <StatusDot tone="danger" pulse label="Opname loopt" />
31
+ * ```
32
+ */
33
+ export declare function StatusDot({ tone, shape, pulse, label, className, }: StatusDotProps): React.JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ export interface StepIndicatorProps {
3
+ /** Step ids in order. Only the position matters; the ids identify `current`. */
4
+ steps: string[];
5
+ /** Id of the step being shown. Unknown ids render as "not started". */
6
+ current: string;
7
+ /**
8
+ * `numbered` draws counted circles joined by a rule — use it when the user
9
+ * needs to know how many steps are left. `dots` is the quieter bar, for a
10
+ * flow whose heading already says where you are.
11
+ */
12
+ variant?: "numbered" | "dots";
13
+ /**
14
+ * Accessible name. Without one the indicator is treated as decorative, which
15
+ * is right when a heading beside it already announces the current step.
16
+ */
17
+ label?: string;
18
+ className?: string;
19
+ }
20
+ /**
21
+ * Progress through a short, linear flow.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * <StepIndicator steps={["provider", "channel", "owner"]} current={step} />
26
+ * <StepIndicator steps={["intent", "channel", "address"]} current={step} variant="dots" />
27
+ * ```
28
+ */
29
+ export declare function StepIndicator({ steps, current, variant, label, className, }: StepIndicatorProps): React.JSX.Element;
@@ -13,11 +13,13 @@ export { DatePicker, type DatePickerProps } from './input/DatePicker';
13
13
  export { FolderSelect, type FolderDestination, type FolderSelectProps } from './input/FolderSelect';
14
14
  export { SearchableTextField, type SearchableTextFieldProps } from './input/SearchableTextField';
15
15
  export { Select, type SelectOption, type SelectProps } from './input/Select';
16
+ export { Switch, type SwitchProps } from './input/Switch';
16
17
  export { StorageInput, type StorageInputProps } from './input/StorageInput';
17
18
  export { TextArea, type TextAreaProps } from './input/TextArea';
18
19
  export { TextField, type TextFieldProps } from './input/TextField';
19
20
  export { Form, type FormButtonProps, type FormFieldType, type FormGroup, type FormGroupItem, type FormProps } from './forms/Form';
20
21
  export { Dropdown, type DropdownOption, type DropdownProps } from './overlays/Dropdown';
22
+ export { ConfirmDialog, type ConfirmDialogProps } from './overlays/ConfirmDialog';
21
23
  export { Modal, type ModalProps } from './overlays/Modal';
22
24
  export { Popover, type PopoverProps } from './overlays/Popover';
23
25
  export { Badge, type BadgeProps } from './feedback/Badge';
@@ -26,6 +28,8 @@ export { EmptyState, type EmptyStateProps } from './feedback/EmptyState';
26
28
  export { Kbd, type KbdProps } from './feedback/Kbd';
27
29
  export { ProgressBar, type ProgressBarProps } from './feedback/ProgressBar';
28
30
  export { Spinner, type SpinnerProps } from './feedback/Spinner';
31
+ export { StatusDot, type StatusDotProps } from './feedback/StatusDot';
32
+ export { StepIndicator, type StepIndicatorProps } from './feedback/StepIndicator';
29
33
  export { AssistantCard, type AssistantCardProps } from './content/AssistantCard';
30
34
  export { Avatar, type AvatarProps } from './content/Avatar';
31
35
  export { AvatarStack, type AvatarStackPerson, type AvatarStackProps } from './content/AvatarStack';
@@ -33,9 +37,12 @@ export { ChannelBadge, type ChannelBadgeProps, type ChannelKey } from './content
33
37
  export { Icon, type IconProps } from './content/Icon';
34
38
  export { Image, type ImageProps } from './content/Image';
35
39
  export { List, type ListProps } from './content/List';
40
+ export { MessageBubble, type MessageBubbleProps } from './content/MessageBubble';
36
41
  export { KpiCard, type KpiCardProps } from './content/KpiCard';
37
42
  export { PageHeader, type PageHeaderAction, type PageHeaderProps } from './content/PageHeader';
38
43
  export { PageToolbar, type PageToolbarProps } from './content/PageToolbar';
44
+ export { SectionDivider, type SectionDividerProps } from './content/SectionDivider';
45
+ export { SettingsRow, SettingsSection, type SettingsRowProps, type SettingsSectionProps } from './content/SettingsRow';
39
46
  export { SourceChip, type SourceChipProps } from './content/SourceChip';
40
47
  export { Table, type TableAction, type TableColumn, type TableFilter, type TableProps } from './content/Table';
41
48
  export { View, type ViewGroup, type ViewProps } from './content/View';
@@ -19,6 +19,12 @@ export interface DatePickerProps {
19
19
  maxDate?: Date;
20
20
  /** Date format for display */
21
21
  format?: 'MM/dd/yyyy' | 'dd/MM/yyyy' | 'yyyy-MM-dd';
22
+ /**
23
+ * Also pick a time. The display gains `HH:mm`, the panel gains a time field,
24
+ * and choosing a day keeps the time already on the value instead of resetting
25
+ * it to midnight. Replaces a native `datetime-local`.
26
+ */
27
+ withTime?: boolean;
22
28
  }
23
29
  /**
24
30
  * DatePicker component with calendar popup
@@ -0,0 +1,25 @@
1
+ import { default as React } from 'react';
2
+ export interface SwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "size" | "checked" | "onChange"> {
3
+ /**
4
+ * Current state. Deliberately required and never held internally: several
5
+ * call sites drive this from an optimistic update that rolls back on a failed
6
+ * request, and internal state would silently swallow the rollback.
7
+ */
8
+ checked: boolean;
9
+ onChange: (checked: boolean) => void;
10
+ /** Text beside the switch. Omit it and pass `aria-label` instead. */
11
+ label?: React.ReactNode;
12
+ disabled?: boolean;
13
+ }
14
+ /**
15
+ * On/off switch for a setting that applies immediately.
16
+ *
17
+ * Use `Checkbox` instead when the value is part of a form the user submits —
18
+ * a switch says "this is now on", a checkbox says "include this when I save".
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <Switch checked={syncEnabled} onChange={setSyncEnabled} label="Agenda synchroniseren" />
23
+ * ```
24
+ */
25
+ export declare const Switch: React.ForwardRefExoticComponent<SwitchProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,38 @@
1
+ import { default as React } from 'react';
2
+ export interface ConfirmDialogProps {
3
+ open: boolean;
4
+ title: string;
5
+ /** What is about to happen, and what it costs if it is wrong. */
6
+ description?: React.ReactNode;
7
+ confirmLabel?: string;
8
+ cancelLabel?: string;
9
+ /** `danger` for anything that destroys or detaches something. */
10
+ tone?: "default" | "danger";
11
+ /** Disables both buttons and puts the confirm button in its loading state. */
12
+ loading?: boolean;
13
+ onConfirm: () => void;
14
+ onCancel: () => void;
15
+ }
16
+ /**
17
+ * Confirmation before a destructive or irreversible action.
18
+ *
19
+ * Replaces `window.confirm`, which blocks the main thread, cannot be styled,
20
+ * cannot be translated, and is suppressed outright in some embedded contexts.
21
+ * Note the shape difference: `confirm()` returns a boolean inline, so callers
22
+ * written as `if (!confirm(...)) return;` have to split into a request step
23
+ * and a confirm step.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <ConfirmDialog
28
+ * open={pendingDelete !== null}
29
+ * tone="danger"
30
+ * title={t("channel_delete_title")}
31
+ * description={t("channel_delete_description")}
32
+ * confirmLabel={t("delete")}
33
+ * onConfirm={() => remove(pendingDelete!)}
34
+ * onCancel={() => setPendingDelete(null)}
35
+ * />
36
+ * ```
37
+ */
38
+ export declare function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, tone, loading, onConfirm, onCancel, }: ConfirmDialogProps): React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/ui-kit",
3
- "version": "3.109.0",
3
+ "version": "3.110.0",
4
4
  "description": "Theme-aware UI component library for OpenCXH platform",
5
5
  "type": "module",
6
6
  "exports": {