@brickclay-org/ui 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,12 +22,16 @@ A powerful calendar suite with advanced date and time selection capabilities. Th
22
22
 
23
23
  A customizable toggle/switch component with support for Angular forms integration via `ngModel` and reactive forms. Features three size variants (small, medium, large), disabled state, and full accessibility support.
24
24
 
25
+ ### Checkbox Component
26
+
27
+ A fully accessible checkbox component with Angular forms integration. Features four size variants (small, medium, large, extra-large), disabled state, keyboard navigation, and seamless integration with both template-driven and reactive forms.
28
+
25
29
  *More components coming soon...*
26
30
 
27
31
  ## Installation
28
32
 
29
33
  ```bash
30
- npm install @brickclay-org/ui
34
+ npm i @brickclay-org/ui@0.0.5
31
35
  ```
32
36
 
33
37
  ### Peer Dependencies
@@ -591,6 +595,248 @@ The toggle component includes:
591
595
  - Focus visible ring for keyboard users
592
596
  - Disabled state properly communicated to assistive technologies
593
597
 
598
+ ## ☑️ Checkbox
599
+
600
+ A fully accessible checkbox component that integrates seamlessly with Angular forms. Supports both template-driven forms (`ngModel`) and reactive forms, with four size variants and comprehensive accessibility features.
601
+
602
+ ### CheckboxComponent
603
+
604
+ A standalone checkbox component that implements `ControlValueAccessor` for seamless Angular forms integration.
605
+
606
+ #### Basic Example
607
+
608
+ ```typescript
609
+ import { CheckboxComponent } from '@brickclay/ui';
610
+ import { FormsModule } from '@angular/forms';
611
+
612
+ @Component({
613
+ template: `
614
+ <brickclay-checkbox
615
+ [(ngModel)]="isAccepted"
616
+ [label]="'I agree to the terms and conditions'"
617
+ (changed)="onCheckboxChange($event)">
618
+ </brickclay-checkbox>
619
+ `,
620
+ imports: [CheckboxComponent, FormsModule]
621
+ })
622
+ export class MyComponent {
623
+ isAccepted = false;
624
+
625
+ onCheckboxChange(value: boolean) {
626
+ console.log('Checkbox state:', value);
627
+ }
628
+ }
629
+ ```
630
+
631
+ #### Component Selector
632
+
633
+ `<brickclay-checkbox>`
634
+
635
+ #### Inputs
636
+
637
+ | Input | Type | Default | Description |
638
+ |-------|------|---------|-------------|
639
+ | `label` | `string` | `''` | Optional label text displayed next to the checkbox |
640
+ | `disabled` | `boolean` | `false` | Disables the checkbox interaction |
641
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Size of the checkbox. Options: `'sm'` (14px), `'md'` (16px), `'lg'` (18px), `'xl'` (20px) |
642
+
643
+ #### Outputs
644
+
645
+ | Output | Type | Description |
646
+ |--------|------|-------------|
647
+ | `changed` | `EventEmitter<boolean>` | Emitted when checkbox state changes (returns new boolean value) |
648
+
649
+ #### Features
650
+
651
+ - ✅ **Angular Forms Integration** - Full support for `ngModel` and reactive forms
652
+ - ✅ **Four Size Variants** - Small (14px), Medium (16px), Large (18px), Extra Large (20px)
653
+ - ✅ **Accessibility** - ARIA attributes, keyboard navigation (Enter/Space), and screen reader support
654
+ - ✅ **Disabled State** - Visual and functional disabled state
655
+ - ✅ **Keyboard Support** - Full keyboard navigation with Enter and Space keys
656
+ - ✅ **Focus Management** - Focus visible ring for keyboard users
657
+ - ✅ **Event Handling** - `changed` event for state change notifications
658
+
659
+ #### Usage Examples
660
+
661
+ **Basic Checkbox with ngModel:**
662
+
663
+ ```typescript
664
+ import { CheckboxComponent } from '@brickclay/ui';
665
+ import { FormsModule } from '@angular/forms';
666
+
667
+ @Component({
668
+ template: `
669
+ <brickclay-checkbox
670
+ [(ngModel)]="isChecked"
671
+ [label]="'Accept terms'">
672
+ </brickclay-checkbox>
673
+ `,
674
+ imports: [CheckboxComponent, FormsModule]
675
+ })
676
+ export class MyComponent {
677
+ isChecked = false;
678
+ }
679
+ ```
680
+
681
+ **Different Sizes:**
682
+
683
+ ```typescript
684
+ <brickclay-checkbox
685
+ [(ngModel)]="value1"
686
+ [size]="'sm'"
687
+ [label]="'Small Checkbox'">
688
+ </brickclay-checkbox>
689
+
690
+ <brickclay-checkbox
691
+ [(ngModel)]="value2"
692
+ [size]="'md'"
693
+ [label]="'Medium Checkbox'">
694
+ </brickclay-checkbox>
695
+
696
+ <brickclay-checkbox
697
+ [(ngModel)]="value3"
698
+ [size]="'lg'"
699
+ [label]="'Large Checkbox'">
700
+ </brickclay-checkbox>
701
+
702
+ <brickclay-checkbox
703
+ [(ngModel)]="value4"
704
+ [size]="'xl'"
705
+ [label]="'Extra Large Checkbox'">
706
+ </brickclay-checkbox>
707
+ ```
708
+
709
+ **Disabled State:**
710
+
711
+ ```typescript
712
+ <brickclay-checkbox
713
+ [ngModel]="true"
714
+ [disabled]="true"
715
+ [label]="'Disabled Checkbox'">
716
+ </brickclay-checkbox>
717
+ ```
718
+
719
+ **With Event Handler:**
720
+
721
+ ```typescript
722
+ @Component({
723
+ template: `
724
+ <brickclay-checkbox
725
+ [(ngModel)]="newsletterSubscribed"
726
+ [label]="'Subscribe to newsletter'"
727
+ (changed)="onNewsletterToggle($event)">
728
+ </brickclay-checkbox>
729
+ `
730
+ })
731
+ export class MyComponent {
732
+ newsletterSubscribed = false;
733
+
734
+ onNewsletterToggle(subscribed: boolean) {
735
+ if (subscribed) {
736
+ this.subscribeToNewsletter();
737
+ } else {
738
+ this.unsubscribeFromNewsletter();
739
+ }
740
+ }
741
+ }
742
+ ```
743
+
744
+ **Reactive Forms Integration:**
745
+
746
+ ```typescript
747
+ import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
748
+ import { CheckboxComponent } from '@brickclay/ui';
749
+
750
+ @Component({
751
+ template: `
752
+ <form [formGroup]="registrationForm">
753
+ <brickclay-checkbox
754
+ formControlName="acceptTerms"
755
+ [label]="'I accept the terms and conditions'">
756
+ </brickclay-checkbox>
757
+
758
+ <brickclay-checkbox
759
+ formControlName="receiveUpdates"
760
+ [label]="'Receive product updates'">
761
+ </brickclay-checkbox>
762
+ </form>
763
+ `,
764
+ imports: [CheckboxComponent, ReactiveFormsModule]
765
+ })
766
+ export class RegistrationComponent {
767
+ registrationForm: FormGroup;
768
+
769
+ constructor(private fb: FormBuilder) {
770
+ this.registrationForm = this.fb.group({
771
+ acceptTerms: [false, Validators.requiredTrue],
772
+ receiveUpdates: [false]
773
+ });
774
+ }
775
+ }
776
+ ```
777
+
778
+ **Without Label:**
779
+
780
+ ```typescript
781
+ <brickclay-checkbox
782
+ [(ngModel)]="isSelected"
783
+ [size]="'md'">
784
+ </brickclay-checkbox>
785
+ ```
786
+
787
+ **Multiple Checkboxes:**
788
+
789
+ ```typescript
790
+ @Component({
791
+ template: `
792
+ <div>
793
+ <brickclay-checkbox
794
+ *ngFor="let option of options"
795
+ [(ngModel)]="option.selected"
796
+ [label]="option.label"
797
+ (changed)="onOptionChange(option)">
798
+ </brickclay-checkbox>
799
+ </div>
800
+ `
801
+ })
802
+ export class MyComponent {
803
+ options = [
804
+ { label: 'Option 1', selected: false },
805
+ { label: 'Option 2', selected: false },
806
+ { label: 'Option 3', selected: false }
807
+ ];
808
+
809
+ onOptionChange(option: any) {
810
+ console.log(`${option.label} is now ${option.selected ? 'selected' : 'deselected'}`);
811
+ }
812
+ }
813
+ ```
814
+
815
+ #### Styling
816
+
817
+ The checkbox component uses size variants:
818
+
819
+ - **Small**: `sm` - 14px × 14px
820
+ - **Medium**: `md` - 16px × 16px (Default)
821
+ - **Large**: `lg` - 18px × 18px
822
+ - **Extra Large**: `xl` - 20px × 20px
823
+
824
+ The component includes built-in styles for:
825
+ - Checked state (black background with white checkmark)
826
+ - Unchecked state (white background with gray border)
827
+ - Hover states (darker border on hover)
828
+ - Disabled states (gray background and border)
829
+ - Focus ring for accessibility (blue ring with offset)
830
+
831
+ #### Accessibility
832
+
833
+ The checkbox component includes:
834
+ - Keyboard navigation support (Enter and Space keys)
835
+ - Focus visible ring for keyboard users
836
+ - Proper ARIA attributes
837
+ - Disabled state properly communicated to assistive technologies
838
+ - Tab navigation support
839
+
594
840
  ## 📐 TypeScript Interfaces
