Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/components/ui/CheckboxGroup/CheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client';

import CheckboxGroupRoot from './fragments/CheckboxGroupRoot';
import CheckboxGroupTrigger from './fragments/CheckboxGroupTrigger';
import CheckboxGroupLabel from './fragments/CheckboxGroupLabel';
import CheckboxGroupIndicator from './fragments/CheckboxGroupIndicator';

const CheckboxGroup = () => {
console.warn('Direct usage of CheckboxGroup is not supported. Please use CheckboxGroup.Root, CheckboxGroup.Item instead.');
return null;
};

CheckboxGroup.Root = CheckboxGroupRoot;
CheckboxGroup.Trigger = CheckboxGroupTrigger;
CheckboxGroup.Label = CheckboxGroupLabel;
CheckboxGroup.Indicator = CheckboxGroupIndicator;

export default CheckboxGroup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use client';

import React from 'react';

export interface CheckboxGroupRootContextProps {
rootClass: string
}

const CheckboxGroupRootContext = React.createContext<CheckboxGroupRootContextProps>({
rootClass: ''
});

export default CheckboxGroupRootContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { useContext } from 'react';
import CheckboxGroupRootContext from '../context/CheckboxGroupRootContext';
import clsx from 'clsx';

const TickIcon = ({ className }: {className?: string}) => (
<svg width="15" height="15" viewBox="0 0 15 15" className={className} fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
);

export type CheckboxGroupIndicatorProps = {
className?: string
}

const CheckboxGroupIndicator = ({ className }: CheckboxGroupIndicatorProps) => {
const { rootClass } = useContext(CheckboxGroupRootContext);
return (
<TickIcon className={clsx(`${rootClass}-tick-icon`, className)}/>
);
};

export default CheckboxGroupIndicator;
21 changes: 21 additions & 0 deletions src/components/ui/CheckboxGroup/fragments/CheckboxGroupLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useContext } from 'react';
import CheckboxGroupRootContext from '../context/CheckboxGroupRootContext';
import clsx from 'clsx';
import Primitive from '~/core/primitives/Primitive';

export type CheckboxGroupLabelProps = {
children: React.ReactNode
className?: string
}

const CheckboxGroupLabel = ({ children, className = '' }: CheckboxGroupLabelProps) => {
const { rootClass } = useContext(CheckboxGroupRootContext);

return (
<Primitive.label className={clsx(`${rootClass}-label`, className)} >
{children}
</Primitive.label>
);
};

export default CheckboxGroupLabel;
39 changes: 39 additions & 0 deletions src/components/ui/CheckboxGroup/fragments/CheckboxGroupRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import CheckboxGroupPrimitive, { CheckboxGroupPrimitiveProps } from '~/core/primitives/CheckboxGroup/CheckboxGroupPrimitive';
import CheckboxGroupRootContext from '../context/CheckboxGroupRootContext';
import { customClassSwitcher } from '~/core';
import clsx from 'clsx';
import { useCreateDataAttribute, useComposeAttributes, useCreateDataAccentColorAttribute } from '~/core/hooks/createDataAttribute';

const COMPONENT_NAME = 'CheckboxGroup';

export type CheckboxGroupRootProps = {
children: React.ReactNode;
className?: string
customRootClass?: string
color?: string
variant?: string
size?: string
name?: string
}& CheckboxGroupPrimitiveProps.Root

const CheckboxGroupRoot = ({ children, customRootClass = '', className = '', color = '', variant = '', size = '', ...props }: CheckboxGroupRootProps) => {
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);

const dataAttributes = useCreateDataAttribute('checkbox-group', { variant, size });
const accentAttributes = useCreateDataAccentColorAttribute(color);
const composedAttributes = useComposeAttributes(dataAttributes(), accentAttributes());

return (

<CheckboxGroupPrimitive.Root className={clsx(`${rootClass}-root`, rootClass, className)} {...props} {...composedAttributes()}>
<CheckboxGroupRootContext.Provider value={{ rootClass }}>

{children}

</CheckboxGroupRootContext.Provider>
</CheckboxGroupPrimitive.Root>
);
};

