Merge pull request 'ui/checkbox: use css modules' (#5228) from hgl-ui-checkbox into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5228
Reviewed-by: brianmcgee <brian@bmcgee.ie>
This commit is contained in:
brianmcgee
2025-09-22 10:11:59 +00:00
7 changed files with 77 additions and 102 deletions

View File

@@ -1,52 +0,0 @@
div.form-field {
&.checkbox {
@apply items-start;
& div.checkbox-control {
@apply w-5 h-5 rounded-sm bg-def-1 border border-inv-1 p-[0.0625rem];
&:hover {
@apply bg-def-acc-2;
}
&[data-disabled] {
@apply border-def-2;
}
&[data-invalid] {
@apply border-semantic-error-4;
}
&[data-readonly] {
@apply cursor-default bg-inherit border-none;
}
}
}
&.inverted {
&.checkbox {
& div.checkbox-control {
@apply bg-inv-1;
&:hover,
&[data-checked] {
@apply bg-inv-acc-4;
}
&[data-disabled] {
@apply bg-def-4 border-none;
}
&[data-readonly] {
@apply bg-inherit;
}
}
}
}
&.s {
& div.checkbox-control {
@apply w-4 h-4;
}
}
}

View File

@@ -0,0 +1,43 @@
.checkbox {
@apply items-start;
}
.checkboxControl {
@apply w-5 h-5 rounded-sm bg-def-1 border border-inv-1 p-[0.0625rem];
&:hover {
@apply bg-def-acc-2;
}
&[data-disabled] {
@apply border-def-2;
}
&[data-invalid] {
@apply border-semantic-error-4;
}
&[data-readonly] {
@apply cursor-default bg-inherit border-none;
}
.checkbox.s & {
@apply w-4 h-4;
}
.checkbox.inverted & {
@apply bg-inv-1;
&:hover,
&[data-checked] {
@apply bg-inv-acc-4;
}
&[data-disabled] {
@apply bg-def-4 border-none;
}
&[data-readonly] {
@apply bg-inherit;
}
}
}

View File

@@ -1,19 +1,18 @@
import {
CheckboxInputProps as KCheckboxInputProps,
CheckboxRootProps as KCheckboxRootProps,
Checkbox as KCheckbox,
} from "@kobalte/core/checkbox";
import { Checkbox as KCheckbox } from "@kobalte/core";
import Icon from "@/src/components/Icon/Icon";
import cx from "classnames";
import { Label } from "./Label";
import { PolymorphicProps } from "@kobalte/core/polymorphic";
import "./Checkbox.css";
import styles from "./Checkbox.module.css";
import { FieldProps } from "./Field";
import { Orienter } from "./Orienter";
import { Match, splitProps, Switch } from "solid-js";
import { Match, mergeProps, splitProps, Switch } from "solid-js";
export type CheckboxProps = FieldProps &
KCheckboxRootProps & {
@@ -21,24 +20,15 @@ export type CheckboxProps = FieldProps &
};
export const Checkbox = (props: CheckboxProps) => {
// we need to separate output the input otherwise it interferes with prop binding
const [_, rootProps] = splitProps(props, ["input"]);
const [styleProps, otherRootProps] = splitProps(rootProps, [
"class",
"size",
"orientation",
"inverted",
"ghost",
]);
const alignment = () =>
(props.orientation || "vertical") == "vertical" ? "start" : "center";
const [local, other] = splitProps(
mergeProps({ size: "default", orientation: "vertical" } as const, props),
["size", "orientation", "inverted", "ghost", "input"],
);
const iconChecked = (
<Icon
icon="Checkmark"
inverted={props.inverted}
inverted={local.inverted}
color="secondary"
size="100%"
/>
@@ -47,50 +37,45 @@ export const Checkbox = (props: CheckboxProps) => {
const iconUnchecked = (
<Icon
icon="Close"
inverted={props.inverted}
inverted={local.inverted}
color="secondary"
size="100%"
/>
);
return (
<KCheckbox.Root
<KCheckbox
class={cx(
styleProps.class,
"form-field",
"checkbox",
styleProps.size,
styleProps.orientation,
styles.checkbox,
local.size != "default" && styles[local.size],
{
inverted: styleProps.inverted,
ghost: styleProps.ghost,
[styles.inverted]: local.inverted,
},
)}
{...otherRootProps}
{...other}
>
{(state) => (
<Orienter orientation={styleProps.orientation} align={alignment()}>
<Orienter
orientation={local.orientation}
align={local.orientation == "vertical" ? "start" : "center"}
>
<Label
labelComponent={KCheckbox.Label}
descriptionComponent={KCheckbox.Description}
{...props}
/>
<KCheckbox.Input {...props.input} />
<KCheckbox.Control class="checkbox-control">
<KCheckbox.Input {...local.input} />
<KCheckbox.Control class={styles.checkboxControl}>
<Switch>
<Match when={!props.readOnly}>
<Match when={!other.readOnly}>
<KCheckbox.Indicator>{iconChecked}</KCheckbox.Indicator>
</Match>
<Match when={props.readOnly && state.checked()}>
{iconChecked}
</Match>
<Match when={props.readOnly && !state.checked()}>
{iconUnchecked}
</Match>
<Match when={state.checked()}>{iconChecked}</Match>
<Match when={true}>{iconUnchecked}</Match>
</Switch>
</KCheckbox.Control>
</Orienter>
)}
</KCheckbox.Root>
</KCheckbox>
);
};

View File

@@ -13,15 +13,4 @@ div.form-label {
& > label {
@apply flex items-center gap-1;
}
& > span[data-required]:not(span[data-readonly]),
& > label[data-required]:not(label[data-readonly]) {
.typography::after {
@apply fg-def-4 ml-1;
content: "*";
font-family: "Commit Mono", monospace;
font-size: 0.6875rem;
}
}
}

View File

@@ -49,6 +49,7 @@ export const Label = (props: LabelProps) => {
color={props.validationState == "invalid" ? "error" : "primary"}
weight={props.labelWeight || "bold"}
inverted={props.inverted}
in="Label"
>
{props.label}
</Typography>

View File

@@ -189,7 +189,15 @@
.typography.in-Button {
@apply max-w-full overflow-hidden whitespace-nowrap text-ellipsis;
}
.typography.in-Label {
[data-required]:not([data-readonly]) &::after {
@apply fg-def-4 ml-1;
content: "*";
font-family: "Commit Mono", monospace;
font-size: 0.6875rem;
}
}
.typography.in-Modal-title {
@apply mx-auto;
}

View File

@@ -53,6 +53,7 @@ export interface TypographyProps<H extends Hierarchy> {
align?: "left" | "center" | "right";
in?:
| "Button"
| "Label"
| "Modal-title"
| "TagSelect-label"
| "Select-item-label"