595
841
 
596
842
  ### CalendarRange
@@ -787,6 +1033,12 @@ For issues, feature requests, or contributions, please visit our [GitHub reposit
787
1033
  - Disabled state support
788
1034
  - Full accessibility features
789
1035
  - Customizable styling
1036
+ - ✅ Checkbox component
1037
+ - Angular forms integration (ngModel & reactive forms)
1038
+ - Four size variants (small, medium, large, extra-large)
1039
+ - Disabled state support
1040
+ - Full keyboard navigation support
1041
+ - Complete accessibility features
790
1042
  - ✅ TypeScript definitions
791
1043
  - ✅ Comprehensive documentation
792
1044
 
@@ -2314,6 +2314,63 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2314
2314
  type: Output
2315
2315
  }] } });
2316
2316
 
2317
+ class CheckboxComponent {
2318
+ // Mapping: sm=14px, md=16px, lg=18px, xl=20px
2319
+ size = 'md';
2320
+ label = '';
2321
+ disabled = false;
2322
+ changed = new EventEmitter();
2323
+ isChecked = false;
2324
+ onChange = (_) => { };
2325
+ onTouched = () => { };
2326
+ toggle() {
2327
+ if (this.disabled)
2328
+ return;
2329
+ this.isChecked = !this.isChecked;
2330
+ this.onChange(this.isChecked);
2331
+ this.onTouched();
2332
+ this.changed.emit(this.isChecked);
2333
+ }
2334
+ writeValue(value) {
2335
+ this.isChecked = value;
2336
+ }
2337
+ registerOnChange(fn) {
2338
+ this.onChange = fn;
2339
+ }
2340
+ registerOnTouched(fn) {
2341
+ this.onTouched = fn;
2342
+ }
2343
+ setDisabledState(isDisabled) {
2344
+ this.disabled = isDisabled;
2345
+ }
2346
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2347
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CheckboxComponent, isStandalone: true, selector: "brickclay-checkbox", inputs: { size: "size", label: "label", disabled: "disabled" }, outputs: { changed: "changed" }, providers: [
2348
+ {
2349
+ provide: NG_VALUE_ACCESSOR,
2350
+ useExisting: forwardRef(() => CheckboxComponent),
2351
+ multi: true
2352
+ }
2353
+ ], ngImport: i0, template: "<div\r\n class=\"inline-flex items-center gap-2 cursor-pointer group outline-none\"\r\n (click)=\"toggle()\"\r\n (keydown.enter)=\"toggle()\"\r\n (keydown.space)=\"$event.preventDefault(); toggle()\"\r\n tabindex=\"0\"\r\n [class.cursor-not-allowed]=\"disabled\"\r\n [attr.aria-disabled]=\"disabled\">\r\n\r\n <div\r\n class=\"relative flex items-center justify-center border-2 transition-all duration-200 ease-in-out\"\r\n [ngClass]=\"[\r\n size === 'sm' ? 'rounded-[3px]' : '',\r\n size === 'md' ? 'rounded' : '',\r\n size === 'lg' ? 'rounded' : '',\r\n size === 'xl' ? 'rounded-md' : '',\r\n\r\n size === 'sm' ? 'w-[14px] h-[14px]' : '',\r\n size === 'md' ? 'w-[16px] h-[16px]' : '',\r\n size === 'lg' ? 'w-[18px] h-[18px]' : '',\r\n size === 'xl' ? 'w-[20px] h-[20px]' : '',\r\n\r\n isChecked && !disabled ? 'bg-black border-black' : '',\r\n !isChecked && !disabled ? 'bg-white border-gray-300 group-hover:border-gray-400' : '',\r\n\r\n disabled && isChecked ? 'bg-gray-300 border-gray-300' : '',\r\n disabled && !isChecked ? 'bg-gray-100 border-gray-200' : '',\r\n\r\n 'group-focus-visible:ring-2 group-focus-visible:ring-blue-600 group-focus-visible:ring-offset-2'\r\n ]\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"3.5\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n class=\"text-white pointer-events-none transition-opacity duration-200\"\r\n [class.opacity-0]=\"!isChecked\"\r\n [class.opacity-100]=\"isChecked\"\r\n [class.w-[10.5px]]=\"size === 'sm'\"\r\n [class.w-[12px]]=\"size === 'md'\"\r\n [class.w-[13.5px]]=\"size === 'lg'\"\r\n [class.w-[14px]]=\"size === 'xl'\"\r\n >\r\n <polyline points=\"20 6 9 17 4 12\"></polyline>\r\n </svg>\r\n </div>\r\n\r\n <span\r\n *ngIf=\"label\"\r\n class=\"font-medium select-none\"\r\n [class.text-xs]=\"size === 'sm'\"\r\n [class.text-sm]=\"size !== 'sm'\"\r\n [class.text-gray-900]=\"!disabled\"\r\n [class.text-gray-400]=\"disabled\"\r\n >\r\n {{ label }}\r\n </span>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None });
2354
+ }
2355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CheckboxComponent, decorators: [{
2356
+ type: Component,
2357
+ args: [{ selector: 'brickclay-checkbox', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, providers: [
2358
+ {
2359
+ provide: NG_VALUE_ACCESSOR,
2360
+ useExisting: forwardRef(() => CheckboxComponent),
2361
+ multi: true
2362
+ }
2363
+ ], template: "<div\r\n class=\"inline-flex items-center gap-2 cursor-pointer group outline-none\"\r\n (click)=\"toggle()\"\r\n (keydown.enter)=\"toggle()\"\r\n (keydown.space)=\"$event.preventDefault(); toggle()\"\r\n tabindex=\"0\"\r\n [class.cursor-not-allowed]=\"disabled\"\r\n [attr.aria-disabled]=\"disabled\">\r\n\r\n <div\r\n class=\"relative flex items-center justify-center border-2 transition-all duration-200 ease-in-out\"\r\n [ngClass]=\"[\r\n size === 'sm' ? 'rounded-[3px]' : '',\r\n size === 'md' ? 'rounded' : '',\r\n size === 'lg' ? 'rounded' : '',\r\n size === 'xl' ? 'rounded-md' : '',\r\n\r\n size === 'sm' ? 'w-[14px] h-[14px]' : '',\r\n size === 'md' ? 'w-[16px] h-[16px]' : '',\r\n size === 'lg' ? 'w-[18px] h-[18px]' : '',\r\n size === 'xl' ? 'w-[20px] h-[20px]' : '',\r\n\r\n isChecked && !disabled ? 'bg-black border-black' : '',\r\n !isChecked && !disabled ? 'bg-white border-gray-300 group-hover:border-gray-400' : '',\r\n\r\n disabled && isChecked ? 'bg-gray-300 border-gray-300' : '',\r\n disabled && !isChecked ? 'bg-gray-100 border-gray-200' : '',\r\n\r\n 'group-focus-visible:ring-2 group-focus-visible:ring-blue-600 group-focus-visible:ring-offset-2'\r\n ]\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"3.5\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n class=\"text-white pointer-events-none transition-opacity duration-200\"\r\n [class.opacity-0]=\"!isChecked\"\r\n [class.opacity-100]=\"isChecked\"\r\n [class.w-[10.5px]]=\"size === 'sm'\"\r\n [class.w-[12px]]=\"size === 'md'\"\r\n [class.w-[13.5px]]=\"size === 'lg'\"\r\n [class.w-[14px]]=\"size === 'xl'\"\r\n >\r\n <polyline points=\"20 6 9 17 4 12\"></polyline>\r\n </svg>\r\n </div>\r\n\r\n <span\r\n *ngIf=\"label\"\r\n class=\"font-medium select-none\"\r\n [class.text-xs]=\"size === 'sm'\"\r\n [class.text-sm]=\"size !== 'sm'\"\r\n [class.text-gray-900]=\"!disabled\"\r\n [class.text-gray-400]=\"disabled\"\r\n >\r\n {{ label }}\r\n </span>\r\n</div>\r\n" }]
2364
+ }], propDecorators: { size: [{
2365
+ type: Input
2366
+ }], label: [{
2367
+ type: Input
2368
+ }], disabled: [{
2369
+ type: Input
2370
+ }], changed: [{
2371
+ type: Output
2372
+ }] } });
2373
+
2317
2374
  /*
2318
2375
  * Public API Surface of brickclay-lib
2319
2376
  */
@@ -2322,5 +2379,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2322
2379
  * Generated bundle index. Do not edit.
2323
2380
  */
2324
2381
 
2325
- export { BrickclayLib, CalendarManagerService, CalendarModule, CustomCalendarComponent, ScheduledDatePickerComponent, TimePickerComponent, ToggleComponent };
2382
+ export { BrickclayLib, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, ScheduledDatePickerComponent, TimePickerComponent, ToggleComponent };
2326
2383
  //# sourceMappingURL=brickclay-org-ui.mjs.map