hazo_ui 2.2.0 → 2.2.2

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
@@ -18,6 +18,8 @@ npm install hazo_ui
18
18
 
19
19
  - **[HazoUiFlexRadio](#hazouiflexradio)** - A flexible radio button/icon selection component with support for single and multi-selection modes, customizable layouts, and react-icons integration. Perfect for settings panels, preference selection, and option groups.
20
20
 
21
+ - **[HazoUiFlexInput](#hazouiflexinput)** - An enhanced input component with type validation, character restrictions, and error messaging. Supports numeric, alpha, email, and mixed input types with built-in validation and formatting guides.
22
+
21
23
  ---
22
24
 
23
25
  ## HazoUiMultiFilterDialog
@@ -149,6 +151,8 @@ function DataTable() {
149
151
  availableFields={availableFields}
150
152
  onFilterChange={handleFilterChange}
151
153
  initialFilters={filters}
154
+ title="Filter Products" // Optional: customize dialog title (default: "Filter")
155
+ description="Filter by product attributes" // Optional: customize description
152
156
  />
153
157
  {/* Your table/grid component */}
154
158
  </div>
@@ -156,6 +160,16 @@ function DataTable() {
156
160
  }
157
161
  ```
158
162
 
163
+ #### Props
164
+
165
+ | Prop | Type | Required | Default | Description |
166
+ |------|------|----------|---------|-------------|
167
+ | `availableFields` | `FilterField[]` | Yes | - | Array of field definitions for filtering |
168
+ | `onFilterChange` | `(filters: FilterConfig[]) => void` | Yes | - | Callback when filters are applied |
169
+ | `initialFilters` | `FilterConfig[]` | No | `[]` | Initial filter configuration |
170
+ | `title` | `string` | No | `"Filter"` | Dialog title text |
171
+ | `description` | `string` | No | `"Add multiple fields to filter by..."` | Dialog description text |
172
+
159
173
  #### Example Input
160
174
 
161
175
  ```tsx
@@ -323,6 +337,8 @@ function DataTable() {
323
337
  availableFields={availableFields}
324
338
  onSortChange={handleSortChange}
325
339
  initialSortFields={sorts}
340
+ title="Sort Products" // Optional: customize dialog title (default: "Sort")
341
+ description="Drag to reorder sort priority" // Optional: customize description
326
342
  />
327
343
  {/* Your table/grid component */}
328
344
  </div>
@@ -330,6 +346,16 @@ function DataTable() {
330
346
  }
331
347
  ```
332
348
 
349
+ #### Props
350
+
351
+ | Prop | Type | Required | Default | Description |
352
+ |------|------|----------|---------|-------------|
353
+ | `availableFields` | `SortField[]` | Yes | - | Array of field definitions for sorting |
354
+ | `onSortChange` | `(sorts: SortConfig[]) => void` | Yes | - | Callback when sorts are applied |
355
+ | `initialSortFields` | `SortConfig[]` | No | `[]` | Initial sort configuration |
356
+ | `title` | `string` | No | `"Sort"` | Dialog title text |
357
+ | `description` | `string` | No | `"Add multiple fields to sort by..."` | Dialog description text |
358
+
333
359
  #### Example Input
334
360
 
335
361
  ```tsx
@@ -667,6 +693,257 @@ The component supports the following react-icons packages:
667
693
 
668
694
  ---
669
695
 
696
+ ## HazoUiFlexInput
697
+
698
+ An enhanced input component with type validation, character restrictions, and error messaging. Extends shadcn Input component with additional validation props for numeric, alpha, email, and mixed input types.
699
+
700
+ #### Features
701
+
702
+ - **Multiple Input Types**: Supports mixed (text), numeric, alpha (letters only), and email input types
703
+ - **Real-time Character Filtering**: Automatically prevents invalid characters from being entered (e.g., numbers in alpha fields)
704
+ - **Validation on Blur**: Validates input when the field loses focus and displays error messages
705
+ - **Numeric Constraints**: Min/max value validation and decimal precision control
706
+ - **Length Constraints**: Configurable minimum and maximum character lengths
707
+ - **Regex Validation**: Custom regex pattern support for complex validation rules
708
+ - **Format Guide**: Optional helper text displayed below the input
709
+ - **Error Messaging**: Clear error messages displayed when validation fails
710
+ - **Controlled & Uncontrolled**: Supports both controlled and uncontrolled usage patterns
711
+ - **TypeScript Support**: Fully typed with TypeScript interfaces
712
+ - **Accessible**: Built with accessibility in mind using shadcn/ui components
713
+
714
+ #### Input Types
715
+
716
+ 1. **Mixed (text)**
717
+ - Allows any characters
718
+ - Supports length constraints (min/max)
719
+ - Supports regex validation
720
+
721
+ 2. **Numeric**
722
+ - Only allows numbers, decimal point, and minus sign
723
+ - Supports min/max value constraints
724
+ - Configurable decimal precision (including integers with 0 decimals)
725
+ - Automatically filters out non-numeric characters
726
+
727
+ 3. **Alpha**
728
+ - Only allows letters and spaces
729
+ - Automatically filters out numbers and special characters
730
+ - Supports length constraints
731
+
732
+ 4. **Email**
733
+ - Validates email format on blur
734
+ - Uses standard email regex pattern
735
+
736
+ #### Props
737
+
738
+ ```typescript
739
+ interface HazoUiFlexInputProps extends Omit<InputProps, "type"> {
740
+ input_type?: "mixed" | "numeric" | "email" | "alpha"; // Input type (default: "mixed")
741
+ text_len_min?: number; // Minimum character length
742
+ text_len_max?: number; // Maximum character length
743
+ num_min?: number; // Minimum numeric value
744
+ num_max?: number; // Maximum numeric value
745
+ regex?: string | RegExp; // Custom regex pattern
746
+ num_decimals?: number; // Number of decimal places allowed
747
+ format_guide?: string; // Helper text displayed below input
748
+ format_guide_info?: boolean; // Show format guide (default: false)
749
+ }
750
+ ```
751
+
752
+ #### Usage
753
+
754
+ **Basic Mixed Input**
755
+
756
+ ```tsx
757
+ import { HazoUiFlexInput } from 'hazo_ui';
758
+ import { useState } from 'react';
759
+
760
+ function BasicForm() {
761
+ const [value, setValue] = useState<string>("");
762
+
763
+ return (
764
+ <HazoUiFlexInput
765
+ input_type="mixed"
766
+ placeholder="Enter text..."
767
+ value={value}
768
+ onChange={(e) => setValue(e.target.value)}
769
+ />
770
+ );
771
+ }
772
+ ```
773
+
774
+ **Numeric Input with Constraints**
775
+
776
+ ```tsx
777
+ import { HazoUiFlexInput } from 'hazo_ui';
778
+ import { useState } from 'react';
779
+
780
+ function PriceInput() {
781
+ const [price, setPrice] = useState<string>("");
782
+
783
+ return (
784
+ <HazoUiFlexInput
785
+ input_type="numeric"
786
+ placeholder="Enter price (0-100)..."
787
+ num_min={0}
788
+ num_max={100}
789
+ num_decimals={2}
790
+ format_guide="Enter a number between 0 and 100 with up to 2 decimal places"
791
+ format_guide_info={true}
792
+ value={price}
793
+ onChange={(e) => setPrice(e.target.value)}
794
+ />
795
+ );
796
+ }
797
+ ```
798
+
799
+ **Integer Input (No Decimals)**
800
+
801
+ ```tsx
802
+ import { HazoUiFlexInput } from 'hazo_ui';
803
+ import { useState } from 'react';
804
+
805
+ function AgeInput() {
806
+ const [age, setAge] = useState<string>("");
807
+
808
+ return (
809
+ <HazoUiFlexInput
810
+ input_type="numeric"
811
+ placeholder="Enter age (1-120)..."
812
+ num_min={1}
813
+ num_max={120}
814
+ num_decimals={0}
815
+ format_guide="Enter a whole number between 1 and 120"
816
+ format_guide_info={true}
817
+ value={age}
818
+ onChange={(e) => setAge(e.target.value)}
819
+ />
820
+ );
821
+ }
822
+ ```
823
+
824
+ **Alpha Input (Letters Only)**
825
+
826
+ ```tsx
827
+ import { HazoUiFlexInput } from 'hazo_ui';
828
+ import { useState } from 'react';
829
+
830
+ function NameInput() {
831
+ const [name, setName] = useState<string>("");
832
+
833
+ return (
834
+ <HazoUiFlexInput
835
+ input_type="alpha"
836
+ placeholder="Enter name (letters only)..."
837
+ format_guide="Only letters and spaces are allowed"
838
+ format_guide_info={true}
839
+ value={name}
840
+ onChange={(e) => setName(e.target.value)}
841
+ />
842
+ );
843
+ }
844
+ ```
845
+
846
+ **Email Input with Validation**
847
+
848
+ ```tsx
849
+ import { HazoUiFlexInput } from 'hazo_ui';
850
+ import { useState } from 'react';
851
+
852
+ function EmailForm() {
853
+ const [email, setEmail] = useState<string>("");
854
+
855
+ return (
856
+ <HazoUiFlexInput
857
+ input_type="email"
858
+ placeholder="Enter email address..."
859
+ format_guide="Enter a valid email address (e.g., user@example.com)"
860
+ format_guide_info={true}
861
+ value={email}
862
+ onChange={(e) => setEmail(e.target.value)}
863
+ />
864
+ );
865
+ }
866
+ ```
867
+
868
+ **Mixed Input with Length Constraints**
869
+
870
+ ```tsx
871
+ import { HazoUiFlexInput } from 'hazo_ui';
872
+ import { useState } from 'react';
873
+
874
+ function UsernameInput() {
875
+ const [username, setUsername] = useState<string>("");
876
+
877
+ return (
878
+ <HazoUiFlexInput
879
+ input_type="mixed"
880
+ placeholder="Enter username (5-20 characters)..."
881
+ text_len_min={5}
882
+ text_len_max={20}
883
+ format_guide="Must be between 5 and 20 characters"
884
+ format_guide_info={true}
885
+ value={username}
886
+ onChange={(e) => setUsername(e.target.value)}
887
+ />
888
+ );
889
+ }
890
+ ```
891
+
892
+ **Input with Regex Validation**
893
+
894
+ ```tsx
895
+ import { HazoUiFlexInput } from 'hazo_ui';
896
+ import { useState } from 'react';
897
+
898
+ function PhoneInput() {
899
+ const [phone, setPhone] = useState<string>("");
900
+
901
+ return (
902
+ <HazoUiFlexInput
903
+ input_type="mixed"
904
+ placeholder="Enter phone number (XXX-XXX-XXXX)..."
905
+ regex={/^\d{3}-\d{3}-\d{4}$/}
906
+ format_guide="Format: XXX-XXX-XXXX (e.g., 123-456-7890)"
907
+ format_guide_info={true}
908
+ value={phone}
909
+ onChange={(e) => setPhone(e.target.value)}
910
+ />
911
+ );
912
+ }
913
+ ```
914
+
915
+ #### Validation Behavior
916
+
917
+ - **Character Filtering**: For `numeric` and `alpha` types, invalid characters are automatically filtered out as the user types
918
+ - **Validation Timing**: Validation occurs when the input loses focus (onBlur event)
919
+ - **Error Display**: Error messages appear below the input in red text when validation fails
920
+ - **Format Guide**: Optional helper text can be displayed below the input (set `format_guide_info={true}`)
921
+ - **Error Priority**: If both an error message and format guide are present, only the error message is shown
922
+
923
+ #### Expected Output
924
+
925
+ The component behaves like a standard input element:
926
+
927
+ ```typescript
928
+ // onChange receives a standard React.ChangeEvent<HTMLInputElement>
929
+ onChange={(e) => {
930
+ const value = e.target.value; // Current input value as string
931
+ // Handle value change
932
+ }}
933
+ ```
934
+
935
+ #### Error Messages
936
+
937
+ The component provides default error messages for common validation failures:
938
+
939
+ - **Numeric**: "Must be a valid number", "Must be at least X", "Must be at most X", "Maximum X decimal places allowed"
940
+ - **Alpha**: "Only letters are allowed"
941
+ - **Email**: "Must be a valid email address"
942
+ - **Mixed**: "Must be at least X characters", "Must be at most X characters"
943
+ - **Regex**: "Invalid format" (or custom message via `format_guide`)
944
+
945
+ ---
946
+
670
947
  ## Styling
671
948
 
672
949
  Both components use Tailwind CSS and follow shadcn/ui design patterns. Make sure your project has Tailwind CSS configured with the following CSS variables:
package/dist/index.cjs CHANGED
@@ -263,7 +263,7 @@ var CommandItem = React6__namespace.forwardRef(({ className, onSelect, value, ..
263
263
  {
264
264
  ref,
265
265
  className: cn(
266
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
266
+ "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
267
267
  className
268
268
  ),
269
269
  onClick: handleClick,
@@ -665,7 +665,9 @@ function FilterFieldItem({
665
665
  function HazoUiMultiFilterDialog({
666
666
  availableFields,
667
667
  onFilterChange,
668
- initialFilters = []
668
+ initialFilters = [],
669
+ title = "Filter",
670
+ description = "Add multiple fields to filter by. Select a field and set its filter value."
669
671
  }) {
670
672
  const [isOpen, setIsOpen] = React6.useState(false);
671
673
  const [filterFields, setFilterFields] = React6.useState(initialFilters);
@@ -787,8 +789,8 @@ function HazoUiMultiFilterDialog({
787
789
  ] }) }),
788
790
  /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "cls_filter_dialog_content max-w-lg w-full max-h-[90vh] overflow-y-auto", children: [
789
791
  /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
790
- /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Filter Images" }),
791
- /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "Add multiple fields to filter by. Select a field and set its filter value." })
792
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: title }),
793
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: description })
792
794
  ] }),
