Skip to content

Commit

Permalink
Merge pull request #66 from plezanje-net/fix-checkbox
Browse files Browse the repository at this point in the history
Fix disabled state of checkbox and add indeterminate state.
  • Loading branch information
demshy authored Jan 19, 2025
2 parents 9b1115d + 515d90e commit 0304215
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 54 deletions.
79 changes: 61 additions & 18 deletions src/app/sandbox/checkbox/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,95 @@ import { useState } from "react";

function CheckboxPage() {
const [checked, setChecked] = useState(false);
const [checkedA, setCheckedA] = useState(false);
const [checkedB, setCheckedB] = useState(true);

return (
<div className="m-8">
<h1 className="text-xl">Checkbox demo</h1>

{/* hui */}
<div className="mt-10">
<Checkbox label="Check me out" />
A regular checkbox
<Checkbox
label="Check me out"
checked={checked}
onChange={setChecked}
/>
</div>

<div className="mt-1">
<Checkbox label="Uncheck me" checked />
Checkbox is: {checked ? <span>checked</span> : <span>not checked</span>}
</div>

<div className="mt-1">
<Checkbox label="Cannot check me" disabled />
<div className="mt-4">
Indeterminate checkbox
<Checkbox
label="All Checked"
checked={checkedA && checkedB}
indeterminate={checkedA != checkedB}
onChange={() => {
if (checkedA && checkedB) {
setCheckedA(false);
setCheckedB(false);
} else {
setCheckedA(true);
setCheckedB(true);
}
}}
/>
<Checkbox
label="Checkbox A"
checked={checkedA}
onChange={setCheckedA}
/>
<Checkbox
label="Checkbox B"
checked={checkedB}
onChange={setCheckedB}
/>
</div>
<div className="mt-1">
<Checkbox label="Cannot uncheck me" disabled checked />

<div className="mt-4">
A disabled unchecked checkbox
<Checkbox
label="Cannot check me"
disabled
checked={false}
onChange={() => {}}
/>
</div>

<div className="mt-1">
<div className="mt-4">
A disabled checked checkbox
<Checkbox
label="Check and control me"
onChange={(value) => setChecked(value)}
checked={checked}
label="Cannot uncheck me"
disabled
checked
onChange={() => {}}
/>
</div>
<div className="mt-1">
State of last checkbox is:{" "}
{checked ? <span>checked</span> : <span>not checked</span>}

<div className="mt-4">
A disabled indeterminate checkbox
<Checkbox
label="Cannot determine me"
indeterminate
disabled
checked
onChange={() => {}}
/>
</div>

<div className="mt-16">
<h3 className="text-lg">Notes</h3>
<div className="pl-4">
<ul className="mt-2 list-outside list-disc">
<li>Click area for checkbox is expanded with negative margins</li>
<li>
Indeterminate and error states are not defined (designed). They
will be added if/when needed.
</li>
</ul>
</div>
</div>
</div>
);
}

export default CheckboxPage;
82 changes: 46 additions & 36 deletions src/components/ui/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,68 @@ import { Switch } from "@headlessui/react";
type TCheckboxProps = {
disabled?: boolean;
label: string;
onChange?: (value: boolean) => void;
checked?: boolean;
hideLabel?: boolean;
onChange: (value: boolean) => void;
checked: boolean;
indeterminate?: boolean;
hideLabel?: boolean | string;
};

function Checkbox({
disabled = false,
label,
onChange,
checked = false,
indeterminate = false,
hideLabel = false,
}: TCheckboxProps) {
let hideLabelClass = "";
if (hideLabel === true) {
hideLabelClass = "sr-only";
} else if (hideLabel !== false) {
hideLabelClass = hideLabel;
} else {
hideLabelClass = "";
}

let squareStyle;
if (indeterminate || checked) {
if (disabled) {
squareStyle = "bg-neutral-300";
} else {
squareStyle = "bg-blue-500 group-active:bg-blue-600";
}
} else {
if (disabled) {
squareStyle = "border border-neutral-300 bg-neutral-100";
} else {
squareStyle =
"border border-neutral-400 bg-white group-active:border-neutral-600";
}
}

return (
<div className="flex items-center">
<Switch
defaultChecked={!onChange ? checked : undefined}
checked={onChange ? checked : undefined}
checked={checked}
onChange={onChange}
className="group -my-0.5 -ml-1.5 flex items-start outline-none"
disabled={disabled}
>
{({ checked }) => (
<>
<div
className={`mx-1.5 my-1.5 h-4 w-4 rounded ring-blue-100 group-focus-visible:ring
${
checked
? `${
!disabled
? "bg-blue-500 group-active:bg-blue-600"
: "bg-neutral-300"
}`
: `border ${
!disabled
? "border-neutral-400 bg-white group-active:border-neutral-600"
: "border-neutral-300 bg-neutral-100"
}`
}
`}
>
<svg width="16" height="16" viewBox="0 0 16 16">
<path
d="M6.36668 12.1333L2.43335 8.19998L3.51668 7.11664L6.36668 9.96664L12.4834 3.84998L13.5667 4.93331L6.36668 12.1333Z"
fill="white"
/>
</svg>
</div>
<span className={`mt-0.5 text-left ${hideLabel ? "sr-only" : ""}`}>
{label}
</span>
</>
)}
<div
className={`mx-1.5 my-1.5 h-4 w-4 rounded ring-blue-100 group-focus-visible:ring ${squareStyle}`}
>
<svg width="16" height="16" viewBox="0 0 16 16">
{indeterminate ? (
<path d="M 3.5 7.2 h9 v1.6 h-9 v -1.6 Z" fill="white" />
) : checked ? (
<path
d="M6.36668 12.1333L2.43335 8.19998L3.51668 7.11664L6.36668 9.96664L12.4834 3.84998L13.5667 4.93331L6.36668 12.1333Z"
fill="white"
/>
) : null}
</svg>
</div>
<span className={`mt-0.5 text-left ${hideLabelClass}`}>{label}</span>
</Switch>
</div>
);
Expand Down

0 comments on commit 0304215

Please sign in to comment.