export default CheckboxGroupRoot;
23 changes: 23 additions & 0 deletions src/components/ui/CheckboxGroup/fragments/CheckboxGroupTrigger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import clsx from 'clsx';
import CheckboxGroupPrimitive, { CheckboxGroupPrimitiveProps } from '~/core/primitives/CheckboxGroup/CheckboxGroupPrimitive';
import CheckboxGroupRootContext from '../context/CheckboxGroupRootContext';

export type CheckboxGroupTriggerProps = {
children?: React.ReactNode
className?: string
value?: string
}& CheckboxGroupPrimitiveProps.Trigger
const CheckboxGroupTrigger = ({ children, className = '', value, ...props }: CheckboxGroupTriggerProps) => {
const { rootClass } = React.useContext(CheckboxGroupRootContext);

return (
<CheckboxGroupPrimitive.Trigger className={clsx(`${rootClass}-trigger`, className)} value={value} {...props}>
<CheckboxGroupPrimitive.Content >
{children}
</CheckboxGroupPrimitive.Content>
</CheckboxGroupPrimitive.Trigger>
);
};

export default CheckboxGroupTrigger;
46 changes: 46 additions & 0 deletions src/components/ui/CheckboxGroup/stories/CheckboxGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useState } from 'react';
import CheckboxGroup from '../CheckboxGroup';
import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor';

const CheckboxGroupExample = (args:any) => {
const options = [
{ id: 'apple', value: 'apple', label: 'Apple' },
{ id: 'banana', value: 'banana', label: 'Banana' },
{ id: 'cherry', value: 'cherry', label: 'Cherry' }
];
const [checked, setChecked] = useState(['apple']);

const handleChange = (value: string[]) => {
setChecked(value);
};

return (
<SandboxEditor>
<CheckboxGroup.Root
className="flex gap-4"
name="fruits"
value={checked}
onValueChange={handleChange}
>
{options.map((option) => (

<CheckboxGroup.Label key={option.id}>
<CheckboxGroup.Trigger value={option.value}>
<CheckboxGroup.Indicator />
</CheckboxGroup.Trigger>
{option.label}
</CheckboxGroup.Label>

))}
</CheckboxGroup.Root>
</SandboxEditor>
);
};

export default {
title: 'WIP/CheckboxGroup',
component: CheckboxGroup,
render: (args:any) => <CheckboxGroupExample {...args} />
};

export const Basic = {};
22 changes: 22 additions & 0 deletions src/core/primitives/CheckboxGroup/CheckboxGroupPrimitive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';

import CheckboxGroupPrimitiveRoot, { CheckboxGroupPrimitiveRootProps } from './fragments/CheckboxGroupPrimitiveRoot';
import CheckboxGroupPrimitiveTrigger, { CheckboxGroupPrimitiveTriggerProps } from './fragments/CheckboxGroupPrimitiveTrigger';
import CheckboxGroupPrimitiveContent, { CheckboxGroupPrimitiveContentProps } from './fragments/CheckboxGroupPrimitiveContent';

const CheckboxGroupPrimitive = () => {
console.warn('Direct usage of CheckboxGroup is not supported. Please use CheckboxGroup.Root, CheckboxGroup.Item instead.');
return null;
};

export namespace CheckboxGroupPrimitiveProps {
export type Root = CheckboxGroupPrimitiveRootProps;
export type Trigger = CheckboxGroupPrimitiveTriggerProps;
export type Content = CheckboxGroupPrimitiveContentProps;
}

CheckboxGroupPrimitive.Root = CheckboxGroupPrimitiveRoot;
CheckboxGroupPrimitive.Trigger = CheckboxGroupPrimitiveTrigger;
CheckboxGroupPrimitive.Content = CheckboxGroupPrimitiveContent;

export default CheckboxGroupPrimitive;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';

import React from 'react';

export interface CheckboxGroupPrimitiveContextProps {
checkedValues: string[],
setCheckedValues: (value: string[]) => void,
name?: string,
required?: boolean,
disabled?: boolean
}

const CheckboxGroupPrimitiveContext = React.createContext<CheckboxGroupPrimitiveContextProps>({
checkedValues: [],
setCheckedValues: () => {},
name: '',
required: false,
disabled: false
});

export default CheckboxGroupPrimitiveContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use client';

import React from 'react';

export interface CheckboxGroupPrimitiveTriggerContextProps {
isChecked: boolean
}

