From 56147300bbb6eb3871c928f3bcae9d4d218b6309 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 6 Jan 2025 17:25:49 +0100 Subject: [PATCH 1/3] update checkbox and radio --- .../src/pages/hello-world-vanilla/index.tsx | 45 +++++++- libs/vanilla/src/index.ts | 20 ++-- libs/vanilla/src/inputs/check.input.tsx | 65 +++++------ libs/vanilla/src/inputs/radio.input.tsx | 104 ++++++++++++++++++ 4 files changed, 185 insertions(+), 49 deletions(-) create mode 100644 libs/vanilla/src/inputs/radio.input.tsx diff --git a/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx b/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx index aa15bef..61634a5 100644 --- a/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx +++ b/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx @@ -8,6 +8,8 @@ export default function HelloWorldVanilla() { name: '', pickedNumbers: [], pickedColors: [], + choosedNumber: '', + choosedColor: '', }); return ( @@ -49,12 +51,51 @@ export default function HelloWorldVanilla() { format: (item) => ( {item.label} ), - keyof: (item) => item.label, - disabled: ['White'], + disabled: (item) => item.value === '#FFFFFF', } ) ); display(
Colors: {JSON.stringify(pickedColors)}!
); + const choosedNumber = view( + 'choosedNumber', + Inputs.Radio(['One', 'Two', 'Three'], { + className: 'border p-0.5 mr-1', + label: 'Choose a number', + containerClassName: 'flex gap-2', + }) + ); + display(
Choosed Number: {choosedNumber}!
); + const choosedColor = view( + 'choosedColor', + Inputs.Radio( + [ + { label: 'Red', value: '#FF0000' }, + { label: 'Green', value: '#00FF00' }, + { label: 'Blue', value: '#0000FF' }, + { label: 'White', value: '#FFFFFF' }, + ], + { + className: 'border p-0.5 mr-1', + label: 'Choose a color', + containerClassName: 'flex gap-2', + valueof: (item) => item.value, + format: (item) => ( + + {item.label} + + ), + disabled: (item) => item.value === '#FFFFFF', + } + ) + ); + display(
Color: {JSON.stringify(choosedColor)}!
); }}
); diff --git a/libs/vanilla/src/index.ts b/libs/vanilla/src/index.ts index dc1cbab..8a5d69b 100644 --- a/libs/vanilla/src/index.ts +++ b/libs/vanilla/src/index.ts @@ -1,13 +1,9 @@ -import { VanillaConfig } from './config'; -import { CheckInput, CheckInputProps } from './inputs/check.input'; -import { TextInput, TextInputProps } from './inputs/text.input'; +import { CheckInput } from './inputs/check.input'; +import { RadioInput } from './inputs/radio.input'; +import { TextInput } from './inputs/text.input'; -export function configureInputs(config?: VanillaConfig) { - return { - Text: (props?: TextInputProps) => TextInput({ ...config, ...props }), - Check: >(data: T[], props: P) => - CheckInput(data, { ...config, ...props }), - }; -} - -export const Inputs = configureInputs(); +export const Inputs = { + Text: TextInput, + Check: CheckInput, + Radio: RadioInput, +}; diff --git a/libs/vanilla/src/inputs/check.input.tsx b/libs/vanilla/src/inputs/check.input.tsx index 91b49eb..fbf0231 100644 --- a/libs/vanilla/src/inputs/check.input.tsx +++ b/libs/vanilla/src/inputs/check.input.tsx @@ -1,30 +1,25 @@ import { - applyWrapper, defineTransformView, defineView, - ExtractDefProps, ViewComponentProps, ViewDefinition, } from '@reactlit/core'; import { DetailedHTMLProps } from 'react'; -import { VanillaConfig } from '../config'; -type BaseCheckInputProps = Omit< +export type CheckInputProps = Omit< DetailedHTMLProps< React.InputHTMLAttributes, HTMLInputElement >, 'value' | 'disabled' -> & - VanillaConfig & { - data: T[]; - label?: string | React.ReactNode; - containerClassName?: string; - format?: (value: T) => string | React.ReactNode; - valueof?: (value: T) => string; - keyof?: (value: T) => string; - disabled?: string[]; - }; +> & { + data: T[]; + label?: string | React.ReactNode; + containerClassName?: string; + format?: (value: T) => string | React.ReactNode; + valueof?: (value: T) => string; + disabled?: string[] | ((value: T) => boolean); +}; export const CheckInputComponent = ({ value, @@ -34,63 +29,63 @@ export const CheckInputComponent = ({ data, containerClassName, format, - keyof, valueof, disabled, label, - wrapper, ...props -}: BaseCheckInputProps & ViewComponentProps<(string | T)[]>) => { - return applyWrapper( +}: CheckInputProps & ViewComponentProps<(string | T)[]>) => { + return (
{label && }
- {data.map((item, index) => { + {data.map((item) => { const isChecked = !!value.find((v) => valueof ? valueof(item) === v : item === v ); + const itemKey = `${stateKey}-${valueof?.(item) ?? item.toString()}`; + let disabledValue = false; + + if (typeof disabled === 'function') { + disabledValue = disabled(item); + } else if (Array.isArray(disabled)) { + disabledValue = disabled.includes( + valueof?.(item) ?? item.toString() + ); + } return ( -
+
{ const _value = valueof?.(item) ?? item; if (e.target.checked) setValue([...value, _value]); else setValue(value.filter((v) => v !== _value)); }} - disabled={ - (keyof && disabled?.includes(keyof(item))) || - disabled?.includes(valueof?.(item) ?? item.toString()) - } + disabled={disabledValue} {...props} /> -
); })}
-
, - wrapper +
); }; -export type CheckInputProps = Omit< - ExtractDefProps>, - 'data' ->; - export type CheckInputDefinition = P extends { valueof: (v: T) => string } ? ViewDefinition : ViewDefinition; export const CheckInput = >( - data: T[], - { valueof, ...props }: P + data: P['data'], + { valueof, ...props }: Omit ): CheckInputDefinition => { if (valueof) { return defineTransformView( diff --git a/libs/vanilla/src/inputs/radio.input.tsx b/libs/vanilla/src/inputs/radio.input.tsx new file mode 100644 index 0000000..5f85aa3 --- /dev/null +++ b/libs/vanilla/src/inputs/radio.input.tsx @@ -0,0 +1,104 @@ +import { + defineTransformView, + defineView, + ViewComponentProps, + ViewDefinition, +} from '@reactlit/core'; +import { DetailedHTMLProps } from 'react'; + +export type RadioInputProps = Omit< + DetailedHTMLProps< + React.InputHTMLAttributes, + HTMLInputElement + >, + 'value' | 'disabled' +> & { + data: T[]; + label?: string | React.ReactNode; + containerClassName?: string; + format?: (value: T) => string | React.ReactNode; + valueof?: (value: T) => string; + disabled?: string[] | ((value: T) => boolean); +}; + +export const RadioInputComponent = ({ + value, + stateKey, + setValue, + onChange, + data, + containerClassName, + format, + valueof, + disabled, + label, + ...props +}: RadioInputProps & ViewComponentProps) => { + return ( +
+ {label && } +
+ {data.map((item) => { + const isChecked = valueof ? valueof(item) === value : item === value; + const itemKey = `${stateKey}-${valueof?.(item) ?? item.toString()}`; + let disabledValue = false; + + if (typeof disabled === 'function') { + disabledValue = disabled(item); + } else if (Array.isArray(disabled)) { + disabledValue = disabled.includes( + valueof?.(item) ?? item.toString() + ); + } + + return ( +
+ { + const _value = valueof?.(item) ?? item; + if (e.target.checked) setValue(_value); + }} + disabled={disabledValue} + {...props} + /> + +
+ ); + })} +
+
+ ); +}; + +export type RadioInputDefinition = P extends { valueof: (v: T) => string } + ? ViewDefinition + : ViewDefinition; + +export const RadioInput = >( + data: P['data'], + { valueof, ...props }: Omit +): RadioInputDefinition => { + if (valueof) { + return defineTransformView( + (viewProps) => ( + + ), + ({ value }) => data.find((d) => valueof(d) === value) + ) as RadioInputDefinition; + } else { + return defineView((viewProps) => ( + + )) as RadioInputDefinition; + } +}; From d880d7ba8d7d6d2c43b912e7cced45b97600b7f6 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 7 Jan 2025 03:53:11 +0100 Subject: [PATCH 2/3] fix checkbox & radio --- .../src/pages/hello-world-vanilla/index.tsx | 52 +++++++++++++------ libs/vanilla/src/inputs/check.input.tsx | 30 ++++++++--- libs/vanilla/src/inputs/radio.input.tsx | 31 ++++++++--- 3 files changed, 81 insertions(+), 32 deletions(-) diff --git a/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx b/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx index ba345b8..9f0d928 100644 --- a/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx +++ b/apps/reactlit-examples/src/pages/hello-world-vanilla/index.tsx @@ -6,8 +6,8 @@ export default function HelloWorldVanilla() { name: '', pickedNumbers: [], pickedColors: [], - choosedNumber: '', - choosedColor: '', + chosenNumber: '', + chosenColor: '', }); return ( @@ -26,9 +26,14 @@ export default function HelloWorldVanilla() { const picked = view( 'pickedNumbers', Inputs.Check(['One', 'Two', 'Three'], { - className: 'border p-0.5 mr-1', + className: { + container: 'flex gap-2 items-center', + wrapper: 'flex gap-2', + item: { + input: 'border p-0.5 mr-1', + }, + }, label: 'Pick any number', - containerClassName: 'flex gap-2', }) ); display(
Picked: {picked.join(', ')}!
); @@ -42,9 +47,14 @@ export default function HelloWorldVanilla() { { label: 'White', value: '#FFFFFF' }, ], { - className: 'border p-0.5 mr-1', + className: { + container: 'flex gap-2 items-center', + wrapper: 'flex gap-2', + item: { + input: 'border p-0.5 mr-1', + }, + }, label: 'Pick any color', - containerClassName: 'flex gap-2', valueof: (item) => item.value, format: (item) => ( Colors: {JSON.stringify(pickedColors)}!); - const choosedNumber = view( - 'choosedNumber', + const chosenNumber = view( + 'chosenNumber', Inputs.Radio(['One', 'Two', 'Three'], { - className: 'border p-0.5 mr-1', + className: { + container: 'flex gap-2 items-center', + wrapper: 'flex gap-2', + item: { + input: 'border p-0.5 mr-1', + }, + }, label: 'Choose a number', - containerClassName: 'flex gap-2', }) ); - display(
Choosed Number: {choosedNumber}!
); - const choosedColor = view( - 'choosedColor', + display(
Chosen Number: {chosenNumber}!
); + const chosenColor = view( + 'chosenColor', Inputs.Radio( [ { label: 'Red', value: '#FF0000' }, @@ -82,9 +97,14 @@ export default function HelloWorldVanilla() { { label: 'White', value: '#FFFFFF' }, ], { - className: 'border p-0.5 mr-1', + className: { + container: 'flex gap-2 items-center', + wrapper: 'flex gap-2', + item: { + input: 'border p-0.5 mr-1', + }, + }, label: 'Choose a color', - containerClassName: 'flex gap-2', valueof: (item) => item.value, format: (item) => ( Color: {JSON.stringify(choosedColor)}!); + display(
Chosen Color: {JSON.stringify(chosenColor)}!
); }}
); diff --git a/libs/vanilla/src/inputs/check.input.tsx b/libs/vanilla/src/inputs/check.input.tsx index fbf0231..3bb376e 100644 --- a/libs/vanilla/src/inputs/check.input.tsx +++ b/libs/vanilla/src/inputs/check.input.tsx @@ -11,11 +11,20 @@ export type CheckInputProps = Omit< React.InputHTMLAttributes, HTMLInputElement >, - 'value' | 'disabled' + 'value' | 'disabled' | 'className' > & { data: T[]; label?: string | React.ReactNode; - containerClassName?: string; + className?: { + container?: string; + wrapper?: string; + label?: string; + item?: { + wrapper?: string; + input?: string; + label?: string; + }; + }; format?: (value: T) => string | React.ReactNode; valueof?: (value: T) => string; disabled?: string[] | ((value: T) => boolean); @@ -27,7 +36,7 @@ export const CheckInputComponent = ({ setValue, onChange, data, - containerClassName, + className, format, valueof, disabled, @@ -35,9 +44,13 @@ export const CheckInputComponent = ({ ...props }: CheckInputProps & ViewComponentProps<(string | T)[]>) => { return ( -
- {label && } -
+
+ {label && ( + + )} +
{data.map((item) => { const isChecked = !!value.find((v) => valueof ? valueof(item) === v : item === v @@ -54,12 +67,13 @@ export const CheckInputComponent = ({ } return ( -
+
{ const _value = valueof?.(item) ?? item; if (e.target.checked) setValue([...value, _value]); @@ -68,7 +82,7 @@ export const CheckInputComponent = ({ disabled={disabledValue} {...props} /> -
diff --git a/libs/vanilla/src/inputs/radio.input.tsx b/libs/vanilla/src/inputs/radio.input.tsx index 5f85aa3..70eaa7e 100644 --- a/libs/vanilla/src/inputs/radio.input.tsx +++ b/libs/vanilla/src/inputs/radio.input.tsx @@ -11,11 +11,20 @@ export type RadioInputProps = Omit< React.InputHTMLAttributes, HTMLInputElement >, - 'value' | 'disabled' + 'value' | 'disabled' | 'className' > & { data: T[]; label?: string | React.ReactNode; - containerClassName?: string; + className?: { + container?: string; + wrapper?: string; + label?: string; + item?: { + wrapper?: string; + input?: string; + label?: string; + }; + }; format?: (value: T) => string | React.ReactNode; valueof?: (value: T) => string; disabled?: string[] | ((value: T) => boolean); @@ -27,17 +36,22 @@ export const RadioInputComponent = ({ setValue, onChange, data, - containerClassName, + className, format, valueof, disabled, label, ...props }: RadioInputProps & ViewComponentProps) => { + console.log(className); return ( -
- {label && } -
+
+ {label && ( + + )} +
{data.map((item) => { const isChecked = valueof ? valueof(item) === value : item === value; const itemKey = `${stateKey}-${valueof?.(item) ?? item.toString()}`; @@ -52,12 +66,13 @@ export const RadioInputComponent = ({ } return ( -
+
{ const _value = valueof?.(item) ?? item; if (e.target.checked) setValue(_value); @@ -65,7 +80,7 @@ export const RadioInputComponent = ({ disabled={disabledValue} {...props} /> -
From f058db7137d5a5c3c17507dc99d9c505ca8782ed Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 7 Jan 2025 03:54:44 +0100 Subject: [PATCH 3/3] remove console log --- libs/vanilla/src/inputs/radio.input.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/vanilla/src/inputs/radio.input.tsx b/libs/vanilla/src/inputs/radio.input.tsx index 70eaa7e..06100b0 100644 --- a/libs/vanilla/src/inputs/radio.input.tsx +++ b/libs/vanilla/src/inputs/radio.input.tsx @@ -43,7 +43,6 @@ export const RadioInputComponent = ({ label, ...props }: RadioInputProps & ViewComponentProps) => { - console.log(className); return (
{label && (