793
795
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_filter_dialog_body space-y-4 py-4", children: [
794
796
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_add_field_section", children: /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isComboboxOpen, onOpenChange: setIsComboboxOpen, children: [
@@ -985,7 +987,9 @@ function SortableSortFieldItem({
985
987
  function HazoUiMultiSortDialog({
986
988
  availableFields,
987
989
  onSortChange,
988
- initialSortFields = []
990
+ initialSortFields = [],
991
+ title = "Sort",
992
+ description = "Add multiple fields to sort by and reorder them. Use the switch to toggle between ascending and descending."
989
993
  }) {
990
994
  const [isOpen, setIsOpen] = React6.useState(false);
991
995
  const [sortFields, setSortFields] = React6.useState(initialSortFields);
@@ -1089,8 +1093,8 @@ function HazoUiMultiSortDialog({
1089
1093
  ] }) }),
1090
1094
  /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "cls_sort_dialog_content max-w-lg", children: [
1091
1095
  /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
1092
- /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Sort Images" }),
1093
- /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "Add multiple fields to sort by and reorder them. Use the switch to toggle between ascending and descending." })
1096
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: title }),
1097
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: description })
1094
1098
  ] }),
1095
1099
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_sort_dialog_body space-y-4 py-4", children: [
1096
1100
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_add_field_section", children: /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isComboboxOpen, onOpenChange: setIsComboboxOpen, children: [