const CheckboxGroupPrimitiveTriggerContext = React.createContext<CheckboxGroupPrimitiveTriggerContextProps>({
isChecked: false
});

export default CheckboxGroupPrimitiveTriggerContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import CheckboxGroupPrimitiveTriggerContext from '../context/CheckboxGroupPrimitiveTriggerContext';

export type CheckboxGroupPrimitiveContentProps = {
children?: React.ReactNode
className?: string
}
const CheckboxGroupPrimitiveContent = ({ children, className }: CheckboxGroupPrimitiveContentProps) => {
const { isChecked } = React.useContext(CheckboxGroupPrimitiveTriggerContext);

return <span className={className}>
{isChecked ? children : null}
</span>;
};

export default CheckboxGroupPrimitiveContent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import RovingFocusGroup from '~/core/utils/RovingFocusGroup';
import { useControllableState } from '~/core/hooks/useControllableState';
import CheckboxGroupPrimitiveContext from '../context/CheckboxGroupPrimitiveContext';

export type CheckboxGroupPrimitiveRootProps = {
children: React.ReactNode;
className?: string
name?: string;
required?: boolean;
disabled?: boolean;
dir?: 'ltr' | 'rtl';
orientation?: 'horizontal' | 'vertical' | 'both';
loop?: boolean;
defaultValue?: string[];
value?: string[];
onValueChange?: (value: string[]) => void;
}

const CheckboxGroupPrimitiveRoot = ({ dir, orientation, loop, defaultValue = [], value, onValueChange, children, name, required, disabled, className = '', ...props }: CheckboxGroupPrimitiveRootProps) => {
const [checkedValues, setCheckedValues] = useControllableState(
value,
defaultValue,
onValueChange
);
console.log(checkedValues);
return (
<div className={className} {...props}>
<RovingFocusGroup.Root dir={dir} orientation={orientation} loop={loop}>
<CheckboxGroupPrimitiveContext.Provider value={{ checkedValues, setCheckedValues, name, required, disabled }}>
<RovingFocusGroup.Group>
{children}
</RovingFocusGroup.Group>
</CheckboxGroupPrimitiveContext.Provider>
</RovingFocusGroup.Root>
</div>
);
};

export default CheckboxGroupPrimitiveRoot;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import RovingFocusGroup from '~/core/utils/RovingFocusGroup';
import CheckboxGroupPrimitiveContext from '../context/CheckboxGroupPrimitiveContext';
import CheckboxGroupPrimitiveTriggerContext from '../context/CheckboxGroupPrimitiveTriggerContext';
import useControllableState from '~/core/hooks/useControllableState';

export type CheckboxGroupPrimitiveTriggerProps = {
children?: React.ReactNode
value: string
className?: string
required?: boolean
disabled?: boolean
checked?: boolean
onCheckedChange?: (checked: boolean) => void
}
const CheckboxGroupPrimitiveTrigger = ({ children, className = '', value, required, disabled, checked, onCheckedChange }: CheckboxGroupPrimitiveTriggerProps) => {
const { checkedValues, setCheckedValues, name, required: groupRequired, disabled: groupDisabled } = React.useContext(CheckboxGroupPrimitiveContext);

const isChecked = checkedValues.includes(value);

const handleClick = (event : React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
if (checkedValues.includes(value)) {
setCheckedValues(checkedValues.filter((v) => v !== value));
} else if (!checkedValues.includes(value)) {
setCheckedValues([...checkedValues, value]);
}
};

return (
<div>
<CheckboxGroupPrimitiveTriggerContext.Provider value={{ isChecked }}>
<RovingFocusGroup.Item>

<button role="checkbox" type="button" onClick={handleClick} className={className} aria-checked={isChecked} disabled={disabled || groupDisabled} aria-required={required || groupRequired}>
{children}
</button>

</RovingFocusGroup.Item>
</CheckboxGroupPrimitiveTriggerContext.Provider>

<input type="checkbox" checked={isChecked} name={name} value={value} style={{ display: 'none' }} required={required || groupRequired} disabled={disabled || groupDisabled} onChange={() => {}} readOnly/>

</div>
);
};

export default CheckboxGroupPrimitiveTrigger;
Loading
Loading