From eae92ec59a569c059c0a3f72ceb323922f23686d Mon Sep 17 00:00:00 2001 From: koharxnp Date: Wed, 23 Sep 2026 19:30:53 +0900 Subject: [PATCH 1/5] feat: checkbox component --- packages/engine/components/Checkbox.d.ts | 15 +++++ packages/engine/components/Checkbox.tsx | 72 ++++++++++++++++++++++++ packages/engine/components/index.d.ts | 2 + packages/engine/components/index.ts | 1 + packages/engine/package.json | 4 ++ 5 files changed, 94 insertions(+) create mode 100644 packages/engine/components/Checkbox.d.ts create mode 100644 packages/engine/components/Checkbox.tsx diff --git a/packages/engine/components/Checkbox.d.ts b/packages/engine/components/Checkbox.d.ts new file mode 100644 index 0000000..f4bfd21 --- /dev/null +++ b/packages/engine/components/Checkbox.d.ts @@ -0,0 +1,15 @@ +import type { ClassValue, KeyEvent, PressHandler, Style } from '@geastack/core' + +export interface CheckboxProps { + checked?: boolean + indeterminate?: boolean + disabled?: boolean + label?: string + class?: ClassValue + style?: Style + onChange?: (checked: boolean) => void + onPress?: PressHandler + onClick?: PressHandler +} + +export function Checkbox(props: CheckboxProps): any \ No newline at end of file diff --git a/packages/engine/components/Checkbox.tsx b/packages/engine/components/Checkbox.tsx new file mode 100644 index 0000000..4c78373 --- /dev/null +++ b/packages/engine/components/Checkbox.tsx @@ -0,0 +1,72 @@ +import type { ClassValue, KeyEvent, PressHandler, Style } from '@geastack/core' + +export interface CheckboxProps { + checked?: boolean + indeterminate?: boolean + disabled?: boolean + label?: string + class?: ClassValue + style?: Style + onChange?: (checked: boolean) => void + onPress?: PressHandler + onClick?: PressHandler +} + +export function Checkbox({ + checked = false, + indeterminate = false, + disabled = false, + label, + class: cls, + style, + onChange, + onPress, + onClick, +}: CheckboxProps) { + const toggle = () => { + if (disabled) return + onChange?.(indeterminate ? true : !checked) + } + + const handlePress = (e: any) => { + toggle() + onPress?.(e) + onClick?.(e) + } + + const handleKeyDown = (e: KeyEvent) => { + if (e.keyCode === 32 || e.which === 32) { + toggle() + } + } + + const checkClass = [ + 'gea-checkbox', + disabled && 'gea-checkbox-disabled', + indeterminate && 'gea-checkbox-indeterminate', + !indeterminate && checked && 'gea-checkbox-checked', + !indeterminate && !checked && 'gea-checkbox-unchecked', + cls, + ] + .filter(Boolean) + .join(' ') + + const mark = indeterminate ? '-' : checked ? '✓' : '' + + return ( + + ) +} \ No newline at end of file diff --git a/packages/engine/components/index.d.ts b/packages/engine/components/index.d.ts index 2e70e2b..94654dc 100644 --- a/packages/engine/components/index.d.ts +++ b/packages/engine/components/index.d.ts @@ -6,3 +6,5 @@ export { Image } from './Image' export type { ImageProps } from '@geastack/core' export { Button } from './Button' export type { ButtonProps } from '@geastack/core' +export { Checkbox } from './Checkbox' +export type { CheckboxProps } from './Checkbox' diff --git a/packages/engine/components/index.ts b/packages/engine/components/index.ts index 6b6c2e0..39a11c1 100644 --- a/packages/engine/components/index.ts +++ b/packages/engine/components/index.ts @@ -2,3 +2,4 @@ export { View } from './View' export { Text } from './Text' export { Image } from './Image' export { Button } from './Button' +export { Checkbox } from './Checkbox' \ No newline at end of file diff --git a/packages/engine/package.json b/packages/engine/package.json index fc09090..809e58a 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -38,6 +38,10 @@ "types": "./components/Button.d.ts", "default": "./components/Button.tsx" }, + "./components/Checkbox": { + "types": "./components/Checkbox.d.ts", + "default": "./components/Checkbox.tsx" + }, "./package.json": "./package.json", "./ui/*": "./ui/*" }, From 7d4c7ab73367fb6cdd44bd6604406a47a2f276f6 Mon Sep 17 00:00:00 2001 From: koharxnp Date: Thu, 24 Sep 2026 17:50:43 +0900 Subject: [PATCH 2/5] refactor: Added JSDoc to and fixed some of the issues pointed out by coderabbit --- packages/engine/components/Checkbox.d.ts | 9 +++++++++ packages/engine/components/Checkbox.tsx | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/engine/components/Checkbox.d.ts b/packages/engine/components/Checkbox.d.ts index f4bfd21..e43f4a1 100644 --- a/packages/engine/components/Checkbox.d.ts +++ b/packages/engine/components/Checkbox.d.ts @@ -1,14 +1,23 @@ import type { ClassValue, KeyEvent, PressHandler, Style } from '@geastack/core' export interface CheckboxProps { + /** Checked state of the checkbox */ checked?: boolean + /** Indeterminate/mixed state of the checkbox */ indeterminate?: boolean + /** Whether the checkbox is disabled */ disabled?: boolean + /** Optional text label to display beside the checkbox */ label?: string + /** Custom class names (string, array, or class map) */ class?: ClassValue + /** Custom inline styles */ style?: Style + /** Triggered when the checked state changes */ onChange?: (checked: boolean) => void + /** Triggered via press/click. Note: Runs through the native click event path */ onPress?: PressHandler + /** Triggered via click. Note: Runs through the native click event path */ onClick?: PressHandler } diff --git a/packages/engine/components/Checkbox.tsx b/packages/engine/components/Checkbox.tsx index 4c78373..5a90f78 100644 --- a/packages/engine/components/Checkbox.tsx +++ b/packages/engine/components/Checkbox.tsx @@ -12,6 +12,19 @@ export interface CheckboxProps { onClick?: PressHandler } +const resolveClass = (val?: ClassValue | null): string => { + if (!val) return '' + if (typeof val === 'string') return val + if (Array.isArray(val)) return val.map(resolveClass).filter(Boolean).join(' ') + if (typeof val === 'object') { + return Object.entries(val) + .filter(([_, enabled]) => Boolean(enabled)) + .map(([key]) => key) + .join(' ') + } + return '' +} + export function Checkbox({ checked = false, indeterminate = false, @@ -29,6 +42,7 @@ export function Checkbox({ } const handlePress = (e: any) => { + if (disabled) return toggle() onPress?.(e) onClick?.(e) @@ -46,7 +60,7 @@ export function Checkbox({ indeterminate && 'gea-checkbox-indeterminate', !indeterminate && checked && 'gea-checkbox-checked', !indeterminate && !checked && 'gea-checkbox-unchecked', - cls, + resolveClass(cls), ] .filter(Boolean) .join(' ') From b203e01506b5c2eb2c2e91d46d3c075376c76b7b Mon Sep 17 00:00:00 2001 From: koharxnp Date: Sun, 27 Sep 2026 14:51:33 +0900 Subject: [PATCH 3/5] fix(engine): add ariaLabel prop and refine keydown event handling for Checkbox --- packages/engine/components/Checkbox.d.ts | 2 ++ packages/engine/components/Checkbox.tsx | 3 +++ 2 files changed, 5 insertions(+) diff --git a/packages/engine/components/Checkbox.d.ts b/packages/engine/components/Checkbox.d.ts index e43f4a1..efb0a54 100644 --- a/packages/engine/components/Checkbox.d.ts +++ b/packages/engine/components/Checkbox.d.ts @@ -19,6 +19,8 @@ export interface CheckboxProps { onPress?: PressHandler /** Triggered via click. Note: Runs through the native click event path */ onClick?: PressHandler + /** Accessibility Labels When Labels Are Not Displayed */ + ariaLabel?: string } export function Checkbox(props: CheckboxProps): any \ No newline at end of file diff --git a/packages/engine/components/Checkbox.tsx b/packages/engine/components/Checkbox.tsx index 5a90f78..6159381 100644 --- a/packages/engine/components/Checkbox.tsx +++ b/packages/engine/components/Checkbox.tsx @@ -10,6 +10,7 @@ export interface CheckboxProps { onChange?: (checked: boolean) => void onPress?: PressHandler onClick?: PressHandler + ariaLabel?: string } const resolveClass = (val?: ClassValue | null): string => { @@ -35,6 +36,7 @@ export function Checkbox({ onChange, onPress, onClick, + ariaLabel }: CheckboxProps) { const toggle = () => { if (disabled) return @@ -77,6 +79,7 @@ export function Checkbox({ role="checkbox" aria-checked={indeterminate ? 'mixed' : checked} aria-disabled={disabled} + aria-label={ariaLabel} tabIndex={disabled ? -1 : 0} > {mark} From ac01c668785b5667444dc4c8f90bfa09295bc843 Mon Sep 17 00:00:00 2001 From: koharxnp Date: Sun, 27 Sep 2026 15:02:12 +0900 Subject: [PATCH 4/5] fix(engine): fallback aria-label when both label and ariaLabel are missing --- packages/engine/components/Checkbox.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/engine/components/Checkbox.tsx b/packages/engine/components/Checkbox.tsx index 6159381..146fe81 100644 --- a/packages/engine/components/Checkbox.tsx +++ b/packages/engine/components/Checkbox.tsx @@ -69,6 +69,8 @@ export function Checkbox({ const mark = indeterminate ? '-' : checked ? '✓' : '' + const computedAriaLabel = ariaLabel || label || 'Checkbox' + return (