@maxio-com/react-ui-components 9.21.0 → 9.22.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.
@@ -0,0 +1,184 @@
1
+ # CopyToClipboard
2
+
3
+ ## Usage Guidelines
4
+
5
+ ### Overview
6
+
7
+ CopyToClipboard writes a value to the clipboard and confirms it, so users can take an identifier, key, or URL out of the interface without selecting text by hand. It swaps to a check mark once copied, confirms in the tooltip, and announces the result to screen readers. Place the button directly beside the value it copies. It is ghost by default, but accepts primary, secondary, and tertiary variants when more emphasis is needed.
8
+
9
+ #### When to Use
10
+
11
+ - Use CopyToClipboard for values users need to move elsewhere but should not have to
12
+ select by hand: API keys, tokens, IDs, commit SHAs, clone URLs, invoice
13
+ numbers.
14
+ - Use the icon-only button inline, directly beside the value it copies. The
15
+ adjacent value says what will be copied and the control stays out of the way.
16
+
17
+ #### When Not to Use
18
+
19
+ - Do not use CopyToClipboard for actions that duplicate a record, such as
20
+ "Duplicate invoice". Use a [Button](components-buttons-button.md)
21
+ with explicit wording.
22
+ - Do not use CopyToClipboard to copy long prose or whole page regions; let users
23
+ select that text themselves.
24
+ - Do not use it as the only way to obtain a value. Keep the value visible or
25
+ otherwise reachable, since clipboard access can fail.
26
+
27
+ ### Variants
28
+
29
+ | Variants | Purpose | Usage notes |
30
+ | :----------------- | :----------------------------- | :-------------------------------------------------------------------------- |
31
+ | Icon-only (inline) | Sits beside the value. | `copyLabel` supplies the accessible name; the tooltip is only a hover hint. |
32
+ | `sm` size | Table rows and dense metadata. | Pair with `Body size="sm"` so the icon matches the line height. |
33
+ | `lg` size | Larger copy actions. | Largest target area; use where the button is not crowded by other controls. |
34
+
35
+ The button is ghost by default so it does not compete with the value beside it.
36
+ Use `variant` to increase its emphasis when needed:
37
+
38
+ | `variant` | When to use |
39
+ | :---------- | :------------------------------------------------------------- |
40
+ | `ghost` | Default. Suits inline use beside the value being copied. |
41
+ | `tertiary` | Use when the copy control needs slightly more emphasis. |
42
+ | `secondary` | Copying is one of the main actions on the surface. |
43
+ | `primary` | Rare. Only when copying is _the_ action users came to perform. |
44
+
45
+ ### Behavior
46
+
47
+ - **Mouse and touch**: pressing the button writes `value` to the clipboard and
48
+ the icon swaps to a check mark and the tooltip shows `copiedLabel`.
49
+ - **Keyboard**: the button is reachable with Tab and activates with Enter or
50
+ Space. Focus also opens the icon-only button's tooltip.
51
+ - **Callbacks**: a native `onClick` observes activation without replacing the
52
+ copy behavior. Use `onCopy` and `onCopyError` for the asynchronous result.
53
+ - **Copied state**: reverts automatically after `resetAfterMs` (2000 ms by
54
+ default). Pressing again restarts the timer. Changing `value` clears feedback
55
+ immediately so a new value is never presented as already copied.
56
+ - **Failure**: if the clipboard write is rejected — an insecure context, or
57
+ denied permission — the icon and tooltip show the failure,
58
+ `copyErrorLabel` is announced, and `onCopyError` fires. It never claims a copy
59
+ that did not happen.
60
+ - **Async values**: keep the button `disabled` until `value` is populated so
61
+ users cannot copy a placeholder.
62
+ - **Sizing**: `size` uses the shared IconButton sizing. Target areas are 28px,
63
+ 32px, and 40px; the glyph remains 16px, consistent with
64
+ other icon buttons in the system.
65
+
66
+ ### Accessibility
67
+
68
+ - The button takes its accessible name from `copyLabel`. A tooltip is
69
+ not an accessible name, so the label is always applied to the button itself.
70
+ - Success and failure are announced through a `role="status"`
71
+ `aria-live="polite"` region rather than the tooltip, because a tooltip is not
72
+ announced on press and may not be open when the user activates the button by
73
+ keyboard.
74
+ - The live region is rendered up front and filled after the copy, so the change
75
+ is announced. A region inserted together with its text is unreliable.
76
+ - Each attempt refreshes the live region, so repeated successful copies are
77
+ announced as separate actions.
78
+ - Icon changes are paired with announcement text and tooltip feedback, so
79
+ success and failure are never conveyed by the glyph alone.
80
+
81
+ ### Content
82
+
83
+ - Keep `copyLabel` specific about what is copied — "Copy API key" beats "Copy"
84
+ when several copyable values sit near each other.
85
+ - Keep `copiedLabel` short; it is announced verbatim.
86
+ - Keep `copyErrorLabel` actionable and concise; "Copy failed" is the default.
87
+ - When showing a truncated value, keep `value` set to the full string so users
88
+ get the complete text.
89
+
90
+ ### Related
91
+
92
+ - **[Button](components-buttons-button.md)**: use for actions
93
+ that are not clipboard copies.
94
+ - **[IconButton](components-buttons-iconbutton.md)**: use for
95
+ other icon-only actions; CopyToClipboard is a purpose-built wrapper around it.
96
+ - **[TooltipTrigger](components-tooltip.md)**: supplies the hover
97
+ and focus hint used here.
98
+
99
+ ## React
100
+
101
+ ```tsx
102
+ import { CopyToClipboard } from '@maxio-com/react-ui-components';
103
+ ```
104
+
105
+ ### Handling Failures
106
+
107
+ Clipboard writes reject in insecure contexts and when the user denies
108
+ permission. CopyToClipboard shows and announces `copyErrorLabel` in that case
109
+ and calls `onCopyError`, so consumers can also surface application-level
110
+ feedback when needed.
111
+
112
+ ```tsx
113
+ <CopyToClipboard
114
+ value={apiKey}
115
+ copyLabel="Copy API key"
116
+ copyErrorLabel="API key could not be copied"
117
+ onCopyError={() => addToast({ sentiment: 'error', title: 'Could not copy' })}
118
+ />
119
+ ```
120
+
121
+ ### Asynchronous Values
122
+
123
+ Keep the button disabled until the value exists, so users cannot copy a
124
+ placeholder.
125
+
126
+ ```tsx
127
+ <CopyToClipboard value={token ?? ''} disabled={!token} copyLabel="Copy token" />
128
+ ```
129
+
130
+ ## Imports
131
+
132
+ ```tsx
133
+ import { Body, CopyToClipboard, Flex } from "@maxio-com/react-ui-components";
134
+ ```
135
+
136
+ ## Prop Types
137
+
138
+ ### CopyToClipboard
139
+
140
+ | Prop | Type | Required | Default | Description | Source |
141
+ | --- | --- | --- | --- | --- | --- |
142
+ | `copiedLabel` | `string` | no | `Copied!` | Success feedback after a copy, shown in the tooltip and announced to screen readers. | TypeLiteral |
143
+ | `copyErrorLabel` | `string` | no | `Copy failed` | Failure feedback after a copy, shown and announced like `copiedLabel`. | TypeLiteral |
144
+ | `copyLabel` | `string` | no | `Copy` | Accessible name for the button, and the idle tooltip text. | TypeLiteral |
145
+ | `disabled` | `boolean` | no | - | Prevents interaction, such as while the value is still loading. | TypeLiteral, ButtonHTMLAttributes |
146
+ | `onCopy` | `((value: string) => void)` | no | - | Called with the copied value once the clipboard write succeeds. | TypeLiteral |
147
+ | `onCopyError` | `((error: unknown) => void)` | no | - | Called when the clipboard write fails, such as in an insecure context or when permission is denied. The copied state is not entered. | TypeLiteral |
148
+ | `resetAfterMs` | `number` | no | `2000` | How long the copied state lasts before reverting, in milliseconds. | TypeLiteral |
149
+ | `size` | `"sm" \| "md" \| "lg"` | no | `md` | Changes the size of the button. | TypeLiteral |
150
+ | `tooltipPlacement` | `"top" \| "right" \| "bottom" \| "left" \| "top-start" \| "top-end" \| "right-start" \| "right-end" \| "bottom-start" \| "bottom-end" \| "left-start" \| "left-end"` | no | `top` | Where the tooltip is positioned relative to the button. | TypeLiteral |
151
+ | `value` | `string` | yes | - | Text written to the clipboard when the button is pressed. | TypeLiteral |
152
+ | `variant` | `"primary" \| "secondary" \| "tertiary" \| "ghost"` | no | `ghost` | Button variant. | TypeLiteral |
153
+
154
+ ## Stories
155
+
156
+ ### Default
157
+
158
+ An icon-only button sitting directly beside the value it copies. `copyLabel` carries the accessible name.
159
+
160
+ ```tsx
161
+ const Default = () => <Flex alignItems="center" gap={1}>
162
+ <Body size="sm" as="span">
163
+ {args.value}
164
+ </Body>
165
+ <CopyToClipboard
166
+ value={API_KEY}
167
+ onCopy={action('Copied')}
168
+ onCopyError={action('Copy failed')}
169
+ size="sm"
170
+ copyLabel="Copy API key" />
171
+ </Flex>;
172
+ ```
173
+
174
+ ### Disabled
175
+
176
+ Disable the button while there is nothing to copy yet, such as before a generated secret has loaded.
177
+
178
+ ```tsx
179
+ const Disabled = () => <CopyToClipboard
180
+ value={API_KEY}
181
+ onCopy={action('Copied')}
182
+ onCopyError={action('Copy failed')}
183
+ disabled />;
184
+ ```
@@ -243,7 +243,7 @@ import {
243
243
  | `actions` | `((row: Row<TData>, table: Table<TData>) => ReactNode)` | no | - | render custom actions cell content | TypeLiteral |
244
244
  | `actionsColumnSize` | `number` | no | - | width (in px) of the actions column; defaults to 40 | TypeLiteral |
245
245
  | `aggregationFns` | `Record<string, AggregationFn<any>>` | no | - | - | TypeLiteral |
246
- | `align` | `"left" \| "right"` | no | - | - | TypeLiteral |
246
+ | `align` | `"right" \| "left"` | no | - | - | TypeLiteral |
247
247
  | `enableExpanding` | `boolean \| "rowsOnly"` | no | - | Enable nested row expansion. Include `'rowsOnly'` for row-level toggles without group expand/collapse header control. | TypeLiteral |
248
248
  | `enableRowReordering` | `boolean` | no | `false` | Enable controlled row reordering for small, non-paginated tables. Intended for approximately 100 total rows or fewer, including nested and collapsed rows. Nested row data must use the `subRows` property; custom `getSubRows` accessors are unsupported. | TypeLiteral |
249
249
  | `enableSorting` | `boolean` | no | `false` | - | TypeLiteral |
@@ -69,7 +69,7 @@ import { Body, Button, DialogTrigger, Drawer } from "@maxio-com/react-ui-compone
69
69
  | `footer` | `ReactNode` | no | - | - | DrawerProps |
70
70
  | `onOpenChange` | `((open: boolean) => void)` | no | - | - | DrawerProps |
71
71
  | `open` | `boolean` | no | - | - | DrawerProps |
72
- | `placement` | `"left" \| "right" \| "top" \| "bottom"` | no | `right` | - | DrawerProps |
72
+ | `placement` | `"top" \| "right" \| "bottom" \| "left"` | no | `right` | - | DrawerProps |
73
73
  | `size` | `"sm" \| "md" \| "lg" \| "xl" \| "full"` | no | `md` | - | DrawerProps |
74
74
  | `title` | `string` | yes | - | - | DrawerProps |
75
75
 
@@ -99,7 +99,7 @@ import { ToggleComponent } from "@maxio-com/react-ui-components";
99
99
  | `name` | `string` | no | - | Toggle name | TypeLiteral |
100
100
  | `onChange` | `((value: boolean, event?: KeyboardEvent<HTMLInputElement> \| ChangeEvent<HTMLInputElement>) => void)` | no | - | The onChange function | TypeLiteral |
101
101
  | `valueDescription` | `string` | no | - | Toggle label | TypeLiteral |
102
- | `valueDescriptionPosition` | `"left" \| "right"` | no | - | Toggle label position | TypeLiteral |
102
+ | `valueDescriptionPosition` | `"right" \| "left"` | no | - | Toggle label position | TypeLiteral |
103
103
 
104
104
  ## Stories
105
105
 
@@ -73,7 +73,7 @@ import { TooltipTrigger } from "@maxio-com/react-ui-components";
73
73
  | `isVisible` | `boolean` | no | - | Tooltip visibility state - use with useExternalState property | TypeLiteral |
74
74
  | `maxWidth` | `number \| "none"` | no | `160` | Maximum tooltip container width in pixels | TypeLiteral |
75
75
  | `onVisibilityUpdate` | `((isVisible: boolean) => void)` | no | - | Tooltip visibility state | TypeLiteral |
76
- | `placement` | `"left" \| "right" \| "top" \| "bottom" \| "left-start" \| "left-end" \| "right-start" \| "right-end" \| "top-start" \| "top-end" \| "bottom-start" \| "bottom-end"` | no | `right` | Overlay element placement | TypeLiteral |
76
+ | `placement` | `"top" \| "right" \| "bottom" \| "left" \| "top-start" \| "top-end" \| "right-start" \| "right-end" \| "bottom-start" \| "bottom-end" \| "left-start" \| "left-end"` | no | `right` | Overlay element placement | TypeLiteral |
77
77
  | `renderContent` | `() => ReactNode` | yes | - | Tooltip content renderer | TypeLiteral |
78
78
  | `size` | `"small" \| "regular"` | no | `regular` | Tooltip component size variant | TypeLiteral |
79
79
  | `usePortal` | `boolean` | no | - | Render floating element in portal container | TypeLiteral |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxio-com/react-ui-components",
3
- "version": "9.21.0",
3
+ "version": "9.22.0",
4
4
  "description": "React UI components",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -65,5 +65,5 @@
65
65
  "publishConfig": {
66
66
  "access": "public"
67
67
  },
68
- "gitHead": "b9a22146235d44ea3b67a38c63bf2d6683bbef90"
68
+ "gitHead": "14695cb6dcb2e06f7fa85daacbffa1e502b6d7ba"
69
69
  }
