@trackunit/react-components 3.5.6 → 3.7.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/index.cjs.js +132 -42
- package/index.esm.js +132 -43
- package/package.json +6 -6
- package/src/components/CopyButton/CopyButton.d.ts +14 -15
- package/src/components/CopyableValue/CopyableValue.d.ts +86 -0
- package/src/components/CopyableValue/CopyableValue.variants.d.ts +60 -0
- package/src/index.d.ts +1 -0
package/index.cjs.js
CHANGED
|
@@ -28,6 +28,7 @@ var zod = require('zod');
|
|
|
28
28
|
var defaultTranslations = {
|
|
29
29
|
"breadcrumb.backButton": "Back",
|
|
30
30
|
"breadcrumb.expandButton": "expand",
|
|
31
|
+
"copyableValue.copyValue": "Copy {{value}}",
|
|
31
32
|
"copyButton.copied": "Copied!",
|
|
32
33
|
"copyButton.copy": "Copy",
|
|
33
34
|
"horizontalOverflowScroller.scrollLeft": "Scroll left",
|
|
@@ -4773,28 +4774,27 @@ const useCopyToClipboard = () => {
|
|
|
4773
4774
|
* element, so the button can be placed next to plain (non-clickable) text.
|
|
4774
4775
|
*
|
|
4775
4776
|
* ### When to use
|
|
4776
|
-
* - Icon-only (default): the copyable value is displayed as plain text (e.g. in a table cell or
|
|
4777
|
-
* row) and only a dedicated icon should trigger the copy, keeping the rest of the row clickable.
|
|
4778
4777
|
* - Labelled (`label` set): copying is the primary action of a panel or section — e.g. an invite
|
|
4779
4778
|
* link — and deserves a full button that is itself the click target.
|
|
4779
|
+
* - Icon-only (default): a copy control on its own, where no value is displayed next to it — e.g.
|
|
4780
|
+
* in the corner of a code block.
|
|
4780
4781
|
*
|
|
4781
4782
|
* ### When not to use
|
|
4782
|
-
*
|
|
4783
|
-
*
|
|
4783
|
+
* - To show a value with a copy button beside it, use `CopyableValue` — it owns the layout,
|
|
4784
|
+
* truncation and hover highlight. Do not pair `Text` and `CopyButton` by hand.
|
|
4785
|
+
* - Do not hand-roll copy affordances (custom icons, click-to-copy text) — use this component or
|
|
4786
|
+
* `CopyableValue` instead.
|
|
4784
4787
|
*
|
|
4785
4788
|
* ### Empty values
|
|
4786
4789
|
* CopyButton renders nothing when `value` is empty, so consumers never present a copy control
|
|
4787
4790
|
* that has no meaningful result.
|
|
4788
4791
|
*
|
|
4789
|
-
* @example
|
|
4792
|
+
* @example Labelled button for an invite link
|
|
4790
4793
|
* ```tsx
|
|
4791
|
-
* import { CopyButton
|
|
4794
|
+
* import { CopyButton } from "@trackunit/react-components";
|
|
4792
4795
|
*
|
|
4793
|
-
* const
|
|
4794
|
-
* <
|
|
4795
|
-
* <Text type="span">{code}</Text>
|
|
4796
|
-
* <CopyButton value={code} />
|
|
4797
|
-
* </div>
|
|
4796
|
+
* const CopyInviteLink = ({ link }: { link: string }) => (
|
|
4797
|
+
* <CopyButton label="Copy link" value={link} variant="secondary" />
|
|
4798
4798
|
* );
|
|
4799
4799
|
* ```
|
|
4800
4800
|
* @param {CopyButtonProps} props - The props for the CopyButton component
|
|
@@ -4826,7 +4826,7 @@ const CopyButton = ({ value, copyLabel, copiedLabel, size = "small", label, vari
|
|
|
4826
4826
|
setShowCopied(true);
|
|
4827
4827
|
clearTimeout(resetTimerRef.current);
|
|
4828
4828
|
resetTimerRef.current = setTimeout(() => setShowCopied(false), COPIED_FEEDBACK_DURATION_MS);
|
|
4829
|
-
onCopy?.();
|
|
4829
|
+
onCopy?.(value);
|
|
4830
4830
|
}, () => undefined);
|
|
4831
4831
|
};
|
|
4832
4832
|
const buttonSize = toButtonSize(size);
|
|
@@ -4844,6 +4844,125 @@ const CopyButton = ({ value, copyLabel, copiedLabel, size = "small", label, vari
|
|
|
4844
4844
|
CopyButton.displayName = "CopyButton";
|
|
4845
4845
|
const COPIED_FEEDBACK_DURATION_MS = 2000;
|
|
4846
4846
|
|
|
4847
|
+
const cvaEmptyValue = cssClassVarianceUtilities.cva({ base: ["text-neutral-400"] });
|
|
4848
|
+
|
|
4849
|
+
/**
|
|
4850
|
+
* EmptyValue renders a consistent dash symbol ("–") to represent missing, null, undefined, or not applicable values.
|
|
4851
|
+
* It is primarily used in tables and detail lists to maintain visual consistency when data is absent.
|
|
4852
|
+
*
|
|
4853
|
+
* ### When to use
|
|
4854
|
+
* Use EmptyValue as a placeholder in table cells, detail lists, or any data display where values may be missing.
|
|
4855
|
+
*
|
|
4856
|
+
* ### When not to use
|
|
4857
|
+
* Do not use EmptyValue for empty pages or sections — use `EmptyState` instead.
|
|
4858
|
+
*
|
|
4859
|
+
* @example Empty value in a details row
|
|
4860
|
+
* ```tsx
|
|
4861
|
+
* import { EmptyValue, Text } from "@trackunit/react-components";
|
|
4862
|
+
*
|
|
4863
|
+
* const AssetDetail = ({ value }: { value?: string }) => (
|
|
4864
|
+
* <div className="flex justify-between">
|
|
4865
|
+
* <Text weight="bold">Serial Number</Text>
|
|
4866
|
+
* {value ? <Text>{value}</Text> : <EmptyValue />}
|
|
4867
|
+
* </div>
|
|
4868
|
+
* );
|
|
4869
|
+
* ```
|
|
4870
|
+
* @param {EmptyValueProps} props - The props for the EmptyValue component
|
|
4871
|
+
* @returns {ReactElement} EmptyValue component
|
|
4872
|
+
*/
|
|
4873
|
+
const EmptyValue = ({ className, "data-testid": dataTestId, style, ref }) => {
|
|
4874
|
+
return (jsxRuntime.jsx("div", { className: cvaEmptyValue({ className }), "data-testid": dataTestId, ref: ref, style: style, children: "-" }));
|
|
4875
|
+
};
|
|
4876
|
+
|
|
4877
|
+
// Named group so the highlight only reacts to this pair's own button, even when pairs are nested
|
|
4878
|
+
// inside other `group` scopes (table rows, cards).
|
|
4879
|
+
// `-ml-1` offsets the value's `pl-1`, so the text lines up with plain text and column headers while
|
|
4880
|
+
// the highlight's left padding reaches into the surrounding space (e.g. the cell padding); the
|
|
4881
|
+
// widened max width keeps the right edge where it was.
|
|
4882
|
+
const cvaCopyableValue = cssClassVarianceUtilities.cva({
|
|
4883
|
+
base: ["group/copyable-value", "items-center", "-ml-1", "max-w-[calc(100%+0.25rem)]"],
|
|
4884
|
+
variants: {
|
|
4885
|
+
inline: {
|
|
4886
|
+
// `overflow-hidden` + the max width defeat the grid/flex `min-width: auto` blowout, so the
|
|
4887
|
+
// value truncates inside table cells instead of widening them.
|
|
4888
|
+
false: ["flex", "min-w-0", "overflow-hidden"],
|
|
4889
|
+
true: ["inline-flex"],
|
|
4890
|
+
},
|
|
4891
|
+
},
|
|
4892
|
+
defaultVariants: {
|
|
4893
|
+
inline: false,
|
|
4894
|
+
},
|
|
4895
|
+
});
|
|
4896
|
+
// The value stretches to the button's height and tints while the button is hovered or keyboard-
|
|
4897
|
+
// focused, so value and button read as one fused control. `focus-visible` (not `focus`) keeps the
|
|
4898
|
+
// tint from sticking on a row after a mouse click. Anchors inside the value do not trigger it.
|
|
4899
|
+
const cvaCopyableValueText = cssClassVarianceUtilities.cva({
|
|
4900
|
+
base: [
|
|
4901
|
+
"min-w-0",
|
|
4902
|
+
"self-stretch",
|
|
4903
|
+
"content-center",
|
|
4904
|
+
"rounded-l-md",
|
|
4905
|
+
"pl-1",
|
|
4906
|
+
"pr-0.5",
|
|
4907
|
+
"transition-colors",
|
|
4908
|
+
"group-has-[button:hover]/copyable-value:bg-primary-100",
|
|
4909
|
+
"group-has-[button:focus-visible]/copyable-value:bg-primary-100",
|
|
4910
|
+
],
|
|
4911
|
+
variants: {
|
|
4912
|
+
inline: {
|
|
4913
|
+
false: ["truncate"],
|
|
4914
|
+
// Lets values without spaces (URLs, tokens) wrap inside their box instead of running under
|
|
4915
|
+
// the button.
|
|
4916
|
+
true: ["[overflow-wrap:anywhere]"],
|
|
4917
|
+
},
|
|
4918
|
+
},
|
|
4919
|
+
defaultVariants: {
|
|
4920
|
+
inline: false,
|
|
4921
|
+
},
|
|
4922
|
+
});
|
|
4923
|
+
// Squares the button's left edge against the value and matches the value's tint, completing the
|
|
4924
|
+
// fused look. The tint is owned here, inside the DS, rather than at call sites — the same way
|
|
4925
|
+
// CopyButton composes Button through cvaIconButton.
|
|
4926
|
+
const cvaCopyableValueButton = cssClassVarianceUtilities.cva({
|
|
4927
|
+
base: ["shrink-0", "rounded-l-none", "hover:bg-primary-100", "focus-visible:bg-primary-100"],
|
|
4928
|
+
});
|
|
4929
|
+
|
|
4930
|
+
/**
|
|
4931
|
+
* CopyableValue displays a value as plain (non-clickable) text with a copy button beside it. While the
|
|
4932
|
+
* button is hovered or focused, the value is highlighted together with it, so the two read as one control
|
|
4933
|
+
* and it is clear what will be copied.
|
|
4934
|
+
*
|
|
4935
|
+
* ### When to use
|
|
4936
|
+
* Use CopyableValue whenever a value such as an ID, short code, URL or token is shown and should be
|
|
4937
|
+
* copyable — in table cells, detail lists, cards and drawers. Long values truncate to the available
|
|
4938
|
+
* width while the full value is still copied.
|
|
4939
|
+
*
|
|
4940
|
+
* ### When not to use
|
|
4941
|
+
* - Do not hand-roll a `Text` + `CopyButton` pair — this component owns the layout, truncation and highlight.
|
|
4942
|
+
* - When copying is the primary action of a panel (e.g. an invite link), use `CopyButton` with a `label` instead.
|
|
4943
|
+
* - Do not make the value itself click-to-copy — only the button copies, so the surrounding row or
|
|
4944
|
+
* card stays clickable.
|
|
4945
|
+
*
|
|
4946
|
+
* @example Copyable account ID in a table cell
|
|
4947
|
+
* ```tsx
|
|
4948
|
+
* import { CopyableValue } from "@trackunit/react-components";
|
|
4949
|
+
*
|
|
4950
|
+
* const AccountIdCell = ({ accountId }: { accountId: string }) => (
|
|
4951
|
+
* <CopyableValue size="extraSmall" value={accountId} />
|
|
4952
|
+
* );
|
|
4953
|
+
* ```
|
|
4954
|
+
* @param {CopyableValueProps} props - The props for the CopyableValue component
|
|
4955
|
+
* @returns {ReactElement} CopyableValue component
|
|
4956
|
+
*/
|
|
4957
|
+
const CopyableValue = ({ value, children, copyLabel, copiedLabel, size = "small", textSize = "medium", inline = false, onCopy, className, style, "data-testid": dataTestId, ref, }) => {
|
|
4958
|
+
const [t] = useTranslation();
|
|
4959
|
+
if (value === "" && children === undefined) {
|
|
4960
|
+
return (jsxRuntime.jsx(EmptyValue, { className: className, "data-testid": dataTestId ? `${dataTestId}-empty` : undefined, style: style }));
|
|
4961
|
+
}
|
|
4962
|
+
return (jsxRuntime.jsxs("span", { className: cvaCopyableValue({ inline, className }), "data-testid": dataTestId, ref: ref, style: style, children: [jsxRuntime.jsx(Text, { className: cvaCopyableValueText({ inline }), size: textSize, type: "span", children: children ?? value }), jsxRuntime.jsx(CopyButton, { className: cvaCopyableValueButton(), copiedLabel: copiedLabel, copyLabel: copyLabel ?? t("copyableValue.copyValue", { value }), "data-testid": dataTestId ? `${dataTestId}-button` : undefined, onCopy: onCopy, size: size, value: value })] }));
|
|
4963
|
+
};
|
|
4964
|
+
CopyableValue.displayName = "CopyableValue";
|
|
4965
|
+
|
|
4847
4966
|
const cvaContainerStyles = cssClassVarianceUtilities.cva({
|
|
4848
4967
|
base: [
|
|
4849
4968
|
"flex",
|
|
@@ -4979,36 +5098,6 @@ const EmptyState = ({ description, altText, image = "SEARCH_DOCUMENT", customIma
|
|
|
4979
5098
|
return (jsxRuntime.jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", ref: ref, style: style, children: loading ? (jsxRuntime.jsxs("div", { className: "flex w-full flex-col items-center gap-4", children: [jsxRuntime.jsx(Spinner, { centering: "centered", "data-testid": "spinner" }), jsxRuntime.jsx(SkeletonLabel, { textSize: "text-base", width: "clamp(20%, 200px, 80%)" })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [customImageSrc !== null && customImageSrc !== undefined ? (typeof customImageSrc === "string" ? (jsxRuntime.jsx("img", { alt: altText, className: cvaImgStyles(), height: 200, src: customImageSrc, width: 200 })) : (customImageSrc)) : (typeof ImageSource !== "undefined" && (jsxRuntime.jsx(ImageSource, { "data-testid": "empty-state-image", height: 200, width: 200 }, image))), description !== undefined && description !== "" ? (jsxRuntime.jsx(Text, { align: "center", size: "medium", children: description })) : null, jsxRuntime.jsxs("div", { className: "mt-4 grid gap-3", children: [jsxRuntime.jsxs("div", { className: "flex gap-3", children: [secondaryAction ? (jsxRuntime.jsx(Button, { "data-testid": "empty-state-secondary-button", disabled: secondaryAction.disabled, onClick: secondaryAction.onClick, variant: "secondary", children: secondaryAction.to ? (jsxRuntime.jsx(reactRouter.Link, { params: secondaryAction.to.parameters, to: secondaryAction.to.pathname, children: secondaryAction.title })) : (secondaryAction.title) })) : null, primaryAction ? (jsxRuntime.jsx(Button, { "data-testid": "empty-state-primary-button", disabled: primaryAction.disabled, onClick: primaryAction.onClick, children: primaryAction.to ? (jsxRuntime.jsx(reactRouter.Link, { params: primaryAction.to.parameters, to: primaryAction.to.pathname, children: primaryAction.title })) : (primaryAction.title) })) : null] }), additionalHelpAction?.to ? (jsxRuntime.jsx(Button, { asChild: true, "data-testid": "empty-state-additional-button", disabled: additionalHelpAction.disabled, onClick: additionalHelpAction.onClick, suffix: jsxRuntime.jsx(Icon, { name: "ArrowTopRightOnSquare", size: "small" }), variant: "ghost", children: jsxRuntime.jsx(reactRouter.Link, { params: additionalHelpAction.to.parameters, target: additionalHelpAction.to.target, to: additionalHelpAction.to.pathname, children: additionalHelpAction.title }) })) : null] })] })) }));
|
|
4980
5099
|
};
|
|
4981
5100
|
|
|
4982
|
-
const cvaEmptyValue = cssClassVarianceUtilities.cva({ base: ["text-neutral-400"] });
|
|
4983
|
-
|
|
4984
|
-
/**
|
|
4985
|
-
* EmptyValue renders a consistent dash symbol ("–") to represent missing, null, undefined, or not applicable values.
|
|
4986
|
-
* It is primarily used in tables and detail lists to maintain visual consistency when data is absent.
|
|
4987
|
-
*
|
|
4988
|
-
* ### When to use
|
|
4989
|
-
* Use EmptyValue as a placeholder in table cells, detail lists, or any data display where values may be missing.
|
|
4990
|
-
*
|
|
4991
|
-
* ### When not to use
|
|
4992
|
-
* Do not use EmptyValue for empty pages or sections — use `EmptyState` instead.
|
|
4993
|
-
*
|
|
4994
|
-
* @example Empty value in a details row
|
|
4995
|
-
* ```tsx
|
|
4996
|
-
* import { EmptyValue, Text } from "@trackunit/react-components";
|
|
4997
|
-
*
|
|
4998
|
-
* const AssetDetail = ({ value }: { value?: string }) => (
|
|
4999
|
-
* <div className="flex justify-between">
|
|
5000
|
-
* <Text weight="bold">Serial Number</Text>
|
|
5001
|
-
* {value ? <Text>{value}</Text> : <EmptyValue />}
|
|
5002
|
-
* </div>
|
|
5003
|
-
* );
|
|
5004
|
-
* ```
|
|
5005
|
-
* @param {EmptyValueProps} props - The props for the EmptyValue component
|
|
5006
|
-
* @returns {ReactElement} EmptyValue component
|
|
5007
|
-
*/
|
|
5008
|
-
const EmptyValue = ({ className, "data-testid": dataTestId, style, ref }) => {
|
|
5009
|
-
return (jsxRuntime.jsx("div", { className: cvaEmptyValue({ className }), "data-testid": dataTestId, ref: ref, style: style, children: "-" }));
|
|
5010
|
-
};
|
|
5011
|
-
|
|
5012
5101
|
const cvaExternalLink = cssClassVarianceUtilities.cva({
|
|
5013
5102
|
base: ["underline", "decoration-[1.5px]", "underline-offset-2", "text-sm"],
|
|
5014
5103
|
variants: {
|
|
@@ -15830,6 +15919,7 @@ exports.CardHeader = CardHeader;
|
|
|
15830
15919
|
exports.Collapse = Collapse;
|
|
15831
15920
|
exports.CompletionStatusIndicator = CompletionStatusIndicator;
|
|
15832
15921
|
exports.CopyButton = CopyButton;
|
|
15922
|
+
exports.CopyableValue = CopyableValue;
|
|
15833
15923
|
exports.DEFAULT_SKELETON_PREFERENCE_CARD_PROPS = DEFAULT_SKELETON_PREFERENCE_CARD_PROPS;
|
|
15834
15924
|
exports.DetailsList = DetailsList;
|
|
15835
15925
|
exports.EmptyState = EmptyState;
|
package/index.esm.js
CHANGED
|
@@ -27,6 +27,7 @@ import { z } from 'zod';
|
|
|
27
27
|
var defaultTranslations = {
|
|
28
28
|
"breadcrumb.backButton": "Back",
|
|
29
29
|
"breadcrumb.expandButton": "expand",
|
|
30
|
+
"copyableValue.copyValue": "Copy {{value}}",
|
|
30
31
|
"copyButton.copied": "Copied!",
|
|
31
32
|
"copyButton.copy": "Copy",
|
|
32
33
|
"horizontalOverflowScroller.scrollLeft": "Scroll left",
|
|
@@ -4772,28 +4773,27 @@ const useCopyToClipboard = () => {
|
|
|
4772
4773
|
* element, so the button can be placed next to plain (non-clickable) text.
|
|
4773
4774
|
*
|
|
4774
4775
|
* ### When to use
|
|
4775
|
-
* - Icon-only (default): the copyable value is displayed as plain text (e.g. in a table cell or
|
|
4776
|
-
* row) and only a dedicated icon should trigger the copy, keeping the rest of the row clickable.
|
|
4777
4776
|
* - Labelled (`label` set): copying is the primary action of a panel or section — e.g. an invite
|
|
4778
4777
|
* link — and deserves a full button that is itself the click target.
|
|
4778
|
+
* - Icon-only (default): a copy control on its own, where no value is displayed next to it — e.g.
|
|
4779
|
+
* in the corner of a code block.
|
|
4779
4780
|
*
|
|
4780
4781
|
* ### When not to use
|
|
4781
|
-
*
|
|
4782
|
-
*
|
|
4782
|
+
* - To show a value with a copy button beside it, use `CopyableValue` — it owns the layout,
|
|
4783
|
+
* truncation and hover highlight. Do not pair `Text` and `CopyButton` by hand.
|
|
4784
|
+
* - Do not hand-roll copy affordances (custom icons, click-to-copy text) — use this component or
|
|
4785
|
+
* `CopyableValue` instead.
|
|
4783
4786
|
*
|
|
4784
4787
|
* ### Empty values
|
|
4785
4788
|
* CopyButton renders nothing when `value` is empty, so consumers never present a copy control
|
|
4786
4789
|
* that has no meaningful result.
|
|
4787
4790
|
*
|
|
4788
|
-
* @example
|
|
4791
|
+
* @example Labelled button for an invite link
|
|
4789
4792
|
* ```tsx
|
|
4790
|
-
* import { CopyButton
|
|
4793
|
+
* import { CopyButton } from "@trackunit/react-components";
|
|
4791
4794
|
*
|
|
4792
|
-
* const
|
|
4793
|
-
* <
|
|
4794
|
-
* <Text type="span">{code}</Text>
|
|
4795
|
-
* <CopyButton value={code} />
|
|
4796
|
-
* </div>
|
|
4795
|
+
* const CopyInviteLink = ({ link }: { link: string }) => (
|
|
4796
|
+
* <CopyButton label="Copy link" value={link} variant="secondary" />
|
|
4797
4797
|
* );
|
|
4798
4798
|
* ```
|
|
4799
4799
|
* @param {CopyButtonProps} props - The props for the CopyButton component
|
|
@@ -4825,7 +4825,7 @@ const CopyButton = ({ value, copyLabel, copiedLabel, size = "small", label, vari
|
|
|
4825
4825
|
setShowCopied(true);
|
|
4826
4826
|
clearTimeout(resetTimerRef.current);
|
|
4827
4827
|
resetTimerRef.current = setTimeout(() => setShowCopied(false), COPIED_FEEDBACK_DURATION_MS);
|
|
4828
|
-
onCopy?.();
|
|
4828
|
+
onCopy?.(value);
|
|
4829
4829
|
}, () => undefined);
|
|
4830
4830
|
};
|
|
4831
4831
|
const buttonSize = toButtonSize(size);
|
|
@@ -4843,6 +4843,125 @@ const CopyButton = ({ value, copyLabel, copiedLabel, size = "small", label, vari
|
|
|
4843
4843
|
CopyButton.displayName = "CopyButton";
|
|
4844
4844
|
const COPIED_FEEDBACK_DURATION_MS = 2000;
|
|
4845
4845
|
|
|
4846
|
+
const cvaEmptyValue = cva({ base: ["text-neutral-400"] });
|
|
4847
|
+
|
|
4848
|
+
/**
|
|
4849
|
+
* EmptyValue renders a consistent dash symbol ("–") to represent missing, null, undefined, or not applicable values.
|
|
4850
|
+
* It is primarily used in tables and detail lists to maintain visual consistency when data is absent.
|
|
4851
|
+
*
|
|
4852
|
+
* ### When to use
|
|
4853
|
+
* Use EmptyValue as a placeholder in table cells, detail lists, or any data display where values may be missing.
|
|
4854
|
+
*
|
|
4855
|
+
* ### When not to use
|
|
4856
|
+
* Do not use EmptyValue for empty pages or sections — use `EmptyState` instead.
|
|
4857
|
+
*
|
|
4858
|
+
* @example Empty value in a details row
|
|
4859
|
+
* ```tsx
|
|
4860
|
+
* import { EmptyValue, Text } from "@trackunit/react-components";
|
|
4861
|
+
*
|
|
4862
|
+
* const AssetDetail = ({ value }: { value?: string }) => (
|
|
4863
|
+
* <div className="flex justify-between">
|
|
4864
|
+
* <Text weight="bold">Serial Number</Text>
|
|
4865
|
+
* {value ? <Text>{value}</Text> : <EmptyValue />}
|
|
4866
|
+
* </div>
|
|
4867
|
+
* );
|
|
4868
|
+
* ```
|
|
4869
|
+
* @param {EmptyValueProps} props - The props for the EmptyValue component
|
|
4870
|
+
* @returns {ReactElement} EmptyValue component
|
|
4871
|
+
*/
|
|
4872
|
+
const EmptyValue = ({ className, "data-testid": dataTestId, style, ref }) => {
|
|
4873
|
+
return (jsx("div", { className: cvaEmptyValue({ className }), "data-testid": dataTestId, ref: ref, style: style, children: "-" }));
|
|
4874
|
+
};
|
|
4875
|
+
|
|
4876
|
+
// Named group so the highlight only reacts to this pair's own button, even when pairs are nested
|
|
4877
|
+
// inside other `group` scopes (table rows, cards).
|
|
4878
|
+
// `-ml-1` offsets the value's `pl-1`, so the text lines up with plain text and column headers while
|
|
4879
|
+
// the highlight's left padding reaches into the surrounding space (e.g. the cell padding); the
|
|
4880
|
+
// widened max width keeps the right edge where it was.
|
|
4881
|
+
const cvaCopyableValue = cva({
|
|
4882
|
+
base: ["group/copyable-value", "items-center", "-ml-1", "max-w-[calc(100%+0.25rem)]"],
|
|
4883
|
+
variants: {
|
|
4884
|
+
inline: {
|
|
4885
|
+
// `overflow-hidden` + the max width defeat the grid/flex `min-width: auto` blowout, so the
|
|
4886
|
+
// value truncates inside table cells instead of widening them.
|
|
4887
|
+
false: ["flex", "min-w-0", "overflow-hidden"],
|
|
4888
|
+
true: ["inline-flex"],
|
|
4889
|
+
},
|
|
4890
|
+
},
|
|
4891
|
+
defaultVariants: {
|
|
4892
|
+
inline: false,
|
|
4893
|
+
},
|
|
4894
|
+
});
|
|
4895
|
+
// The value stretches to the button's height and tints while the button is hovered or keyboard-
|
|
4896
|
+
// focused, so value and button read as one fused control. `focus-visible` (not `focus`) keeps the
|
|
4897
|
+
// tint from sticking on a row after a mouse click. Anchors inside the value do not trigger it.
|
|
4898
|
+
const cvaCopyableValueText = cva({
|
|
4899
|
+
base: [
|
|
4900
|
+
"min-w-0",
|
|
4901
|
+
"self-stretch",
|
|
4902
|
+
"content-center",
|
|
4903
|
+
"rounded-l-md",
|
|
4904
|
+
"pl-1",
|
|
4905
|
+
"pr-0.5",
|
|
4906
|
+
"transition-colors",
|
|
4907
|
+
"group-has-[button:hover]/copyable-value:bg-primary-100",
|
|
4908
|
+
"group-has-[button:focus-visible]/copyable-value:bg-primary-100",
|
|
4909
|
+
],
|
|
4910
|
+
variants: {
|
|
4911
|
+
inline: {
|
|
4912
|
+
false: ["truncate"],
|
|
4913
|
+
// Lets values without spaces (URLs, tokens) wrap inside their box instead of running under
|
|
4914
|
+
// the button.
|
|
4915
|
+
true: ["[overflow-wrap:anywhere]"],
|
|
4916
|
+
},
|
|
4917
|
+
},
|
|
4918
|
+
defaultVariants: {
|
|
4919
|
+
inline: false,
|
|
4920
|
+
},
|
|
4921
|
+
});
|
|
4922
|
+
// Squares the button's left edge against the value and matches the value's tint, completing the
|
|
4923
|
+
// fused look. The tint is owned here, inside the DS, rather than at call sites — the same way
|
|
4924
|
+
// CopyButton composes Button through cvaIconButton.
|
|
4925
|
+
const cvaCopyableValueButton = cva({
|
|
4926
|
+
base: ["shrink-0", "rounded-l-none", "hover:bg-primary-100", "focus-visible:bg-primary-100"],
|
|
4927
|
+
});
|
|
4928
|
+
|
|
4929
|
+
/**
|
|
4930
|
+
* CopyableValue displays a value as plain (non-clickable) text with a copy button beside it. While the
|
|
4931
|
+
* button is hovered or focused, the value is highlighted together with it, so the two read as one control
|
|
4932
|
+
* and it is clear what will be copied.
|
|
4933
|
+
*
|
|
4934
|
+
* ### When to use
|
|
4935
|
+
* Use CopyableValue whenever a value such as an ID, short code, URL or token is shown and should be
|
|
4936
|
+
* copyable — in table cells, detail lists, cards and drawers. Long values truncate to the available
|
|
4937
|
+
* width while the full value is still copied.
|
|
4938
|
+
*
|
|
4939
|
+
* ### When not to use
|
|
4940
|
+
* - Do not hand-roll a `Text` + `CopyButton` pair — this component owns the layout, truncation and highlight.
|
|
4941
|
+
* - When copying is the primary action of a panel (e.g. an invite link), use `CopyButton` with a `label` instead.
|
|
4942
|
+
* - Do not make the value itself click-to-copy — only the button copies, so the surrounding row or
|
|
4943
|
+
* card stays clickable.
|
|
4944
|
+
*
|
|
4945
|
+
* @example Copyable account ID in a table cell
|
|
4946
|
+
* ```tsx
|
|
4947
|
+
* import { CopyableValue } from "@trackunit/react-components";
|
|
4948
|
+
*
|
|
4949
|
+
* const AccountIdCell = ({ accountId }: { accountId: string }) => (
|
|
4950
|
+
* <CopyableValue size="extraSmall" value={accountId} />
|
|
4951
|
+
* );
|
|
4952
|
+
* ```
|
|
4953
|
+
* @param {CopyableValueProps} props - The props for the CopyableValue component
|
|
4954
|
+
* @returns {ReactElement} CopyableValue component
|
|
4955
|
+
*/
|
|
4956
|
+
const CopyableValue = ({ value, children, copyLabel, copiedLabel, size = "small", textSize = "medium", inline = false, onCopy, className, style, "data-testid": dataTestId, ref, }) => {
|
|
4957
|
+
const [t] = useTranslation();
|
|
4958
|
+
if (value === "" && children === undefined) {
|
|
4959
|
+
return (jsx(EmptyValue, { className: className, "data-testid": dataTestId ? `${dataTestId}-empty` : undefined, style: style }));
|
|
4960
|
+
}
|
|
4961
|
+
return (jsxs("span", { className: cvaCopyableValue({ inline, className }), "data-testid": dataTestId, ref: ref, style: style, children: [jsx(Text, { className: cvaCopyableValueText({ inline }), size: textSize, type: "span", children: children ?? value }), jsx(CopyButton, { className: cvaCopyableValueButton(), copiedLabel: copiedLabel, copyLabel: copyLabel ?? t("copyableValue.copyValue", { value }), "data-testid": dataTestId ? `${dataTestId}-button` : undefined, onCopy: onCopy, size: size, value: value })] }));
|
|
4962
|
+
};
|
|
4963
|
+
CopyableValue.displayName = "CopyableValue";
|
|
4964
|
+
|
|
4846
4965
|
const cvaContainerStyles = cva({
|
|
4847
4966
|
base: [
|
|
4848
4967
|
"flex",
|
|
@@ -4978,36 +5097,6 @@ const EmptyState = ({ description, altText, image = "SEARCH_DOCUMENT", customIma
|
|
|
4978
5097
|
return (jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", ref: ref, style: style, children: loading ? (jsxs("div", { className: "flex w-full flex-col items-center gap-4", children: [jsx(Spinner, { centering: "centered", "data-testid": "spinner" }), jsx(SkeletonLabel, { textSize: "text-base", width: "clamp(20%, 200px, 80%)" })] })) : (jsxs(Fragment, { children: [customImageSrc !== null && customImageSrc !== undefined ? (typeof customImageSrc === "string" ? (jsx("img", { alt: altText, className: cvaImgStyles(), height: 200, src: customImageSrc, width: 200 })) : (customImageSrc)) : (typeof ImageSource !== "undefined" && (jsx(ImageSource, { "data-testid": "empty-state-image", height: 200, width: 200 }, image))), description !== undefined && description !== "" ? (jsx(Text, { align: "center", size: "medium", children: description })) : null, jsxs("div", { className: "mt-4 grid gap-3", children: [jsxs("div", { className: "flex gap-3", children: [secondaryAction ? (jsx(Button, { "data-testid": "empty-state-secondary-button", disabled: secondaryAction.disabled, onClick: secondaryAction.onClick, variant: "secondary", children: secondaryAction.to ? (jsx(Link, { params: secondaryAction.to.parameters, to: secondaryAction.to.pathname, children: secondaryAction.title })) : (secondaryAction.title) })) : null, primaryAction ? (jsx(Button, { "data-testid": "empty-state-primary-button", disabled: primaryAction.disabled, onClick: primaryAction.onClick, children: primaryAction.to ? (jsx(Link, { params: primaryAction.to.parameters, to: primaryAction.to.pathname, children: primaryAction.title })) : (primaryAction.title) })) : null] }), additionalHelpAction?.to ? (jsx(Button, { asChild: true, "data-testid": "empty-state-additional-button", disabled: additionalHelpAction.disabled, onClick: additionalHelpAction.onClick, suffix: jsx(Icon, { name: "ArrowTopRightOnSquare", size: "small" }), variant: "ghost", children: jsx(Link, { params: additionalHelpAction.to.parameters, target: additionalHelpAction.to.target, to: additionalHelpAction.to.pathname, children: additionalHelpAction.title }) })) : null] })] })) }));
|
|
4979
5098
|
};
|
|
4980
5099
|
|
|
4981
|
-
const cvaEmptyValue = cva({ base: ["text-neutral-400"] });
|
|
4982
|
-
|
|
4983
|
-
/**
|
|
4984
|
-
* EmptyValue renders a consistent dash symbol ("–") to represent missing, null, undefined, or not applicable values.
|
|
4985
|
-
* It is primarily used in tables and detail lists to maintain visual consistency when data is absent.
|
|
4986
|
-
*
|
|
4987
|
-
* ### When to use
|
|
4988
|
-
* Use EmptyValue as a placeholder in table cells, detail lists, or any data display where values may be missing.
|
|
4989
|
-
*
|
|
4990
|
-
* ### When not to use
|
|
4991
|
-
* Do not use EmptyValue for empty pages or sections — use `EmptyState` instead.
|
|
4992
|
-
*
|
|
4993
|
-
* @example Empty value in a details row
|
|
4994
|
-
* ```tsx
|
|
4995
|
-
* import { EmptyValue, Text } from "@trackunit/react-components";
|
|
4996
|
-
*
|
|
4997
|
-
* const AssetDetail = ({ value }: { value?: string }) => (
|
|
4998
|
-
* <div className="flex justify-between">
|
|
4999
|
-
* <Text weight="bold">Serial Number</Text>
|
|
5000
|
-
* {value ? <Text>{value}</Text> : <EmptyValue />}
|
|
5001
|
-
* </div>
|
|
5002
|
-
* );
|
|
5003
|
-
* ```
|
|
5004
|
-
* @param {EmptyValueProps} props - The props for the EmptyValue component
|
|
5005
|
-
* @returns {ReactElement} EmptyValue component
|
|
5006
|
-
*/
|
|
5007
|
-
const EmptyValue = ({ className, "data-testid": dataTestId, style, ref }) => {
|
|
5008
|
-
return (jsx("div", { className: cvaEmptyValue({ className }), "data-testid": dataTestId, ref: ref, style: style, children: "-" }));
|
|
5009
|
-
};
|
|
5010
|
-
|
|
5011
5100
|
const cvaExternalLink = cva({
|
|
5012
5101
|
base: ["underline", "decoration-[1.5px]", "underline-offset-2", "text-sm"],
|
|
5013
5102
|
variants: {
|
|
@@ -15814,4 +15903,4 @@ const useWindowActivity = ({ onFocus, onBlur, skip = false } = { onBlur: undefin
|
|
|
15814
15903
|
*/
|
|
15815
15904
|
setupLibraryTranslations();
|
|
15816
15905
|
|
|
15817
|
-
export { Alert, Badge, Breadcrumb, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyButton, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, IdentityCard, IdentityCardSkeleton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, LabeledValue, LabeledValueList, List, ListItem, MAX_HASH_LENGTH, MAX_URL_LENGTH, MenuContent, MenuDivider, MenuItem, MenuTree, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SHEET_TRANSITION_DURATION, SHEET_TRANSITION_DURATION_MS, SHEET_TRANSITION_EASING, SectionHeader, SegmentedValueBar, Sheet, Sidebar, SidebarContentLayout, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useBidirectionalScroll, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCursorUrlSync, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGridAreas, useHashParamSync, useHold, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useKeyboardShortcut, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMenuSearchField, useMenuTree, useMergeRefs, useModifierKey, useOptionalPopoverContext, useOverflowBorder, useOverflowItems, usePersistedState, usePopoverContext, usePrevious, usePrompt, useRandomCSSLengths, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSearchParamSync, useSelfUpdatingRef, useSessionStorage, useSessionStorageReducer, useSheet, useSheetSnap, useStorageKey, useStripForeignIdentityHash, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
|
|
15906
|
+
export { Alert, Badge, Breadcrumb, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyButton, CopyableValue, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, IdentityCard, IdentityCardSkeleton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, LabeledValue, LabeledValueList, List, ListItem, MAX_HASH_LENGTH, MAX_URL_LENGTH, MenuContent, MenuDivider, MenuItem, MenuTree, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SHEET_TRANSITION_DURATION, SHEET_TRANSITION_DURATION_MS, SHEET_TRANSITION_EASING, SectionHeader, SegmentedValueBar, Sheet, Sidebar, SidebarContentLayout, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useBidirectionalScroll, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCursorUrlSync, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGridAreas, useHashParamSync, useHold, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useKeyboardShortcut, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMenuSearchField, useMenuTree, useMergeRefs, useModifierKey, useOptionalPopoverContext, useOverflowBorder, useOverflowItems, usePersistedState, usePopoverContext, usePrevious, usePrompt, useRandomCSSLengths, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSearchParamSync, useSelfUpdatingRef, useSessionStorage, useSessionStorageReducer, useSheet, useSheetSnap, useStorageKey, useStripForeignIdentityHash, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"migrations": "./migrations.json",
|
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
"@floating-ui/react": "^0.26.25",
|
|
15
15
|
"string-ts": "^2.0.0",
|
|
16
16
|
"tailwind-merge": "^2.0.0",
|
|
17
|
-
"@trackunit/ui-design-tokens": "1.
|
|
18
|
-
"@trackunit/css-class-variance-utilities": "2.
|
|
19
|
-
"@trackunit/shared-utils": "1.
|
|
20
|
-
"@trackunit/ui-icons": "1.
|
|
17
|
+
"@trackunit/ui-design-tokens": "1.18.0",
|
|
18
|
+
"@trackunit/css-class-variance-utilities": "2.3.0",
|
|
19
|
+
"@trackunit/shared-utils": "1.19.0",
|
|
20
|
+
"@trackunit/ui-icons": "1.17.0",
|
|
21
21
|
"es-toolkit": "^1.52.0",
|
|
22
22
|
"@tanstack/react-virtual": "^3.14.13",
|
|
23
23
|
"dequal": "^2.0.3",
|
|
24
24
|
"fflate": "^0.8.3",
|
|
25
25
|
"zod": "^3.25.76",
|
|
26
|
-
"@trackunit/i18n-library-translation": "2.
|
|
26
|
+
"@trackunit/i18n-library-translation": "2.8.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": "^19.0.0",
|
|
@@ -41,11 +41,11 @@ export interface CopyButtonProps extends CommonProps, Styleable, Refable<HTMLBut
|
|
|
41
41
|
*/
|
|
42
42
|
variant?: ButtonVariant;
|
|
43
43
|
/**
|
|
44
|
-
* Called
|
|
45
|
-
* Clipboard API or the `execCommand("copy")` fallback. Only skipped
|
|
46
|
-
* both mechanisms fail.
|
|
44
|
+
* Called with the copied value after it has been written to the clipboard —
|
|
45
|
+
* via the Clipboard API or the `execCommand("copy")` fallback. Only skipped
|
|
46
|
+
* when both mechanisms fail.
|
|
47
47
|
*/
|
|
48
|
-
onCopy?: () => void;
|
|
48
|
+
onCopy?: (value: string) => void;
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* CopyButton is a standalone icon button that copies a value to the clipboard when clicked,
|
|
@@ -53,28 +53,27 @@ export interface CopyButtonProps extends CommonProps, Styleable, Refable<HTMLBut
|
|
|
53
53
|
* element, so the button can be placed next to plain (non-clickable) text.
|
|
54
54
|
*
|
|
55
55
|
* ### When to use
|
|
56
|
-
* - Icon-only (default): the copyable value is displayed as plain text (e.g. in a table cell or
|
|
57
|
-
* row) and only a dedicated icon should trigger the copy, keeping the rest of the row clickable.
|
|
58
56
|
* - Labelled (`label` set): copying is the primary action of a panel or section — e.g. an invite
|
|
59
57
|
* link — and deserves a full button that is itself the click target.
|
|
58
|
+
* - Icon-only (default): a copy control on its own, where no value is displayed next to it — e.g.
|
|
59
|
+
* in the corner of a code block.
|
|
60
60
|
*
|
|
61
61
|
* ### When not to use
|
|
62
|
-
*
|
|
63
|
-
*
|
|
62
|
+
* - To show a value with a copy button beside it, use `CopyableValue` — it owns the layout,
|
|
63
|
+
* truncation and hover highlight. Do not pair `Text` and `CopyButton` by hand.
|
|
64
|
+
* - Do not hand-roll copy affordances (custom icons, click-to-copy text) — use this component or
|
|
65
|
+
* `CopyableValue` instead.
|
|
64
66
|
*
|
|
65
67
|
* ### Empty values
|
|
66
68
|
* CopyButton renders nothing when `value` is empty, so consumers never present a copy control
|
|
67
69
|
* that has no meaningful result.
|
|
68
70
|
*
|
|
69
|
-
* @example
|
|
71
|
+
* @example Labelled button for an invite link
|
|
70
72
|
* ```tsx
|
|
71
|
-
* import { CopyButton
|
|
73
|
+
* import { CopyButton } from "@trackunit/react-components";
|
|
72
74
|
*
|
|
73
|
-
* const
|
|
74
|
-
* <
|
|
75
|
-
* <Text type="span">{code}</Text>
|
|
76
|
-
* <CopyButton value={code} />
|
|
77
|
-
* </div>
|
|
75
|
+
* const CopyInviteLink = ({ link }: { link: string }) => (
|
|
76
|
+
* <CopyButton label="Copy link" value={link} variant="secondary" />
|
|
78
77
|
* );
|
|
79
78
|
* ```
|
|
80
79
|
* @param {CopyButtonProps} props - The props for the CopyButton component
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { CommonProps } from "../../common/CommonProps";
|
|
3
|
+
import { Refable } from "../../common/Refable";
|
|
4
|
+
import { Size } from "../../common/Size";
|
|
5
|
+
import type { Styleable } from "../../common/Styleable";
|
|
6
|
+
import { IconButtonSize } from "../buttons/shared/IconButtonSize";
|
|
7
|
+
export interface CopyableValueProps extends CommonProps, Styleable, Refable<HTMLSpanElement> {
|
|
8
|
+
/**
|
|
9
|
+
* The value copied to the clipboard. It is also what is displayed, unless `children` is set.
|
|
10
|
+
* An empty string renders the `EmptyValue` dash with no copy button, so missing data looks the
|
|
11
|
+
* same as everywhere else.
|
|
12
|
+
*/
|
|
13
|
+
value: string;
|
|
14
|
+
/**
|
|
15
|
+
* What to display in place of `value` — e.g. a link that stands in for the id being copied.
|
|
16
|
+
* The copy button still copies `value`.
|
|
17
|
+
*/
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Accessible label for the copy button, and tooltip text shown before copying. Defaults to the
|
|
21
|
+
* library's localized "Copy {value}", so the full value is available even when the displayed
|
|
22
|
+
* text is truncated, and every row in a table gets a distinct accessible name. Pass a short label
|
|
23
|
+
* that names the value (e.g. "Copy token") for long values such as tokens, keys or long URLs.
|
|
24
|
+
*/
|
|
25
|
+
copyLabel?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Tooltip text shown briefly after a successful copy. Defaults to the library's localized "Copied!".
|
|
28
|
+
*/
|
|
29
|
+
copiedLabel?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The size of the copy button. Use `extraSmall` inside fixed-height contexts such as table rows,
|
|
32
|
+
* where the button must fit a single text line.
|
|
33
|
+
*
|
|
34
|
+
* @default "small"
|
|
35
|
+
*/
|
|
36
|
+
size?: IconButtonSize;
|
|
37
|
+
/**
|
|
38
|
+
* The text size of the displayed value.
|
|
39
|
+
*
|
|
40
|
+
* @default "medium"
|
|
41
|
+
*/
|
|
42
|
+
textSize?: Size;
|
|
43
|
+
/**
|
|
44
|
+
* Places the pair inside running text, such as a sentence. The pair sits in the line as one inline
|
|
45
|
+
* box, and a long value wraps inside that box instead of truncating. Set `textSize` to match the
|
|
46
|
+
* surrounding text.
|
|
47
|
+
*
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
inline?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Called with the copied value after it has been written to the clipboard.
|
|
53
|
+
*/
|
|
54
|
+
onCopy?: (value: string) => void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* CopyableValue displays a value as plain (non-clickable) text with a copy button beside it. While the
|
|
58
|
+
* button is hovered or focused, the value is highlighted together with it, so the two read as one control
|
|
59
|
+
* and it is clear what will be copied.
|
|
60
|
+
*
|
|
61
|
+
* ### When to use
|
|
62
|
+
* Use CopyableValue whenever a value such as an ID, short code, URL or token is shown and should be
|
|
63
|
+
* copyable — in table cells, detail lists, cards and drawers. Long values truncate to the available
|
|
64
|
+
* width while the full value is still copied.
|
|
65
|
+
*
|
|
66
|
+
* ### When not to use
|
|
67
|
+
* - Do not hand-roll a `Text` + `CopyButton` pair — this component owns the layout, truncation and highlight.
|
|
68
|
+
* - When copying is the primary action of a panel (e.g. an invite link), use `CopyButton` with a `label` instead.
|
|
69
|
+
* - Do not make the value itself click-to-copy — only the button copies, so the surrounding row or
|
|
70
|
+
* card stays clickable.
|
|
71
|
+
*
|
|
72
|
+
* @example Copyable account ID in a table cell
|
|
73
|
+
* ```tsx
|
|
74
|
+
* import { CopyableValue } from "@trackunit/react-components";
|
|
75
|
+
*
|
|
76
|
+
* const AccountIdCell = ({ accountId }: { accountId: string }) => (
|
|
77
|
+
* <CopyableValue size="extraSmall" value={accountId} />
|
|
78
|
+
* );
|
|
79
|
+
* ```
|
|
80
|
+
* @param {CopyableValueProps} props - The props for the CopyableValue component
|
|
81
|
+
* @returns {ReactElement} CopyableValue component
|
|
82
|
+
*/
|
|
83
|
+
export declare const CopyableValue: {
|
|
84
|
+
({ value, children, copyLabel, copiedLabel, size, textSize, inline, onCopy, className, style, "data-testid": dataTestId, ref, }: CopyableValueProps): ReactElement;
|
|
85
|
+
displayName: string;
|
|
86
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export declare const cvaCopyableValue: import("cva").CVAComponent<Omit<{
|
|
2
|
+
base: string[];
|
|
3
|
+
variants: {
|
|
4
|
+
inline: {
|
|
5
|
+
false: string[];
|
|
6
|
+
true: string[];
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
defaultVariants: {
|
|
10
|
+
inline: false;
|
|
11
|
+
};
|
|
12
|
+
}, "defaultVariants"> & {
|
|
13
|
+
variants: {
|
|
14
|
+
inline: {
|
|
15
|
+
false: string[];
|
|
16
|
+
true: string[];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
defaultVariants: Omit<{}, "inline"> & {
|
|
20
|
+
inline: false;
|
|
21
|
+
};
|
|
22
|
+
}, {
|
|
23
|
+
inline: {
|
|
24
|
+
false: string[];
|
|
25
|
+
true: string[];
|
|
26
|
+
};
|
|
27
|
+
}>;
|
|
28
|
+
export declare const cvaCopyableValueText: import("cva").CVAComponent<Omit<{
|
|
29
|
+
base: string[];
|
|
30
|
+
variants: {
|
|
31
|
+
inline: {
|
|
32
|
+
false: string[];
|
|
33
|
+
true: string[];
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
inline: false;
|
|
38
|
+
};
|
|
39
|
+
}, "defaultVariants"> & {
|
|
40
|
+
variants: {
|
|
41
|
+
inline: {
|
|
42
|
+
false: string[];
|
|
43
|
+
true: string[];
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
defaultVariants: Omit<{}, "inline"> & {
|
|
47
|
+
inline: false;
|
|
48
|
+
};
|
|
49
|
+
}, {
|
|
50
|
+
inline: {
|
|
51
|
+
false: string[];
|
|
52
|
+
true: string[];
|
|
53
|
+
};
|
|
54
|
+
}>;
|
|
55
|
+
export declare const cvaCopyableValueButton: import("cva").CVAComponent<Omit<{
|
|
56
|
+
base: string[];
|
|
57
|
+
}, "defaultVariants"> & {
|
|
58
|
+
variants: unknown;
|
|
59
|
+
defaultVariants: Omit<{}, never>;
|
|
60
|
+
}, unknown>;
|
package/src/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export * from "./components/Card/CardHeader";
|
|
|
24
24
|
export * from "./components/Clickable/Clickable.variants";
|
|
25
25
|
export * from "./components/Collapse/Collapse";
|
|
26
26
|
export * from "./components/CompletionStatusIndicator/CompletionStatusIndicator";
|
|
27
|
+
export * from "./components/CopyableValue/CopyableValue";
|
|
27
28
|
export * from "./components/CopyButton/CopyButton";
|
|
28
29
|
export * from "./components/DetailsList/DetailsList";
|
|
29
30
|
export * from "./components/EmptyState/EmptyState";
|