Clamp Checkbox labels to one line, add wrap prop
Labels no longer overflow their container: by default they are clamped to a single line with an ellipsis and the full label is shown as the wrapper's title on hover. Pass `wrap` to let the label flow onto multiple lines. Adds the `twui-checkbox-label` class. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -147,6 +147,15 @@ export default function FormsPage() {
|
|||||||
title="Beta features"
|
title="Beta features"
|
||||||
label="Opt into experimental components."
|
label="Opt into experimental components."
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
title="Long label (clamped)"
|
||||||
|
label="This label is far too long to fit on a single line, so it is clamped to one line with an ellipsis - hover it to see the full text."
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
title="Long label (wrapped)"
|
||||||
|
label="This label is far too long to fit on a single line, but the wrap prop lets it flow onto as many lines as it needs."
|
||||||
|
wrap
|
||||||
|
/>
|
||||||
<Row className="gap-3">
|
<Row className="gap-3">
|
||||||
<Span size="small" variant="faded">
|
<Span size="small" variant="faded">
|
||||||
Notifications are {notifications ? "on" : "off"}
|
Notifications are {notifications ? "on" : "off"}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@moduletrace/twui",
|
"name": "@moduletrace/twui",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"description": "A modular skeletal React component framework for Tailwind CSS v4",
|
"description": "A modular skeletal React component framework for Tailwind CSS v4",
|
||||||
"author": "Benjamin Toby",
|
"author": "Benjamin Toby",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+31
-6
@@ -39,6 +39,12 @@ export type CheckboxProps = Omit<
|
|||||||
clickDispatchHandler?: (value: boolean) => void;
|
clickDispatchHandler?: (value: boolean) => void;
|
||||||
info?: string | ReactNode;
|
info?: string | ReactNode;
|
||||||
wrapperWrapperProps?: ComponentProps<typeof Stack>;
|
wrapperWrapperProps?: ComponentProps<typeof Stack>;
|
||||||
|
/**
|
||||||
|
* Let the label wrap onto multiple lines. By default the label is
|
||||||
|
* clamped to a single line with an ellipsis, and the full text is
|
||||||
|
* shown as the wrapper's `title` on hover.
|
||||||
|
*/
|
||||||
|
wrap?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,6 +52,7 @@ export type CheckboxProps = Omit<
|
|||||||
* @className twui-checkbox
|
* @className twui-checkbox
|
||||||
* @className twui-checkbox-checked
|
* @className twui-checkbox-checked
|
||||||
* @className twui-checkbox-unchecked
|
* @className twui-checkbox-unchecked
|
||||||
|
* @className twui-checkbox-label
|
||||||
*/
|
*/
|
||||||
export default function Checkbox({
|
export default function Checkbox({
|
||||||
wrapperProps,
|
wrapperProps,
|
||||||
@@ -63,6 +70,7 @@ export default function Checkbox({
|
|||||||
noLabel,
|
noLabel,
|
||||||
title,
|
title,
|
||||||
clickDispatchHandler,
|
clickDispatchHandler,
|
||||||
|
wrap,
|
||||||
...props
|
...props
|
||||||
}: CheckboxProps) {
|
}: CheckboxProps) {
|
||||||
const finalSize = size || 20;
|
const finalSize = size || 20;
|
||||||
@@ -72,6 +80,15 @@ export default function Checkbox({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const finalTitle = title;
|
const finalTitle = title;
|
||||||
|
const labelContent = label || finalTitle;
|
||||||
|
|
||||||
|
const hoverTitle =
|
||||||
|
wrapperProps?.title ??
|
||||||
|
(typeof labelContent == "string" || typeof labelContent == "number"
|
||||||
|
? String(labelContent)
|
||||||
|
: typeof finalTitle == "string"
|
||||||
|
? finalTitle
|
||||||
|
: undefined);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (typeof externalChecked == "undefined") return;
|
if (typeof externalChecked == "undefined") return;
|
||||||
@@ -85,12 +102,17 @@ export default function Checkbox({
|
|||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
{...wrapperWrapperProps}
|
{...wrapperWrapperProps}
|
||||||
className={twMerge("gap-1.5", wrapperWrapperProps?.className)}
|
className={twMerge(
|
||||||
|
"gap-1.5 min-w-0 max-w-full",
|
||||||
|
wrapperWrapperProps?.className,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
{...wrapperProps}
|
{...wrapperProps}
|
||||||
|
title={hoverTitle}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex items-start md:items-center gap-2 flex-wrap md:flex-nowrap",
|
"flex gap-2 flex-nowrap min-w-0 max-w-full",
|
||||||
|
wrap ? "items-start" : "items-center",
|
||||||
readOnly ? "opacity-70 pointer-events-none" : "",
|
readOnly ? "opacity-70 pointer-events-none" : "",
|
||||||
wrapperClassName,
|
wrapperClassName,
|
||||||
wrapperProps?.className,
|
wrapperProps?.className,
|
||||||
@@ -104,7 +126,7 @@ export default function Checkbox({
|
|||||||
<div
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex items-center justify-center p-[3px] rounded-default",
|
"flex items-center justify-center p-[3px] rounded-default shrink-0",
|
||||||
checked
|
checked
|
||||||
? "bg-primary twui-checkbox-checked text-white outline-slate-400"
|
? "bg-primary twui-checkbox-checked text-white outline-slate-400"
|
||||||
: "dark:outline-white/50 outline-2 -outline-offset-2 twui-checkbox-unchecked",
|
: "dark:outline-white/50 outline-2 -outline-offset-2 twui-checkbox-unchecked",
|
||||||
@@ -121,15 +143,18 @@ export default function Checkbox({
|
|||||||
{checked && <CheckMarkSVG />}
|
{checked && <CheckMarkSVG />}
|
||||||
</div>
|
</div>
|
||||||
{!noLabel && (
|
{!noLabel && (
|
||||||
<Stack className="gap-0.5">
|
<Stack className="gap-0.5 min-w-0 max-w-full">
|
||||||
<div
|
<div
|
||||||
{...labelProps}
|
{...labelProps}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"select-none whitespace-normal md:whitespace-nowrap",
|
"select-none max-w-full twui-checkbox-label",
|
||||||
|
wrap
|
||||||
|
? "whitespace-normal break-words"
|
||||||
|
: "line-clamp-1 break-all",
|
||||||
labelProps?.className,
|
labelProps?.className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{label || finalTitle}
|
{labelContent}
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user