@@ -1,4 +1,4 @@
1
- import React$1, { ReactNode, HTMLAttributes, LiHTMLAttributes, AnchorHTMLAttributes, RefObject, FC, ElementType, InputHTMLAttributes } from 'react';
1
+ import React$1, { ReactNode, HTMLAttributes, LiHTMLAttributes, ButtonHTMLAttributes, AnchorHTMLAttributes, RefObject, FC, ElementType, InputHTMLAttributes } from 'react';
2
2
  import { RowData, Row, Table, ExpandedState, RowSelectionState, SortingState, TableOptions } from '@tanstack/react-table';
3
3
  export { createColumnHelper } from '@tanstack/react-table';
4
4
  import { PopoverProps as PopoverProps$1, ToggleButtonProps, ToggleButtonGroupProps, ListBoxProps as ListBoxProps$1, ListBoxItemProps as ListBoxItemProps$1, ListBoxSectionProps as ListBoxSectionProps$1, HeaderProps, ComboBoxProps as ComboBoxProps$1, ValidationResult, TextFieldProps as TextFieldProps$1 } from 'react-aria-components';
@@ -161,6 +161,32 @@ type IconButtonProps = (Omit<InternalButtonProps, 'fullWidth' | 'children'> | Om
161
161
  declare const IconButton: ({ "aria-label": ariaLabel, className, ...props }: IconButtonProps) => React$1.JSX.Element;
162
162
  //# sourceMappingURL=IconButton.d.ts.map
163
163
 
164
+ type Alignment = 'start' | 'end';
165
+ type Side = 'top' | 'right' | 'bottom' | 'left';
166
+ type AlignedPlacement = `${Side}-${Alignment}`;
167
+ type Placement = Side | AlignedPlacement;
168
+ type Interactions = 'click' | 'hover' | 'focus' | 'dismiss';
169
+ type OverlayAriaRole = 'tooltip' | 'dialog' | 'alertdialog' | 'menu' | 'listbox' | 'grid' | 'tree';
170
+
171
+ type CopyToClipboardSize = 'sm' | 'md' | 'lg';
172
+ type CopyToClipboardVariant = 'primary' | 'secondary' | 'tertiary' | 'ghost';
173
+ type CopyToClipboardProps = {
174
+ value: string;
175
+ copyLabel?: string;
176
+ copiedLabel?: string;
177
+ copyErrorLabel?: string;
178
+ size?: CopyToClipboardSize;
179
+ variant?: CopyToClipboardVariant;
180
+ disabled?: boolean;
181
+ resetAfterMs?: number;
182
+ tooltipPlacement?: Placement;
183
+ onCopy?: (value: string) => void;
184
+ onCopyError?: (error: unknown) => void;
185
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onCopy' | 'children' | 'value' | 'type'>;
186
+
187
+ declare const CopyToClipboard: ({ value, copyLabel, copiedLabel, copyErrorLabel, size, variant, disabled, resetAfterMs, tooltipPlacement, onCopy, onCopyError, onClick, className, ...rest }: CopyToClipboardProps) => React$1.JSX.Element;
188
+ //# sourceMappingURL=CopyToClipboard.d.ts.map
189
+
164
190
  type TableSize = 'sm' | 'md' | 'lg';
165
191
  type ColumnAlign = 'left' | 'right';
166
192
  type RowReorderMode = 'siblings' | 'same-level' | 'all';
@@ -281,13 +307,6 @@ type LoadingSpinnerProps = {
281
307
  declare const LoadingSpinner: ({ size, white, className, }: LoadingSpinnerProps) => React$1.JSX.Element;
282
308
  //# sourceMappingURL=LoadingSpinner.d.ts.map
283
309
 
284
- type Alignment = 'start' | 'end';
285
- type Side = 'top' | 'right' | 'bottom' | 'left';
286
- type AlignedPlacement = `${Side}-${Alignment}`;
287
- type Placement = Side | AlignedPlacement;
288
- type Interactions = 'click' | 'hover' | 'focus' | 'dismiss';
289
- type OverlayAriaRole = 'tooltip' | 'dialog' | 'alertdialog' | 'menu' | 'listbox' | 'grid' | 'tree';
290
-
291
310
  type OverlayTriggerSettings = {
292
311
  placement?: Placement;
293
312
  isInitiallyActive?: boolean;
@@ -932,5 +951,5 @@ type AuthLayoutProps = {
932
951
  declare const AuthLayout: ({ children, heading, description, sentiment, leadingElement, alert, topAddon, footer, }: AuthLayoutProps) => React$1.JSX.Element;
933
952
  //# sourceMappingURL=AuthLayout.d.ts.map
934
953
 
935
- export { ActionList, Alert, AuthLayout, Avatar, Banner, Body, Breadcrumbs, BreadcrumbsItem, Button, Card, Checkbox, CheckboxGroup, Chip, Code, ComboBox, DataTable, Display, Drawer, EmptyState, Flex, FormErrorMessage, FormHelperText, GlobalToastProvider, Grid, Heading, Icon, IconButton, Label, Link, ListBox, LoadingSpinner, Logo, Menu, MenuButton, MenuList, OverlayTrigger, Pagination, Popover, ProgressBar, Radio, RadioGroup, SegmentedControl, Select, SideNavWrapper as SideNav, Tab, TabList, TabPanel, TabPanels, Tabs, Tag, TextArea, TextField, TextInput, Tile, ToastProvider, ToastQueue, Toggle, Tooltip, TooltipTrigger, TopBar, useElementResizeObserver, useHover, useOverlayTrigger, useToast, useWindowSize };
936
- export type { ComboBoxProps, DrawerProps, EmptyStateHeadingLevel, EmptyStateProps, Interactions, OverlayAriaRole, Placement, PopoverProps, SegmentedControlItemProps, SegmentedControlProps, SegmentedControlSize, TextAreaProps, TextFieldProps, TextInputProps };
954
+ export { ActionList, Alert, AuthLayout, Avatar, Banner, Body, Breadcrumbs, BreadcrumbsItem, Button, Card, Checkbox, CheckboxGroup, Chip, Code, ComboBox, CopyToClipboard, DataTable, Display, Drawer, EmptyState, Flex, FormErrorMessage, FormHelperText, GlobalToastProvider, Grid, Heading, Icon, IconButton, Label, Link, ListBox, LoadingSpinner, Logo, Menu, MenuButton, MenuList, OverlayTrigger, Pagination, Popover, ProgressBar, Radio, RadioGroup, SegmentedControl, Select, SideNavWrapper as SideNav, Tab, TabList, TabPanel, TabPanels, Tabs, Tag, TextArea, TextField, TextInput, Tile, ToastProvider, ToastQueue, Toggle, Tooltip, TooltipTrigger, TopBar, useElementResizeObserver, useHover, useOverlayTrigger, useToast, useWindowSize };
955
+ export type { ComboBoxProps, CopyToClipboardProps, CopyToClipboardSize, CopyToClipboardVariant, DrawerProps, EmptyStateHeadingLevel, EmptyStateProps, Interactions, OverlayAriaRole, Placement, PopoverProps, SegmentedControlItemProps, SegmentedControlProps, SegmentedControlSize, TextAreaProps, TextFieldProps, TextInputProps };