@noxickon/onyx 2.0.4 → 3.0.0
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/AI-README.md +4942 -0
- package/README.md +222 -80
- package/dist/_internal/CustomSvgIcon.d.ts +1 -0
- package/dist/_internal/constants.d.ts +1 -0
- package/dist/_internal/hooks/useEventCallback.d.ts +1 -0
- package/dist/_internal/styles/button.variants.d.ts +8 -0
- package/dist/_internal/styles/color.helpers.d.ts +154 -0
- package/dist/_internal/styles/index.d.ts +4 -0
- package/dist/_internal/styles/link.variants.d.ts +6 -0
- package/dist/_internal/styles/status.styles.d.ts +38 -0
- package/dist/_internal/utils/Portal/Portal.d.ts +2 -0
- package/dist/_internal/utils/Portal/Portal.types.d.ts +5 -0
- package/dist/_internal/utils/Portal/index.d.ts +2 -0
- package/dist/_internal/utils/Slot/Slot.types.d.ts +6 -0
- package/dist/_internal/utils/Slot/createSlot.d.ts +5 -0
- package/dist/_internal/utils/Slot/index.d.ts +2 -0
- package/dist/_internal/utils/index.d.ts +4 -0
- package/dist/_internal/utils/isIconOnly.d.ts +2 -0
- package/dist/_internal/utils/pickClasses.d.ts +1 -0
- package/dist/chunks/{hooks-CUNBk2tZ.js → hooks-6tQOArBm.js} +6406 -5884
- package/dist/chunks/hooks-CNgb50x1.js +31 -0
- package/dist/chunks/ui-B0Ma5GdK.js +9 -0
- package/dist/chunks/ui-DrKTwQ0M.js +20527 -0
- package/dist/contexts/AuthContext/index.d.ts +5 -0
- package/dist/contexts/AuthContext/src/AuthContext.d.ts +2 -0
- package/dist/contexts/AuthContext/src/AuthContext.types.d.ts +27 -0
- package/dist/contexts/AuthContext/src/AuthProvider.d.ts +2 -0
- package/dist/contexts/AuthContext/src/query.d.ts +1 -0
- package/dist/contexts/AuthContext/src/useAuth.d.ts +1 -0
- package/dist/contexts/AuthContext/test/AuthContext.test.d.ts +1 -0
- package/dist/contexts/DialogContext/DialogContext.stories.d.ts +9 -0
- package/dist/contexts/DialogContext/index.d.ts +3 -0
- package/dist/contexts/DialogContext/src/DialogContext.d.ts +2 -0
- package/dist/contexts/DialogContext/src/DialogContext.types.d.ts +26 -0
- package/dist/contexts/DialogContext/src/DialogProvider.d.ts +2 -0
- package/dist/contexts/DialogContext/src/useDialog.d.ts +2 -0
- package/dist/contexts/DialogContext/test/DialogProvider.test.d.ts +1 -0
- package/dist/contexts/FeatureFlagContext/index.d.ts +5 -0
- package/dist/contexts/FeatureFlagContext/src/FeatureContext.d.ts +2 -0
- package/dist/contexts/FeatureFlagContext/src/FeatureContext.types.d.ts +28 -0
- package/dist/contexts/FeatureFlagContext/src/FeatureFlag.d.ts +2 -0
- package/dist/contexts/FeatureFlagContext/src/FeatureProvider.d.ts +2 -0
- package/dist/contexts/FeatureFlagContext/src/query.d.ts +1 -0
- package/dist/contexts/FeatureFlagContext/src/useFeature.d.ts +1 -0
- package/dist/contexts/FeatureFlagContext/test/Feature.test.d.ts +1 -0
- package/dist/contexts/contexts.cjs.js +11 -2
- package/dist/contexts/contexts.es.js +94 -82
- package/dist/contexts/index.d.ts +3 -0
- package/dist/forms/Checkbox/Checkbox.stories.d.ts +80 -0
- package/dist/forms/Checkbox/index.d.ts +5 -0
- package/dist/forms/Checkbox/src/Checkbox.d.ts +2 -0
- package/dist/forms/Checkbox/src/Checkbox.types.d.ts +20 -0
- package/dist/forms/Checkbox/src/CheckboxGroup.d.ts +2 -0
- package/dist/forms/Checkbox/src/CheckboxGroupItem.d.ts +2 -0
- package/dist/forms/Checkbox/src/context/CheckboxGroupContext.d.ts +2 -0
- package/dist/forms/Checkbox/src/context/CheckboxGroupContext.types.d.ts +6 -0
- package/dist/forms/Checkbox/src/context/useCheckboxGroup.d.ts +1 -0
- package/dist/forms/Checkbox/test/Checkbox.test.d.ts +1 -0
- package/dist/forms/FileUpload/FileUpload.stories.d.ts +67 -0
- package/dist/forms/FileUpload/index.d.ts +6 -0
- package/dist/forms/FileUpload/src/FileUpload.d.ts +2 -0
- package/dist/forms/FileUpload/src/FileUpload.types.d.ts +24 -0
- package/dist/forms/FileUpload/src/FileUploadArea.d.ts +2 -0
- package/dist/forms/FileUpload/src/FileUploadButton.d.ts +2 -0
- package/dist/forms/FileUpload/src/FileUploadOverlay.d.ts +2 -0
- package/dist/forms/FileUpload/src/context/FileUploadContext.d.ts +2 -0
- package/dist/forms/FileUpload/src/context/FileUploadContext.types.d.ts +17 -0
- package/dist/forms/FileUpload/src/context/useFileUploadContext.d.ts +1 -0
- package/dist/forms/FileUpload/src/utils.d.ts +1 -0
- package/dist/forms/FileUpload/test/FileUpload.test.d.ts +1 -0
- package/dist/forms/Form/Form.stories.d.ts +52 -0
- package/dist/forms/Form/index.d.ts +9 -0
- package/dist/forms/Form/src/Form.d.ts +2 -0
- package/dist/forms/Form/src/Form.types.d.ts +18 -0
- package/dist/forms/Form/src/FormDescription.d.ts +2 -0
- package/dist/forms/Form/src/FormError.d.ts +2 -0
- package/dist/forms/Form/src/FormField.d.ts +2 -0
- package/dist/forms/Form/src/FormGroup.d.ts +2 -0
- package/dist/forms/Form/src/FormGroupTitle.d.ts +2 -0
- package/dist/forms/Form/src/FormLabel.d.ts +2 -0
- package/dist/forms/Form/src/context/FormFieldContext.d.ts +2 -0
- package/dist/forms/Form/src/context/FormFieldContext.types.d.ts +7 -0
- package/dist/forms/Form/src/context/useFormFieldContext.d.ts +1 -0
- package/dist/forms/Form/test/Form.test.d.ts +1 -0
- package/dist/forms/Input/Date/Date.stories.d.ts +91 -0
- package/dist/forms/Input/Date/index.d.ts +5 -0
- package/dist/forms/Input/Date/src/Date.types.d.ts +23 -0
- package/dist/forms/Input/Date/src/DateButton.d.ts +2 -0
- package/dist/forms/Input/Date/src/DateContainer.d.ts +2 -0
- package/dist/forms/Input/Date/src/DateInput.d.ts +1 -0
- package/dist/forms/Input/Date/src/DatePickerButton.d.ts +2 -0
- package/dist/forms/Input/Date/src/DatePickerContent.d.ts +1 -0
- package/dist/forms/Input/Date/src/context/DateContext.d.ts +2 -0
- package/dist/forms/Input/Date/src/context/DateContext.types.d.ts +18 -0
- package/dist/forms/Input/Date/src/context/useDateContext.d.ts +1 -0
- package/dist/forms/Input/Date/src/date.styles.d.ts +10 -0
- package/dist/forms/Input/Date/src/useDateInput.d.ts +16 -0
- package/dist/forms/Input/Date/test/Date.test.d.ts +1 -0
- package/dist/forms/Input/Number/Number.stories.d.ts +95 -0
- package/dist/forms/Input/Number/index.d.ts +7 -0
- package/dist/forms/Input/Number/src/Number.types.d.ts +26 -0
- package/dist/forms/Input/Number/src/NumberButton.d.ts +2 -0
- package/dist/forms/Input/Number/src/NumberContainer.d.ts +2 -0
- package/dist/forms/Input/Number/src/NumberDecrementButton.d.ts +1 -0
- package/dist/forms/Input/Number/src/NumberIncrementButton.d.ts +1 -0
- package/dist/forms/Input/Number/src/NumberInput.d.ts +2 -0
- package/dist/forms/Input/Number/src/NumberInput.utils.d.ts +2 -0
- package/dist/forms/Input/Number/src/context/NumberContext.d.ts +2 -0
- package/dist/forms/Input/Number/src/context/NumberContext.types.d.ts +15 -0
- package/dist/forms/Input/Number/src/context/useNumberContext.d.ts +1 -0
- package/dist/forms/Input/Number/test/NumberInput.test.d.ts +1 -0
- package/dist/forms/Input/Otp/Otp.stories.d.ts +69 -0
- package/dist/forms/Input/Otp/index.d.ts +2 -0
- package/dist/forms/Input/Otp/src/Otp.types.d.ts +11 -0
- package/dist/forms/Input/Otp/src/OtpInput.d.ts +2 -0
- package/dist/forms/Input/Otp/test/OtpInput.test.d.ts +1 -0
- package/dist/forms/Input/Text/Text.stories.d.ts +84 -0
- package/dist/forms/Input/Text/index.d.ts +8 -0
- package/dist/forms/Input/Text/src/Text.types.d.ts +31 -0
- package/dist/forms/Input/Text/src/TextButton.d.ts +2 -0
- package/dist/forms/Input/Text/src/TextClearButton.d.ts +1 -0
- package/dist/forms/Input/Text/src/TextContainer.d.ts +2 -0
- package/dist/forms/Input/Text/src/TextCounter.d.ts +2 -0
- package/dist/forms/Input/Text/src/TextInput.d.ts +2 -0
- package/dist/forms/Input/Text/src/TextVisibilityButton.d.ts +1 -0
- package/dist/forms/Input/Text/src/context/TextContext.d.ts +2 -0
- package/dist/forms/Input/Text/src/context/TextContext.types.d.ts +10 -0
- package/dist/forms/Input/Text/src/context/useTextContext.d.ts +1 -0
- package/dist/forms/Input/Text/test/TextInput.test.d.ts +1 -0
- package/dist/forms/Input/Time/Time.stories.d.ts +62 -0
- package/dist/forms/Input/Time/index.d.ts +4 -0
- package/dist/forms/Input/Time/src/Time.types.d.ts +17 -0
- package/dist/forms/Input/Time/src/TimeButton.d.ts +2 -0
- package/dist/forms/Input/Time/src/TimeContainer.d.ts +2 -0
- package/dist/forms/Input/Time/src/TimeInput.d.ts +1 -0
- package/dist/forms/Input/Time/src/context/TimeContext.d.ts +2 -0
- package/dist/forms/Input/Time/src/context/TimeContext.types.d.ts +6 -0
- package/dist/forms/Input/Time/src/context/useTimeContext.d.ts +1 -0
- package/dist/forms/Input/Time/src/useTimeInput.d.ts +9 -0
- package/dist/forms/Input/Time/test/Time.test.d.ts +1 -0
- package/dist/forms/Input/_internal/context/BaseInputContext.d.ts +2 -0
- package/dist/forms/Input/_internal/context/BaseInputContext.types.d.ts +10 -0
- package/dist/forms/Input/_internal/context/useBaseInputContext.d.ts +1 -0
- package/dist/forms/Input/_internal/src/BaseInput.d.ts +2 -0
- package/dist/forms/Input/_internal/src/BaseInput.types.d.ts +13 -0
- package/dist/forms/Input/_internal/src/BaseInputButton.d.ts +2 -0
- package/dist/forms/Input/_internal/src/BaseInputContainer.d.ts +2 -0
- package/dist/forms/Input/_internal/src/BaseInputCounter.d.ts +2 -0
- package/dist/forms/Input/_internal/test/BaseInput.test.d.ts +1 -0
- package/dist/forms/Input/index.d.ts +5 -0
- package/dist/forms/Radio/Radio.stories.d.ts +61 -0
- package/dist/forms/Radio/index.d.ts +6 -0
- package/dist/forms/Radio/src/Radio.types.d.ts +16 -0
- package/dist/forms/Radio/src/RadioGroup.d.ts +2 -0
- package/dist/forms/Radio/src/RadioGroupItem.d.ts +2 -0
- package/dist/forms/Radio/src/context/RadioGroupContext.d.ts +2 -0
- package/dist/forms/Radio/src/context/RadioGroupContext.types.d.ts +6 -0
- package/dist/forms/Radio/src/context/useRadioGroup.d.ts +1 -0
- package/dist/forms/Radio/test/Radio.test.d.ts +1 -0
- package/dist/forms/Select/Select.stories.d.ts +13 -0
- package/dist/forms/Select/index.d.ts +7 -0
- package/dist/forms/Select/src/Select.d.ts +2 -0
- package/dist/forms/Select/src/Select.types.d.ts +28 -0
- package/dist/forms/Select/src/SelectContent.d.ts +2 -0
- package/dist/forms/Select/src/SelectGroup.d.ts +2 -0
- package/dist/forms/Select/src/SelectItem.d.ts +2 -0
- package/dist/forms/Select/src/SelectTrigger.d.ts +2 -0
- package/dist/forms/Select/src/context/SelectContext.d.ts +2 -0
- package/dist/forms/Select/src/context/SelectContext.types.d.ts +25 -0
- package/dist/forms/Select/src/context/useSelectContext.d.ts +1 -0
- package/dist/forms/Select/src/extractItemsFromContent.d.ts +2 -0
- package/dist/forms/Select/test/Select.test.d.ts +1 -0
- package/dist/forms/Switch/Switch.stories.d.ts +9 -0
- package/dist/forms/Switch/index.d.ts +2 -0
- package/dist/forms/Switch/src/Switch.d.ts +2 -0
- package/dist/forms/Switch/src/Switch.types.d.ts +7 -0
- package/dist/forms/Switch/test/Switch.test.d.ts +1 -0
- package/dist/forms/Textarea/Textarea.stories.d.ts +10 -0
- package/dist/forms/Textarea/index.d.ts +2 -0
- package/dist/forms/Textarea/src/Textarea.d.ts +2 -0
- package/dist/forms/Textarea/src/Textarea.types.d.ts +5 -0
- package/dist/forms/Textarea/test/Textarea.test.d.ts +1 -0
- package/dist/forms/index.d.ts +8 -0
- package/dist/hooks/hooks.cjs.js +1 -1
- package/dist/hooks/hooks.es.js +14 -9
- package/dist/hooks/index.d.ts +13 -0
- package/dist/hooks/useControllableState/UseControllableState.stories.d.ts +6 -0
- package/dist/hooks/useControllableState/index.d.ts +2 -0
- package/dist/hooks/useControllableState/src/useControllableState.d.ts +2 -0
- package/dist/hooks/useControllableState/src/useControllableState.types.d.ts +10 -0
- package/dist/hooks/useControllableState/test/useControllableState.test.d.ts +1 -0
- package/dist/hooks/useFileUpload/index.d.ts +2 -0
- package/dist/hooks/useFileUpload/src/useFileUpload.d.ts +2 -0
- package/dist/hooks/useFileUpload/src/useFileUpload.types.d.ts +20 -0
- package/dist/hooks/useFileUpload/tests/useFileUpload.test.d.ts +1 -0
- package/dist/hooks/useFilterFetch/UseFilterFetch.stories.d.ts +7 -0
- package/dist/hooks/useFilterFetch/index.d.ts +2 -0
- package/dist/hooks/useFilterFetch/src/useFilterFetch.d.ts +2 -0
- package/dist/hooks/useFilterFetch/src/useFilterFetch.types.d.ts +22 -0
- package/dist/hooks/useFilterFetch/test/useFilterFetch.test.d.ts +1 -0
- package/dist/hooks/useFilterQuery/index.d.ts +2 -0
- package/dist/hooks/useFilterQuery/src/useFilterQuery.d.ts +2 -0
- package/dist/hooks/useFilterQuery/src/useFilterQuery.types.d.ts +24 -0
- package/dist/hooks/useFilterQuery/test/useFilter.test.d.ts +1 -0
- package/dist/hooks/useFocus/UseFocus.stories.d.ts +6 -0
- package/dist/hooks/useFocus/index.d.ts +2 -0
- package/dist/hooks/useFocus/src/useFocus.d.ts +2 -0
- package/dist/hooks/useFocus/src/useFocus.types.d.ts +17 -0
- package/dist/hooks/useFocus/test/useFocus.test.d.ts +1 -0
- package/dist/hooks/useForm/index.d.ts +2 -0
- package/dist/hooks/useForm/src/useForm.d.ts +2 -0
- package/dist/hooks/useForm/src/useForm.types.d.ts +20 -0
- package/dist/hooks/useForm/test/useForm.test.d.ts +1 -0
- package/dist/hooks/useFormFetch/UseFormFetch.stories.d.ts +6 -0
- package/dist/hooks/useFormFetch/index.d.ts +2 -0
- package/dist/hooks/useFormFetch/src/useFormFetch.d.ts +2 -0
- package/dist/hooks/useFormFetch/src/useFormFetch.types.d.ts +18 -0
- package/dist/hooks/useFormFetch/test/useFormFetch.test.d.ts +1 -0
- package/dist/hooks/useFormState/index.d.ts +4 -0
- package/dist/hooks/useFormState/src/config/arrayRules.d.ts +8 -0
- package/dist/hooks/useFormState/src/config/simpleRules.d.ts +18 -0
- package/dist/hooks/useFormState/src/handleRules.d.ts +3 -0
- package/dist/hooks/useFormState/src/rulesHelper.d.ts +3 -0
- package/dist/hooks/useFormState/src/useFormState.d.ts +2 -0
- package/dist/hooks/useFormState/src/useFormState.types.d.ts +37 -0
- package/dist/hooks/useFormState/src/validator.d.ts +2 -0
- package/dist/hooks/useFormState/test/useFormState.test.d.ts +1 -0
- package/dist/hooks/useKeyboard/UseKeyboard.stories.d.ts +6 -0
- package/dist/hooks/useKeyboard/index.d.ts +2 -0
- package/dist/hooks/useKeyboard/src/useKeyboard.d.ts +2 -0
- package/dist/hooks/useKeyboard/src/useKeyboard.types.d.ts +9 -0
- package/dist/hooks/useKeyboard/test/useKeyboard.test.d.ts +1 -0
- package/dist/hooks/useOverflowDetection/UseOverflowDetection.stories.d.ts +6 -0
- package/dist/hooks/useOverflowDetection/index.d.ts +2 -0
- package/dist/hooks/useOverflowDetection/src/useOverflowDetection.d.ts +2 -0
- package/dist/hooks/useOverflowDetection/src/useOverflowDetection.types.d.ts +8 -0
- package/dist/hooks/useOverflowDetection/test/useOverflowDetection.test.d.ts +1 -0
- package/dist/hooks/useScrollSpy/UseScrollSpy.stories.d.ts +6 -0
- package/dist/hooks/useScrollSpy/index.d.ts +2 -0
- package/dist/hooks/useScrollSpy/src/useScrollSpy.d.ts +2 -0
- package/dist/hooks/useScrollSpy/src/useScrollSpy.types.d.ts +9 -0
- package/dist/hooks/useScrollSpy/test/useScrollSpy.test.d.ts +1 -0
- package/dist/hooks/useSize/UseSize.stories.d.ts +6 -0
- package/dist/hooks/useSize/index.d.ts +1 -0
- package/dist/hooks/useSize/src/useSize.d.ts +6 -0
- package/dist/hooks/useSize/test/useSize.test.d.ts +1 -0
- package/dist/hooks/useTheme/UseTheme.stories.d.ts +7 -0
- package/dist/hooks/useTheme/index.d.ts +2 -0
- package/dist/hooks/useTheme/src/useTheme.d.ts +2 -0
- package/dist/hooks/useTheme/src/useTheme.types.d.ts +10 -0
- package/dist/hooks/useTheme/test/useTheme.test.d.ts +1 -0
- package/dist/index.d.ts +3 -1960
- package/dist/layouts/AppLayout/AppLayout.stories.d.ts +29 -0
- package/dist/layouts/AppLayout/index.d.ts +2 -0
- package/dist/layouts/AppLayout/src/AppLayout.d.ts +2 -0
- package/dist/layouts/AppLayout/src/AppLayout.types.d.ts +4 -0
- package/dist/layouts/AppLayout/test/AppLayout.test.d.ts +1 -0
- package/dist/layouts/MainContent/MainContent.stories.d.ts +40 -0
- package/dist/layouts/MainContent/index.d.ts +5 -0
- package/dist/layouts/MainContent/src/MainContent.d.ts +2 -0
- package/dist/layouts/MainContent/src/MainContent.types.d.ts +10 -0
- package/dist/layouts/MainContent/src/MainContentBody.d.ts +2 -0
- package/dist/layouts/MainContent/src/MainContentHeader.d.ts +2 -0
- package/dist/layouts/MainContent/test/MainContent.test.d.ts +1 -0
- package/dist/layouts/Sidebar/Sidebar.stories.d.ts +57 -0
- package/dist/layouts/Sidebar/index.d.ts +26 -0
- package/dist/layouts/Sidebar/src/Sidebar.d.ts +2 -0
- package/dist/layouts/Sidebar/src/Sidebar.types.d.ts +75 -0
- package/dist/layouts/Sidebar/src/SidebarContent.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarFooter.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarGroup.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarGroupAction.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarGroupContent.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarGroupLabel.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarHeader.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenu.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuAction.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuButton.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuIcon.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuItem.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuSkeleton.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuSub.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuSubButton.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuSubItem.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarMenuTag.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarSeparator.d.ts +2 -0
- package/dist/layouts/Sidebar/src/SidebarTrigger.d.ts +2 -0
- package/dist/layouts/Sidebar/src/context/MenuItem/MenuItemContext.d.ts +2 -0
- package/dist/layouts/Sidebar/src/context/MenuItem/MenuItemContext.types.d.ts +12 -0
- package/dist/layouts/Sidebar/src/context/MenuItem/MenuItemProvider.d.ts +2 -0
- package/dist/layouts/Sidebar/src/context/MenuItem/useMenuItemContext.d.ts +1 -0
- package/dist/layouts/Sidebar/src/context/Sidebar/SidebarContext.d.ts +2 -0
- package/dist/layouts/Sidebar/src/context/Sidebar/SidebarContext.types.d.ts +7 -0
- package/dist/layouts/Sidebar/src/context/Sidebar/SidebarProvider.d.ts +2 -0
- package/dist/layouts/Sidebar/src/context/Sidebar/SidebarProvider.types.d.ts +4 -0
- package/dist/layouts/Sidebar/src/context/Sidebar/useSidebarContext.d.ts +1 -0
- package/dist/layouts/Sidebar/test/Sidebar.test.d.ts +1 -0
- package/dist/layouts/index.d.ts +3 -0
- package/dist/layouts/layouts.cjs.js +1 -1
- package/dist/layouts/layouts.es.js +406 -668
- package/dist/legacy/hooks/useVirtualList/index.d.ts +2 -0
- package/dist/legacy/hooks/useVirtualList/src/useVirtualList.d.ts +2 -0
- package/dist/legacy/hooks/useVirtualList/src/useVirtualList.types.d.ts +23 -0
- package/dist/legacy/hooks/useVirtualList/src/utils/binarySearch.d.ts +1 -0
- package/dist/legacy/index.d.ts +6 -0
- package/dist/legacy/layouts/Header/Header.stories.d.ts +43 -0
- package/dist/legacy/layouts/Header/index.d.ts +1 -0
- package/dist/legacy/layouts/Header/src/Header.d.ts +2 -0
- package/dist/legacy/layouts/Header/src/Header.types.d.ts +8 -0
- package/dist/legacy/layouts/Header/test/Header.test.d.ts +1 -0
- package/dist/legacy/layouts/MainContent/MainContent.stories.d.ts +42 -0
- package/dist/legacy/layouts/MainContent/index.d.ts +1 -0
- package/dist/legacy/layouts/MainContent/src/MainContent.d.ts +2 -0
- package/dist/legacy/layouts/MainContent/src/MainContent.types.d.ts +10 -0
- package/dist/legacy/layouts/MainContent/test/MainContent.test.d.ts +1 -0
- package/dist/legacy/layouts/Sidebar/Sidebar.stories.d.ts +61 -0
- package/dist/legacy/layouts/Sidebar/index.d.ts +10 -0
- package/dist/legacy/layouts/Sidebar/src/Sidebar.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/Sidebar.types.d.ts +37 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarBody.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarButton.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarFooter.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarGroup.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarHeader.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarIcon.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarLabel.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/SidebarLink.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/constants.d.ts +5 -0
- package/dist/legacy/layouts/Sidebar/src/context/SidebarContext.d.ts +2 -0
- package/dist/legacy/layouts/Sidebar/src/context/SidebarContext.types.d.ts +4 -0
- package/dist/legacy/layouts/Sidebar/src/context/useSidebarContext.d.ts +1 -0
- package/dist/legacy/layouts/Sidebar/test/Sidebar.test.d.ts +1 -0
- package/dist/legacy/legacy.cjs.js +1 -0
- package/dist/legacy/legacy.es.js +842 -0
- package/dist/legacy/ui/EndlessList/EndlessList.stories.d.ts +27 -0
- package/dist/legacy/ui/EndlessList/index.d.ts +6 -0
- package/dist/legacy/ui/EndlessList/src/EndlessList.d.ts +2 -0
- package/dist/legacy/ui/EndlessList/src/EndlessList.types.d.ts +25 -0
- package/dist/legacy/ui/EndlessList/src/EndlessListEmpty.d.ts +2 -0
- package/dist/legacy/ui/EndlessList/src/EndlessListError.d.ts +2 -0
- package/dist/legacy/ui/EndlessList/src/EndlessListLoader.d.ts +2 -0
- package/dist/legacy/ui/EndlessList/src/context/EndlessListContext.d.ts +2 -0
- package/dist/legacy/ui/EndlessList/src/context/EndlessListContext.types.d.ts +7 -0
- package/dist/legacy/ui/EndlessList/src/context/index.d.ts +3 -0
- package/dist/legacy/ui/EndlessList/src/context/useEndlessListContext.d.ts +2 -0
- package/dist/legacy/ui/EndlessList/test/EndlessList.test.d.ts +1 -0
- package/dist/legacy/ui/Floating/Floating.stories.d.ts +80 -0
- package/dist/legacy/ui/Floating/index.d.ts +8 -0
- package/dist/legacy/ui/Floating/src/Floating.d.ts +2 -0
- package/dist/legacy/ui/Floating/src/Floating.types.d.ts +29 -0
- package/dist/legacy/ui/Floating/src/FloatingContent.d.ts +2 -0
- package/dist/legacy/ui/Floating/src/FloatingTrigger.d.ts +2 -0
- package/dist/legacy/ui/Floating/src/context/FloatingContext.d.ts +2 -0
- package/dist/legacy/ui/Floating/src/context/FloatingContext.types.d.ts +17 -0
- package/dist/legacy/ui/Floating/src/context/useFloatingContext.d.ts +1 -0
- package/dist/legacy/ui/Floating/test/Floating.test.d.ts +1 -0
- package/dist/onyx.cjs.js +1 -1
- package/dist/onyx.es.js +68 -51
- package/dist/pages/Login/Login.stories.d.ts +56 -0
- package/dist/pages/Login/index.d.ts +2 -0
- package/dist/pages/Login/src/Login.d.ts +2 -0
- package/dist/pages/Login/src/Login.types.d.ts +18 -0
- package/dist/pages/index.d.ts +1 -0
- package/dist/pages/pages.cjs.js +1 -13
- package/dist/pages/pages.es.js +94 -126
- package/dist/routes/GuestRoute/index.d.ts +2 -0
- package/dist/routes/GuestRoute/src/GuestRoute.d.ts +2 -0
- package/dist/routes/GuestRoute/src/GuestRoute.types.d.ts +6 -0
- package/dist/routes/GuestRoute/test/GuestRoute.test.d.ts +1 -0
- package/dist/routes/ProtectedRoute/index.d.ts +2 -0
- package/dist/routes/ProtectedRoute/src/ProtectedRoute.d.ts +2 -0
- package/dist/routes/ProtectedRoute/src/ProtectedRoute.types.d.ts +6 -0
- package/dist/routes/ProtectedRoute/test/ProtectedRoute.test.d.ts +1 -0
- package/dist/routes/RoleRoute/index.d.ts +2 -0
- package/dist/routes/RoleRoute/src/RoleRoute.d.ts +2 -0
- package/dist/routes/RoleRoute/src/RoleRoute.types.d.ts +7 -0
- package/dist/routes/RoleRoute/test/RoleRoute.test.d.ts +1 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/routes.cjs.js +1 -1
- package/dist/routes/routes.es.js +8 -8
- package/dist/typography/Description/Description.stories.d.ts +38 -0
- package/dist/typography/Description/index.d.ts +2 -0
- package/dist/typography/Description/src/Description.d.ts +2 -0
- package/dist/typography/Description/src/Description.types.d.ts +5 -0
- package/dist/typography/Description/test/Description.test.d.ts +1 -0
- package/dist/typography/Error/Error.stories.d.ts +34 -0
- package/dist/typography/Error/index.d.ts +2 -0
- package/dist/typography/Error/src/Error.d.ts +2 -0
- package/dist/typography/Error/src/Error.types.d.ts +2 -0
- package/dist/typography/Error/test/Error.test.d.ts +1 -0
- package/dist/typography/Heading/Heading.stories.d.ts +326 -0
- package/dist/typography/Heading/index.d.ts +2 -0
- package/dist/typography/Heading/src/Heading.d.ts +283 -0
- package/dist/typography/Heading/src/Heading.types.d.ts +9 -0
- package/dist/typography/Heading/test/Heading.test.d.ts +1 -0
- package/dist/typography/Label/Label.stories.d.ts +46 -0
- package/dist/typography/Label/index.d.ts +2 -0
- package/dist/typography/Label/src/Label.d.ts +2 -0
- package/dist/typography/Label/src/Label.types.d.ts +6 -0
- package/dist/typography/Label/test/Label.test.d.ts +1 -0
- package/dist/typography/Legend/Legend.stories.d.ts +42 -0
- package/dist/typography/Legend/index.d.ts +2 -0
- package/dist/typography/Legend/src/Legend.d.ts +2 -0
- package/dist/typography/Legend/src/Legend.types.d.ts +6 -0
- package/dist/typography/Legend/test/Legend.test.d.ts +1 -0
- package/dist/typography/index.d.ts +5 -0
- package/dist/ui/Accordion/Accordion.stories.d.ts +59 -0
- package/dist/ui/Accordion/index.d.ts +6 -0
- package/dist/ui/Accordion/src/Accordion.d.ts +2 -0
- package/dist/ui/Accordion/src/Accordion.types.d.ts +16 -0
- package/dist/ui/Accordion/src/AccordionContent.d.ts +2 -0
- package/dist/ui/Accordion/src/AccordionItem.d.ts +2 -0
- package/dist/ui/Accordion/src/AccordionTrigger.d.ts +2 -0
- package/dist/ui/Accordion/src/context/AccordionContext.d.ts +2 -0
- package/dist/ui/Accordion/src/context/AccordionContext.types.d.ts +12 -0
- package/dist/ui/Accordion/src/context/AccordionItemContext.d.ts +2 -0
- package/dist/ui/Accordion/src/context/useAccordionContext.d.ts +1 -0
- package/dist/ui/Accordion/src/context/useAccordionItemContext.d.ts +1 -0
- package/dist/ui/Accordion/test/Accordion.test.d.ts +1 -0
- package/dist/ui/Alert/Alert.stories.d.ts +36 -0
- package/dist/ui/Alert/index.d.ts +5 -0
- package/dist/ui/Alert/src/Alert.d.ts +2 -0
- package/dist/ui/Alert/src/Alert.types.d.ts +8 -0
- package/dist/ui/Alert/src/AlertDescription.d.ts +2 -0
- package/dist/ui/Alert/src/AlertTitle.d.ts +2 -0
- package/dist/ui/Alert/src/alert.styles.d.ts +3 -0
- package/dist/ui/Alert/src/context/AlertContext.d.ts +2 -0
- package/dist/ui/Alert/src/context/AlertContext.types.d.ts +4 -0
- package/dist/ui/Alert/src/context/useAlertContext.d.ts +1 -0
- package/dist/ui/Alert/test/Alert.test.d.ts +1 -0
- package/dist/ui/AspectRatio/AspectRatio.stories.d.ts +48 -0
- package/dist/ui/AspectRatio/index.d.ts +6 -0
- package/dist/ui/AspectRatio/src/AspectRatio.d.ts +2 -0
- package/dist/ui/AspectRatio/src/AspectRatio.types.d.ts +30 -0
- package/dist/ui/AspectRatio/src/AspectRatioFallback.d.ts +2 -0
- package/dist/ui/AspectRatio/src/AspectRatioImage.d.ts +2 -0
- package/dist/ui/AspectRatio/src/AspectRatioVideo.d.ts +2 -0
- package/dist/ui/AspectRatio/src/context/AspectRatioContext.d.ts +2 -0
- package/dist/ui/AspectRatio/src/context/AspectRatioContext.types.d.ts +11 -0
- package/dist/ui/AspectRatio/src/context/useAspectRatioContext.d.ts +1 -0
- package/dist/ui/AspectRatio/test/AspectRatio.test.d.ts +1 -0
- package/dist/ui/Avatar/Avatar.stories.d.ts +70 -0
- package/dist/ui/Avatar/index.d.ts +7 -0
- package/dist/ui/Avatar/src/Avatar.d.ts +2 -0
- package/dist/ui/Avatar/src/Avatar.types.d.ts +26 -0
- package/dist/ui/Avatar/src/AvatarField.d.ts +2 -0
- package/dist/ui/Avatar/src/AvatarFieldContent.d.ts +2 -0
- package/dist/ui/Avatar/src/AvatarFieldName.d.ts +2 -0
- package/dist/ui/Avatar/src/AvatarFieldSubtitle.d.ts +2 -0
- package/dist/ui/Avatar/src/avatar.styles.d.ts +11 -0
- package/dist/ui/Avatar/test/Avatar.test.d.ts +1 -0
- package/dist/ui/Badge/Badge.stories.d.ts +60 -0
- package/dist/ui/Badge/index.d.ts +2 -0
- package/dist/ui/Badge/src/Badge.d.ts +2 -0
- package/dist/ui/Badge/src/Badge.types.d.ts +11 -0
- package/dist/ui/Badge/src/badge.styles.d.ts +9 -0
- package/dist/ui/Badge/test/Badge.test.d.ts +1 -0
- package/dist/ui/Breadcrumb/Breadcrumb.stories.d.ts +42 -0
- package/dist/ui/Breadcrumb/index.d.ts +8 -0
- package/dist/ui/Breadcrumb/src/Breadcrumb.d.ts +2 -0
- package/dist/ui/Breadcrumb/src/Breadcrumb.types.d.ts +18 -0
- package/dist/ui/Breadcrumb/src/BreadcrumbEllipsis.d.ts +2 -0
- package/dist/ui/Breadcrumb/src/BreadcrumbItem.d.ts +2 -0
- package/dist/ui/Breadcrumb/src/BreadcrumbLink.d.ts +2 -0
- package/dist/ui/Breadcrumb/src/BreadcrumbPage.d.ts +2 -0
- package/dist/ui/Breadcrumb/src/BreadcrumbSeparator.d.ts +2 -0
- package/dist/ui/Breadcrumb/test/Breadcrumb.test.d.ts +1 -0
- package/dist/ui/Button/Button.stories.d.ts +74 -0
- package/dist/ui/Button/index.d.ts +2 -0
- package/dist/ui/Button/src/Button.d.ts +2 -0
- package/dist/ui/Button/src/Button.types.d.ts +9 -0
- package/dist/ui/Button/test/Button.test.d.ts +1 -0
- package/dist/ui/Calendar/Calendar.stories.d.ts +33 -0
- package/dist/ui/Calendar/index.d.ts +2 -0
- package/dist/ui/Calendar/src/Calendar.d.ts +2 -0
- package/dist/ui/Calendar/src/Calendar.types.d.ts +20 -0
- package/dist/ui/Calendar/src/CalendarGrid.d.ts +2 -0
- package/dist/ui/Calendar/src/CalendarHeader.d.ts +2 -0
- package/dist/ui/Calendar/test/Calendar.test.d.ts +1 -0
- package/dist/ui/Card/Card.stories.d.ts +73 -0
- package/dist/ui/Card/index.d.ts +7 -0
- package/dist/ui/Card/src/Card.d.ts +2 -0
- package/dist/ui/Card/src/Card.types.d.ts +20 -0
- package/dist/ui/Card/src/CardAction.d.ts +2 -0
- package/dist/ui/Card/src/CardContent.d.ts +2 -0
- package/dist/ui/Card/src/CardFooter.d.ts +2 -0
- package/dist/ui/Card/src/CardHeader.d.ts +2 -0
- package/dist/ui/Card/test/Card.test.d.ts +1 -0
- package/dist/ui/DescriptionList/DescriptionList.stories.d.ts +11 -0
- package/dist/ui/DescriptionList/index.d.ts +4 -0
- package/dist/ui/DescriptionList/src/DescriptionList.d.ts +2 -0
- package/dist/ui/DescriptionList/src/DescriptionList.types.d.ts +11 -0
- package/dist/ui/DescriptionList/src/DescriptionListTerm.d.ts +2 -0
- package/dist/ui/DescriptionList/src/DescriptionListValue.d.ts +2 -0
- package/dist/ui/DescriptionList/test/DescriptionList.test.d.ts +1 -0
- package/dist/ui/Dialog/Dialog.stories.d.ts +11 -0
- package/dist/ui/Dialog/index.d.ts +6 -0
- package/dist/ui/Dialog/src/Dialog.d.ts +2 -0
- package/dist/ui/Dialog/src/Dialog.types.d.ts +20 -0
- package/dist/ui/Dialog/src/DialogBody.d.ts +2 -0
- package/dist/ui/Dialog/src/DialogFooter.d.ts +2 -0
- package/dist/ui/Dialog/src/DialogHeader.d.ts +2 -0
- package/dist/ui/Dialog/src/context/DialogContext.d.ts +2 -0
- package/dist/ui/Dialog/src/context/DialogContext.types.d.ts +7 -0
- package/dist/ui/Dialog/src/context/useDialog.d.ts +1 -0
- package/dist/ui/Dialog/test/Dialog.test.d.ts +1 -0
- package/dist/ui/Dnd/OxDnd.stories.d.ts +50 -0
- package/dist/ui/Dnd/index.d.ts +11 -0
- package/dist/ui/Dnd/src/DndContext.d.ts +2 -0
- package/dist/ui/Dnd/src/DndContext.types.d.ts +9 -0
- package/dist/ui/Dnd/src/Draggable.d.ts +2 -0
- package/dist/ui/Dnd/src/Draggable.types.d.ts +7 -0
- package/dist/ui/Dnd/src/Droppable.d.ts +2 -0
- package/dist/ui/Dnd/src/Droppable.types.d.ts +7 -0
- package/dist/ui/Dnd/src/context/DndContext.d.ts +2 -0
- package/dist/ui/Dnd/src/context/DndContext.types.d.ts +9 -0
- package/dist/ui/Dnd/src/context/useDndContext.d.ts +1 -0
- package/dist/ui/Dnd/test/Dnd.test.d.ts +1 -0
- package/dist/ui/Drawer/Drawer.stories.d.ts +53 -0
- package/dist/ui/Drawer/index.d.ts +8 -0
- package/dist/ui/Drawer/src/Drawer.d.ts +2 -0
- package/dist/ui/Drawer/src/Drawer.types.d.ts +26 -0
- package/dist/ui/Drawer/src/DrawerBody.d.ts +2 -0
- package/dist/ui/Drawer/src/DrawerContent.d.ts +2 -0
- package/dist/ui/Drawer/src/DrawerFooter.d.ts +2 -0
- package/dist/ui/Drawer/src/DrawerHeader.d.ts +2 -0
- package/dist/ui/Drawer/src/DrawerTrigger.d.ts +2 -0
- package/dist/ui/Drawer/src/context/DrawerContext.d.ts +2 -0
- package/dist/ui/Drawer/src/context/DrawerContext.types.d.ts +5 -0
- package/dist/ui/Drawer/src/context/useDrawerContext.d.ts +1 -0
- package/dist/ui/Drawer/test/Drawer.test.d.ts +1 -0
- package/dist/ui/Dropdown/Dropdown.stories.d.ts +78 -0
- package/dist/ui/Dropdown/index.d.ts +12 -0
- package/dist/ui/Dropdown/src/Dropdown.d.ts +2 -0
- package/dist/ui/Dropdown/src/Dropdown.types.d.ts +52 -0
- package/dist/ui/Dropdown/src/DropdownBody.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownContent.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownFooter.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownHeader.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownItem.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownLabel.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownSeparator.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownShortcut.d.ts +2 -0
- package/dist/ui/Dropdown/src/DropdownTrigger.d.ts +2 -0
- package/dist/ui/Dropdown/src/context/DropdownContext.d.ts +2 -0
- package/dist/ui/Dropdown/src/context/DropdownContext.types.d.ts +11 -0
- package/dist/ui/Dropdown/src/context/useDropdownContext.d.ts +1 -0
- package/dist/ui/Dropdown/test/Dropdown.test.d.ts +1 -0
- package/dist/ui/EndlessList/EndlessList.stories.d.ts +89 -0
- package/dist/ui/EndlessList/index.d.ts +8 -0
- package/dist/ui/EndlessList/src/EndlessList.d.ts +2 -0
- package/dist/ui/EndlessList/src/EndlessList.types.d.ts +32 -0
- package/dist/ui/EndlessList/src/EndlessListEmpty.d.ts +2 -0
- package/dist/ui/EndlessList/src/EndlessListError.d.ts +2 -0
- package/dist/ui/EndlessList/src/EndlessListItem.d.ts +2 -0
- package/dist/ui/EndlessList/src/EndlessListItems.d.ts +2 -0
- package/dist/ui/EndlessList/src/EndlessListLoader.d.ts +2 -0
- package/dist/ui/EndlessList/src/context/EndlessListContext.d.ts +16 -0
- package/dist/ui/EndlessList/src/context/ItemContext.d.ts +10 -0
- package/dist/ui/EndlessList/src/context/index.d.ts +4 -0
- package/dist/ui/EndlessList/test/EndlessList.test.d.ts +1 -0
- package/dist/ui/HoverCard/HoverCard.stories.d.ts +71 -0
- package/dist/ui/HoverCard/index.d.ts +5 -0
- package/dist/ui/HoverCard/src/HoverCard.d.ts +2 -0
- package/dist/ui/HoverCard/src/HoverCard.types.d.ts +21 -0
- package/dist/ui/HoverCard/src/HoverCardContent.d.ts +2 -0
- package/dist/ui/HoverCard/src/HoverCardTrigger.d.ts +2 -0
- package/dist/ui/HoverCard/src/context/HoverCardContext.d.ts +2 -0
- package/dist/ui/HoverCard/src/context/HoverCardContext.types.d.ts +14 -0
- package/dist/ui/HoverCard/src/context/useHoverCardContext.d.ts +1 -0
- package/dist/ui/HoverCard/test/HoverCard.test.d.ts +1 -0
- package/dist/ui/Icon/Icon.stories.d.ts +52 -0
- package/dist/ui/Icon/index.d.ts +4 -0
- package/dist/ui/Icon/src/Icon.d.ts +2 -0
- package/dist/ui/Icon/src/Icon.types.d.ts +21 -0
- package/dist/ui/Icon/src/IconBox.d.ts +2 -0
- package/dist/ui/Icon/src/constants.d.ts +9 -0
- package/dist/ui/Icon/src/context/IconBoxContext.d.ts +2 -0
- package/dist/ui/Icon/src/context/IconBoxContext.types.d.ts +5 -0
- package/dist/ui/Icon/src/context/useIconBoxContext.d.ts +1 -0
- package/dist/ui/Icon/src/icon.styles.d.ts +3 -0
- package/dist/ui/Icon/test/Icon.test.d.ts +1 -0
- package/dist/ui/Image/Image.stories.d.ts +50 -0
- package/dist/ui/Image/index.d.ts +2 -0
- package/dist/ui/Image/src/Image.d.ts +2 -0
- package/dist/ui/Image/src/Image.types.d.ts +9 -0
- package/dist/ui/Image/test/Image.test.d.ts +1 -0
- package/dist/ui/JsonField/JsonField.stories.d.ts +32 -0
- package/dist/ui/JsonField/index.d.ts +4 -0
- package/dist/ui/JsonField/src/JsonField.d.ts +2 -0
- package/dist/ui/JsonField/src/JsonField.types.d.ts +12 -0
- package/dist/ui/JsonField/src/JsonNode.d.ts +2 -0
- package/dist/ui/JsonField/test/JsonField.test.d.ts +1 -0
- package/dist/ui/Kanban/Kanban.stories.d.ts +49 -0
- package/dist/ui/Kanban/index.d.ts +8 -0
- package/dist/ui/Kanban/src/Kanban.d.ts +2 -0
- package/dist/ui/Kanban/src/Kanban.types.d.ts +30 -0
- package/dist/ui/Kanban/src/KanbanCard.d.ts +2 -0
- package/dist/ui/Kanban/src/KanbanCardHandle.d.ts +2 -0
- package/dist/ui/Kanban/src/KanbanColumn.d.ts +2 -0
- package/dist/ui/Kanban/src/KanbanColumnFooter.d.ts +2 -0
- package/dist/ui/Kanban/src/KanbanColumnHeader.d.ts +2 -0
- package/dist/ui/Kanban/src/context/KanbanCardContext.d.ts +3 -0
- package/dist/ui/Kanban/src/context/KanbanColumnContext.d.ts +3 -0
- package/dist/ui/Kanban/src/context/KanbanContext.d.ts +3 -0
- package/dist/ui/Kanban/src/context/KanbanContext.types.d.ts +23 -0
- package/dist/ui/Kanban/src/context/index.d.ts +4 -0
- package/dist/ui/Kanban/test/Kanban.test.d.ts +1 -0
- package/dist/ui/Link/Link.stories.d.ts +51 -0
- package/dist/ui/Link/index.d.ts +2 -0
- package/dist/ui/Link/src/Link.d.ts +2 -0
- package/dist/ui/Link/src/Link.types.d.ts +7 -0
- package/dist/ui/Link/test/Link.test.d.ts +1 -0
- package/dist/ui/Masonry/Masonry.stories.d.ts +31 -0
- package/dist/ui/Masonry/index.d.ts +2 -0
- package/dist/ui/Masonry/src/Masonry.d.ts +2 -0
- package/dist/ui/Masonry/src/Masonry.types.d.ts +11 -0
- package/dist/ui/Masonry/src/hooks/useMasonry.d.ts +10 -0
- package/dist/ui/Masonry/test/Masonry.test.d.ts +1 -0
- package/dist/ui/Notifications/Notifications.stories.d.ts +70 -0
- package/dist/ui/Notifications/index.d.ts +7 -0
- package/dist/ui/Notifications/src/Notifications.d.ts +2 -0
- package/dist/ui/Notifications/src/Notifications.types.d.ts +50 -0
- package/dist/ui/Notifications/src/NotificationsBody.d.ts +2 -0
- package/dist/ui/Notifications/src/NotificationsButton.d.ts +2 -0
- package/dist/ui/Notifications/src/NotificationsLink.d.ts +2 -0
- package/dist/ui/Notifications/src/NotificationsTrigger.d.ts +2 -0
- package/dist/ui/Notifications/src/context/NotificationsContext.d.ts +2 -0
- package/dist/ui/Notifications/src/context/NotificationsContext.types.d.ts +12 -0
- package/dist/ui/Notifications/src/context/useNotificationsContext.d.ts +1 -0
- package/dist/ui/Notifications/test/Notifications.test.d.ts +1 -0
- package/dist/ui/PageOutline/PageOutline.stories.d.ts +41 -0
- package/dist/ui/PageOutline/index.d.ts +6 -0
- package/dist/ui/PageOutline/src/PageOutline.d.ts +2 -0
- package/dist/ui/PageOutline/src/PageOutline.types.d.ts +17 -0
- package/dist/ui/PageOutline/src/PageOutlineContent.d.ts +2 -0
- package/dist/ui/PageOutline/src/PageOutlineHeader.d.ts +2 -0
- package/dist/ui/PageOutline/src/PageOutlineItem.d.ts +2 -0
- package/dist/ui/PageOutline/src/context/PageOutlineContext.d.ts +11 -0
- package/dist/ui/PageOutline/test/PageOutline.test.d.ts +1 -0
- package/dist/ui/Pagination/Pagination.stories.d.ts +43 -0
- package/dist/ui/Pagination/index.d.ts +8 -0
- package/dist/ui/Pagination/src/Pagination.d.ts +2 -0
- package/dist/ui/Pagination/src/Pagination.types.d.ts +27 -0
- package/dist/ui/Pagination/src/PaginationEllipsis.d.ts +2 -0
- package/dist/ui/Pagination/src/PaginationItem.d.ts +2 -0
- package/dist/ui/Pagination/src/PaginationLink.d.ts +2 -0
- package/dist/ui/Pagination/src/PaginationNext.d.ts +2 -0
- package/dist/ui/Pagination/src/PaginationPrevious.d.ts +2 -0
- package/dist/ui/Pagination/test/Pagination.test.d.ts +1 -0
- package/dist/ui/Popover/Popover.stories.d.ts +66 -0
- package/dist/ui/Popover/index.d.ts +5 -0
- package/dist/ui/Popover/src/Popover.d.ts +2 -0
- package/dist/ui/Popover/src/Popover.types.d.ts +20 -0
- package/dist/ui/Popover/src/PopoverContent.d.ts +2 -0
- package/dist/ui/Popover/src/PopoverTrigger.d.ts +2 -0
- package/dist/ui/Popover/src/context/PopoverContext.d.ts +2 -0
- package/dist/ui/Popover/src/context/PopoverContext.types.d.ts +10 -0
- package/dist/ui/Popover/src/context/usePopoverContext.d.ts +1 -0
- package/dist/ui/Popover/test/Popover.test.d.ts +1 -0
- package/dist/ui/Popper/Popper.stories.d.ts +10 -0
- package/dist/ui/Popper/index.d.ts +9 -0
- package/dist/ui/Popper/src/Popper.d.ts +2 -0
- package/dist/ui/Popper/src/Popper.types.d.ts +27 -0
- package/dist/ui/Popper/src/Popper.utils.d.ts +10 -0
- package/dist/ui/Popper/src/PopperAnchor.d.ts +2 -0
- package/dist/ui/Popper/src/PopperArrow.d.ts +1 -0
- package/dist/ui/Popper/src/PopperContent.d.ts +2 -0
- package/dist/ui/Popper/src/context/PopperContext.d.ts +3 -0
- package/dist/ui/Popper/src/context/PopperContext.types.d.ts +14 -0
- package/dist/ui/Popper/src/context/usePopperContext.d.ts +2 -0
- package/dist/ui/Popper/src/hooks/index.d.ts +2 -0
- package/dist/ui/Popper/src/hooks/usePopperState.d.ts +2 -0
- package/dist/ui/Popper/src/hooks/usePopperState.types.d.ts +19 -0
- package/dist/ui/Popper/test/Popper.test.d.ts +1 -0
- package/dist/ui/Progress/Progress.stories.d.ts +59 -0
- package/dist/ui/Progress/index.d.ts +4 -0
- package/dist/ui/Progress/src/Progress.d.ts +2 -0
- package/dist/ui/Progress/src/Progress.types.d.ts +11 -0
- package/dist/ui/Progress/src/ProgressCircular.d.ts +2 -0
- package/dist/ui/Progress/test/Progress.test.d.ts +1 -0
- package/dist/ui/Progress/test/ProgressCircular.test.d.ts +1 -0
- package/dist/ui/Separator/Separator.stories.d.ts +36 -0
- package/dist/ui/Separator/index.d.ts +2 -0
- package/dist/ui/Separator/src/Separator.d.ts +2 -0
- package/dist/ui/Separator/src/Separator.types.d.ts +5 -0
- package/dist/ui/Separator/test/Separator.test.d.ts +1 -0
- package/dist/ui/Skeleton/Skeleton.stories.d.ts +50 -0
- package/dist/ui/Skeleton/index.d.ts +2 -0
- package/dist/ui/Skeleton/src/Skeleton.d.ts +2 -0
- package/dist/ui/Skeleton/src/Skeleton.types.d.ts +5 -0
- package/dist/ui/Skeleton/test/Skeleton.test.d.ts +1 -0
- package/dist/ui/Slider/Slider.stories.d.ts +79 -0
- package/dist/ui/Slider/index.d.ts +2 -0
- package/dist/ui/Slider/src/Slider.d.ts +2 -0
- package/dist/ui/Slider/src/Slider.types.d.ts +15 -0
- package/dist/ui/Slider/test/Slider.test.d.ts +1 -0
- package/dist/ui/SortableList/SortableList.stories.d.ts +50 -0
- package/dist/ui/SortableList/index.d.ts +5 -0
- package/dist/ui/SortableList/src/SortableList.d.ts +2 -0
- package/dist/ui/SortableList/src/SortableList.types.d.ts +21 -0
- package/dist/ui/SortableList/src/SortableListHandle.d.ts +2 -0
- package/dist/ui/SortableList/src/SortableListItem.d.ts +2 -0
- package/dist/ui/SortableList/src/context/SortableListContext.d.ts +2 -0
- package/dist/ui/SortableList/src/context/SortableListContext.types.d.ts +15 -0
- package/dist/ui/SortableList/src/context/SortableListItemContext.d.ts +2 -0
- package/dist/ui/SortableList/src/context/index.d.ts +4 -0
- package/dist/ui/SortableList/src/context/useSortableListContext.d.ts +2 -0
- package/dist/ui/SortableList/test/SortableList.test.d.ts +1 -0
- package/dist/ui/Spinner/Spinner.stories.d.ts +36 -0
- package/dist/ui/Spinner/index.d.ts +2 -0
- package/dist/ui/Spinner/src/Spinner.d.ts +2 -0
- package/dist/ui/Spinner/src/Spinner.types.d.ts +5 -0
- package/dist/ui/Spinner/test/Spinner.test.d.ts +1 -0
- package/dist/ui/StatusDisplay/StatusDisplay.stories.d.ts +62 -0
- package/dist/ui/StatusDisplay/index.d.ts +2 -0
- package/dist/ui/StatusDisplay/src/StatusDisplay.d.ts +2 -0
- package/dist/ui/StatusDisplay/src/StatusDisplay.types.d.ts +18 -0
- package/dist/ui/StatusDisplay/test/StatusDisplay.test.d.ts +1 -0
- package/dist/ui/Stepper/Stepper.stories.d.ts +11 -0
- package/dist/ui/Stepper/index.d.ts +5 -0
- package/dist/ui/Stepper/src/Stepper.d.ts +2 -0
- package/dist/ui/Stepper/src/Stepper.types.d.ts +29 -0
- package/dist/ui/Stepper/src/StepperActions.d.ts +2 -0
- package/dist/ui/Stepper/src/StepperConnector.d.ts +2 -0
- package/dist/ui/Stepper/src/StepperItem.d.ts +2 -0
- package/dist/ui/Stepper/src/context/StepperContext.d.ts +2 -0
- package/dist/ui/Stepper/src/context/StepperContext.types.d.ts +10 -0
- package/dist/ui/Stepper/src/context/useStepperContext.d.ts +1 -0
- package/dist/ui/Stepper/src/stepperHelper.d.ts +131 -0
- package/dist/ui/Stepper/test/Stepper.test.d.ts +1 -0
- package/dist/ui/Table/Table.stories.d.ts +79 -0
- package/dist/ui/Table/index.d.ts +11 -0
- package/dist/ui/Table/src/Table.d.ts +2 -0
- package/dist/ui/Table/src/Table.types.d.ts +35 -0
- package/dist/ui/Table/src/TableBody.d.ts +2 -0
- package/dist/ui/Table/src/TableCell.d.ts +2 -0
- package/dist/ui/Table/src/TableControls.d.ts +2 -0
- package/dist/ui/Table/src/TableFooter.d.ts +2 -0
- package/dist/ui/Table/src/TableHead.d.ts +2 -0
- package/dist/ui/Table/src/TableHeader.d.ts +2 -0
- package/dist/ui/Table/src/TableRow.d.ts +2 -0
- package/dist/ui/Table/src/TableSkeletonRow.d.ts +4 -0
- package/dist/ui/Table/src/context/TableContext.d.ts +2 -0
- package/dist/ui/Table/src/context/TableContext.types.d.ts +20 -0
- package/dist/ui/Table/src/context/TableSectionContext.d.ts +6 -0
- package/dist/ui/Table/src/context/useTableContext.d.ts +1 -0
- package/dist/ui/Table/test/Table.test.d.ts +1 -0
- package/dist/ui/Tabs/Tab.stories.d.ts +50 -0
- package/dist/ui/Tabs/index.d.ts +6 -0
- package/dist/ui/Tabs/src/Tabs.d.ts +2 -0
- package/dist/ui/Tabs/src/Tabs.types.d.ts +19 -0
- package/dist/ui/Tabs/src/TabsContent.d.ts +2 -0
- package/dist/ui/Tabs/src/TabsList.d.ts +2 -0
- package/dist/ui/Tabs/src/TabsTrigger.d.ts +2 -0
- package/dist/ui/Tabs/src/context/TabContext.d.ts +2 -0
- package/dist/ui/Tabs/src/context/TabContext.types.d.ts +5 -0
- package/dist/ui/Tabs/src/context/useTabContext.d.ts +1 -0
- package/dist/ui/Tabs/src/tabs.styles.d.ts +9 -0
- package/dist/ui/Tabs/test/Tabs.test.d.ts +1 -0
- package/dist/ui/Tag/Tag.stories.d.ts +34 -0
- package/dist/ui/Tag/index.d.ts +2 -0
- package/dist/ui/Tag/src/Tag.d.ts +2 -0
- package/dist/ui/Tag/src/Tag.types.d.ts +6 -0
- package/dist/ui/Tag/test/Tag.test.d.ts +1 -0
- package/dist/ui/ThemeSwitcher/ThemeSwitcher.stories.d.ts +35 -0
- package/dist/ui/ThemeSwitcher/index.d.ts +2 -0
- package/dist/ui/ThemeSwitcher/src/ThemeSwitcher.d.ts +2 -0
- package/dist/ui/ThemeSwitcher/src/ThemeSwitcher.types.d.ts +8 -0
- package/dist/ui/ThemeSwitcher/test/ThemeSwitcher.test.d.ts +1 -0
- package/dist/ui/Timeline/Timeline.stories.d.ts +10 -0
- package/dist/ui/Timeline/index.d.ts +4 -0
- package/dist/ui/Timeline/src/Timeline.d.ts +2 -0
- package/dist/ui/Timeline/src/Timeline.types.d.ts +16 -0
- package/dist/ui/Timeline/src/TimelineItem.d.ts +2 -0
- package/dist/ui/Timeline/test/Timeline.test.d.ts +1 -0
- package/dist/ui/Toast/Toast.stories.d.ts +12 -0
- package/dist/ui/Toast/index.d.ts +5 -0
- package/dist/ui/Toast/src/Toast.d.ts +2 -0
- package/dist/ui/Toast/src/Toast.types.d.ts +29 -0
- package/dist/ui/Toast/src/ToastItem.d.ts +2 -0
- package/dist/ui/Toast/src/context/ToastContext.d.ts +2 -0
- package/dist/ui/Toast/src/context/ToastContext.types.d.ts +28 -0
- package/dist/ui/Toast/src/context/ToastProvider.d.ts +2 -0
- package/dist/ui/Toast/src/context/useToast.d.ts +2 -0
- package/dist/ui/Toast/test/Toast.test.d.ts +1 -0
- package/dist/ui/Toggle/Toggle.stories.d.ts +53 -0
- package/dist/ui/Toggle/index.d.ts +4 -0
- package/dist/ui/Toggle/src/Toggle.d.ts +2 -0
- package/dist/ui/Toggle/src/Toggle.types.d.ts +28 -0
- package/dist/ui/Toggle/src/ToggleGroup.d.ts +2 -0
- package/dist/ui/Toggle/src/context/ToggleGroupContext.d.ts +2 -0
- package/dist/ui/Toggle/src/context/ToggleGroupContext.types.d.ts +8 -0
- package/dist/ui/Toggle/src/context/useToggleGroup.d.ts +1 -0
- package/dist/ui/Toggle/test/Toggle.test.d.ts +1 -0
- package/dist/ui/Tooltip/Tooltip.stories.d.ts +9 -0
- package/dist/ui/Tooltip/index.d.ts +5 -0
- package/dist/ui/Tooltip/src/Tooltip.d.ts +2 -0
- package/dist/ui/Tooltip/src/Tooltip.types.d.ts +14 -0
- package/dist/ui/Tooltip/src/TooltipProvider.d.ts +2 -0
- package/dist/ui/Tooltip/src/context/TooltipContext.d.ts +2 -0
- package/dist/ui/Tooltip/src/context/TooltipContext.types.d.ts +11 -0
- package/dist/ui/Tooltip/src/context/useOxTooltipContext.d.ts +2 -0
- package/dist/ui/Tooltip/test/Tooltip.test.d.ts +1 -0
- package/dist/ui/index.d.ts +43 -0
- package/dist/ui.css +1 -1
- package/dist/utils/builder/index.d.ts +3 -0
- package/dist/utils/builder/src/buildMailTo.d.ts +2 -0
- package/dist/utils/builder/src/buildUrl.d.ts +2 -0
- package/dist/utils/builder/src/builder.types.d.ts +10 -0
- package/dist/utils/builder/test/buildMailTo.test.d.ts +1 -0
- package/dist/utils/builder/test/buildUrl.test.d.ts +1 -0
- package/dist/utils/cn/index.d.ts +1 -0
- package/dist/utils/cn/src/cn.d.ts +2 -0
- package/dist/utils/cn/test/cn.test.d.ts +1 -0
- package/dist/utils/copyToClipboard/index.d.ts +1 -0
- package/dist/utils/copyToClipboard/src/copyToClipboard.d.ts +1 -0
- package/dist/utils/copyToClipboard/test/copyToClipboard.test.d.ts +1 -0
- package/dist/utils/createId/index.d.ts +2 -0
- package/dist/utils/createId/src/createId.d.ts +3 -0
- package/dist/utils/createId/src/createId.types.d.ts +10 -0
- package/dist/utils/createId/test/createId.test.d.ts +1 -0
- package/dist/utils/errorHelper/index.d.ts +2 -0
- package/dist/utils/errorHelper/src/errorHelper.d.ts +4 -0
- package/dist/utils/errorHelper/src/errorHelper.types.d.ts +17 -0
- package/dist/utils/errorHelper/test/errorHelper.test.d.ts +1 -0
- package/dist/utils/formatter/index.d.ts +6 -0
- package/dist/utils/formatter/src/formatBytes.d.ts +2 -0
- package/dist/utils/formatter/src/formatCurrency.d.ts +2 -0
- package/dist/utils/formatter/src/formatDate.d.ts +2 -0
- package/dist/utils/formatter/src/formatNumber.d.ts +2 -0
- package/dist/utils/formatter/src/formatTimestamp.d.ts +1 -0
- package/dist/utils/formatter/src/formatter.types.d.ts +18 -0
- package/dist/utils/formatter/test/formatBytes.test.d.ts +1 -0
- package/dist/utils/formatter/test/formatCurrency.test.d.ts +1 -0
- package/dist/utils/formatter/test/formatDate.test.d.ts +1 -0
- package/dist/utils/formatter/test/formatNumber.test.d.ts +1 -0
- package/dist/utils/formatter/test/formatTimestamp.test.d.ts +1 -0
- package/dist/utils/hasDataSlot/index.d.ts +1 -0
- package/dist/utils/hasDataSlot/src/hasDataSlot.d.ts +3 -0
- package/dist/utils/hasDataSlot/test/hasDataSlot.test.d.ts +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/scrollToElement/index.d.ts +2 -0
- package/dist/utils/scrollToElement/src/scrollToElement.d.ts +2 -0
- package/dist/utils/scrollToElement/src/scrollToElement.types.d.ts +7 -0
- package/dist/utils/scrollToElement/test/scrollToElement.test.d.ts +1 -0
- package/dist/utils/utils.cjs.js +1 -1
- package/dist/utils/utils.es.js +6 -6
- package/dist/utils/validation/index.d.ts +4 -0
- package/dist/utils/validation/src/constants.d.ts +57 -0
- package/dist/utils/validation/src/validateEmail.d.ts +2 -0
- package/dist/utils/validation/src/validatePhone.d.ts +2 -0
- package/dist/utils/validation/src/validateUrl.d.ts +2 -0
- package/dist/utils/validation/src/validation.types.d.ts +13 -0
- package/dist/utils/validation/test/validateEmail.test.d.ts +1 -0
- package/dist/utils/validation/test/validatePhone.test.d.ts +1 -0
- package/dist/utils/validation/test/validateUrl.test.d.ts +1 -0
- package/package.json +62 -37
- package/dist/animations.css +0 -217
- package/dist/chunks/hooks-UiKjBucd.js +0 -47
- package/dist/chunks/query-CIsrxNi-.js +0 -10
- package/dist/chunks/query-D2S9cFBP.js +0 -14
- package/dist/chunks/ui-C-FlF6Nz.js +0 -13291
- package/dist/chunks/ui-DtOB5MIE.js +0 -20
- package/dist/contexts.d.ts +0 -117
- package/dist/hooks.d.ts +0 -192
- package/dist/layouts.d.ts +0 -440
- package/dist/pages.d.ts +0 -15
- package/dist/routes.d.ts +0 -28
- package/dist/shared.css +0 -129
- package/dist/utils.d.ts +0 -133
package/AI-README.md
ADDED
|
@@ -0,0 +1,4942 @@
|
|
|
1
|
+
# Onyx Component Library - AI Reference Guide
|
|
2
|
+
|
|
3
|
+
**Version:** 3.0.0 • **Updated:** 2025-12-16 • **Built from:** bfdd6f72
|
|
4
|
+
|
|
5
|
+
> This comprehensive reference guide provides detailed information about all components, hooks, contexts, utilities, and patterns in the Onyx component library. Designed for AI assistants and developers to quickly understand component APIs and usage.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install Onyx and its peer dependencies:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @noxickon/onyx lucide-react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Peer Dependencies
|
|
16
|
+
|
|
17
|
+
**Required:**
|
|
18
|
+
|
|
19
|
+
- `react` ^19.0.0
|
|
20
|
+
- `react-dom` ^19.0.0
|
|
21
|
+
- `lucide-react` ^0.500.0
|
|
22
|
+
|
|
23
|
+
**Optional:**
|
|
24
|
+
|
|
25
|
+
- `@apollo/client` ^4.0.0 (if using Apollo features)
|
|
26
|
+
- `graphql` ^16.0.0 (if using GraphQL features)
|
|
27
|
+
- `@mdi/js` ^7.4.47 (alternative icon library)
|
|
28
|
+
- `@mdi/react` ^1.6.1 (alternative icon library)
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
Import Onyx components and styles:
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { OxButton, OxCard } from '@noxickon/onyx';
|
|
36
|
+
import { Check } from 'lucide-react';
|
|
37
|
+
import '@noxickon/onyx/styles';
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
return (
|
|
41
|
+
<OxButton variant="primary">
|
|
42
|
+
<OxIcon icon={Check} />
|
|
43
|
+
Click Me
|
|
44
|
+
</OxButton>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Package Exports
|
|
50
|
+
|
|
51
|
+
Onyx uses a multi-entry build system for optimal tree-shaking:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// UI Components (main export)
|
|
55
|
+
import { OxButton, OxCard, OxDialog } from '@noxickon/onyx';
|
|
56
|
+
|
|
57
|
+
// Forms
|
|
58
|
+
import { OxCheckbox, OxInput, OxSelect } from '@noxickon/onyx/forms';
|
|
59
|
+
|
|
60
|
+
// Typography
|
|
61
|
+
import { OxHeading, OxLabel } from '@noxickon/onyx/typography';
|
|
62
|
+
|
|
63
|
+
// Contexts
|
|
64
|
+
import { OxAuthProvider, useOxAuth } from '@noxickon/onyx/contexts';
|
|
65
|
+
|
|
66
|
+
// Hooks
|
|
67
|
+
import { useForm, useFilterQuery, useFormState } from '@noxickon/onyx/hooks';
|
|
68
|
+
|
|
69
|
+
// Layouts
|
|
70
|
+
import { OxAppLayout, OxSidebar, OxMainContent } from '@noxickon/onyx/layouts';
|
|
71
|
+
|
|
72
|
+
// Route Guards
|
|
73
|
+
import { OxProtectedRoute, OxGuestRoute, OxRoleRoute } from '@noxickon/onyx/routes';
|
|
74
|
+
|
|
75
|
+
// Utilities
|
|
76
|
+
import { cn, buildUrl, validateEmail } from '@noxickon/onyx/utils';
|
|
77
|
+
|
|
78
|
+
// Legacy Components (deprecated)
|
|
79
|
+
import { OxFloating, useVirtualList } from '@noxickon/onyx/legacy';
|
|
80
|
+
|
|
81
|
+
// Styles
|
|
82
|
+
import '@noxickon/onyx/styles'; // Component styles (required)
|
|
83
|
+
import '@noxickon/onyx/shared'; // Shared utility classes (optional)
|
|
84
|
+
import '@noxickon/onyx/animations'; // Animations (optional)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# UI Components (43)
|
|
90
|
+
|
|
91
|
+
## OxAccordion
|
|
92
|
+
|
|
93
|
+
Collapsible accordion component with single or multiple open items.
|
|
94
|
+
|
|
95
|
+
**Pattern**: Compound Component
|
|
96
|
+
**Sub-components**: `Content`, `Item`, `Trigger`
|
|
97
|
+
|
|
98
|
+
### Props
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
interface OxAccordionProps extends ComponentPropsWithRef<'div'> {
|
|
102
|
+
children: ReactNode;
|
|
103
|
+
defaultValue?: string | string[]; // Default open accordion items
|
|
104
|
+
onValueChange?: (value: string | string[]) => void;
|
|
105
|
+
type?: 'single' | 'multiple'; // Single or multiple items can be open
|
|
106
|
+
value?: string | string[]; // Controlled value
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface OxAccordionItemProps extends ComponentPropsWithRef<'div'> {
|
|
110
|
+
children: ReactNode;
|
|
111
|
+
disabled?: boolean;
|
|
112
|
+
value: string; // Required - Unique identifier for the item
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface OxAccordionTriggerProps extends ComponentPropsWithRef<'button'> {}
|
|
116
|
+
interface OxAccordionContentProps extends ComponentPropsWithRef<'div'> {}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Example
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<OxAccordion type="multiple" defaultValue={['item-1']}>
|
|
123
|
+
<OxAccordion.Item value="item-1">
|
|
124
|
+
<OxAccordion.Trigger>Is it accessible?</OxAccordion.Trigger>
|
|
125
|
+
<OxAccordion.Content>Yes. It adheres to the WAI-ARIA design pattern.</OxAccordion.Content>
|
|
126
|
+
</OxAccordion.Item>
|
|
127
|
+
</OxAccordion>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## OxAlert
|
|
133
|
+
|
|
134
|
+
Alert component with different types and compound pattern.
|
|
135
|
+
|
|
136
|
+
**Pattern**: Compound Component
|
|
137
|
+
**Sub-components**: `Title`, `Description`
|
|
138
|
+
|
|
139
|
+
### Props
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
interface OxAlertProps extends ComponentPropsWithRef<'div'> {
|
|
143
|
+
children: ReactNode;
|
|
144
|
+
variant?: 'default' | 'destructive' | 'success' | 'warning' | 'info';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface OxAlertTitleProps extends ComponentPropsWithRef<'div'> {}
|
|
148
|
+
interface OxAlertDescriptionProps extends ComponentPropsWithRef<'div'> {}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Example
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<OxAlert variant="success">
|
|
155
|
+
<OxAlert.Title>Success</OxAlert.Title>
|
|
156
|
+
<OxAlert.Description>Your changes have been saved.</OxAlert.Description>
|
|
157
|
+
</OxAlert>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## OxAspectRatio
|
|
163
|
+
|
|
164
|
+
Container that maintains a specific aspect ratio for its content.
|
|
165
|
+
|
|
166
|
+
**Pattern**: Compound Component
|
|
167
|
+
**Sub-components**: `Fallback`, `Image`, `Video`
|
|
168
|
+
|
|
169
|
+
### Props
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
interface OxAspectRatioProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
173
|
+
children: ReactNode;
|
|
174
|
+
isLoading?: boolean;
|
|
175
|
+
ratio: number; // Width/height ratio (e.g., 16/9 = 1.78)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface OxAspectRatioImageProps {
|
|
179
|
+
alt: string;
|
|
180
|
+
className?: string;
|
|
181
|
+
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
|
182
|
+
loading?: 'eager' | 'lazy';
|
|
183
|
+
objectFit?: 'contain' | 'cover' | 'fill';
|
|
184
|
+
referrerPolicy?: ReferrerPolicy;
|
|
185
|
+
src: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
interface OxAspectRatioVideoProps {
|
|
189
|
+
alt?: string;
|
|
190
|
+
autoPlay?: boolean;
|
|
191
|
+
className?: string;
|
|
192
|
+
controls?: boolean;
|
|
193
|
+
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
|
194
|
+
loop?: boolean;
|
|
195
|
+
muted?: boolean;
|
|
196
|
+
objectFit?: 'contain' | 'cover' | 'fill';
|
|
197
|
+
playsInline?: boolean;
|
|
198
|
+
src: string;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
interface OxAspectRatioFallbackProps extends ComponentPropsWithRef<'div'> {
|
|
202
|
+
children: ReactNode;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Usage
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<OxAspectRatio ratio={16 / 9}>
|
|
210
|
+
<OxAspectRatio.Image src="/image.jpg" alt="Example" objectFit="cover" />
|
|
211
|
+
<OxAspectRatio.Fallback>Loading...</OxAspectRatio.Fallback>
|
|
212
|
+
</OxAspectRatio>
|
|
213
|
+
|
|
214
|
+
<OxAspectRatio ratio={4 / 3}>
|
|
215
|
+
<OxAspectRatio.Video src="/video.mp4" controls autoPlay muted />
|
|
216
|
+
</OxAspectRatio>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## OxAvatar
|
|
222
|
+
|
|
223
|
+
Avatar component with status indicators and loading states.
|
|
224
|
+
|
|
225
|
+
**Pattern**: Simple Component
|
|
226
|
+
|
|
227
|
+
### Props
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
interface OxAvatarProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
231
|
+
alt?: string;
|
|
232
|
+
fallback?: string | ReactNode; // Shown when image fails to load
|
|
233
|
+
isLoading?: boolean; // Shows loading state
|
|
234
|
+
shape?: 'rectangle' | 'circle';
|
|
235
|
+
showStatus?: boolean; // Display status indicator
|
|
236
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
|
|
237
|
+
skeleton?: boolean; // Shows skeleton loader
|
|
238
|
+
src?: string;
|
|
239
|
+
status?: 'online' | 'offline' | 'away' | 'busy';
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Example
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
<OxAvatar
|
|
247
|
+
src="/avatar.jpg"
|
|
248
|
+
alt="User Name"
|
|
249
|
+
size="lg"
|
|
250
|
+
shape="circle"
|
|
251
|
+
showStatus
|
|
252
|
+
status="online"
|
|
253
|
+
fallback="UN"
|
|
254
|
+
/>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## OxBadge
|
|
260
|
+
|
|
261
|
+
Notification badge for displaying counts or status indicators on UI elements. Designed for use with ghost buttons and icons.
|
|
262
|
+
|
|
263
|
+
**Pattern**: Simple Component
|
|
264
|
+
|
|
265
|
+
> **Note:** Badges are optimized for ghost buttons and icons. For colored backgrounds, use the `bgColor` prop for custom solutions. Count badges use subtle backgrounds; dot badges use solid colors for status indicators.
|
|
266
|
+
|
|
267
|
+
### Props
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
interface OxBadgeProps extends ComponentPropsWithRef<'span'> {
|
|
271
|
+
children?: ReactNode;
|
|
272
|
+
count?: number;
|
|
273
|
+
max?: number;
|
|
274
|
+
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
275
|
+
showZero?: boolean;
|
|
276
|
+
type?: 'dot' | 'count';
|
|
277
|
+
variant?: ColorStyleName;
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Example
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
{/* Standalone count badge */}
|
|
285
|
+
<OxBadge count={5} />
|
|
286
|
+
|
|
287
|
+
{/* Badge on button */}
|
|
288
|
+
<OxBadge count={12} position="top-right">
|
|
289
|
+
<OxButton variant="ghost" aria-label="Messages">
|
|
290
|
+
<OxIcon icon={Mail} />
|
|
291
|
+
</OxButton>
|
|
292
|
+
</OxBadge>
|
|
293
|
+
|
|
294
|
+
{/* Overflow handling */}
|
|
295
|
+
<OxBadge count={100} max={99} /> {/* Shows "99+" */}
|
|
296
|
+
|
|
297
|
+
{/* Dot badge for status */}
|
|
298
|
+
<OxBadge type="dot" variant="green" position="top-right">
|
|
299
|
+
<OxIcon icon={Bell} className="size-6" />
|
|
300
|
+
</OxBadge>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## OxTag
|
|
306
|
+
|
|
307
|
+
Inline label component for categorization and metadata display.
|
|
308
|
+
|
|
309
|
+
**Pattern**: Simple Component
|
|
310
|
+
|
|
311
|
+
### Props
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
interface OxTagProps extends ComponentPropsWithRef<'span'> {
|
|
315
|
+
children: ReactNode; // Tag content
|
|
316
|
+
color?: ColorStyleName; // Predefined theme color
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Example
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
{/* Basic tags */}
|
|
324
|
+
<OxTag color="blue">New</OxTag>
|
|
325
|
+
<OxTag color="green">Active</OxTag>
|
|
326
|
+
<OxTag color="purple">Featured</OxTag>
|
|
327
|
+
|
|
328
|
+
{/* In context */}
|
|
329
|
+
<div className="flex gap-2">
|
|
330
|
+
<OxTag color="purple">React</OxTag>
|
|
331
|
+
<OxTag color="blue">TypeScript</OxTag>
|
|
332
|
+
<OxTag color="green">Tailwind</OxTag>
|
|
333
|
+
</div>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## OxBreadcrumb
|
|
339
|
+
|
|
340
|
+
Breadcrumb navigation with asChild pattern support.
|
|
341
|
+
|
|
342
|
+
**Pattern**: Compound Component
|
|
343
|
+
**Sub-components**: `Ellipsis`, `Item`, `Link`, `Page`, `Separator`
|
|
344
|
+
|
|
345
|
+
### Props
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
interface OxBreadcrumbProps extends ComponentPropsWithRef<'ol'> {
|
|
349
|
+
children: ReactNode;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface OxBreadcrumbItemProps extends ComponentPropsWithRef<'li'> {
|
|
353
|
+
children: ReactNode;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
interface OxBreadcrumbLinkProps extends ComponentPropsWithRef<'a'> {
|
|
357
|
+
asChild?: boolean; // Renders as child component (e.g., Next.js Link)
|
|
358
|
+
children: ReactNode;
|
|
359
|
+
current?: boolean; // Marks as current page
|
|
360
|
+
href?: string;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
interface OxBreadcrumbPageProps extends ComponentPropsWithRef<'span'> {
|
|
364
|
+
children: ReactNode;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
interface OxBreadcrumbSeparatorProps extends ComponentPropsWithRef<'li'> {}
|
|
368
|
+
interface OxBreadcrumbEllipsisProps extends ComponentPropsWithRef<'span'> {}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Example
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
<OxBreadcrumb>
|
|
375
|
+
<OxBreadcrumb.Item>
|
|
376
|
+
<OxBreadcrumb.Link href="/">Home</OxBreadcrumb.Link>
|
|
377
|
+
</OxBreadcrumb.Item>
|
|
378
|
+
<OxBreadcrumb.Separator />
|
|
379
|
+
<OxBreadcrumb.Item>
|
|
380
|
+
<OxBreadcrumb.Link href="/products">Products</OxBreadcrumb.Link>
|
|
381
|
+
</OxBreadcrumb.Item>
|
|
382
|
+
<OxBreadcrumb.Separator />
|
|
383
|
+
<OxBreadcrumb.Item>
|
|
384
|
+
<OxBreadcrumb.Page>Current Page</OxBreadcrumb.Page>
|
|
385
|
+
</OxBreadcrumb.Item>
|
|
386
|
+
</OxBreadcrumb>;
|
|
387
|
+
|
|
388
|
+
{
|
|
389
|
+
/* Using asChild with Next.js Link */
|
|
390
|
+
}
|
|
391
|
+
<OxBreadcrumb.Link asChild>
|
|
392
|
+
<Link to="/dashboard">Dashboard</Link>
|
|
393
|
+
</OxBreadcrumb.Link>;
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## OxButton
|
|
399
|
+
|
|
400
|
+
Button component with variants, sizes, and loading states.
|
|
401
|
+
|
|
402
|
+
**Pattern**: Simple Component
|
|
403
|
+
|
|
404
|
+
> **Note:** For links styled as buttons, use `OxButton` with `asChild` prop. Use `OxLink` only for text-based links with underline.
|
|
405
|
+
|
|
406
|
+
### Props
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
interface OxButtonProps extends ComponentPropsWithRef<'button'> {
|
|
410
|
+
asChild?: boolean; // Renders as child element (useful for Link components)
|
|
411
|
+
fullWidth?: boolean; // Expands to full container width
|
|
412
|
+
isLoading?: boolean; // Shows spinner, disables interaction
|
|
413
|
+
size?: 'sm' | 'md' | 'lg';
|
|
414
|
+
variant?: 'gradient' | 'primary' | 'accent' | 'default' | 'secondary' | 'ghost' | 'danger';
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Example
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
<OxButton variant="primary" size="md" isLoading>
|
|
422
|
+
Save Changes
|
|
423
|
+
</OxButton>;
|
|
424
|
+
|
|
425
|
+
{
|
|
426
|
+
/* Render as Link while keeping button styling */
|
|
427
|
+
}
|
|
428
|
+
<OxButton asChild variant="primary">
|
|
429
|
+
<Link to="/dashboard">Dashboard</Link>
|
|
430
|
+
</OxButton>;
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## OxCalendar
|
|
436
|
+
|
|
437
|
+
Date picker component.
|
|
438
|
+
|
|
439
|
+
**Pattern**: Simple Component
|
|
440
|
+
|
|
441
|
+
### Props
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
interface OxCalendarProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'> {
|
|
445
|
+
onChange?: (value: string) => void;
|
|
446
|
+
value?: string; // Date string
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Example
|
|
451
|
+
|
|
452
|
+
```tsx
|
|
453
|
+
<OxCalendar value="2024-01-15" onChange={(date) => console.log(date)} />
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## OxCard
|
|
459
|
+
|
|
460
|
+
Card component with hover effects and loading states.
|
|
461
|
+
|
|
462
|
+
**Pattern**: Compound Component
|
|
463
|
+
**Sub-components**: `Action`, `Content`, `Footer`, `Header`
|
|
464
|
+
|
|
465
|
+
### Props
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
interface OxCardProps extends ComponentPropsWithRef<'div'> {
|
|
469
|
+
children: ReactNode;
|
|
470
|
+
color?: Exclude<ColorStyleName, 'default'>; // Card accent color
|
|
471
|
+
equalHeight?: boolean; // Makes card take full height
|
|
472
|
+
hoverable?: boolean; // Enables hover effects
|
|
473
|
+
hoverVariant?: 'none' | 'lift' | 'border';
|
|
474
|
+
isLoading?: boolean; // Shows loading state
|
|
475
|
+
padding?: 'none' | 'sm' | 'md';
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
interface OxCardContentProps extends ComponentPropsWithRef<'div'> {
|
|
479
|
+
children: ReactNode;
|
|
480
|
+
flexGrow?: boolean; // Makes content grow to fill space
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
interface OxCardFooterProps extends ComponentPropsWithRef<'div'> {
|
|
484
|
+
children: ReactNode;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
interface OxCardHeaderProps extends ComponentPropsWithRef<'div'> {}
|
|
488
|
+
interface OxCardActionProps extends ComponentPropsWithRef<'div'> {}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Example
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
<OxCard hoverable hoverVariant="lift" color="blue" isLoading={false}>
|
|
495
|
+
<OxCard.Header>Card Title</OxCard.Header>
|
|
496
|
+
<OxCard.Content flexGrow>Main content here</OxCard.Content>
|
|
497
|
+
<OxCard.Footer>Footer content</OxCard.Footer>
|
|
498
|
+
<OxCard.Action>
|
|
499
|
+
<button>Action</button>
|
|
500
|
+
</OxCard.Action>
|
|
501
|
+
</OxCard>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## OxDescriptionList (beta)
|
|
507
|
+
|
|
508
|
+
Semantic description list component.
|
|
509
|
+
|
|
510
|
+
**Pattern**: Compound Component
|
|
511
|
+
**Sub-components**: `Term`, `Value`
|
|
512
|
+
|
|
513
|
+
### Props
|
|
514
|
+
|
|
515
|
+
```tsx
|
|
516
|
+
interface OxDescriptionListProps extends ComponentPropsWithRef<'dl'> {
|
|
517
|
+
children: ReactNode;
|
|
518
|
+
outline?: boolean; // Adds border outline
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
interface OxDescriptionListTermProps extends ComponentPropsWithRef<'dt'> {
|
|
522
|
+
children: ReactNode;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
interface OxDescriptionListValueProps extends ComponentPropsWithRef<'dd'> {
|
|
526
|
+
children?: ReactNode;
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Example
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
<OxDescriptionList outline>
|
|
534
|
+
<OxDescriptionList.Term>Name</OxDescriptionList.Term>
|
|
535
|
+
<OxDescriptionList.Value>John Doe</OxDescriptionList.Value>
|
|
536
|
+
<OxDescriptionList.Term>Email</OxDescriptionList.Term>
|
|
537
|
+
<OxDescriptionList.Value>john@example.com</OxDescriptionList.Value>
|
|
538
|
+
</OxDescriptionList>
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## OxDialog
|
|
544
|
+
|
|
545
|
+
Modal dialog component.
|
|
546
|
+
|
|
547
|
+
**Pattern**: Compound Component
|
|
548
|
+
**Sub-components**: `Body`, `Footer`, `Header`
|
|
549
|
+
|
|
550
|
+
### Props
|
|
551
|
+
|
|
552
|
+
```tsx
|
|
553
|
+
interface OxDialogProps extends ComponentPropsWithoutRef<'dialog'> {
|
|
554
|
+
children: ReactNode;
|
|
555
|
+
closeOnOutsideClick?: boolean; // Closes on backdrop click
|
|
556
|
+
isOpen: boolean; // Required - Controls visibility
|
|
557
|
+
onClose: () => void; // Required - Close callback
|
|
558
|
+
persistent?: boolean; // Prevents closing on outside click/escape
|
|
559
|
+
showCloseButton?: boolean;
|
|
560
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
interface OxDialogHeaderProps extends ComponentPropsWithRef<'div'> {
|
|
564
|
+
children: ReactNode;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
interface OxDialogBodyProps extends ComponentPropsWithRef<'div'> {
|
|
568
|
+
children: ReactNode;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
interface OxDialogFooterProps extends ComponentPropsWithRef<'div'> {
|
|
572
|
+
children: ReactNode;
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Example
|
|
577
|
+
|
|
578
|
+
```tsx
|
|
579
|
+
<OxDialog isOpen={open} onClose={() => setOpen(false)} size="md">
|
|
580
|
+
<OxDialog.Header>Confirm Action</OxDialog.Header>
|
|
581
|
+
<OxDialog.Body>Are you sure you want to continue?</OxDialog.Body>
|
|
582
|
+
<OxDialog.Footer>
|
|
583
|
+
<OxButton onClick={() => setOpen(false)}>Cancel</OxButton>
|
|
584
|
+
<OxButton variant="primary">Confirm</OxButton>
|
|
585
|
+
</OxDialog.Footer>
|
|
586
|
+
</OxDialog>
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## OxDndContext
|
|
592
|
+
|
|
593
|
+
Drag and drop context provider using @dnd-kit/core.
|
|
594
|
+
|
|
595
|
+
**Pattern**: Compound Component (Context-based)
|
|
596
|
+
**Sub-components**: `Draggable`, `Droppable`
|
|
597
|
+
**Additional Exports**: `useDndContext`
|
|
598
|
+
|
|
599
|
+
### Props
|
|
600
|
+
|
|
601
|
+
```tsx
|
|
602
|
+
interface DndDragEvent {
|
|
603
|
+
active: Active; // @dnd-kit Active type
|
|
604
|
+
over: Over | null; // @dnd-kit Over type
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
interface OxDndContextProps {
|
|
608
|
+
children: ReactNode;
|
|
609
|
+
onDragCancel?: (event: DndDragEvent) => void;
|
|
610
|
+
onDragEnd?: (event: DndDragEvent) => void;
|
|
611
|
+
onDragOver?: (event: DndDragEvent) => void;
|
|
612
|
+
onDragStart?: (event: DndDragEvent) => void;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
interface OxDraggableProps extends Omit<ComponentPropsWithRef<'div'>, 'id'> {
|
|
616
|
+
children: ReactNode;
|
|
617
|
+
data?: Record<string, unknown>; // Custom data passed to drag events
|
|
618
|
+
disabled?: boolean;
|
|
619
|
+
id?: string;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
interface OxDroppableProps extends Omit<ComponentPropsWithRef<'div'>, 'id'> {
|
|
623
|
+
children: ReactNode;
|
|
624
|
+
data?: Record<string, unknown>; // Custom data for drop zone identification
|
|
625
|
+
disabled?: boolean;
|
|
626
|
+
id?: string;
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Usage
|
|
631
|
+
|
|
632
|
+
```tsx
|
|
633
|
+
<OxDndContext
|
|
634
|
+
onDragEnd={({ active, over }) => {
|
|
635
|
+
if (over) {
|
|
636
|
+
console.log(`Dropped ${active.id} onto ${over.id}`);
|
|
637
|
+
}
|
|
638
|
+
}}
|
|
639
|
+
>
|
|
640
|
+
<OxDndContext.Droppable id="drop-zone">
|
|
641
|
+
<OxDndContext.Draggable id="item-1">
|
|
642
|
+
Drag me
|
|
643
|
+
</OxDndContext.Draggable>
|
|
644
|
+
</OxDndContext.Droppable>
|
|
645
|
+
</OxDndContext>
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
**Note**: For simple reordering within a list, prefer `OxSortableList`. Use `OxDndContext` for complex drag-and-drop scenarios across different containers.
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
## OxDrawer
|
|
653
|
+
|
|
654
|
+
Slide-out drawer component.
|
|
655
|
+
|
|
656
|
+
**Pattern**: Compound Component
|
|
657
|
+
**Sub-components**: `Body`, `Content`, `Footer`, `Header`, `Trigger`
|
|
658
|
+
|
|
659
|
+
### Props
|
|
660
|
+
|
|
661
|
+
```tsx
|
|
662
|
+
interface OxDrawerProps {
|
|
663
|
+
children: ReactNode;
|
|
664
|
+
defaultOpen?: boolean;
|
|
665
|
+
onOpenChange?: (open: boolean) => void;
|
|
666
|
+
open?: boolean; // Controlled open state
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
interface OxDrawerTriggerProps extends ComponentPropsWithRef<'button'> {
|
|
670
|
+
children: ReactNode;
|
|
671
|
+
disabled?: boolean;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
interface OxDrawerContentProps extends ComponentPropsWithRef<'div'> {
|
|
675
|
+
children: ReactNode;
|
|
676
|
+
closeOnOutsideClick?: boolean;
|
|
677
|
+
side?: 'left' | 'right' | 'top' | 'bottom'; // Slide-in direction
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
interface OxDrawerHeaderProps extends ComponentPropsWithRef<'div'> {
|
|
681
|
+
children: ReactNode;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
interface OxDrawerBodyProps extends ComponentPropsWithRef<'div'> {
|
|
685
|
+
children: ReactNode;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
interface OxDrawerFooterProps extends ComponentPropsWithRef<'div'> {
|
|
689
|
+
children: ReactNode;
|
|
690
|
+
}
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### Example
|
|
694
|
+
|
|
695
|
+
```tsx
|
|
696
|
+
<OxDrawer>
|
|
697
|
+
<OxDrawer.Trigger>Open Drawer</OxDrawer.Trigger>
|
|
698
|
+
<OxDrawer.Content side="right">
|
|
699
|
+
<OxDrawer.Header>Drawer Title</OxDrawer.Header>
|
|
700
|
+
<OxDrawer.Body>Drawer content</OxDrawer.Body>
|
|
701
|
+
<OxDrawer.Footer>Footer</OxDrawer.Footer>
|
|
702
|
+
</OxDrawer.Content>
|
|
703
|
+
</OxDrawer>
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## OxDropdown
|
|
709
|
+
|
|
710
|
+
Dropdown menu with extensive sub-components.
|
|
711
|
+
|
|
712
|
+
**Pattern**: Compound Component
|
|
713
|
+
**Sub-components**: `Body`, `Content`, `Footer`, `Header`, `Item`, `Label`, `Separator`, `Shortcut`, `Trigger`
|
|
714
|
+
|
|
715
|
+
### Props
|
|
716
|
+
|
|
717
|
+
```tsx
|
|
718
|
+
interface OxDropdownProps {
|
|
719
|
+
align?: 'start' | 'center' | 'end';
|
|
720
|
+
children: ReactNode;
|
|
721
|
+
defaultOpen?: boolean;
|
|
722
|
+
disabled?: boolean;
|
|
723
|
+
onOpenChange?: (open: boolean) => void;
|
|
724
|
+
open?: boolean; // Controlled open state
|
|
725
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
interface OxDropdownTriggerProps extends ComponentPropsWithRef<'button'> {
|
|
729
|
+
animateIcon?: boolean; // Animates icon on open/close
|
|
730
|
+
asChild?: boolean; // Renders as child component (useful for custom triggers)
|
|
731
|
+
children: ReactNode;
|
|
732
|
+
hideIcon?: boolean;
|
|
733
|
+
icon?: LucideIcon;
|
|
734
|
+
size?: OxButtonProps['size'];
|
|
735
|
+
variant?: OxButtonProps['variant'];
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
interface OxDropdownContentProps extends ComponentPropsWithRef<'div'> {
|
|
739
|
+
children: ReactNode;
|
|
740
|
+
className?: string;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
interface OxDropdownBodyProps extends ComponentPropsWithRef<'ul'> {
|
|
744
|
+
children: ReactNode;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Item can be either button or link
|
|
748
|
+
type OxDropdownItemProps = OxDropdownItemButtonProps | OxDropdownItemLinkProps;
|
|
749
|
+
|
|
750
|
+
interface OxDropdownItemButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'type'> {
|
|
751
|
+
disabled?: boolean;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
interface OxDropdownItemLinkProps extends ComponentPropsWithRef<'a'> {
|
|
755
|
+
disabled?: boolean; // Disables link navigation
|
|
756
|
+
href: string; // Required for link variant
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
interface OxDropdownLabelProps extends ComponentPropsWithRef<'div'> {
|
|
760
|
+
children: ReactNode;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
interface OxDropdownShortcutProps extends ComponentPropsWithRef<'span'> {
|
|
764
|
+
children: ReactNode;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
interface OxDropdownHeaderProps extends ComponentPropsWithRef<'div'> {
|
|
768
|
+
children: ReactNode;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
interface OxDropdownFooterProps extends ComponentPropsWithRef<'div'> {
|
|
772
|
+
children: ReactNode;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
interface OxDropdownSeparatorProps extends Omit<OxSeparatorProps, 'orientation' | 'vertical'> {}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Example
|
|
779
|
+
|
|
780
|
+
```tsx
|
|
781
|
+
<OxDropdown side="bottom" align="start">
|
|
782
|
+
<OxDropdown.Trigger>Options</OxDropdown.Trigger>
|
|
783
|
+
<OxDropdown.Content>
|
|
784
|
+
<OxDropdown.Header>Actions</OxDropdown.Header>
|
|
785
|
+
<OxDropdown.Body>
|
|
786
|
+
<OxDropdown.Item onClick={handleEdit}>
|
|
787
|
+
Edit <OxDropdown.Shortcut>⌘E</OxDropdown.Shortcut>
|
|
788
|
+
</OxDropdown.Item>
|
|
789
|
+
<OxDropdown.Item href="/settings">Settings</OxDropdown.Item>
|
|
790
|
+
<OxDropdown.Separator />
|
|
791
|
+
<OxDropdown.Item onClick={handleDelete}>Delete</OxDropdown.Item>
|
|
792
|
+
</OxDropdown.Body>
|
|
793
|
+
</OxDropdown.Content>
|
|
794
|
+
</OxDropdown>
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
---
|
|
798
|
+
|
|
799
|
+
## OxEndlessList
|
|
800
|
+
|
|
801
|
+
Virtualized infinite scroll list using @tanstack/react-virtual.
|
|
802
|
+
|
|
803
|
+
**Pattern**: Compound Component
|
|
804
|
+
**Sub-components**: `Empty`, `Error`, `Item`, `Items`, `Loader`
|
|
805
|
+
|
|
806
|
+
### Props
|
|
807
|
+
|
|
808
|
+
```tsx
|
|
809
|
+
interface OxEndlessListProps<TData> extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
810
|
+
children: ReactNode;
|
|
811
|
+
data: TData[];
|
|
812
|
+
estimateSize: number | ((index: number) => number); // Estimated row height in pixels
|
|
813
|
+
gap?: number;
|
|
814
|
+
getItemKey: (item: TData, index: number) => string | number;
|
|
815
|
+
hasNextPage?: boolean;
|
|
816
|
+
isError?: boolean;
|
|
817
|
+
isLoading?: boolean;
|
|
818
|
+
loadMoreThreshold?: number; // Distance from bottom to trigger load (px)
|
|
819
|
+
onLoadMore?: () => void;
|
|
820
|
+
overscan?: number; // Number of items to render outside visible area
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
interface OxEndlessListItemsProps {
|
|
824
|
+
children: ReactNode;
|
|
825
|
+
className?: string;
|
|
826
|
+
paddingY?: number;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
interface OxEndlessListItemProps<TData = unknown> {
|
|
830
|
+
children: (item: TData, index: number) => ReactNode; // Render function
|
|
831
|
+
className?: string;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
interface OxEndlessListLoaderProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
835
|
+
children?: ReactNode;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
interface OxEndlessListEmptyProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
839
|
+
children?: ReactNode;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
interface OxEndlessListErrorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
843
|
+
children?: ReactNode;
|
|
844
|
+
}
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
### Usage
|
|
848
|
+
|
|
849
|
+
```tsx
|
|
850
|
+
<OxEndlessList
|
|
851
|
+
data={items}
|
|
852
|
+
estimateSize={50}
|
|
853
|
+
getItemKey={(item) => item.id}
|
|
854
|
+
hasNextPage={hasMore}
|
|
855
|
+
isLoading={isLoading}
|
|
856
|
+
onLoadMore={fetchMore}
|
|
857
|
+
>
|
|
858
|
+
<OxEndlessList.Items>
|
|
859
|
+
<OxEndlessList.Item>
|
|
860
|
+
{(item, index) => <div>{item.name}</div>}
|
|
861
|
+
</OxEndlessList.Item>
|
|
862
|
+
</OxEndlessList.Items>
|
|
863
|
+
<OxEndlessList.Loader>Loading more...</OxEndlessList.Loader>
|
|
864
|
+
<OxEndlessList.Empty>No items found</OxEndlessList.Empty>
|
|
865
|
+
<OxEndlessList.Error>Failed to load</OxEndlessList.Error>
|
|
866
|
+
</OxEndlessList>
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## OxFloating ⚠️ DEPRECATED
|
|
872
|
+
|
|
873
|
+
> **Deprecated**: Moved to `@noxickon/onyx/legacy`. Use `OxPopper` or higher-level components (OxPopover, OxDropdown, OxTooltip) instead.
|
|
874
|
+
|
|
875
|
+
Low-level floating UI primitive.
|
|
876
|
+
|
|
877
|
+
**Import**: `import { OxFloating } from '@noxickon/onyx/legacy';`
|
|
878
|
+
**Pattern**: Compound Component (Primitive)
|
|
879
|
+
**Sub-components**: `Content`, `Trigger`
|
|
880
|
+
**Additional Exports**: `OxFloatingContext`, `useOxFloatingContext`
|
|
881
|
+
|
|
882
|
+
### Props
|
|
883
|
+
|
|
884
|
+
```tsx
|
|
885
|
+
interface OxFloatingProps {
|
|
886
|
+
align?: 'start' | 'center' | 'end';
|
|
887
|
+
animationDuration?: number; // Animation duration in ms
|
|
888
|
+
ariaHaspopup?: 'menu' | 'dialog' | 'listbox' | 'tree' | 'grid' | true;
|
|
889
|
+
children: ReactNode;
|
|
890
|
+
defaultOpen?: boolean;
|
|
891
|
+
disabled?: boolean;
|
|
892
|
+
hoverDelay?: number; // Delay before showing on hover (ms)
|
|
893
|
+
id?: string;
|
|
894
|
+
offset?: number; // Distance from trigger element
|
|
895
|
+
onOpenChange?: (open: boolean) => void;
|
|
896
|
+
open?: boolean; // Controlled open state
|
|
897
|
+
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
898
|
+
trigger?: 'hover' | 'click' | 'manual' | 'focus'; // Interaction type
|
|
899
|
+
useAriaControls?: boolean;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
interface OxFloatingTriggerProps extends ComponentPropsWithRef<'div'> {
|
|
903
|
+
asChild?: boolean; // Renders as child component
|
|
904
|
+
children: ReactNode;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
interface OxFloatingContentProps extends ComponentPropsWithoutRef<'div'> {
|
|
908
|
+
children: ReactNode;
|
|
909
|
+
portal?: boolean; // Renders in portal
|
|
910
|
+
}
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Note**: Base component for Popover, Dropdown, and Tooltip. Use higher-level components instead when possible.
|
|
914
|
+
|
|
915
|
+
---
|
|
916
|
+
|
|
917
|
+
## OxHoverCard
|
|
918
|
+
|
|
919
|
+
Card that appears on hover with configurable delays.
|
|
920
|
+
|
|
921
|
+
**Pattern**: Compound Component
|
|
922
|
+
**Sub-components**: `Content`, `Trigger`
|
|
923
|
+
|
|
924
|
+
### Props
|
|
925
|
+
|
|
926
|
+
```tsx
|
|
927
|
+
type OxPopperSide = 'top' | 'right' | 'bottom' | 'left';
|
|
928
|
+
type OxPopperAlign = 'start' | 'center' | 'end';
|
|
929
|
+
|
|
930
|
+
interface OxHoverCardProps {
|
|
931
|
+
align?: OxPopperAlign;
|
|
932
|
+
children: ReactNode;
|
|
933
|
+
closeDelay?: number; // Delay before closing (ms)
|
|
934
|
+
defaultOpen?: boolean;
|
|
935
|
+
disabled?: boolean;
|
|
936
|
+
onOpenChange?: (open: boolean) => void;
|
|
937
|
+
open?: boolean; // Controlled open state
|
|
938
|
+
openDelay?: number; // Delay before opening (ms)
|
|
939
|
+
side?: OxPopperSide;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
interface OxHoverCardTriggerProps extends ComponentPropsWithoutRef<'div'> {
|
|
943
|
+
asChild?: boolean;
|
|
944
|
+
children: ReactNode;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
interface OxHoverCardContentProps extends ComponentPropsWithoutRef<'div'> {
|
|
948
|
+
children: ReactNode;
|
|
949
|
+
forceMount?: boolean; // Keep mounted when closed
|
|
950
|
+
}
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
### Usage
|
|
954
|
+
|
|
955
|
+
```tsx
|
|
956
|
+
<OxHoverCard openDelay={200} closeDelay={100}>
|
|
957
|
+
<OxHoverCard.Trigger asChild>
|
|
958
|
+
<OxButton>Hover me</OxButton>
|
|
959
|
+
</OxHoverCard.Trigger>
|
|
960
|
+
<OxHoverCard.Content>
|
|
961
|
+
<p>Additional information appears here</p>
|
|
962
|
+
</OxHoverCard.Content>
|
|
963
|
+
</OxHoverCard>
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
---
|
|
967
|
+
|
|
968
|
+
## OxIcon
|
|
969
|
+
|
|
970
|
+
Icon component with Lucide icon support.
|
|
971
|
+
|
|
972
|
+
**Pattern**: Compound Component
|
|
973
|
+
**Sub-components**: `Box`
|
|
974
|
+
|
|
975
|
+
### Props
|
|
976
|
+
|
|
977
|
+
```tsx
|
|
978
|
+
interface OxIconProps {
|
|
979
|
+
children?: React.ReactNode;
|
|
980
|
+
className?: string;
|
|
981
|
+
icon?: LucideIcon; // Lucide icon component
|
|
982
|
+
path?: string; // SVG path for custom icon
|
|
983
|
+
placeholder?: boolean; // Shows placeholder icon
|
|
984
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | number;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
interface OxIconBoxProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
988
|
+
children: ReactNode;
|
|
989
|
+
color?: ColorStyleName; // Background color
|
|
990
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
### Example
|
|
995
|
+
|
|
996
|
+
```tsx
|
|
997
|
+
import { Check } from 'lucide-react';
|
|
998
|
+
|
|
999
|
+
<OxIcon icon={Check} size="lg" />
|
|
1000
|
+
<OxIcon path="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
|
|
1001
|
+
|
|
1002
|
+
<OxIcon.Box color="blue" size="lg">
|
|
1003
|
+
<OxIcon icon={Check} />
|
|
1004
|
+
</OxIcon.Box>
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## OxJsonField (alpha)
|
|
1010
|
+
|
|
1011
|
+
JSON data display with syntax highlighting.
|
|
1012
|
+
|
|
1013
|
+
**Pattern**: Compound Component
|
|
1014
|
+
**Sub-components**: `Node`
|
|
1015
|
+
|
|
1016
|
+
### Props
|
|
1017
|
+
|
|
1018
|
+
```tsx
|
|
1019
|
+
type OxJsonValue =
|
|
1020
|
+
| string
|
|
1021
|
+
| number
|
|
1022
|
+
| boolean
|
|
1023
|
+
| null
|
|
1024
|
+
| undefined
|
|
1025
|
+
| OxJsonValue[]
|
|
1026
|
+
| { [key: string]: OxJsonValue };
|
|
1027
|
+
|
|
1028
|
+
interface OxJsonFieldProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
1029
|
+
value: string | OxJsonValue; // Required - JSON to display
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
interface OxJsonNodeProps {
|
|
1033
|
+
data: OxJsonValue;
|
|
1034
|
+
level: number; // Nesting level for indentation
|
|
1035
|
+
trailingComma?: boolean;
|
|
1036
|
+
}
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
### Example
|
|
1040
|
+
|
|
1041
|
+
```tsx
|
|
1042
|
+
<OxJsonField value={{ name: "John", age: 30, active: true }} />
|
|
1043
|
+
<OxJsonField value='{"key": "value"}' />
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
---
|
|
1047
|
+
|
|
1048
|
+
## OxKanban
|
|
1049
|
+
|
|
1050
|
+
Kanban board for drag-and-drop card management across columns.
|
|
1051
|
+
|
|
1052
|
+
**Pattern**: Compound Component
|
|
1053
|
+
**Sub-components**: `Card`, `Card.Handle`, `Column`, `Column.Footer`, `Column.Header`
|
|
1054
|
+
|
|
1055
|
+
### Props
|
|
1056
|
+
|
|
1057
|
+
```tsx
|
|
1058
|
+
interface OxKanbanProps extends ComponentPropsWithRef<'div'> {
|
|
1059
|
+
children: ReactNode;
|
|
1060
|
+
columnGap?: number;
|
|
1061
|
+
disabled?: boolean;
|
|
1062
|
+
onMove: (cardId: string, fromColumn: string, toColumn: string, newIndex: number) => void;
|
|
1063
|
+
onReorder?: (cardId: string, column: string, fromIndex: number, toIndex: number) => void;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
interface OxKanbanColumnProps extends ComponentPropsWithRef<'div'> {
|
|
1067
|
+
children?: ReactNode;
|
|
1068
|
+
disabled?: boolean;
|
|
1069
|
+
emptyState?: ReactNode; // Shown when column has no cards
|
|
1070
|
+
gap?: number;
|
|
1071
|
+
heading?: ReactNode;
|
|
1072
|
+
id: string; // Unique column identifier
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
interface OxKanbanColumnHeaderProps extends ComponentPropsWithRef<'div'> {
|
|
1076
|
+
children: ReactNode;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
interface OxKanbanColumnFooterProps extends ComponentPropsWithRef<'div'> {
|
|
1080
|
+
children: ReactNode;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
interface OxKanbanCardProps extends ComponentPropsWithRef<'div'> {
|
|
1084
|
+
children: ReactNode;
|
|
1085
|
+
disabled?: boolean;
|
|
1086
|
+
id: string; // Unique card identifier
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
interface OxKanbanCardHandleProps extends ComponentPropsWithRef<'button'> {
|
|
1090
|
+
children?: ReactNode;
|
|
1091
|
+
}
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
### Usage
|
|
1095
|
+
|
|
1096
|
+
```tsx
|
|
1097
|
+
<OxKanban
|
|
1098
|
+
onMove={(cardId, from, to, index) => moveCard(cardId, from, to, index)}
|
|
1099
|
+
onReorder={(cardId, column, from, to) => reorderCard(cardId, column, from, to)}
|
|
1100
|
+
>
|
|
1101
|
+
<OxKanban.Column id="todo">
|
|
1102
|
+
<OxKanban.Column.Header>To Do</OxKanban.Column.Header>
|
|
1103
|
+
<OxKanban.Card id="task-1">
|
|
1104
|
+
<OxKanban.Card.Handle />
|
|
1105
|
+
<span>Task 1</span>
|
|
1106
|
+
</OxKanban.Card>
|
|
1107
|
+
<OxKanban.Column.Footer>
|
|
1108
|
+
<OxButton>Add task</OxButton>
|
|
1109
|
+
</OxKanban.Column.Footer>
|
|
1110
|
+
</OxKanban.Column>
|
|
1111
|
+
<OxKanban.Column id="done" emptyState="No completed tasks">
|
|
1112
|
+
<OxKanban.Column.Header>Done</OxKanban.Column.Header>
|
|
1113
|
+
</OxKanban.Column>
|
|
1114
|
+
</OxKanban>
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
---
|
|
1118
|
+
|
|
1119
|
+
## OxLink
|
|
1120
|
+
|
|
1121
|
+
Link component for text-based navigation with hover underline.
|
|
1122
|
+
|
|
1123
|
+
**Pattern**: Simple Component
|
|
1124
|
+
|
|
1125
|
+
> **Note:** For button-styled links (e.g., call-to-action buttons), use `OxButton` with `asChild` instead. `OxLink` is for inline and text-based links only.
|
|
1126
|
+
|
|
1127
|
+
### Props
|
|
1128
|
+
|
|
1129
|
+
```tsx
|
|
1130
|
+
interface OxLinkProps extends ComponentPropsWithRef<'a'> {
|
|
1131
|
+
asChild?: boolean; // Renders as child element (useful for routing libraries)
|
|
1132
|
+
disabled?: boolean; // Disables link navigation
|
|
1133
|
+
size?: 'sm' | 'md' | 'lg';
|
|
1134
|
+
variant?: 'primary' | 'secondary' | 'accent'; // primary = violet text, secondary = white text
|
|
1135
|
+
}
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
### Example
|
|
1139
|
+
|
|
1140
|
+
```tsx
|
|
1141
|
+
<OxLink href="/about" variant="primary">
|
|
1142
|
+
About Us
|
|
1143
|
+
</OxLink>;
|
|
1144
|
+
|
|
1145
|
+
{
|
|
1146
|
+
/* Use with React Router */
|
|
1147
|
+
}
|
|
1148
|
+
<OxLink asChild>
|
|
1149
|
+
<Link to="/dashboard">Dashboard</Link>
|
|
1150
|
+
</OxLink>;
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
---
|
|
1154
|
+
|
|
1155
|
+
## OxMasonry
|
|
1156
|
+
|
|
1157
|
+
Responsive masonry layout with configurable columns.
|
|
1158
|
+
|
|
1159
|
+
**Pattern**: Simple Component
|
|
1160
|
+
|
|
1161
|
+
### Props
|
|
1162
|
+
|
|
1163
|
+
```tsx
|
|
1164
|
+
type Breakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
1165
|
+
|
|
1166
|
+
type ResponsiveColumns = {
|
|
1167
|
+
default: number;
|
|
1168
|
+
} & Partial<Record<Breakpoint, number>>;
|
|
1169
|
+
|
|
1170
|
+
interface OxMasonryProps {
|
|
1171
|
+
children: ReactNode;
|
|
1172
|
+
className?: string;
|
|
1173
|
+
columns: number | ResponsiveColumns; // Fixed columns or responsive breakpoints
|
|
1174
|
+
gap?: number;
|
|
1175
|
+
}
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
### Usage
|
|
1179
|
+
|
|
1180
|
+
```tsx
|
|
1181
|
+
// Fixed columns
|
|
1182
|
+
<OxMasonry columns={3} gap={16}>
|
|
1183
|
+
<div>Item 1</div>
|
|
1184
|
+
<div>Item 2</div>
|
|
1185
|
+
<div>Item 3</div>
|
|
1186
|
+
</OxMasonry>
|
|
1187
|
+
|
|
1188
|
+
// Responsive columns
|
|
1189
|
+
<OxMasonry
|
|
1190
|
+
columns={{ default: 1, sm: 2, md: 3, lg: 4 }}
|
|
1191
|
+
gap={24}
|
|
1192
|
+
>
|
|
1193
|
+
{items.map(item => <Card key={item.id}>{item.content}</Card>)}
|
|
1194
|
+
</OxMasonry>
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
---
|
|
1198
|
+
|
|
1199
|
+
## OxNotifications
|
|
1200
|
+
|
|
1201
|
+
Notification center component.
|
|
1202
|
+
|
|
1203
|
+
**Pattern**: Compound Component
|
|
1204
|
+
**Sub-components**: `Body`, `Button`, `Link`, `Trigger`
|
|
1205
|
+
|
|
1206
|
+
### Props
|
|
1207
|
+
|
|
1208
|
+
```tsx
|
|
1209
|
+
interface OxNotificationItemProps {
|
|
1210
|
+
icon?: LucideIcon;
|
|
1211
|
+
id: string; // Required - Unique ID
|
|
1212
|
+
img?: string;
|
|
1213
|
+
message?: string;
|
|
1214
|
+
path?: string; // SVG path
|
|
1215
|
+
read: boolean; // Required
|
|
1216
|
+
timestamp: Date; // Required
|
|
1217
|
+
title: string; // Required
|
|
1218
|
+
type?: 'default' | 'info' | 'success' | 'warning' | 'error';
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
interface OxNotificationsProps extends ComponentPropsWithRef<'div'> {
|
|
1222
|
+
children?: ReactNode;
|
|
1223
|
+
disabled?: boolean;
|
|
1224
|
+
emptyMessage?: string;
|
|
1225
|
+
items?: OxNotificationItemProps[];
|
|
1226
|
+
onItemClick?: (item: OxNotificationItemProps) => void;
|
|
1227
|
+
onMarkAllRead?: () => void;
|
|
1228
|
+
unreadCount?: number;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
interface OxNotificationsTriggerProps extends ComponentPropsWithRef<'button'> {
|
|
1232
|
+
children?: ReactNode;
|
|
1233
|
+
customIcon?: LucideIcon;
|
|
1234
|
+
unreadCount?: number; // Badge count
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
interface OxNotificationsBodyProps extends ComponentPropsWithRef<'ul'> {
|
|
1238
|
+
children?: ReactNode;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
interface OxNotificationsButtonProps extends Omit<
|
|
1242
|
+
ComponentPropsWithRef<'button'>,
|
|
1243
|
+
'children' | 'type' | 'onClick'
|
|
1244
|
+
> {
|
|
1245
|
+
children?: ReactNode;
|
|
1246
|
+
icon?: LucideIcon;
|
|
1247
|
+
img?: string;
|
|
1248
|
+
message?: string;
|
|
1249
|
+
path?: string;
|
|
1250
|
+
timestamp?: Date;
|
|
1251
|
+
title?: string;
|
|
1252
|
+
type?: 'default' | 'info' | 'success' | 'warning' | 'error';
|
|
1253
|
+
unread?: boolean;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
interface OxNotificationsLinkProps extends Omit<
|
|
1257
|
+
ComponentPropsWithRef<'a'>,
|
|
1258
|
+
'children' | 'type' | 'onClick'
|
|
1259
|
+
> {
|
|
1260
|
+
children?: ReactNode;
|
|
1261
|
+
href: string; // Required
|
|
1262
|
+
icon?: LucideIcon;
|
|
1263
|
+
img?: string;
|
|
1264
|
+
message?: string;
|
|
1265
|
+
path?: string;
|
|
1266
|
+
timestamp?: Date;
|
|
1267
|
+
title?: string;
|
|
1268
|
+
type?: 'default' | 'info' | 'success' | 'warning' | 'error';
|
|
1269
|
+
unread?: boolean;
|
|
1270
|
+
}
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
### Example
|
|
1274
|
+
|
|
1275
|
+
```tsx
|
|
1276
|
+
<OxNotifications
|
|
1277
|
+
items={notifications}
|
|
1278
|
+
unreadCount={5}
|
|
1279
|
+
onItemClick={(item) => console.log(item)}
|
|
1280
|
+
onMarkAllRead={() => console.log('Mark all read')}
|
|
1281
|
+
>
|
|
1282
|
+
<OxNotifications.Trigger unreadCount={5} />
|
|
1283
|
+
<OxNotifications.Body>
|
|
1284
|
+
<OxNotifications.Button title="New message" message="You have a new message" />
|
|
1285
|
+
<OxNotifications.Link href="/notifications" title="View all" />
|
|
1286
|
+
</OxNotifications.Body>
|
|
1287
|
+
</OxNotifications>
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
---
|
|
1291
|
+
|
|
1292
|
+
## OxPageOutline
|
|
1293
|
+
|
|
1294
|
+
Table of contents / page outline component with scroll spy.
|
|
1295
|
+
|
|
1296
|
+
**Pattern**: Compound Component
|
|
1297
|
+
**Sub-components**: `Content`, `Header`, `Item`
|
|
1298
|
+
|
|
1299
|
+
### Props
|
|
1300
|
+
|
|
1301
|
+
```tsx
|
|
1302
|
+
type OxPageOutlineLevel = 1 | 2 | 3;
|
|
1303
|
+
|
|
1304
|
+
interface OxPageOutlineProps extends ComponentPropsWithRef<'nav'> {
|
|
1305
|
+
children: ReactNode;
|
|
1306
|
+
scrollOffset?: number; // Offset for scroll position detection (px)
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
interface OxPageOutlineHeaderProps extends ComponentPropsWithRef<'div'> {
|
|
1310
|
+
children: ReactNode;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
interface OxPageOutlineContentProps extends ComponentPropsWithRef<'div'> {
|
|
1314
|
+
children: ReactNode;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
interface OxPageOutlineItemProps extends ComponentPropsWithRef<'a'> {
|
|
1318
|
+
children: ReactNode;
|
|
1319
|
+
href: string; // Anchor link (e.g., "#section-1")
|
|
1320
|
+
level?: OxPageOutlineLevel; // Indentation level (1-3)
|
|
1321
|
+
}
|
|
1322
|
+
```
|
|
1323
|
+
|
|
1324
|
+
### Usage
|
|
1325
|
+
|
|
1326
|
+
```tsx
|
|
1327
|
+
<OxPageOutline scrollOffset={80}>
|
|
1328
|
+
<OxPageOutline.Header>On this page</OxPageOutline.Header>
|
|
1329
|
+
<OxPageOutline.Content>
|
|
1330
|
+
<OxPageOutline.Item href="#introduction" level={1}>Introduction</OxPageOutline.Item>
|
|
1331
|
+
<OxPageOutline.Item href="#getting-started" level={1}>Getting Started</OxPageOutline.Item>
|
|
1332
|
+
<OxPageOutline.Item href="#installation" level={2}>Installation</OxPageOutline.Item>
|
|
1333
|
+
<OxPageOutline.Item href="#configuration" level={2}>Configuration</OxPageOutline.Item>
|
|
1334
|
+
<OxPageOutline.Item href="#api" level={1}>API Reference</OxPageOutline.Item>
|
|
1335
|
+
</OxPageOutline.Content>
|
|
1336
|
+
</OxPageOutline>
|
|
1337
|
+
```
|
|
1338
|
+
|
|
1339
|
+
---
|
|
1340
|
+
|
|
1341
|
+
## OxPagination
|
|
1342
|
+
|
|
1343
|
+
Pagination component with routing support.
|
|
1344
|
+
|
|
1345
|
+
**Pattern**: Compound Component
|
|
1346
|
+
**Sub-components**: `Ellipsis`, `Item`, `Link`, `Next`, `Previous`
|
|
1347
|
+
|
|
1348
|
+
### Props
|
|
1349
|
+
|
|
1350
|
+
```tsx
|
|
1351
|
+
interface OxPaginationProps extends ComponentPropsWithRef<'nav'> {
|
|
1352
|
+
children: ReactNode;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
interface OxPaginationItemProps extends ComponentPropsWithRef<'li'> {
|
|
1356
|
+
children: ReactNode;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
interface OxPaginationLinkProps extends ComponentPropsWithRef<'a'> {
|
|
1360
|
+
asChild?: boolean; // Renders as child component (for routing libraries)
|
|
1361
|
+
children: ReactNode;
|
|
1362
|
+
disabled?: boolean;
|
|
1363
|
+
href?: string;
|
|
1364
|
+
isActive?: boolean; // Marks as active page
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
interface OxPaginationPreviousProps extends ComponentPropsWithRef<'a'> {
|
|
1368
|
+
asChild?: boolean; // Renders as child component
|
|
1369
|
+
children?: ReactNode;
|
|
1370
|
+
disabled?: boolean;
|
|
1371
|
+
href?: string;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
interface OxPaginationNextProps extends ComponentPropsWithRef<'a'> {
|
|
1375
|
+
asChild?: boolean; // Renders as child component
|
|
1376
|
+
children?: ReactNode;
|
|
1377
|
+
disabled?: boolean;
|
|
1378
|
+
href?: string;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
interface OxPaginationEllipsisProps extends ComponentPropsWithRef<'span'> {}
|
|
1382
|
+
```
|
|
1383
|
+
|
|
1384
|
+
### Example
|
|
1385
|
+
|
|
1386
|
+
```tsx
|
|
1387
|
+
<OxPagination>
|
|
1388
|
+
<OxPagination.Item>
|
|
1389
|
+
<OxPagination.Previous href="?page=1" />
|
|
1390
|
+
</OxPagination.Item>
|
|
1391
|
+
<OxPagination.Item>
|
|
1392
|
+
<OxPagination.Link href="?page=1">1</OxPagination.Link>
|
|
1393
|
+
</OxPagination.Item>
|
|
1394
|
+
<OxPagination.Item>
|
|
1395
|
+
<OxPagination.Link href="?page=2" isActive>
|
|
1396
|
+
2
|
|
1397
|
+
</OxPagination.Link>
|
|
1398
|
+
</OxPagination.Item>
|
|
1399
|
+
<OxPagination.Item>
|
|
1400
|
+
<OxPagination.Ellipsis />
|
|
1401
|
+
</OxPagination.Item>
|
|
1402
|
+
<OxPagination.Item>
|
|
1403
|
+
<OxPagination.Next href="?page=3" />
|
|
1404
|
+
</OxPagination.Item>
|
|
1405
|
+
</OxPagination>
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1408
|
+
---
|
|
1409
|
+
|
|
1410
|
+
## OxPopover
|
|
1411
|
+
|
|
1412
|
+
Popover component built on Floating.
|
|
1413
|
+
|
|
1414
|
+
**Pattern**: Compound Component
|
|
1415
|
+
**Sub-components**: `Content`, `Trigger`
|
|
1416
|
+
|
|
1417
|
+
### Props
|
|
1418
|
+
|
|
1419
|
+
```tsx
|
|
1420
|
+
interface OxPopoverProps extends ComponentPropsWithRef<'div'> {
|
|
1421
|
+
align?: 'start' | 'center' | 'end';
|
|
1422
|
+
children: React.ReactNode;
|
|
1423
|
+
defaultOpen?: boolean;
|
|
1424
|
+
disabled?: boolean;
|
|
1425
|
+
onOpenChange?: (open: boolean) => void;
|
|
1426
|
+
open?: boolean; // Controlled open state
|
|
1427
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
interface OxPopoverTriggerProps extends ComponentPropsWithRef<'button'> {
|
|
1431
|
+
asChild?: boolean; // Renders as child component
|
|
1432
|
+
children: React.ReactNode;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
interface OxPopoverContentProps extends ComponentPropsWithRef<'div'> {
|
|
1436
|
+
children: React.ReactNode;
|
|
1437
|
+
showCloseButton?: boolean;
|
|
1438
|
+
}
|
|
1439
|
+
```
|
|
1440
|
+
|
|
1441
|
+
### Example
|
|
1442
|
+
|
|
1443
|
+
```tsx
|
|
1444
|
+
<OxPopover side="bottom">
|
|
1445
|
+
<OxPopover.Trigger>Click me</OxPopover.Trigger>
|
|
1446
|
+
<OxPopover.Content showCloseButton>Popover content here</OxPopover.Content>
|
|
1447
|
+
</OxPopover>
|
|
1448
|
+
```
|
|
1449
|
+
|
|
1450
|
+
---
|
|
1451
|
+
|
|
1452
|
+
## OxPopper
|
|
1453
|
+
|
|
1454
|
+
Low-level positioning primitive for floating elements. Built on @floating-ui/react.
|
|
1455
|
+
|
|
1456
|
+
**Pattern**: Compound Component (Primitive)
|
|
1457
|
+
**Sub-components**: `Anchor`, `Arrow`, `Content`
|
|
1458
|
+
**Additional Exports**: `usePopperState`, `usePopperContext`, `usePopperContentContext`
|
|
1459
|
+
|
|
1460
|
+
### Props
|
|
1461
|
+
|
|
1462
|
+
```tsx
|
|
1463
|
+
type OxPopperSide = 'top' | 'right' | 'bottom' | 'left';
|
|
1464
|
+
type OxPopperAlign = 'start' | 'center' | 'end';
|
|
1465
|
+
type OxPopperSticky = 'partial' | 'always';
|
|
1466
|
+
|
|
1467
|
+
interface OxPopperProps {
|
|
1468
|
+
children: ReactNode;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
interface OxPopperAnchorProps extends ComponentPropsWithoutRef<'div'> {
|
|
1472
|
+
asChild?: boolean;
|
|
1473
|
+
children?: ReactNode;
|
|
1474
|
+
virtualRef?: RefObject<Measurable | null>; // For virtual anchor positioning
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
interface OxPopperContentProps extends ComponentPropsWithoutRef<'div'> {
|
|
1478
|
+
align?: OxPopperAlign;
|
|
1479
|
+
alignOffset?: number;
|
|
1480
|
+
arrowPadding?: number;
|
|
1481
|
+
avoidCollisions?: boolean;
|
|
1482
|
+
children: ReactNode;
|
|
1483
|
+
collisionPadding?: number;
|
|
1484
|
+
hideWhenDetached?: boolean; // Hide when anchor is out of view
|
|
1485
|
+
onPlaced?: () => void;
|
|
1486
|
+
side?: OxPopperSide;
|
|
1487
|
+
sideOffset?: number;
|
|
1488
|
+
sticky?: OxPopperSticky; // How content sticks to viewport edges
|
|
1489
|
+
}
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
### Usage
|
|
1493
|
+
|
|
1494
|
+
```tsx
|
|
1495
|
+
<OxPopper>
|
|
1496
|
+
<OxPopper.Anchor asChild>
|
|
1497
|
+
<button>Anchor element</button>
|
|
1498
|
+
</OxPopper.Anchor>
|
|
1499
|
+
<OxPopper.Content side="bottom" align="start" sideOffset={8}>
|
|
1500
|
+
<OxPopper.Arrow />
|
|
1501
|
+
<div>Positioned content</div>
|
|
1502
|
+
</OxPopper.Content>
|
|
1503
|
+
</OxPopper>
|
|
1504
|
+
```
|
|
1505
|
+
|
|
1506
|
+
**Note**: This is a low-level primitive. Prefer `OxPopover`, `OxDropdown`, or `OxTooltip` for common use cases.
|
|
1507
|
+
|
|
1508
|
+
---
|
|
1509
|
+
|
|
1510
|
+
## OxProgress
|
|
1511
|
+
|
|
1512
|
+
Progress bar component with linear and circular variants.
|
|
1513
|
+
|
|
1514
|
+
**Pattern**: Compound Component
|
|
1515
|
+
**Sub-components**: `Circular`
|
|
1516
|
+
|
|
1517
|
+
### Props
|
|
1518
|
+
|
|
1519
|
+
```tsx
|
|
1520
|
+
interface OxProgressProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
1521
|
+
buffer?: number; // Buffer value for buffering state
|
|
1522
|
+
max?: number;
|
|
1523
|
+
value?: number;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
interface OxProgressCircularProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
1527
|
+
max?: number;
|
|
1528
|
+
showPercentage?: boolean; // Shows percentage text in center
|
|
1529
|
+
value?: number;
|
|
1530
|
+
}
|
|
1531
|
+
```
|
|
1532
|
+
|
|
1533
|
+
### Example
|
|
1534
|
+
|
|
1535
|
+
```tsx
|
|
1536
|
+
<OxProgress value={60} max={100} />
|
|
1537
|
+
<OxProgress value={30} buffer={60} max={100} />
|
|
1538
|
+
|
|
1539
|
+
<OxProgress.Circular value={75} max={100} showPercentage />
|
|
1540
|
+
```
|
|
1541
|
+
|
|
1542
|
+
---
|
|
1543
|
+
|
|
1544
|
+
## OxSeparator
|
|
1545
|
+
|
|
1546
|
+
Divider component.
|
|
1547
|
+
|
|
1548
|
+
**Pattern**: Simple Component
|
|
1549
|
+
|
|
1550
|
+
### Props
|
|
1551
|
+
|
|
1552
|
+
```tsx
|
|
1553
|
+
interface OxSeparatorProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
1554
|
+
decorative?: boolean; // Marks as decorative (aria-hidden)
|
|
1555
|
+
orientation?: 'horizontal' | 'vertical';
|
|
1556
|
+
}
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
### Example
|
|
1560
|
+
|
|
1561
|
+
```tsx
|
|
1562
|
+
<OxSeparator orientation="horizontal" />
|
|
1563
|
+
<OxSeparator orientation="vertical" decorative />
|
|
1564
|
+
```
|
|
1565
|
+
|
|
1566
|
+
---
|
|
1567
|
+
|
|
1568
|
+
## OxSkeleton
|
|
1569
|
+
|
|
1570
|
+
Loading skeleton placeholder.
|
|
1571
|
+
|
|
1572
|
+
**Pattern**: Simple Component
|
|
1573
|
+
|
|
1574
|
+
### Props
|
|
1575
|
+
|
|
1576
|
+
```tsx
|
|
1577
|
+
interface OxSkeletonProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
1578
|
+
animate?: boolean; // Enables pulse animation
|
|
1579
|
+
shape?: 'rectangle' | 'circle';
|
|
1580
|
+
}
|
|
1581
|
+
```
|
|
1582
|
+
|
|
1583
|
+
### Example
|
|
1584
|
+
|
|
1585
|
+
```tsx
|
|
1586
|
+
<OxSkeleton animate shape="rectangle" />
|
|
1587
|
+
<OxSkeleton shape="circle" />
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
---
|
|
1591
|
+
|
|
1592
|
+
## OxSlider
|
|
1593
|
+
|
|
1594
|
+
Slider component with range support.
|
|
1595
|
+
|
|
1596
|
+
**Pattern**: Simple Component
|
|
1597
|
+
|
|
1598
|
+
### Props
|
|
1599
|
+
|
|
1600
|
+
```tsx
|
|
1601
|
+
interface OxSliderProps extends Omit<
|
|
1602
|
+
ComponentPropsWithRef<'div'>,
|
|
1603
|
+
'onChange' | 'defaultValue' | 'children'
|
|
1604
|
+
> {
|
|
1605
|
+
defaultValue?: number | [number, number]; // Single value or range
|
|
1606
|
+
disabled?: boolean;
|
|
1607
|
+
marks?: boolean | number[]; // Shows marks at positions
|
|
1608
|
+
max?: number;
|
|
1609
|
+
min?: number;
|
|
1610
|
+
onChange?: (value: number | [number, number]) => void;
|
|
1611
|
+
onChangeCommitted?: (value: number | [number, number]) => void; // Called when user releases
|
|
1612
|
+
orientation?: 'horizontal' | 'vertical';
|
|
1613
|
+
showValue?: boolean; // Shows current value
|
|
1614
|
+
step?: number;
|
|
1615
|
+
value?: number | [number, number]; // Controlled value
|
|
1616
|
+
}
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
### Example
|
|
1620
|
+
|
|
1621
|
+
```tsx
|
|
1622
|
+
{
|
|
1623
|
+
/* Single value slider */
|
|
1624
|
+
}
|
|
1625
|
+
<OxSlider min={0} max={100} value={50} onChange={setValue} />;
|
|
1626
|
+
|
|
1627
|
+
{
|
|
1628
|
+
/* Range slider */
|
|
1629
|
+
}
|
|
1630
|
+
<OxSlider value={[20, 80]} onChange={setRange} />;
|
|
1631
|
+
|
|
1632
|
+
{
|
|
1633
|
+
/* With marks */
|
|
1634
|
+
}
|
|
1635
|
+
<OxSlider marks={[0, 25, 50, 75, 100]} showValue />;
|
|
1636
|
+
```
|
|
1637
|
+
|
|
1638
|
+
---
|
|
1639
|
+
|
|
1640
|
+
## OxSortableList
|
|
1641
|
+
|
|
1642
|
+
Sortable list with drag-and-drop reordering. Built on @dnd-kit.
|
|
1643
|
+
|
|
1644
|
+
**Pattern**: Compound Component
|
|
1645
|
+
**Sub-components**: `Handle`, `Item`
|
|
1646
|
+
|
|
1647
|
+
### Props
|
|
1648
|
+
|
|
1649
|
+
```tsx
|
|
1650
|
+
type OxSortableListDirection = 'vertical' | 'horizontal' | 'grid';
|
|
1651
|
+
|
|
1652
|
+
interface OxSortableListProps extends ComponentPropsWithRef<'div'> {
|
|
1653
|
+
children: ReactNode;
|
|
1654
|
+
columns?: number; // For grid direction
|
|
1655
|
+
direction?: OxSortableListDirection;
|
|
1656
|
+
disabled?: boolean;
|
|
1657
|
+
gap?: number;
|
|
1658
|
+
onReorder: (fromIndex: number, toIndex: number) => void;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
interface OxSortableListItemProps extends ComponentPropsWithRef<'div'> {
|
|
1662
|
+
children: ReactNode;
|
|
1663
|
+
disabled?: boolean;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
interface OxSortableListHandleProps extends ComponentPropsWithRef<'button'> {
|
|
1667
|
+
children?: ReactNode;
|
|
1668
|
+
}
|
|
1669
|
+
```
|
|
1670
|
+
|
|
1671
|
+
### Usage
|
|
1672
|
+
|
|
1673
|
+
```tsx
|
|
1674
|
+
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
|
|
1675
|
+
|
|
1676
|
+
const handleReorder = (from: number, to: number) => {
|
|
1677
|
+
const newItems = [...items];
|
|
1678
|
+
const [moved] = newItems.splice(from, 1);
|
|
1679
|
+
newItems.splice(to, 0, moved);
|
|
1680
|
+
setItems(newItems);
|
|
1681
|
+
};
|
|
1682
|
+
|
|
1683
|
+
<OxSortableList direction="vertical" gap={8} onReorder={handleReorder}>
|
|
1684
|
+
{items.map((item, index) => (
|
|
1685
|
+
<OxSortableList.Item key={item}>
|
|
1686
|
+
<OxSortableList.Handle />
|
|
1687
|
+
<span>{item}</span>
|
|
1688
|
+
</OxSortableList.Item>
|
|
1689
|
+
))}
|
|
1690
|
+
</OxSortableList>
|
|
1691
|
+
|
|
1692
|
+
// Grid layout
|
|
1693
|
+
<OxSortableList direction="grid" columns={3} gap={16} onReorder={handleReorder}>
|
|
1694
|
+
{items.map(item => (
|
|
1695
|
+
<OxSortableList.Item key={item}>{item}</OxSortableList.Item>
|
|
1696
|
+
))}
|
|
1697
|
+
</OxSortableList>
|
|
1698
|
+
```
|
|
1699
|
+
|
|
1700
|
+
---
|
|
1701
|
+
|
|
1702
|
+
## OxSpinner
|
|
1703
|
+
|
|
1704
|
+
Loading spinner.
|
|
1705
|
+
|
|
1706
|
+
**Pattern**: Simple Component
|
|
1707
|
+
|
|
1708
|
+
### Props
|
|
1709
|
+
|
|
1710
|
+
```tsx
|
|
1711
|
+
interface OxSpinnerProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
1712
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | number;
|
|
1713
|
+
}
|
|
1714
|
+
```
|
|
1715
|
+
|
|
1716
|
+
### Example
|
|
1717
|
+
|
|
1718
|
+
```tsx
|
|
1719
|
+
<OxSpinner size="lg" />
|
|
1720
|
+
<OxSpinner size={48} />
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
---
|
|
1724
|
+
|
|
1725
|
+
## OxStatusDisplay (beta)
|
|
1726
|
+
|
|
1727
|
+
Status display for errors and states.
|
|
1728
|
+
|
|
1729
|
+
**Pattern**: Simple Component
|
|
1730
|
+
|
|
1731
|
+
### Props
|
|
1732
|
+
|
|
1733
|
+
```tsx
|
|
1734
|
+
type OxStatusType =
|
|
1735
|
+
| 'loading'
|
|
1736
|
+
| 'empty'
|
|
1737
|
+
| 'offline'
|
|
1738
|
+
| 'success'
|
|
1739
|
+
| 'warning'
|
|
1740
|
+
| 'info'
|
|
1741
|
+
| 401
|
|
1742
|
+
| 403
|
|
1743
|
+
| 404
|
|
1744
|
+
| 408
|
|
1745
|
+
| 429
|
|
1746
|
+
| 500
|
|
1747
|
+
| 502
|
|
1748
|
+
| 503
|
|
1749
|
+
| 504; // HTTP status codes
|
|
1750
|
+
|
|
1751
|
+
interface OxStatusDisplayProps extends ComponentPropsWithRef<'div'> {
|
|
1752
|
+
action?: ReactNode; // Custom action element
|
|
1753
|
+
inline?: boolean;
|
|
1754
|
+
message?: string;
|
|
1755
|
+
onBack?: () => void; // Back button callback
|
|
1756
|
+
title?: string;
|
|
1757
|
+
type: OxStatusType; // Required
|
|
1758
|
+
}
|
|
1759
|
+
```
|
|
1760
|
+
|
|
1761
|
+
### Example
|
|
1762
|
+
|
|
1763
|
+
```tsx
|
|
1764
|
+
<OxStatusDisplay type={404} title="Page Not Found" message="The page you're looking for doesn't exist." />
|
|
1765
|
+
<OxStatusDisplay type="loading" />
|
|
1766
|
+
<OxStatusDisplay type={500} onBack={() => navigate(-1)} />
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
---
|
|
1770
|
+
|
|
1771
|
+
## OxStepper (experimental)
|
|
1772
|
+
|
|
1773
|
+
Multi-step navigation component.
|
|
1774
|
+
|
|
1775
|
+
**Pattern**: Compound Component
|
|
1776
|
+
**Sub-components**: `Actions`, `Item`
|
|
1777
|
+
|
|
1778
|
+
### Props
|
|
1779
|
+
|
|
1780
|
+
```tsx
|
|
1781
|
+
interface OxStepperProps extends ComponentPropsWithRef<'div'> {
|
|
1782
|
+
children: ReactNode;
|
|
1783
|
+
color?: ColorStyleName;
|
|
1784
|
+
currentStep?: number; // Current active step index
|
|
1785
|
+
onStepChange?: (step: number) => void;
|
|
1786
|
+
orientation?: 'horizontal' | 'vertical';
|
|
1787
|
+
type?: 'linear' | 'non-linear'; // Linear or non-linear navigation
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
interface OxStepperItemProps extends ComponentPropsWithRef<'div'> {
|
|
1791
|
+
children: ReactNode;
|
|
1792
|
+
description?: string;
|
|
1793
|
+
icon?: LucideIcon;
|
|
1794
|
+
state?: 'completed' | 'current' | 'upcoming' | 'error' | 'warning';
|
|
1795
|
+
stepIndex?: number; // Auto-assigned
|
|
1796
|
+
title: string; // Required
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
interface OxStepperActionsProps extends ComponentPropsWithRef<'div'> {
|
|
1800
|
+
children: ReactNode;
|
|
1801
|
+
}
|
|
1802
|
+
```
|
|
1803
|
+
|
|
1804
|
+
### Example
|
|
1805
|
+
|
|
1806
|
+
```tsx
|
|
1807
|
+
<OxStepper currentStep={1} type="linear" orientation="horizontal">
|
|
1808
|
+
<OxStepper.Item title="Account" description="Create account" state="completed" />
|
|
1809
|
+
<OxStepper.Item title="Profile" description="Add profile" state="current" />
|
|
1810
|
+
<OxStepper.Item title="Finish" description="Complete setup" state="upcoming" />
|
|
1811
|
+
<OxStepper.Actions>
|
|
1812
|
+
<OxButton>Previous</OxButton>
|
|
1813
|
+
<OxButton variant="primary">Next</OxButton>
|
|
1814
|
+
</OxStepper.Actions>
|
|
1815
|
+
</OxStepper>
|
|
1816
|
+
```
|
|
1817
|
+
|
|
1818
|
+
---
|
|
1819
|
+
|
|
1820
|
+
## OxTable
|
|
1821
|
+
|
|
1822
|
+
Table component with selection and loading states.
|
|
1823
|
+
|
|
1824
|
+
**Pattern**: Compound Component
|
|
1825
|
+
**Sub-components**: `Body`, `Cell`, `Controls`, `Footer`, `Head`, `Header`, `Row`, `SkeletonRow`
|
|
1826
|
+
|
|
1827
|
+
### Props
|
|
1828
|
+
|
|
1829
|
+
```tsx
|
|
1830
|
+
interface OxTableProps extends ComponentPropsWithRef<'table'> {
|
|
1831
|
+
allRowIds?: string[]; // All row IDs for selection
|
|
1832
|
+
children: ReactNode;
|
|
1833
|
+
draggable?: boolean; // Enables row reordering via drag and drop
|
|
1834
|
+
isLoading?: boolean; // Shows loading skeleton
|
|
1835
|
+
onRowReorder?: (newOrder: string[]) => void; // Callback when rows are reordered
|
|
1836
|
+
onSelectionChange?: (selectedRows: string[]) => void;
|
|
1837
|
+
selectable?: boolean; // Enables row selection with checkboxes
|
|
1838
|
+
selectedRows?: string[];
|
|
1839
|
+
skeletonColumns?: number;
|
|
1840
|
+
skeletonRows?: number;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
interface OxTableCellProps extends ComponentPropsWithoutRef<'td'> {
|
|
1844
|
+
children?: ReactNode;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
interface OxTableHeadProps extends ComponentPropsWithRef<'th'> {
|
|
1848
|
+
children?: ReactNode;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
interface OxTableRowProps extends ComponentPropsWithRef<'tr'> {
|
|
1852
|
+
children: ReactNode;
|
|
1853
|
+
rowId?: string; // Required for selection
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
interface OxTableBodyProps extends ComponentPropsWithRef<'tbody'> {
|
|
1857
|
+
children: ReactNode;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
interface OxTableFooterProps extends ComponentPropsWithRef<'tfoot'> {
|
|
1861
|
+
children: ReactNode;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
interface OxTableControlsProps extends ComponentPropsWithRef<'div'> {
|
|
1865
|
+
children: ReactNode;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
interface OxTableHeaderProps extends ComponentPropsWithRef<'thead'> {
|
|
1869
|
+
children: ReactNode;
|
|
1870
|
+
}
|
|
1871
|
+
```
|
|
1872
|
+
|
|
1873
|
+
### Example
|
|
1874
|
+
|
|
1875
|
+
```tsx
|
|
1876
|
+
<OxTable selectable selectedRows={selected} onSelectionChange={setSelected}>
|
|
1877
|
+
<OxTable.Header>
|
|
1878
|
+
<OxTable.Row>
|
|
1879
|
+
<OxTable.Head>Name</OxTable.Head>
|
|
1880
|
+
<OxTable.Head>Email</OxTable.Head>
|
|
1881
|
+
</OxTable.Row>
|
|
1882
|
+
</OxTable.Header>
|
|
1883
|
+
<OxTable.Body>
|
|
1884
|
+
<OxTable.Row rowId="1">
|
|
1885
|
+
<OxTable.Cell>John Doe</OxTable.Cell>
|
|
1886
|
+
<OxTable.Cell>john@example.com</OxTable.Cell>
|
|
1887
|
+
</OxTable.Row>
|
|
1888
|
+
</OxTable.Body>
|
|
1889
|
+
</OxTable>
|
|
1890
|
+
```
|
|
1891
|
+
|
|
1892
|
+
---
|
|
1893
|
+
|
|
1894
|
+
## OxTabs
|
|
1895
|
+
|
|
1896
|
+
Tabs component with bordered and pills variants.
|
|
1897
|
+
|
|
1898
|
+
**Pattern**: Compound Component
|
|
1899
|
+
**Sub-components**: `Content`, `List`, `Trigger`
|
|
1900
|
+
|
|
1901
|
+
### Props
|
|
1902
|
+
|
|
1903
|
+
```tsx
|
|
1904
|
+
interface OxTabsProps extends ComponentPropsWithRef<'div'> {
|
|
1905
|
+
children: ReactNode;
|
|
1906
|
+
defaultValue?: string;
|
|
1907
|
+
onValueChange?: (value: string) => void;
|
|
1908
|
+
value?: string; // Controlled active tab
|
|
1909
|
+
variant?: 'bordered' | 'pills';
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
interface OxTabsListProps extends ComponentPropsWithRef<'div'> {
|
|
1913
|
+
children: ReactNode;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
interface OxTabsTriggerProps extends ComponentPropsWithRef<'button'> {
|
|
1917
|
+
children: ReactNode;
|
|
1918
|
+
value: string; // Required - Tab identifier
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
interface OxTabsContentProps extends ComponentPropsWithRef<'div'> {
|
|
1922
|
+
children: ReactNode;
|
|
1923
|
+
value: string; // Required - Tab identifier
|
|
1924
|
+
}
|
|
1925
|
+
```
|
|
1926
|
+
|
|
1927
|
+
### Example
|
|
1928
|
+
|
|
1929
|
+
```tsx
|
|
1930
|
+
<OxTabs defaultValue="account" variant="bordered">
|
|
1931
|
+
<OxTabs.List>
|
|
1932
|
+
<OxTabs.Trigger value="account">Account</OxTabs.Trigger>
|
|
1933
|
+
<OxTabs.Trigger value="password">Password</OxTabs.Trigger>
|
|
1934
|
+
</OxTabs.List>
|
|
1935
|
+
<OxTabs.Content value="account">Account settings</OxTabs.Content>
|
|
1936
|
+
<OxTabs.Content value="password">Password settings</OxTabs.Content>
|
|
1937
|
+
</OxTabs>
|
|
1938
|
+
```
|
|
1939
|
+
|
|
1940
|
+
---
|
|
1941
|
+
|
|
1942
|
+
## OxTimeline (alpha)
|
|
1943
|
+
|
|
1944
|
+
Timeline component.
|
|
1945
|
+
|
|
1946
|
+
**Pattern**: Compound Component
|
|
1947
|
+
**Sub-components**: `Item`
|
|
1948
|
+
|
|
1949
|
+
### Props
|
|
1950
|
+
|
|
1951
|
+
```tsx
|
|
1952
|
+
interface OxTimelineProps extends ComponentPropsWithRef<'ol'> {
|
|
1953
|
+
children: ReactNode;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
interface OxTimelineItemProps extends ComponentPropsWithRef<'li'> {
|
|
1957
|
+
align?: 'left' | 'right';
|
|
1958
|
+
color?: ColorStyleName;
|
|
1959
|
+
description?: string;
|
|
1960
|
+
icon?: LucideIcon;
|
|
1961
|
+
label: string; // Required
|
|
1962
|
+
showSeparator?: boolean; // Shows connecting line
|
|
1963
|
+
timestamp?: string;
|
|
1964
|
+
}
|
|
1965
|
+
```
|
|
1966
|
+
|
|
1967
|
+
### Example
|
|
1968
|
+
|
|
1969
|
+
```tsx
|
|
1970
|
+
<OxTimeline>
|
|
1971
|
+
<OxTimeline.Item
|
|
1972
|
+
label="Created"
|
|
1973
|
+
description="Project initialized"
|
|
1974
|
+
timestamp="2024-01-01"
|
|
1975
|
+
color="blue"
|
|
1976
|
+
/>
|
|
1977
|
+
<OxTimeline.Item
|
|
1978
|
+
label="Updated"
|
|
1979
|
+
description="Files modified"
|
|
1980
|
+
timestamp="2024-01-15"
|
|
1981
|
+
color="green"
|
|
1982
|
+
/>
|
|
1983
|
+
</OxTimeline>
|
|
1984
|
+
```
|
|
1985
|
+
|
|
1986
|
+
---
|
|
1987
|
+
|
|
1988
|
+
## OxToast (beta)
|
|
1989
|
+
|
|
1990
|
+
Toast notification system.
|
|
1991
|
+
|
|
1992
|
+
**Pattern**: Context-based Component
|
|
1993
|
+
**Additional Exports**: `OxToastProvider`, `useOxToast`
|
|
1994
|
+
|
|
1995
|
+
### Props
|
|
1996
|
+
|
|
1997
|
+
```tsx
|
|
1998
|
+
interface OxToastData {
|
|
1999
|
+
color?: ColorStyleName;
|
|
2000
|
+
customContent?: ReactNode;
|
|
2001
|
+
description?: string;
|
|
2002
|
+
duration?: number; // Auto-dismiss duration in ms
|
|
2003
|
+
icon?: LucideIcon;
|
|
2004
|
+
id: string; // Required - Unique ID
|
|
2005
|
+
isRemoving?: boolean; // Internal removal state
|
|
2006
|
+
path?: string; // SVG path
|
|
2007
|
+
title: string; // Required
|
|
2008
|
+
type: StatusType; // Required
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
interface OxToastProps {
|
|
2012
|
+
align: 'left' | 'center' | 'right'; // Required
|
|
2013
|
+
onRemove: (id: string) => void; // Required
|
|
2014
|
+
position: 'top' | 'bottom'; // Required
|
|
2015
|
+
toasts: OxToastData[]; // Required
|
|
2016
|
+
}
|
|
2017
|
+
```
|
|
2018
|
+
|
|
2019
|
+
### Hook
|
|
2020
|
+
|
|
2021
|
+
```tsx
|
|
2022
|
+
const { showToast } = useOxToast();
|
|
2023
|
+
|
|
2024
|
+
showToast({
|
|
2025
|
+
title: 'Success!',
|
|
2026
|
+
description: 'Your changes have been saved.',
|
|
2027
|
+
type: 'success',
|
|
2028
|
+
duration: 5000,
|
|
2029
|
+
});
|
|
2030
|
+
```
|
|
2031
|
+
|
|
2032
|
+
### Example
|
|
2033
|
+
|
|
2034
|
+
```tsx
|
|
2035
|
+
// Wrap app
|
|
2036
|
+
<OxToastProvider>
|
|
2037
|
+
<App />
|
|
2038
|
+
</OxToastProvider>;
|
|
2039
|
+
|
|
2040
|
+
// Use in component
|
|
2041
|
+
function MyComponent() {
|
|
2042
|
+
const { showToast } = useOxToast();
|
|
2043
|
+
|
|
2044
|
+
return <button onClick={() => showToast({ title: 'Hello', type: 'info' })}>Show Toast</button>;
|
|
2045
|
+
}
|
|
2046
|
+
```
|
|
2047
|
+
|
|
2048
|
+
---
|
|
2049
|
+
|
|
2050
|
+
## OxToggle
|
|
2051
|
+
|
|
2052
|
+
Toggle button component with groups.
|
|
2053
|
+
|
|
2054
|
+
**Pattern**: Compound Component
|
|
2055
|
+
**Sub-components**: `Group`
|
|
2056
|
+
|
|
2057
|
+
### Props
|
|
2058
|
+
|
|
2059
|
+
```tsx
|
|
2060
|
+
interface OxToggleProps extends Omit<ComponentPropsWithRef<'button'>, 'onChange'> {
|
|
2061
|
+
children?: ReactNode;
|
|
2062
|
+
onPressedChange?: (pressed: boolean) => void;
|
|
2063
|
+
pressed?: boolean; // Controlled pressed state
|
|
2064
|
+
size?: 'sm' | 'md' | 'lg';
|
|
2065
|
+
value?: string; // For use in groups
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
interface OxToggleGroupSingleProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'> {
|
|
2069
|
+
children: ReactNode;
|
|
2070
|
+
disabled?: boolean;
|
|
2071
|
+
name?: string;
|
|
2072
|
+
onValueChange?: (value: string) => void;
|
|
2073
|
+
size?: 'sm' | 'md' | 'lg';
|
|
2074
|
+
type?: 'single'; // Single selection
|
|
2075
|
+
value?: string; // Controlled selected value
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
interface OxToggleGroupMultipleProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'> {
|
|
2079
|
+
children: ReactNode;
|
|
2080
|
+
disabled?: boolean;
|
|
2081
|
+
name?: string;
|
|
2082
|
+
onValueChange?: (value: string[]) => void;
|
|
2083
|
+
size?: 'sm' | 'md' | 'lg';
|
|
2084
|
+
type: 'multiple'; // Required - Multiple selection
|
|
2085
|
+
value?: string[]; // Controlled selected values
|
|
2086
|
+
}
|
|
2087
|
+
```
|
|
2088
|
+
|
|
2089
|
+
### Example
|
|
2090
|
+
|
|
2091
|
+
```tsx
|
|
2092
|
+
{
|
|
2093
|
+
/* Single toggle */
|
|
2094
|
+
}
|
|
2095
|
+
<OxToggle pressed={bold} onPressedChange={setBold}>
|
|
2096
|
+
Bold
|
|
2097
|
+
</OxToggle>;
|
|
2098
|
+
|
|
2099
|
+
{
|
|
2100
|
+
/* Toggle group - single selection */
|
|
2101
|
+
}
|
|
2102
|
+
<OxToggle.Group type="single" value={alignment} onValueChange={setAlignment}>
|
|
2103
|
+
<OxToggle value="left">Left</OxToggle>
|
|
2104
|
+
<OxToggle value="center">Center</OxToggle>
|
|
2105
|
+
<OxToggle value="right">Right</OxToggle>
|
|
2106
|
+
</OxToggle.Group>;
|
|
2107
|
+
|
|
2108
|
+
{
|
|
2109
|
+
/* Toggle group - multiple selection */
|
|
2110
|
+
}
|
|
2111
|
+
<OxToggle.Group type="multiple" value={formats} onValueChange={setFormats}>
|
|
2112
|
+
<OxToggle value="bold">Bold</OxToggle>
|
|
2113
|
+
<OxToggle value="italic">Italic</OxToggle>
|
|
2114
|
+
<OxToggle value="underline">Underline</OxToggle>
|
|
2115
|
+
</OxToggle.Group>;
|
|
2116
|
+
```
|
|
2117
|
+
|
|
2118
|
+
---
|
|
2119
|
+
|
|
2120
|
+
## OxTooltip
|
|
2121
|
+
|
|
2122
|
+
Tooltip component.
|
|
2123
|
+
|
|
2124
|
+
**Pattern**: Simple Component
|
|
2125
|
+
|
|
2126
|
+
### Props
|
|
2127
|
+
|
|
2128
|
+
```tsx
|
|
2129
|
+
interface OxTooltipProps {
|
|
2130
|
+
align?: 'start' | 'center' | 'end';
|
|
2131
|
+
children: ReactNode; // Required - Trigger element
|
|
2132
|
+
className?: string;
|
|
2133
|
+
content: ReactNode; // Required - Tooltip content
|
|
2134
|
+
disabled?: boolean;
|
|
2135
|
+
hoverDelay?: number; // Delay before showing (ms)
|
|
2136
|
+
interactive?: boolean; // Keep tooltip open when hovering over it
|
|
2137
|
+
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
2138
|
+
trigger?: 'hover' | 'click' | 'focus';
|
|
2139
|
+
}
|
|
2140
|
+
```
|
|
2141
|
+
|
|
2142
|
+
### Example
|
|
2143
|
+
|
|
2144
|
+
```tsx
|
|
2145
|
+
<OxTooltip content="Click to save" position="top">
|
|
2146
|
+
<button>Save</button>
|
|
2147
|
+
</OxTooltip>
|
|
2148
|
+
|
|
2149
|
+
<OxTooltip content="Error occurred" position="bottom">
|
|
2150
|
+
<OxIcon icon={AlertCircle} />
|
|
2151
|
+
</OxTooltip>
|
|
2152
|
+
```
|
|
2153
|
+
|
|
2154
|
+
---
|
|
2155
|
+
|
|
2156
|
+
# Form Components (8)
|
|
2157
|
+
|
|
2158
|
+
## OxCheckbox
|
|
2159
|
+
|
|
2160
|
+
Checkbox component with group support.
|
|
2161
|
+
|
|
2162
|
+
**Pattern**: Compound Component
|
|
2163
|
+
**Sub-components**: `Group`, `GroupItem`
|
|
2164
|
+
|
|
2165
|
+
### Props
|
|
2166
|
+
|
|
2167
|
+
```tsx
|
|
2168
|
+
type OxCheckedState = boolean | 'indeterminate';
|
|
2169
|
+
|
|
2170
|
+
interface OxCheckboxProps extends Omit<
|
|
2171
|
+
ComponentPropsWithoutRef<'input'>,
|
|
2172
|
+
'type' | 'value' | 'children' | 'checked' | 'size' | 'defaultChecked'
|
|
2173
|
+
> {
|
|
2174
|
+
checked?: OxCheckedState; // Can be boolean or 'indeterminate'
|
|
2175
|
+
defaultChecked?: OxCheckedState;
|
|
2176
|
+
error?: boolean; // Error state styling
|
|
2177
|
+
onCheckedChange?: (checked: OxCheckedState) => void;
|
|
2178
|
+
value?: string;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
interface OxCheckboxGroupProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'> {
|
|
2182
|
+
children: ReactNode;
|
|
2183
|
+
disabled?: boolean; // Disables all checkboxes in group
|
|
2184
|
+
name: string; // Required
|
|
2185
|
+
onChange?: (value: (string | number)[]) => void;
|
|
2186
|
+
value?: (string | number)[]; // Controlled selected values
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
interface OxCheckboxGroupItemProps extends Omit<
|
|
2190
|
+
ComponentPropsWithoutRef<'input'>,
|
|
2191
|
+
'type' | 'size' | 'children' | 'checked' | 'defaultChecked' | 'value'
|
|
2192
|
+
> {
|
|
2193
|
+
error?: boolean;
|
|
2194
|
+
value: string | number; // Required
|
|
2195
|
+
}
|
|
2196
|
+
```
|
|
2197
|
+
|
|
2198
|
+
### Example
|
|
2199
|
+
|
|
2200
|
+
```tsx
|
|
2201
|
+
{
|
|
2202
|
+
/* Single checkbox */
|
|
2203
|
+
}
|
|
2204
|
+
<OxCheckbox checked={agreed} onCheckedChange={setAgreed} />;
|
|
2205
|
+
|
|
2206
|
+
{
|
|
2207
|
+
/* Indeterminate state */
|
|
2208
|
+
}
|
|
2209
|
+
<OxCheckbox checked="indeterminate" />;
|
|
2210
|
+
|
|
2211
|
+
{
|
|
2212
|
+
/* Checkbox group */
|
|
2213
|
+
}
|
|
2214
|
+
<OxCheckbox.Group name="features" value={selected} onChange={setSelected}>
|
|
2215
|
+
<OxCheckbox.GroupItem value="feature1" />
|
|
2216
|
+
<OxCheckbox.GroupItem value="feature2" />
|
|
2217
|
+
</OxCheckbox.Group>;
|
|
2218
|
+
```
|
|
2219
|
+
|
|
2220
|
+
---
|
|
2221
|
+
|
|
2222
|
+
## OxFileUpload (experimental)
|
|
2223
|
+
|
|
2224
|
+
File upload component with drag-and-drop.
|
|
2225
|
+
|
|
2226
|
+
**Pattern**: Compound Component
|
|
2227
|
+
**Sub-components**: `Area`, `Button`, `Overlay`
|
|
2228
|
+
|
|
2229
|
+
### Props
|
|
2230
|
+
|
|
2231
|
+
```tsx
|
|
2232
|
+
interface OxFileUploadProps extends Omit<ComponentPropsWithRef<'div'>, 'onError'> {
|
|
2233
|
+
accept?: string[]; // Allowed file types (e.g., ['.jpg', '.png', 'image/*'])
|
|
2234
|
+
children: ReactNode; // Required
|
|
2235
|
+
disabled?: boolean;
|
|
2236
|
+
maxFiles?: number; // Maximum number of files
|
|
2237
|
+
maxSize?: string; // Max file size (e.g., '5MB', '1GB')
|
|
2238
|
+
minSize?: string; // Min file size
|
|
2239
|
+
multiple?: boolean; // Allow multiple files
|
|
2240
|
+
onError?: (error: string) => void; // Validation error callback
|
|
2241
|
+
onFileSelect?: (files: File[]) => void; // Files selected callback
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
interface OxFileUploadAreaProps extends ComponentPropsWithRef<'div'> {
|
|
2245
|
+
children: ReactNode;
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
interface OxFileUploadButtonProps extends ComponentPropsWithRef<'button'> {
|
|
2249
|
+
children: ReactNode;
|
|
2250
|
+
size?: 'sm' | 'md';
|
|
2251
|
+
variant?: 'primary' | 'secondary' | 'ghost' | 'link' | 'danger';
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
interface OxFileUploadOverlayProps extends ComponentPropsWithRef<'div'> {
|
|
2255
|
+
children: ReactNode;
|
|
2256
|
+
}
|
|
2257
|
+
```
|
|
2258
|
+
|
|
2259
|
+
### Example
|
|
2260
|
+
|
|
2261
|
+
```tsx
|
|
2262
|
+
<OxFileUpload
|
|
2263
|
+
accept={['image/*']}
|
|
2264
|
+
maxSize="5MB"
|
|
2265
|
+
multiple
|
|
2266
|
+
onFileSelect={(files) => console.log(files)}
|
|
2267
|
+
onError={(error) => console.error(error)}
|
|
2268
|
+
>
|
|
2269
|
+
<OxFileUpload.Area>
|
|
2270
|
+
<p>Drag files here or click to browse</p>
|
|
2271
|
+
<OxFileUpload.Button>Select Files</OxFileUpload.Button>
|
|
2272
|
+
</OxFileUpload.Area>
|
|
2273
|
+
<OxFileUpload.Overlay>Drop files here</OxFileUpload.Overlay>
|
|
2274
|
+
</OxFileUpload>
|
|
2275
|
+
```
|
|
2276
|
+
|
|
2277
|
+
---
|
|
2278
|
+
|
|
2279
|
+
## OxForm
|
|
2280
|
+
|
|
2281
|
+
Form container with field management.
|
|
2282
|
+
|
|
2283
|
+
**Pattern**: Compound Component
|
|
2284
|
+
**Sub-components**: `Group`, `Field`, `Label`, `Description`, `Error`, `GroupTitle`
|
|
2285
|
+
|
|
2286
|
+
### Props
|
|
2287
|
+
|
|
2288
|
+
```tsx
|
|
2289
|
+
interface OxFormProps extends ComponentPropsWithRef<'form'> {
|
|
2290
|
+
children: ReactNode; // Required
|
|
2291
|
+
fullWidth?: boolean; // Makes form full width
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
interface OxFormGroupProps extends ComponentPropsWithRef<'div'> {
|
|
2295
|
+
children: ReactNode;
|
|
2296
|
+
layout?: 'grid'; // Optional grid layout
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
interface OxFormFieldProps extends ComponentPropsWithRef<'div'> {
|
|
2300
|
+
children: ReactNode;
|
|
2301
|
+
disabled?: boolean; // Disables all form controls within
|
|
2302
|
+
error?: boolean | string; // Error state or message
|
|
2303
|
+
id?: string; // Field ID for label association
|
|
2304
|
+
labelPosition?: 'start' | 'end'; // Position of label relative to control (default: 'start')
|
|
2305
|
+
orientation?: 'vertical' | 'horizontal'; // Layout direction
|
|
2306
|
+
required?: boolean; // Marks field as required
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
interface OxFormLabelProps extends ComponentPropsWithRef<'label'> {
|
|
2310
|
+
children: ReactNode; // Required
|
|
2311
|
+
disabled?: boolean;
|
|
2312
|
+
required?: boolean; // Shows required indicator (red asterisk)
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
interface OxFormDescriptionProps extends ComponentPropsWithRef<'div'> {
|
|
2316
|
+
children: ReactNode; // Required
|
|
2317
|
+
disabled?: boolean;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
interface OxFormErrorProps extends ComponentPropsWithRef<'div'> {}
|
|
2321
|
+
|
|
2322
|
+
interface OxFormGroupTitleProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
2323
|
+
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div';
|
|
2324
|
+
children: ReactNode; // Required
|
|
2325
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6 | 7; // Heading level for styling
|
|
2326
|
+
}
|
|
2327
|
+
```
|
|
2328
|
+
|
|
2329
|
+
### Example
|
|
2330
|
+
|
|
2331
|
+
```tsx
|
|
2332
|
+
<OxForm>
|
|
2333
|
+
<OxForm.Group>
|
|
2334
|
+
<OxForm.GroupTitle level={2}>Account Information</OxForm.GroupTitle>
|
|
2335
|
+
<OxForm.Field orientation="vertical">
|
|
2336
|
+
<OxForm.Label required>Email</OxForm.Label>
|
|
2337
|
+
<OxForm.Description>Your email address</OxForm.Description>
|
|
2338
|
+
<input type="email" />
|
|
2339
|
+
<OxForm.Error>Email is required</OxForm.Error>
|
|
2340
|
+
</OxForm.Field>
|
|
2341
|
+
</OxForm.Group>
|
|
2342
|
+
</OxForm>
|
|
2343
|
+
```
|
|
2344
|
+
|
|
2345
|
+
---
|
|
2346
|
+
|
|
2347
|
+
## Input Components
|
|
2348
|
+
|
|
2349
|
+
### OxTextInput
|
|
2350
|
+
|
|
2351
|
+
Text input with validation and various types.
|
|
2352
|
+
|
|
2353
|
+
**Pattern**: Compound Component
|
|
2354
|
+
**Parts**: `Container`, input element, `Button`, `ClearButton`, `Counter`, `VisibilityButton`
|
|
2355
|
+
|
|
2356
|
+
### Props
|
|
2357
|
+
|
|
2358
|
+
```tsx
|
|
2359
|
+
type OxTextInputType = 'email' | 'password' | 'search' | 'tel' | 'text' | 'url';
|
|
2360
|
+
|
|
2361
|
+
interface OxTextInputContainerProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'> {
|
|
2362
|
+
defaultValue?: string;
|
|
2363
|
+
disabled?: boolean;
|
|
2364
|
+
error?: boolean;
|
|
2365
|
+
onChange?: (value: string) => void;
|
|
2366
|
+
required?: boolean;
|
|
2367
|
+
type?: OxTextInputType;
|
|
2368
|
+
validationMessage?: string; // Custom error message shown below input
|
|
2369
|
+
value?: string; // Controlled value
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
// Input element props
|
|
2373
|
+
interface OxTextInputProps {
|
|
2374
|
+
[key: `data-${string}`]?: string | undefined;
|
|
2375
|
+
'aria-describedby'?: string;
|
|
2376
|
+
'aria-label'?: string;
|
|
2377
|
+
'aria-labelledby'?: string;
|
|
2378
|
+
autoComplete?: string;
|
|
2379
|
+
className?: string;
|
|
2380
|
+
id?: string;
|
|
2381
|
+
maxLength?: number;
|
|
2382
|
+
minLength?: number;
|
|
2383
|
+
name?: string;
|
|
2384
|
+
pattern?: string; // Validation regex
|
|
2385
|
+
placeholder?: string;
|
|
2386
|
+
readOnly?: boolean;
|
|
2387
|
+
spellCheck?: boolean;
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
interface OxTextInputButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'type'> {}
|
|
2391
|
+
|
|
2392
|
+
interface OxTextInputCounterProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
2393
|
+
currentLength?: number;
|
|
2394
|
+
disabled?: boolean;
|
|
2395
|
+
maxLength: number; // Required
|
|
2396
|
+
}
|
|
2397
|
+
```
|
|
2398
|
+
|
|
2399
|
+
### Example
|
|
2400
|
+
|
|
2401
|
+
```tsx
|
|
2402
|
+
<OxTextInput.Container
|
|
2403
|
+
type="email"
|
|
2404
|
+
value={email}
|
|
2405
|
+
onChange={setEmail}
|
|
2406
|
+
error={!!emailError}
|
|
2407
|
+
validationMessage="Please enter a valid email"
|
|
2408
|
+
>
|
|
2409
|
+
<OxTextInput placeholder="Enter email" maxLength={100} />
|
|
2410
|
+
<OxTextInput.ClearButton />
|
|
2411
|
+
<OxTextInput.Counter maxLength={100} />
|
|
2412
|
+
</OxTextInput.Container>;
|
|
2413
|
+
|
|
2414
|
+
{
|
|
2415
|
+
/* Password input */
|
|
2416
|
+
}
|
|
2417
|
+
<OxTextInput.Container type="password">
|
|
2418
|
+
<OxTextInput placeholder="Password" />
|
|
2419
|
+
<OxTextInput.VisibilityButton />
|
|
2420
|
+
</OxTextInput.Container>;
|
|
2421
|
+
```
|
|
2422
|
+
|
|
2423
|
+
---
|
|
2424
|
+
|
|
2425
|
+
### OxNumberInput
|
|
2426
|
+
|
|
2427
|
+
Numeric input with increment/decrement controls.
|
|
2428
|
+
|
|
2429
|
+
**Pattern**: Compound Component
|
|
2430
|
+
**Parts**: `Container`, input element, `Button`, `IncrementButton`, `DecrementButton`
|
|
2431
|
+
|
|
2432
|
+
### Props
|
|
2433
|
+
|
|
2434
|
+
```tsx
|
|
2435
|
+
interface OxNumberInputContainerProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'> {
|
|
2436
|
+
defaultValue?: number;
|
|
2437
|
+
disabled?: boolean;
|
|
2438
|
+
error?: boolean;
|
|
2439
|
+
locale?: string; // Locale for number formatting
|
|
2440
|
+
max?: number; // Maximum value
|
|
2441
|
+
maximumFractionDigits?: number; // Max decimal places
|
|
2442
|
+
min?: number; // Minimum value
|
|
2443
|
+
minimumFractionDigits?: number; // Min decimal places
|
|
2444
|
+
onChange?: (value: number | undefined) => void;
|
|
2445
|
+
required?: boolean;
|
|
2446
|
+
steps?: number; // Increment/decrement step value
|
|
2447
|
+
useGrouping?: boolean; // Use thousand separators
|
|
2448
|
+
validationMessage?: string; // Custom error message shown below input
|
|
2449
|
+
value?: number; // Controlled value
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
interface OxNumberInputProps {
|
|
2453
|
+
'aria-label'?: string;
|
|
2454
|
+
'aria-labelledby'?: string;
|
|
2455
|
+
className?: string;
|
|
2456
|
+
id?: string;
|
|
2457
|
+
placeholder?: string;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
interface OxNumberInputButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'type'> {}
|
|
2461
|
+
```
|
|
2462
|
+
|
|
2463
|
+
### Example
|
|
2464
|
+
|
|
2465
|
+
```tsx
|
|
2466
|
+
<OxNumberInput.Container
|
|
2467
|
+
value={quantity}
|
|
2468
|
+
onChange={setQuantity}
|
|
2469
|
+
min={0}
|
|
2470
|
+
max={100}
|
|
2471
|
+
steps={5}
|
|
2472
|
+
useGrouping
|
|
2473
|
+
>
|
|
2474
|
+
<OxNumberInput.DecrementButton />
|
|
2475
|
+
<OxNumberInput placeholder="Quantity" />
|
|
2476
|
+
<OxNumberInput.IncrementButton />
|
|
2477
|
+
</OxNumberInput.Container>
|
|
2478
|
+
```
|
|
2479
|
+
|
|
2480
|
+
---
|
|
2481
|
+
|
|
2482
|
+
### OxDateInput
|
|
2483
|
+
|
|
2484
|
+
Date input with picker.
|
|
2485
|
+
|
|
2486
|
+
**Pattern**: Compound Component
|
|
2487
|
+
**Parts**: `Container`, `Button`, `PickerButton`
|
|
2488
|
+
|
|
2489
|
+
### Props
|
|
2490
|
+
|
|
2491
|
+
```tsx
|
|
2492
|
+
interface OxDateInputContainerProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'> {
|
|
2493
|
+
defaultOpen?: boolean; // Default open state of picker
|
|
2494
|
+
defaultValue?: string; // Date string
|
|
2495
|
+
disabled?: boolean;
|
|
2496
|
+
error?: boolean;
|
|
2497
|
+
onChange?: (value: string) => void;
|
|
2498
|
+
onOpenChange?: (open: boolean) => void;
|
|
2499
|
+
open?: boolean; // Controlled open state
|
|
2500
|
+
pickOnly?: boolean; // Only allow picking from calendar, no manual input
|
|
2501
|
+
readOnly?: boolean;
|
|
2502
|
+
required?: boolean;
|
|
2503
|
+
validationMessage?: string; // Custom error message shown below input
|
|
2504
|
+
value?: string; // Controlled value
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
interface OxDateInputButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'type'> {}
|
|
2508
|
+
|
|
2509
|
+
// Picker button - opens calendar
|
|
2510
|
+
interface OxDatePickerButtonProps extends Omit<
|
|
2511
|
+
ComponentPropsWithRef<'button'>,
|
|
2512
|
+
'type' | 'children'
|
|
2513
|
+
> {}
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
### Example
|
|
2517
|
+
|
|
2518
|
+
```tsx
|
|
2519
|
+
<OxDateInput.Container value={date} onChange={setDate}>
|
|
2520
|
+
<OxDateInput.PickerButton />
|
|
2521
|
+
</OxDateInput.Container>;
|
|
2522
|
+
|
|
2523
|
+
{
|
|
2524
|
+
/* Pick-only mode (no manual input) */
|
|
2525
|
+
}
|
|
2526
|
+
<OxDateInput.Container pickOnly>
|
|
2527
|
+
<OxDateInput.PickerButton />
|
|
2528
|
+
</OxDateInput.Container>;
|
|
2529
|
+
```
|
|
2530
|
+
|
|
2531
|
+
---
|
|
2532
|
+
|
|
2533
|
+
### OxTimeInput
|
|
2534
|
+
|
|
2535
|
+
Time input component.
|
|
2536
|
+
|
|
2537
|
+
**Pattern**: Compound Component
|
|
2538
|
+
**Parts**: `Container`, `Button`
|
|
2539
|
+
|
|
2540
|
+
### Props
|
|
2541
|
+
|
|
2542
|
+
```tsx
|
|
2543
|
+
interface OxTimeInputContainerProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'> {
|
|
2544
|
+
defaultValue?: string; // Time string
|
|
2545
|
+
disabled?: boolean;
|
|
2546
|
+
error?: boolean;
|
|
2547
|
+
onChange?: (value: string) => void;
|
|
2548
|
+
required?: boolean;
|
|
2549
|
+
validationMessage?: string; // Custom error message shown below input
|
|
2550
|
+
value?: string; // Controlled value
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
interface OxTimeInputButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'type'> {}
|
|
2554
|
+
```
|
|
2555
|
+
|
|
2556
|
+
### Example
|
|
2557
|
+
|
|
2558
|
+
```tsx
|
|
2559
|
+
<OxTimeInput.Container value={time} onChange={setTime}>
|
|
2560
|
+
<input type="time" />
|
|
2561
|
+
</OxTimeInput.Container>
|
|
2562
|
+
```
|
|
2563
|
+
|
|
2564
|
+
---
|
|
2565
|
+
|
|
2566
|
+
### OxOtpInput
|
|
2567
|
+
|
|
2568
|
+
One-Time Password input.
|
|
2569
|
+
|
|
2570
|
+
**Pattern**: Simple Component
|
|
2571
|
+
|
|
2572
|
+
### Props
|
|
2573
|
+
|
|
2574
|
+
```tsx
|
|
2575
|
+
type OxOtpInputType = 'numeric' | 'alphanumeric' | 'text';
|
|
2576
|
+
|
|
2577
|
+
interface OxOtpInputProps {
|
|
2578
|
+
className?: string;
|
|
2579
|
+
disabled?: boolean;
|
|
2580
|
+
error?: boolean;
|
|
2581
|
+
length?: number; // Number of digits/characters
|
|
2582
|
+
onComplete?: (value: string) => void; // Called when all digits filled
|
|
2583
|
+
onValueChange?: (value: string) => void; // Called on any change
|
|
2584
|
+
placeholder?: string;
|
|
2585
|
+
type?: OxOtpInputType; // Type of characters allowed
|
|
2586
|
+
}
|
|
2587
|
+
```
|
|
2588
|
+
|
|
2589
|
+
### Example
|
|
2590
|
+
|
|
2591
|
+
```tsx
|
|
2592
|
+
<OxOtpInput
|
|
2593
|
+
length={6}
|
|
2594
|
+
type="numeric"
|
|
2595
|
+
onComplete={(code) => verifyCode(code)}
|
|
2596
|
+
onValueChange={(value) => console.log(value)}
|
|
2597
|
+
/>
|
|
2598
|
+
```
|
|
2599
|
+
|
|
2600
|
+
---
|
|
2601
|
+
|
|
2602
|
+
## OxRadio
|
|
2603
|
+
|
|
2604
|
+
Radio button group.
|
|
2605
|
+
|
|
2606
|
+
**Pattern**: Compound Component (Namespace - no standalone component)
|
|
2607
|
+
**Sub-components**: `Group`, `Item`
|
|
2608
|
+
|
|
2609
|
+
### Props
|
|
2610
|
+
|
|
2611
|
+
```tsx
|
|
2612
|
+
interface OxRadioGroupProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'> {
|
|
2613
|
+
children: ReactNode; // Required
|
|
2614
|
+
defaultValue?: string | number;
|
|
2615
|
+
disabled?: boolean; // Disables all radio buttons
|
|
2616
|
+
error?: boolean;
|
|
2617
|
+
name?: string;
|
|
2618
|
+
onChange?: (value: string | number) => void;
|
|
2619
|
+
required?: boolean;
|
|
2620
|
+
value?: string | number; // Controlled selected value
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
interface OxRadioItemProps extends Omit<
|
|
2624
|
+
ComponentPropsWithoutRef<'input'>,
|
|
2625
|
+
'type' | 'size' | 'required'
|
|
2626
|
+
> {
|
|
2627
|
+
error?: boolean;
|
|
2628
|
+
onCheckedChange?: (checked: boolean) => void; // Called when this item is checked
|
|
2629
|
+
value?: string | number;
|
|
2630
|
+
}
|
|
2631
|
+
```
|
|
2632
|
+
|
|
2633
|
+
### Example
|
|
2634
|
+
|
|
2635
|
+
```tsx
|
|
2636
|
+
<OxRadio.Group name="plan" value={plan} onChange={setPlan}>
|
|
2637
|
+
<OxRadio.Item value="basic" />
|
|
2638
|
+
<OxRadio.Item value="pro" />
|
|
2639
|
+
<OxRadio.Item value="enterprise" />
|
|
2640
|
+
</OxRadio.Group>
|
|
2641
|
+
```
|
|
2642
|
+
|
|
2643
|
+
**Note**: No standalone `OxRadio` component - only `OxRadio.Group` and `OxRadio.Item`.
|
|
2644
|
+
|
|
2645
|
+
---
|
|
2646
|
+
|
|
2647
|
+
## OxSelect
|
|
2648
|
+
|
|
2649
|
+
Select dropdown with single and multi-select.
|
|
2650
|
+
|
|
2651
|
+
**Pattern**: Compound Component
|
|
2652
|
+
**Sub-components**: `Trigger`, `Content`, `Group`, `Item`
|
|
2653
|
+
|
|
2654
|
+
### Props
|
|
2655
|
+
|
|
2656
|
+
```tsx
|
|
2657
|
+
interface OxSelectProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'defaultValue'> {
|
|
2658
|
+
defaultOpen?: boolean;
|
|
2659
|
+
defaultValue?: string | number | null | Array<string | number | null>;
|
|
2660
|
+
disabled?: boolean;
|
|
2661
|
+
error?: boolean;
|
|
2662
|
+
id?: string;
|
|
2663
|
+
isLoading?: boolean; // Shows loading state
|
|
2664
|
+
multiple?: boolean; // Enable multi-select
|
|
2665
|
+
onChange?: (value: string | number | null | Array<string | number | null>) => void;
|
|
2666
|
+
onOpenChange?: (open: boolean) => void;
|
|
2667
|
+
open?: boolean; // Controlled open state
|
|
2668
|
+
required?: boolean;
|
|
2669
|
+
value?: string | number | null | Array<string | number | null>; // Controlled value
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
interface OxSelectTriggerProps extends ComponentPropsWithRef<'button'> {
|
|
2673
|
+
displayAsTag?: boolean; // Display selected items as tags (multi-select)
|
|
2674
|
+
placeholder?: string;
|
|
2675
|
+
tagColor?: ColorStyleName;
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
interface OxSelectContentProps extends ComponentPropsWithRef<'ul'> {}
|
|
2679
|
+
|
|
2680
|
+
interface OxSelectGroupProps extends ComponentPropsWithRef<'div'> {
|
|
2681
|
+
label?: ReactNode; // Group label
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
interface OxSelectItemProps extends Omit<ComponentPropsWithRef<'button'>, 'value'> {
|
|
2685
|
+
value: string | number | null; // Required
|
|
2686
|
+
}
|
|
2687
|
+
```
|
|
2688
|
+
|
|
2689
|
+
### Example
|
|
2690
|
+
|
|
2691
|
+
```tsx
|
|
2692
|
+
{
|
|
2693
|
+
/* Single select */
|
|
2694
|
+
}
|
|
2695
|
+
<OxSelect value={country} onChange={setCountry}>
|
|
2696
|
+
<OxSelect.Trigger placeholder="Select country" />
|
|
2697
|
+
<OxSelect.Content>
|
|
2698
|
+
<OxSelect.Item value="us">United States</OxSelect.Item>
|
|
2699
|
+
<OxSelect.Item value="uk">United Kingdom</OxSelect.Item>
|
|
2700
|
+
</OxSelect.Content>
|
|
2701
|
+
</OxSelect>;
|
|
2702
|
+
|
|
2703
|
+
{
|
|
2704
|
+
/* Multi-select with badges */
|
|
2705
|
+
}
|
|
2706
|
+
<OxSelect multiple value={tags} onChange={setTags}>
|
|
2707
|
+
<OxSelect.Trigger displayAsTag tagColor="blue" />
|
|
2708
|
+
<OxSelect.Content>
|
|
2709
|
+
<OxSelect.Group label="Categories">
|
|
2710
|
+
<OxSelect.Item value="react">React</OxSelect.Item>
|
|
2711
|
+
<OxSelect.Item value="typescript">TypeScript</OxSelect.Item>
|
|
2712
|
+
</OxSelect.Group>
|
|
2713
|
+
</OxSelect.Content>
|
|
2714
|
+
</OxSelect>;
|
|
2715
|
+
```
|
|
2716
|
+
|
|
2717
|
+
---
|
|
2718
|
+
|
|
2719
|
+
## OxSwitch
|
|
2720
|
+
|
|
2721
|
+
Toggle switch for boolean values.
|
|
2722
|
+
|
|
2723
|
+
**Pattern**: Simple Component
|
|
2724
|
+
|
|
2725
|
+
### Props
|
|
2726
|
+
|
|
2727
|
+
```tsx
|
|
2728
|
+
interface OxSwitchProps extends Omit<
|
|
2729
|
+
ComponentPropsWithRef<'button'>,
|
|
2730
|
+
'type' | 'size' | 'onChange' | 'children'
|
|
2731
|
+
> {
|
|
2732
|
+
checked?: boolean; // Controlled checked state
|
|
2733
|
+
error?: boolean;
|
|
2734
|
+
onChange?: (checked: boolean) => void;
|
|
2735
|
+
required?: boolean;
|
|
2736
|
+
}
|
|
2737
|
+
```
|
|
2738
|
+
|
|
2739
|
+
### Example
|
|
2740
|
+
|
|
2741
|
+
```tsx
|
|
2742
|
+
<OxSwitch checked={enabled} onChange={setEnabled} />
|
|
2743
|
+
```
|
|
2744
|
+
|
|
2745
|
+
---
|
|
2746
|
+
|
|
2747
|
+
## OxTextarea
|
|
2748
|
+
|
|
2749
|
+
Multi-line text input.
|
|
2750
|
+
|
|
2751
|
+
**Pattern**: Simple Component
|
|
2752
|
+
|
|
2753
|
+
### Props
|
|
2754
|
+
|
|
2755
|
+
```tsx
|
|
2756
|
+
interface OxTextareaProps extends Omit<ComponentPropsWithRef<'textarea'>, 'children'> {
|
|
2757
|
+
error?: boolean;
|
|
2758
|
+
showMaxLength?: boolean; // Show character count indicator
|
|
2759
|
+
}
|
|
2760
|
+
```
|
|
2761
|
+
|
|
2762
|
+
### Example
|
|
2763
|
+
|
|
2764
|
+
```tsx
|
|
2765
|
+
<OxTextarea placeholder="Enter description" maxLength={500} showMaxLength rows={5} />
|
|
2766
|
+
```
|
|
2767
|
+
|
|
2768
|
+
---
|
|
2769
|
+
|
|
2770
|
+
# Layout Components (3)
|
|
2771
|
+
|
|
2772
|
+
## OxAppLayout
|
|
2773
|
+
|
|
2774
|
+
Top-level application layout wrapper.
|
|
2775
|
+
|
|
2776
|
+
**Pattern**: Simple Wrapper
|
|
2777
|
+
|
|
2778
|
+
### Props
|
|
2779
|
+
|
|
2780
|
+
```tsx
|
|
2781
|
+
interface OxAppLayoutProps extends ComponentPropsWithRef<'div'> {
|
|
2782
|
+
children: ReactNode; // Required
|
|
2783
|
+
className?: string;
|
|
2784
|
+
}
|
|
2785
|
+
```
|
|
2786
|
+
|
|
2787
|
+
### Example
|
|
2788
|
+
|
|
2789
|
+
```tsx
|
|
2790
|
+
<OxSidebarProvider>
|
|
2791
|
+
<OxAppLayout>
|
|
2792
|
+
<OxSidebar>...</OxSidebar>
|
|
2793
|
+
<OxMainContent>...</OxMainContent>
|
|
2794
|
+
</OxAppLayout>
|
|
2795
|
+
</OxSidebarProvider>
|
|
2796
|
+
```
|
|
2797
|
+
|
|
2798
|
+
**Notes**:
|
|
2799
|
+
|
|
2800
|
+
- Responsive grid layout (single column on mobile, two-column on desktop)
|
|
2801
|
+
- Mobile breakpoint: 640px
|
|
2802
|
+
- Requires `OxSidebarProvider` in component tree
|
|
2803
|
+
- Max width: `max-w-16xl`
|
|
2804
|
+
|
|
2805
|
+
---
|
|
2806
|
+
|
|
2807
|
+
## OxMainContent
|
|
2808
|
+
|
|
2809
|
+
Main content area with header and body.
|
|
2810
|
+
|
|
2811
|
+
**Pattern**: Compound Component
|
|
2812
|
+
**Sub-components**: `Header`, `Body`
|
|
2813
|
+
|
|
2814
|
+
### Props
|
|
2815
|
+
|
|
2816
|
+
```tsx
|
|
2817
|
+
interface OxMainContentProps extends ComponentPropsWithRef<'main'> {
|
|
2818
|
+
children: ReactNode; // Required
|
|
2819
|
+
className?: string;
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
interface OxMainContentHeaderProps extends ComponentPropsWithRef<'div'> {
|
|
2823
|
+
children: ReactNode; // Required
|
|
2824
|
+
className?: string;
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
interface OxMainContentBodyProps extends ComponentPropsWithRef<'div'> {
|
|
2828
|
+
children: ReactNode; // Required
|
|
2829
|
+
className?: string;
|
|
2830
|
+
}
|
|
2831
|
+
```
|
|
2832
|
+
|
|
2833
|
+
### Example
|
|
2834
|
+
|
|
2835
|
+
```tsx
|
|
2836
|
+
<OxMainContent>
|
|
2837
|
+
<OxMainContent.Header>
|
|
2838
|
+
<h1>Page Title</h1>
|
|
2839
|
+
</OxMainContent.Header>
|
|
2840
|
+
<OxMainContent.Body>Page content here</OxMainContent.Body>
|
|
2841
|
+
</OxMainContent>
|
|
2842
|
+
```
|
|
2843
|
+
|
|
2844
|
+
---
|
|
2845
|
+
|
|
2846
|
+
## OxSidebar
|
|
2847
|
+
|
|
2848
|
+
Comprehensive sidebar navigation system.
|
|
2849
|
+
|
|
2850
|
+
**Pattern**: Complex Compound Component
|
|
2851
|
+
**Main Exports**: `OxSidebar`, `OxSidebarMenu`, `OxSidebarGroup`, `OxSidebarProvider`, `useOxSidebar`
|
|
2852
|
+
|
|
2853
|
+
### Sidebar Sub-components
|
|
2854
|
+
|
|
2855
|
+
- `OxSidebar.Header` - Top section
|
|
2856
|
+
- `OxSidebar.Content` - Main scrollable area
|
|
2857
|
+
- `OxSidebar.Footer` - Bottom section
|
|
2858
|
+
- `OxSidebar.Separator` - Visual divider
|
|
2859
|
+
- `OxSidebar.Trigger` - Toggle button (mobile)
|
|
2860
|
+
|
|
2861
|
+
### Menu Sub-components
|
|
2862
|
+
|
|
2863
|
+
- `OxSidebarMenu.Item` - Menu item wrapper
|
|
2864
|
+
- `OxSidebarMenu.Button` - Clickable button/link (polymorphic)
|
|
2865
|
+
- `OxSidebarMenu.Icon` - Icon for menu items
|
|
2866
|
+
- `OxSidebarMenu.Tag` - Tag/counter
|
|
2867
|
+
- `OxSidebarMenu.Action` - Action button
|
|
2868
|
+
- `OxSidebarMenu.Skeleton` - Loading skeleton
|
|
2869
|
+
- `OxSidebarMenu.Sub` - Submenu container
|
|
2870
|
+
- `OxSidebarMenu.SubItem` - Submenu item wrapper
|
|
2871
|
+
- `OxSidebarMenu.SubButton` - Submenu button
|
|
2872
|
+
|
|
2873
|
+
### Group Sub-components
|
|
2874
|
+
|
|
2875
|
+
- `OxSidebarGroup.Label` - Group heading
|
|
2876
|
+
- `OxSidebarGroup.Action` - Group action button
|
|
2877
|
+
- `OxSidebarGroup.Content` - Group content container
|
|
2878
|
+
|
|
2879
|
+
### Props
|
|
2880
|
+
|
|
2881
|
+
```tsx
|
|
2882
|
+
// Provider
|
|
2883
|
+
interface OxSidebarProviderProps {
|
|
2884
|
+
children: ReactNode; // Required
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
// Root Sidebar
|
|
2888
|
+
interface OxSidebarProps extends ComponentPropsWithoutRef<'aside'> {
|
|
2889
|
+
children: ReactNode; // Required
|
|
2890
|
+
className?: string;
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
// Menu Item
|
|
2894
|
+
interface OxSidebarMenuItemProps extends ComponentPropsWithRef<'li'> {
|
|
2895
|
+
children: ReactNode;
|
|
2896
|
+
className?: string;
|
|
2897
|
+
defaultOpen?: boolean; // Initial open state for submenu
|
|
2898
|
+
hasSub?: boolean; // Indicates if item has submenu
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
// Menu Button (Polymorphic - renders as link or button)
|
|
2902
|
+
type OxSidebarMenuButtonProps =
|
|
2903
|
+
| {
|
|
2904
|
+
children: ReactNode;
|
|
2905
|
+
href: string; // Required for link variant
|
|
2906
|
+
disabled?: boolean;
|
|
2907
|
+
className?: string;
|
|
2908
|
+
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
2909
|
+
}
|
|
2910
|
+
| {
|
|
2911
|
+
children: ReactNode;
|
|
2912
|
+
href?: never;
|
|
2913
|
+
type?: 'button';
|
|
2914
|
+
disabled?: boolean;
|
|
2915
|
+
className?: string;
|
|
2916
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
2917
|
+
};
|
|
2918
|
+
```
|
|
2919
|
+
|
|
2920
|
+
### Hook
|
|
2921
|
+
|
|
2922
|
+
```tsx
|
|
2923
|
+
const { isMobile, mobileOpen, openSidebar, closeSidebar, toggleSidebar } = useOxSidebar();
|
|
2924
|
+
|
|
2925
|
+
// Returns:
|
|
2926
|
+
// - isMobile: boolean (viewport < 640px)
|
|
2927
|
+
// - mobileOpen: boolean (current mobile open state)
|
|
2928
|
+
// - openSidebar: () => void
|
|
2929
|
+
// - closeSidebar: () => void
|
|
2930
|
+
// - toggleSidebar: () => void
|
|
2931
|
+
```
|
|
2932
|
+
|
|
2933
|
+
### Example
|
|
2934
|
+
|
|
2935
|
+
```tsx
|
|
2936
|
+
<OxSidebarProvider>
|
|
2937
|
+
<OxAppLayout>
|
|
2938
|
+
<OxSidebar>
|
|
2939
|
+
<OxSidebar.Header>
|
|
2940
|
+
<img src="/logo.png" alt="Logo" />
|
|
2941
|
+
</OxSidebar.Header>
|
|
2942
|
+
|
|
2943
|
+
<OxSidebar.Content>
|
|
2944
|
+
<OxSidebarMenu>
|
|
2945
|
+
{/* Simple menu item */}
|
|
2946
|
+
<OxSidebarMenu.Item>
|
|
2947
|
+
<OxSidebarMenu.Button href="/dashboard">
|
|
2948
|
+
<OxSidebarMenu.Icon icon={Home} />
|
|
2949
|
+
Dashboard
|
|
2950
|
+
</OxSidebarMenu.Button>
|
|
2951
|
+
</OxSidebarMenu.Item>
|
|
2952
|
+
|
|
2953
|
+
{/* Menu item with submenu */}
|
|
2954
|
+
<OxSidebarMenu.Item hasSub defaultOpen>
|
|
2955
|
+
<OxSidebarMenu.Button>
|
|
2956
|
+
<OxSidebarMenu.Icon icon={Settings} />
|
|
2957
|
+
Settings
|
|
2958
|
+
<OxSidebarMenu.Tag>3</OxSidebarMenu.Tag>
|
|
2959
|
+
</OxSidebarMenu.Button>
|
|
2960
|
+
<OxSidebarMenu.Sub>
|
|
2961
|
+
<OxSidebarMenu.SubItem>
|
|
2962
|
+
<OxSidebarMenu.SubButton>Profile</OxSidebarMenu.SubButton>
|
|
2963
|
+
</OxSidebarMenu.SubItem>
|
|
2964
|
+
</OxSidebarMenu.Sub>
|
|
2965
|
+
</OxSidebarMenu.Item>
|
|
2966
|
+
</OxSidebarMenu>
|
|
2967
|
+
|
|
2968
|
+
<OxSidebarGroup>
|
|
2969
|
+
<OxSidebarGroup.Label>Projects</OxSidebarGroup.Label>
|
|
2970
|
+
<OxSidebarGroup.Content>{/* Group items */}</OxSidebarGroup.Content>
|
|
2971
|
+
</OxSidebarGroup>
|
|
2972
|
+
</OxSidebar.Content>
|
|
2973
|
+
|
|
2974
|
+
<OxSidebar.Footer>User info</OxSidebar.Footer>
|
|
2975
|
+
</OxSidebar>
|
|
2976
|
+
|
|
2977
|
+
<OxMainContent>...</OxMainContent>
|
|
2978
|
+
</OxAppLayout>
|
|
2979
|
+
</OxSidebarProvider>
|
|
2980
|
+
```
|
|
2981
|
+
|
|
2982
|
+
**Notes**:
|
|
2983
|
+
|
|
2984
|
+
- Responsive: Drawer on mobile (< 640px), fixed sidebar on desktop
|
|
2985
|
+
- Width: 256px on desktop
|
|
2986
|
+
- Automatically manages submenu state
|
|
2987
|
+
- MenuButton with `hasSub` automatically shows chevron and toggles submenu
|
|
2988
|
+
- Polymorphic MenuButton (renders as `<a>` if href provided, otherwise `<button>`)
|
|
2989
|
+
|
|
2990
|
+
---
|
|
2991
|
+
|
|
2992
|
+
# Typography Components (5)
|
|
2993
|
+
|
|
2994
|
+
## OxDescription
|
|
2995
|
+
|
|
2996
|
+
Helper text component.
|
|
2997
|
+
|
|
2998
|
+
**Pattern**: Simple Component
|
|
2999
|
+
|
|
3000
|
+
### Props
|
|
3001
|
+
|
|
3002
|
+
```tsx
|
|
3003
|
+
interface OxDescriptionProps extends ComponentPropsWithRef<'div'> {
|
|
3004
|
+
children: ReactNode; // Required
|
|
3005
|
+
disabled?: boolean; // Applies disabled styling
|
|
3006
|
+
}
|
|
3007
|
+
```
|
|
3008
|
+
|
|
3009
|
+
### Example
|
|
3010
|
+
|
|
3011
|
+
```tsx
|
|
3012
|
+
<OxDescription>This field is required for verification.</OxDescription>
|
|
3013
|
+
<OxDescription disabled>This field is disabled.</OxDescription>
|
|
3014
|
+
```
|
|
3015
|
+
|
|
3016
|
+
---
|
|
3017
|
+
|
|
3018
|
+
## OxError
|
|
3019
|
+
|
|
3020
|
+
Error message component.
|
|
3021
|
+
|
|
3022
|
+
**Pattern**: Simple Component
|
|
3023
|
+
|
|
3024
|
+
### Props
|
|
3025
|
+
|
|
3026
|
+
```tsx
|
|
3027
|
+
interface OxErrorProps extends ComponentPropsWithRef<'div'> {
|
|
3028
|
+
children?: ReactNode; // Returns null if not provided
|
|
3029
|
+
}
|
|
3030
|
+
```
|
|
3031
|
+
|
|
3032
|
+
### Example
|
|
3033
|
+
|
|
3034
|
+
```tsx
|
|
3035
|
+
<OxError>Email is required</OxError>
|
|
3036
|
+
<OxError>
|
|
3037
|
+
<AlertCircle /> Invalid format
|
|
3038
|
+
</OxError>
|
|
3039
|
+
```
|
|
3040
|
+
|
|
3041
|
+
**Notes**:
|
|
3042
|
+
|
|
3043
|
+
- Renders with `role="alert"`
|
|
3044
|
+
- Returns `null` if no children
|
|
3045
|
+
- Auto-styles SVG children with proper sizing
|
|
3046
|
+
|
|
3047
|
+
---
|
|
3048
|
+
|
|
3049
|
+
## OxHeading
|
|
3050
|
+
|
|
3051
|
+
Heading component with 7 visual levels.
|
|
3052
|
+
|
|
3053
|
+
**Pattern**: Simple Component
|
|
3054
|
+
|
|
3055
|
+
### Props
|
|
3056
|
+
|
|
3057
|
+
```tsx
|
|
3058
|
+
interface OxHeadingProps extends HTMLAttributes<HTMLElement> {
|
|
3059
|
+
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div'; // Element to render (default: 'h2')
|
|
3060
|
+
children: ReactNode; // Required
|
|
3061
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6 | 7; // Visual styling level
|
|
3062
|
+
}
|
|
3063
|
+
```
|
|
3064
|
+
|
|
3065
|
+
**Level Styles**:
|
|
3066
|
+
|
|
3067
|
+
- 1: `text-2xl font-bold`
|
|
3068
|
+
- 2: `text-xl font-semibold` (default)
|
|
3069
|
+
- 3: `text-lg font-semibold`
|
|
3070
|
+
- 4: `text-base font-semibold`
|
|
3071
|
+
- 5: `text-sm font-semibold`
|
|
3072
|
+
- 6: `text-sm font-medium`
|
|
3073
|
+
- 7: `text-sm`
|
|
3074
|
+
|
|
3075
|
+
### Example
|
|
3076
|
+
|
|
3077
|
+
```tsx
|
|
3078
|
+
<OxHeading as="h1" level={1}>Page Title</OxHeading>
|
|
3079
|
+
<OxHeading as="h2">Section Title</OxHeading>
|
|
3080
|
+
<OxHeading as="div" level={3}>Custom Element</OxHeading>
|
|
3081
|
+
```
|
|
3082
|
+
|
|
3083
|
+
**Notes**:
|
|
3084
|
+
|
|
3085
|
+
- Semantic level derived from `as` prop if `level` not provided
|
|
3086
|
+
- Adds `role="heading"` and `aria-level` when `as="div"`
|
|
3087
|
+
- No ref support
|
|
3088
|
+
|
|
3089
|
+
---
|
|
3090
|
+
|
|
3091
|
+
## OxLabel
|
|
3092
|
+
|
|
3093
|
+
Form label component.
|
|
3094
|
+
|
|
3095
|
+
**Pattern**: Simple Component
|
|
3096
|
+
|
|
3097
|
+
### Props
|
|
3098
|
+
|
|
3099
|
+
```tsx
|
|
3100
|
+
interface OxLabelProps extends ComponentPropsWithRef<'label'> {
|
|
3101
|
+
children: ReactNode; // Required
|
|
3102
|
+
disabled?: boolean;
|
|
3103
|
+
required?: boolean; // Shows red asterisk (*)
|
|
3104
|
+
}
|
|
3105
|
+
```
|
|
3106
|
+
|
|
3107
|
+
### Example
|
|
3108
|
+
|
|
3109
|
+
```tsx
|
|
3110
|
+
<OxLabel htmlFor="email" required>Email Address</OxLabel>
|
|
3111
|
+
<OxLabel disabled>Disabled Field</OxLabel>
|
|
3112
|
+
```
|
|
3113
|
+
|
|
3114
|
+
**Notes**:
|
|
3115
|
+
|
|
3116
|
+
- Cursor changes to `pointer` when `htmlFor` provided and not disabled
|
|
3117
|
+
- Required indicator: red asterisk appended when `required={true}`
|
|
3118
|
+
|
|
3119
|
+
---
|
|
3120
|
+
|
|
3121
|
+
## OxLegend
|
|
3122
|
+
|
|
3123
|
+
Fieldset legend component.
|
|
3124
|
+
|
|
3125
|
+
**Pattern**: Simple Component
|
|
3126
|
+
|
|
3127
|
+
### Props
|
|
3128
|
+
|
|
3129
|
+
```tsx
|
|
3130
|
+
interface OxLegendProps extends ComponentPropsWithRef<'legend'> {
|
|
3131
|
+
children: ReactNode; // Required
|
|
3132
|
+
disabled?: boolean;
|
|
3133
|
+
required?: boolean; // Shows red asterisk (*)
|
|
3134
|
+
}
|
|
3135
|
+
```
|
|
3136
|
+
|
|
3137
|
+
### Example
|
|
3138
|
+
|
|
3139
|
+
```tsx
|
|
3140
|
+
<fieldset>
|
|
3141
|
+
<OxLegend required>Personal Information</OxLegend>
|
|
3142
|
+
...
|
|
3143
|
+
</fieldset>
|
|
3144
|
+
```
|
|
3145
|
+
|
|
3146
|
+
**Notes**:
|
|
3147
|
+
|
|
3148
|
+
- Use with `<fieldset>` elements
|
|
3149
|
+
- Similar to Label but larger font size (`text-base` vs `text-sm`)
|
|
3150
|
+
|
|
3151
|
+
---
|
|
3152
|
+
|
|
3153
|
+
# Contexts (3)
|
|
3154
|
+
|
|
3155
|
+
## OxAuthContext
|
|
3156
|
+
|
|
3157
|
+
User authentication and authorization.
|
|
3158
|
+
|
|
3159
|
+
**Exports**: `OxAuthProvider`, `useOxAuth`, `OxAuthContext`, `DEFAULT_GET_CURRENT_USER`
|
|
3160
|
+
|
|
3161
|
+
### Props
|
|
3162
|
+
|
|
3163
|
+
```tsx
|
|
3164
|
+
interface OxAuthProviderProps {
|
|
3165
|
+
adminRole?: string; // Default: 'ADMIN'
|
|
3166
|
+
children: ReactNode;
|
|
3167
|
+
getCurrentUserQuery?: DocumentNode; // Custom GraphQL query
|
|
3168
|
+
options?: Parameters<typeof useQuery>[1]; // Apollo options
|
|
3169
|
+
pollInterval?: number; // Default: 300000 (5 minutes)
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
type OxUser = {
|
|
3173
|
+
id: string;
|
|
3174
|
+
role: string;
|
|
3175
|
+
email?: string;
|
|
3176
|
+
username?: string;
|
|
3177
|
+
} | null;
|
|
3178
|
+
|
|
3179
|
+
type OxAuthContextType = {
|
|
3180
|
+
error: ErrorLike | null;
|
|
3181
|
+
isAdmin: boolean; // Computed from user.role === adminRole
|
|
3182
|
+
isLoggedIn: boolean; // Computed from !!user
|
|
3183
|
+
loading: boolean;
|
|
3184
|
+
refreshUser: RefetchFunction; // Apollo refetch
|
|
3185
|
+
user: OxUser;
|
|
3186
|
+
};
|
|
3187
|
+
```
|
|
3188
|
+
|
|
3189
|
+
### Example
|
|
3190
|
+
|
|
3191
|
+
```tsx
|
|
3192
|
+
// Wrap app
|
|
3193
|
+
<OxAuthProvider adminRole="ADMIN" pollInterval={300000}>
|
|
3194
|
+
<App />
|
|
3195
|
+
</OxAuthProvider>;
|
|
3196
|
+
|
|
3197
|
+
// Use in component
|
|
3198
|
+
function MyComponent() {
|
|
3199
|
+
const { user, isLoggedIn, isAdmin, loading, refreshUser } = useOxAuth();
|
|
3200
|
+
|
|
3201
|
+
if (loading) return <Spinner />;
|
|
3202
|
+
if (!isLoggedIn) return <Login />;
|
|
3203
|
+
|
|
3204
|
+
return <Dashboard user={user} />;
|
|
3205
|
+
}
|
|
3206
|
+
```
|
|
3207
|
+
|
|
3208
|
+
**Notes**:
|
|
3209
|
+
|
|
3210
|
+
- Apollo GraphQL integration with auto-polling
|
|
3211
|
+
- Default query fetches: id, username, email, role
|
|
3212
|
+
- Throws error if hook used outside provider
|
|
3213
|
+
|
|
3214
|
+
---
|
|
3215
|
+
|
|
3216
|
+
## OxDialogContext
|
|
3217
|
+
|
|
3218
|
+
Modal dialog management.
|
|
3219
|
+
|
|
3220
|
+
**Exports**: `OxDialogProvider`, `useOxDialog`
|
|
3221
|
+
|
|
3222
|
+
### Props
|
|
3223
|
+
|
|
3224
|
+
```tsx
|
|
3225
|
+
interface OxDialogProviderProps {
|
|
3226
|
+
children: ReactNode;
|
|
3227
|
+
maxDialogs?: number; // Default: 3
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
interface OxDialogOptions {
|
|
3231
|
+
className?: string;
|
|
3232
|
+
closeOnOutsideClick?: boolean;
|
|
3233
|
+
persistent?: boolean;
|
|
3234
|
+
showCloseButton?: boolean;
|
|
3235
|
+
size?: OxDialogSize;
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
// Callable API
|
|
3239
|
+
interface OxDialogContextApi {
|
|
3240
|
+
// Shorthand
|
|
3241
|
+
(content: ReactNode, options?: OxDialogOptions): string; // Returns dialog ID
|
|
3242
|
+
|
|
3243
|
+
// Methods
|
|
3244
|
+
dismiss: () => void; // Close top dialog
|
|
3245
|
+
dismissAll: () => void; // Close all dialogs
|
|
3246
|
+
open: (content: ReactNode, options?: OxDialogOptions) => string;
|
|
3247
|
+
}
|
|
3248
|
+
```
|
|
3249
|
+
|
|
3250
|
+
### Example
|
|
3251
|
+
|
|
3252
|
+
```tsx
|
|
3253
|
+
// Wrap app
|
|
3254
|
+
<OxDialogProvider maxDialogs={3}>
|
|
3255
|
+
<App />
|
|
3256
|
+
</OxDialogProvider>;
|
|
3257
|
+
|
|
3258
|
+
// Use in component
|
|
3259
|
+
function MyComponent() {
|
|
3260
|
+
const dialog = useOxDialog();
|
|
3261
|
+
|
|
3262
|
+
const showConfirm = () => {
|
|
3263
|
+
dialog(
|
|
3264
|
+
<div>
|
|
3265
|
+
<h2>Confirm Action</h2>
|
|
3266
|
+
<button onClick={dialog.dismiss}>Cancel</button>
|
|
3267
|
+
</div>,
|
|
3268
|
+
{ size: 'md', showCloseButton: true }
|
|
3269
|
+
);
|
|
3270
|
+
};
|
|
3271
|
+
|
|
3272
|
+
return <button onClick={showConfirm}>Show Dialog</button>;
|
|
3273
|
+
}
|
|
3274
|
+
```
|
|
3275
|
+
|
|
3276
|
+
**Notes**:
|
|
3277
|
+
|
|
3278
|
+
- Stack-based management (LIFO)
|
|
3279
|
+
- Auto body scroll lock when dialogs open
|
|
3280
|
+
- Max dialog limit prevents too many concurrent dialogs
|
|
3281
|
+
|
|
3282
|
+
---
|
|
3283
|
+
|
|
3284
|
+
## OxFeatureFlagContext
|
|
3285
|
+
|
|
3286
|
+
Feature flag system with dev mode.
|
|
3287
|
+
|
|
3288
|
+
**Exports**: `OxFeatureProvider`, `OxFeatureFlag`, `useOxFeature`, `DEFAULT_GET_FEATURES`
|
|
3289
|
+
|
|
3290
|
+
### Props
|
|
3291
|
+
|
|
3292
|
+
```tsx
|
|
3293
|
+
interface OxFeatureProviderProps {
|
|
3294
|
+
children: ReactNode;
|
|
3295
|
+
enableDevMode?: boolean; // Default: false (enables all features)
|
|
3296
|
+
getFeaturesQuery?: DocumentNode; // Custom GraphQL query
|
|
3297
|
+
options?: Parameters<typeof useQuery>[1]; // Apollo options
|
|
3298
|
+
pollInterval?: number; // Default: 300000 (5 minutes)
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
type FeatureFlags = Record<string, boolean>;
|
|
3302
|
+
|
|
3303
|
+
type OxFeatureContextType = {
|
|
3304
|
+
enableDevMode: boolean;
|
|
3305
|
+
error: ErrorLike | null;
|
|
3306
|
+
features: FeatureFlags;
|
|
3307
|
+
isEnabled: (featureName: string) => boolean;
|
|
3308
|
+
loading: boolean;
|
|
3309
|
+
refreshFeatures: RefetchFunction;
|
|
3310
|
+
};
|
|
3311
|
+
|
|
3312
|
+
interface OxFeatureFlagProps {
|
|
3313
|
+
children: ReactNode;
|
|
3314
|
+
className?: string;
|
|
3315
|
+
fallback?: ReactNode; // Rendered when feature disabled
|
|
3316
|
+
name: string; // Required - Feature flag name
|
|
3317
|
+
}
|
|
3318
|
+
```
|
|
3319
|
+
|
|
3320
|
+
### Example
|
|
3321
|
+
|
|
3322
|
+
```tsx
|
|
3323
|
+
// Wrap app
|
|
3324
|
+
<OxFeatureProvider enableDevMode={false}>
|
|
3325
|
+
<App />
|
|
3326
|
+
</OxFeatureProvider>;
|
|
3327
|
+
|
|
3328
|
+
// Use hook
|
|
3329
|
+
function MyComponent() {
|
|
3330
|
+
const { isEnabled, features } = useOxFeature();
|
|
3331
|
+
|
|
3332
|
+
if (isEnabled('new-dashboard')) {
|
|
3333
|
+
return <NewDashboard />;
|
|
3334
|
+
}
|
|
3335
|
+
return <OldDashboard />;
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
// Use component
|
|
3339
|
+
<OxFeatureFlag name="beta-feature" fallback={<ComingSoon />}>
|
|
3340
|
+
<BetaFeature />
|
|
3341
|
+
</OxFeatureFlag>;
|
|
3342
|
+
```
|
|
3343
|
+
|
|
3344
|
+
**Notes**:
|
|
3345
|
+
|
|
3346
|
+
- Dev mode enables ALL features and skips GraphQL query
|
|
3347
|
+
- Apollo integration with auto-polling
|
|
3348
|
+
- Declarative `OxFeatureFlag` component for conditional rendering
|
|
3349
|
+
|
|
3350
|
+
---
|
|
3351
|
+
|
|
3352
|
+
# Hooks (13)
|
|
3353
|
+
|
|
3354
|
+
## useControllableState
|
|
3355
|
+
|
|
3356
|
+
Manages state that can be both controlled and uncontrolled.
|
|
3357
|
+
|
|
3358
|
+
### Signature
|
|
3359
|
+
|
|
3360
|
+
```tsx
|
|
3361
|
+
function useControllableState<T>(params: {
|
|
3362
|
+
defaultValue?: T;
|
|
3363
|
+
name?: string; // Default: 'Component'
|
|
3364
|
+
onChange?: (value: T | undefined) => void;
|
|
3365
|
+
value?: T;
|
|
3366
|
+
}): [
|
|
3367
|
+
T | undefined, // Current value
|
|
3368
|
+
(value: T | undefined | ((prev: T | undefined) => T | undefined)) => void, // Setter
|
|
3369
|
+
];
|
|
3370
|
+
```
|
|
3371
|
+
|
|
3372
|
+
### Example
|
|
3373
|
+
|
|
3374
|
+
```tsx
|
|
3375
|
+
function MyComponent({ value, defaultValue, onChange }) {
|
|
3376
|
+
const [internalValue, setValue] = useControllableState({
|
|
3377
|
+
value,
|
|
3378
|
+
defaultValue,
|
|
3379
|
+
onChange,
|
|
3380
|
+
});
|
|
3381
|
+
|
|
3382
|
+
return <input value={internalValue} onChange={(e) => setValue(e.target.value)} />;
|
|
3383
|
+
}
|
|
3384
|
+
```
|
|
3385
|
+
|
|
3386
|
+
**Notes**:
|
|
3387
|
+
|
|
3388
|
+
- Provides dev warnings when switching between modes
|
|
3389
|
+
- Uses `Object.is()` for efficient comparison
|
|
3390
|
+
- Follows Radix UI patterns
|
|
3391
|
+
|
|
3392
|
+
---
|
|
3393
|
+
|
|
3394
|
+
## useFileUpload
|
|
3395
|
+
|
|
3396
|
+
Handles file uploads with validation and progress.
|
|
3397
|
+
|
|
3398
|
+
### Signature
|
|
3399
|
+
|
|
3400
|
+
```tsx
|
|
3401
|
+
function useFileUpload<T extends Record<string, unknown>>(options: {
|
|
3402
|
+
endpoint: string; // Required
|
|
3403
|
+
headers?: Record<string, string>;
|
|
3404
|
+
initialData: T; // Required
|
|
3405
|
+
validationRules?: ValidatorConfig;
|
|
3406
|
+
}): UseFileUploadReturn<T>;
|
|
3407
|
+
|
|
3408
|
+
// Returns UseFormStateReturn<T> + additional:
|
|
3409
|
+
{
|
|
3410
|
+
cancel: () => void;
|
|
3411
|
+
isUploading: boolean;
|
|
3412
|
+
upload: (options?: {
|
|
3413
|
+
onBefore?: () => void;
|
|
3414
|
+
onCancel?: (error: unknown) => void;
|
|
3415
|
+
onError?: (error: unknown) => void;
|
|
3416
|
+
onFinish?: () => void;
|
|
3417
|
+
onSuccess?: (response: AxiosResponse) => void;
|
|
3418
|
+
onUpload?: (progressEvent: AxiosProgressEvent) => void;
|
|
3419
|
+
skipValidation?: boolean;
|
|
3420
|
+
}) => void;
|
|
3421
|
+
}
|
|
3422
|
+
```
|
|
3423
|
+
|
|
3424
|
+
### Example
|
|
3425
|
+
|
|
3426
|
+
```tsx
|
|
3427
|
+
function FileUploadForm() {
|
|
3428
|
+
const { data, setData, upload, isUploading, cancel, errors } = useFileUpload({
|
|
3429
|
+
endpoint: '/api/upload',
|
|
3430
|
+
initialData: { file: null, description: '' },
|
|
3431
|
+
validationRules: {
|
|
3432
|
+
file: ['required', 'file', 'fileSize:5MB'],
|
|
3433
|
+
description: ['required', 'minLength:10'],
|
|
3434
|
+
},
|
|
3435
|
+
});
|
|
3436
|
+
|
|
3437
|
+
return (
|
|
3438
|
+
<form
|
|
3439
|
+
onSubmit={(e) => {
|
|
3440
|
+
e.preventDefault();
|
|
3441
|
+
upload();
|
|
3442
|
+
}}
|
|
3443
|
+
>
|
|
3444
|
+
<input type="file" onChange={(e) => setData('file', e.target.files[0])} />
|
|
3445
|
+
<input value={data.description} onChange={(e) => setData('description', e.target.value)} />
|
|
3446
|
+
<button type="submit" disabled={isUploading}>
|
|
3447
|
+
Upload
|
|
3448
|
+
</button>
|
|
3449
|
+
{isUploading && <button onClick={cancel}>Cancel</button>}
|
|
3450
|
+
</form>
|
|
3451
|
+
);
|
|
3452
|
+
}
|
|
3453
|
+
```
|
|
3454
|
+
|
|
3455
|
+
**Notes**:
|
|
3456
|
+
|
|
3457
|
+
- Axios `multipart/form-data` uploads
|
|
3458
|
+
- Upload progress tracking
|
|
3459
|
+
- Cancellation via `AbortController`
|
|
3460
|
+
- Pre-upload validation
|
|
3461
|
+
|
|
3462
|
+
---
|
|
3463
|
+
|
|
3464
|
+
## useFilterFetch
|
|
3465
|
+
|
|
3466
|
+
Manages filter state with REST API fetch requests.
|
|
3467
|
+
|
|
3468
|
+
### Signature
|
|
3469
|
+
|
|
3470
|
+
```tsx
|
|
3471
|
+
function useFilterFetch<T extends Record<string, any>, TResponse = unknown>(options: {
|
|
3472
|
+
headers?: Record<string, string>;
|
|
3473
|
+
initialData: T; // Required - initial filter values
|
|
3474
|
+
method?: 'GET' | 'POST'; // Default: 'GET'
|
|
3475
|
+
url: string; // Required - API endpoint
|
|
3476
|
+
validationRules?: ValidatorConfig;
|
|
3477
|
+
}): UseFilterFetchReturn<T, TResponse>;
|
|
3478
|
+
|
|
3479
|
+
interface UseFilterFetchReturn<T, TResponse> {
|
|
3480
|
+
data: TResponse | undefined; // Response data
|
|
3481
|
+
error: unknown;
|
|
3482
|
+
errors: Record<keyof T, string[]>; // Validation errors per field
|
|
3483
|
+
filter: T; // Current filter values
|
|
3484
|
+
isDirty: boolean;
|
|
3485
|
+
isLoading: boolean;
|
|
3486
|
+
isValid: boolean;
|
|
3487
|
+
query: (options?: FilterFetchQueryOptions<TResponse>) => void;
|
|
3488
|
+
refetch: () => void; // Re-run with current filters
|
|
3489
|
+
reset: () => void;
|
|
3490
|
+
setFilter: (key: keyof T, value: T[keyof T]) => void;
|
|
3491
|
+
validate: () => boolean;
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
interface FilterFetchQueryOptions<TResponse> {
|
|
3495
|
+
onBefore?: () => void;
|
|
3496
|
+
onError?: (error: unknown) => void;
|
|
3497
|
+
onFinish?: () => void;
|
|
3498
|
+
onSuccess?: (response: TResponse) => void;
|
|
3499
|
+
skipValidation?: boolean;
|
|
3500
|
+
}
|
|
3501
|
+
```
|
|
3502
|
+
|
|
3503
|
+
### Example
|
|
3504
|
+
|
|
3505
|
+
```tsx
|
|
3506
|
+
function ProductList() {
|
|
3507
|
+
const { data, filter, setFilter, query, isLoading } = useFilterFetch<
|
|
3508
|
+
{ category: string; search: string },
|
|
3509
|
+
Product[]
|
|
3510
|
+
>({
|
|
3511
|
+
url: '/api/products',
|
|
3512
|
+
initialData: { category: '', search: '' },
|
|
3513
|
+
});
|
|
3514
|
+
|
|
3515
|
+
return (
|
|
3516
|
+
<div>
|
|
3517
|
+
<input
|
|
3518
|
+
value={filter.search}
|
|
3519
|
+
onChange={(e) => setFilter('search', e.target.value)}
|
|
3520
|
+
onKeyDown={(e) => e.key === 'Enter' && query()}
|
|
3521
|
+
/>
|
|
3522
|
+
<select
|
|
3523
|
+
value={filter.category}
|
|
3524
|
+
onChange={(e) => {
|
|
3525
|
+
setFilter('category', e.target.value);
|
|
3526
|
+
query();
|
|
3527
|
+
}}
|
|
3528
|
+
>
|
|
3529
|
+
<option value="">All</option>
|
|
3530
|
+
<option value="electronics">Electronics</option>
|
|
3531
|
+
</select>
|
|
3532
|
+
{isLoading ? <Spinner /> : data?.map(product => <ProductCard key={product.id} {...product} />)}
|
|
3533
|
+
</div>
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
3536
|
+
```
|
|
3537
|
+
|
|
3538
|
+
**Notes**:
|
|
3539
|
+
|
|
3540
|
+
- Similar to `useFilterQuery` but for REST APIs instead of GraphQL
|
|
3541
|
+
- Supports GET (query params) and POST (body) methods
|
|
3542
|
+
- Built on `useFormState` for validation and state management
|
|
3543
|
+
|
|
3544
|
+
---
|
|
3545
|
+
|
|
3546
|
+
## useFilterQuery
|
|
3547
|
+
|
|
3548
|
+
Manages filter state with GraphQL queries.
|
|
3549
|
+
|
|
3550
|
+
### Signature
|
|
3551
|
+
|
|
3552
|
+
```tsx
|
|
3553
|
+
function useFilterQuery<T extends Record<string, any>, TResponse = unknown>(options: {
|
|
3554
|
+
query: DocumentNode; // Required
|
|
3555
|
+
headers?: Record<string, string>;
|
|
3556
|
+
initialData: T; // Required
|
|
3557
|
+
validationRules?: ValidatorConfig;
|
|
3558
|
+
options?: Parameters<typeof useLazyQuery<TResponse>>[1];
|
|
3559
|
+
}): UseFilterQueryReturn<T, TResponse>;
|
|
3560
|
+
|
|
3561
|
+
// Returns:
|
|
3562
|
+
{
|
|
3563
|
+
data: TResponse | undefined; // Query response
|
|
3564
|
+
error: unknown;
|
|
3565
|
+
filter: T; // Filter values
|
|
3566
|
+
setFilter: (data: Partial<T>) => void | ((path: string, value: any) => void);
|
|
3567
|
+
isLoading: boolean;
|
|
3568
|
+
query: (options?: QueryOptions<TResponse>) => void;
|
|
3569
|
+
refetch: () => void;
|
|
3570
|
+
// + UseFormState methods (errors, validate, etc.)
|
|
3571
|
+
}
|
|
3572
|
+
```
|
|
3573
|
+
|
|
3574
|
+
### Example
|
|
3575
|
+
|
|
3576
|
+
```tsx
|
|
3577
|
+
const GET_USERS = gql`
|
|
3578
|
+
query GetUsers($filters: UserFilters) {
|
|
3579
|
+
users(filters: $filters) {
|
|
3580
|
+
id
|
|
3581
|
+
name
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
`;
|
|
3585
|
+
|
|
3586
|
+
function UserFilter() {
|
|
3587
|
+
const { filter, setFilter, query, data, isLoading } = useFilterQuery({
|
|
3588
|
+
query: GET_USERS,
|
|
3589
|
+
initialData: { name: '', role: '' },
|
|
3590
|
+
});
|
|
3591
|
+
|
|
3592
|
+
return (
|
|
3593
|
+
<div>
|
|
3594
|
+
<input value={filter.name} onChange={(e) => setFilter('name', e.target.value)} />
|
|
3595
|
+
<button onClick={() => query()}>Search</button>
|
|
3596
|
+
{isLoading && <Spinner />}
|
|
3597
|
+
{data?.users.map((user) => (
|
|
3598
|
+
<div key={user.id}>{user.name}</div>
|
|
3599
|
+
))}
|
|
3600
|
+
</div>
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3603
|
+
```
|
|
3604
|
+
|
|
3605
|
+
**Notes**:
|
|
3606
|
+
|
|
3607
|
+
- Apollo `useLazyQuery` integration
|
|
3608
|
+
- Default fetch policy: `network-only`
|
|
3609
|
+
- Passes filters as `{ filters: data }` to query
|
|
3610
|
+
|
|
3611
|
+
---
|
|
3612
|
+
|
|
3613
|
+
## useFocus
|
|
3614
|
+
|
|
3615
|
+
Manages focus within a container.
|
|
3616
|
+
|
|
3617
|
+
### Signature
|
|
3618
|
+
|
|
3619
|
+
```tsx
|
|
3620
|
+
function useFocus(params: {
|
|
3621
|
+
containerRef: RefObject<HTMLElement | null>; // Required
|
|
3622
|
+
options?: {
|
|
3623
|
+
autoFocus?: boolean; // Default: false
|
|
3624
|
+
enabled?: boolean; // Default: true
|
|
3625
|
+
restoreFocus?: boolean; // Default: false
|
|
3626
|
+
trapFocus?: boolean; // Default: false
|
|
3627
|
+
};
|
|
3628
|
+
}): {
|
|
3629
|
+
focusFirst: () => void;
|
|
3630
|
+
focusLast: () => void;
|
|
3631
|
+
getCurrentFocus: () => HTMLElement | null;
|
|
3632
|
+
getFocusableElements: () => HTMLElement[];
|
|
3633
|
+
};
|
|
3634
|
+
```
|
|
3635
|
+
|
|
3636
|
+
### Example
|
|
3637
|
+
|
|
3638
|
+
```tsx
|
|
3639
|
+
function Modal() {
|
|
3640
|
+
const containerRef = useRef(null);
|
|
3641
|
+
const { focusFirst } = useFocus({
|
|
3642
|
+
containerRef,
|
|
3643
|
+
options: { autoFocus: true, trapFocus: true, restoreFocus: true },
|
|
3644
|
+
});
|
|
3645
|
+
|
|
3646
|
+
return (
|
|
3647
|
+
<div ref={containerRef}>
|
|
3648
|
+
<button>First</button>
|
|
3649
|
+
<button>Second</button>
|
|
3650
|
+
<button>Last</button>
|
|
3651
|
+
</div>
|
|
3652
|
+
);
|
|
3653
|
+
}
|
|
3654
|
+
```
|
|
3655
|
+
|
|
3656
|
+
**Notes**:
|
|
3657
|
+
|
|
3658
|
+
- Auto-focus on mount
|
|
3659
|
+
- Circular Tab navigation with `trapFocus`
|
|
3660
|
+
- Focus restoration on unmount
|
|
3661
|
+
- Shift+Tab support
|
|
3662
|
+
|
|
3663
|
+
---
|
|
3664
|
+
|
|
3665
|
+
## useForm
|
|
3666
|
+
|
|
3667
|
+
Form state with GraphQL mutations.
|
|
3668
|
+
|
|
3669
|
+
### Signature
|
|
3670
|
+
|
|
3671
|
+
```tsx
|
|
3672
|
+
function useForm<T extends Record<string, any>, TResponse = unknown>(options: {
|
|
3673
|
+
operation: DocumentNode; // Required - GraphQL mutation
|
|
3674
|
+
headers?: Record<string, string>;
|
|
3675
|
+
initialData: T; // Required
|
|
3676
|
+
validationRules?: ValidatorConfig;
|
|
3677
|
+
options?: Parameters<typeof useMutation<TResponse>>[1];
|
|
3678
|
+
}): UseFormReturn<T, TResponse>;
|
|
3679
|
+
|
|
3680
|
+
// Returns UseFormStateReturn<T> + additional:
|
|
3681
|
+
{
|
|
3682
|
+
isLoading: boolean;
|
|
3683
|
+
submit: (options?: {
|
|
3684
|
+
onBefore?: () => void;
|
|
3685
|
+
onError?: (error: unknown) => void;
|
|
3686
|
+
onFinish?: () => void;
|
|
3687
|
+
onSuccess?: (response: any) => void;
|
|
3688
|
+
skipValidation?: boolean;
|
|
3689
|
+
}) => void;
|
|
3690
|
+
transform: (fn: (data: T) => unknown) => Omit<UseFormReturn<T, TResponse>, 'transform'>;
|
|
3691
|
+
}
|
|
3692
|
+
```
|
|
3693
|
+
|
|
3694
|
+
### Example
|
|
3695
|
+
|
|
3696
|
+
```tsx
|
|
3697
|
+
const CREATE_USER = gql`
|
|
3698
|
+
mutation CreateUser($input: UserInput!) {
|
|
3699
|
+
createUser(input: $input) {
|
|
3700
|
+
id
|
|
3701
|
+
name
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
`;
|
|
3705
|
+
|
|
3706
|
+
function UserForm() {
|
|
3707
|
+
const { data, setData, submit, isLoading, errors } = useForm({
|
|
3708
|
+
operation: CREATE_USER,
|
|
3709
|
+
initialData: { name: '', email: '' },
|
|
3710
|
+
validationRules: {
|
|
3711
|
+
name: ['required', 'minLength:2'],
|
|
3712
|
+
email: ['required', 'email'],
|
|
3713
|
+
},
|
|
3714
|
+
});
|
|
3715
|
+
|
|
3716
|
+
return (
|
|
3717
|
+
<form
|
|
3718
|
+
onSubmit={(e) => {
|
|
3719
|
+
e.preventDefault();
|
|
3720
|
+
submit();
|
|
3721
|
+
}}
|
|
3722
|
+
>
|
|
3723
|
+
<input value={data.name} onChange={(e) => setData('name', e.target.value)} />
|
|
3724
|
+
{errors.name && <OxError>{errors.name}</OxError>}
|
|
3725
|
+
<button type="submit" disabled={isLoading}>
|
|
3726
|
+
Submit
|
|
3727
|
+
</button>
|
|
3728
|
+
</form>
|
|
3729
|
+
);
|
|
3730
|
+
}
|
|
3731
|
+
```
|
|
3732
|
+
|
|
3733
|
+
**Notes**:
|
|
3734
|
+
|
|
3735
|
+
- Apollo `useMutation` integration
|
|
3736
|
+
- Passes data as `{ input: data }`
|
|
3737
|
+
- `transform()` for pre-submission data transformation
|
|
3738
|
+
- Pre-submission validation
|
|
3739
|
+
|
|
3740
|
+
---
|
|
3741
|
+
|
|
3742
|
+
## useFormFetch
|
|
3743
|
+
|
|
3744
|
+
Form state management with REST API submission via fetch.
|
|
3745
|
+
|
|
3746
|
+
### Signature
|
|
3747
|
+
|
|
3748
|
+
```tsx
|
|
3749
|
+
function useFormFetch<T extends Record<string, any>, TResponse = unknown>(options: {
|
|
3750
|
+
headers?: Record<string, string>;
|
|
3751
|
+
initialData: T; // Required
|
|
3752
|
+
method?: 'DELETE' | 'PATCH' | 'POST' | 'PUT'; // Default: 'POST'
|
|
3753
|
+
url: string; // Required - API endpoint
|
|
3754
|
+
validationRules?: ValidatorConfig;
|
|
3755
|
+
}): UseFormFetchReturn<T, TResponse>;
|
|
3756
|
+
|
|
3757
|
+
interface UseFormFetchReturn<T, TResponse> extends UseFormStateReturn<T> {
|
|
3758
|
+
isLoading: boolean;
|
|
3759
|
+
submit: (options?: FormFetchSubmitOptions<TResponse>) => void;
|
|
3760
|
+
transform: (fn: (data: T) => unknown) => Omit<UseFormFetchReturn<T, TResponse>, 'transform'>;
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
interface FormFetchSubmitOptions<TResponse> {
|
|
3764
|
+
onBefore?: () => void;
|
|
3765
|
+
onError?: (error: unknown) => void;
|
|
3766
|
+
onFinish?: () => void;
|
|
3767
|
+
onSuccess?: (response: TResponse) => void;
|
|
3768
|
+
skipValidation?: boolean;
|
|
3769
|
+
}
|
|
3770
|
+
```
|
|
3771
|
+
|
|
3772
|
+
### Example
|
|
3773
|
+
|
|
3774
|
+
```tsx
|
|
3775
|
+
function CreateUserForm() {
|
|
3776
|
+
const { data, setData, submit, errors, isLoading } = useFormFetch<
|
|
3777
|
+
{ email: string; name: string },
|
|
3778
|
+
{ id: string }
|
|
3779
|
+
>({
|
|
3780
|
+
url: '/api/users',
|
|
3781
|
+
method: 'POST',
|
|
3782
|
+
initialData: { email: '', name: '' },
|
|
3783
|
+
validationRules: {
|
|
3784
|
+
email: ['required', 'email'],
|
|
3785
|
+
name: ['required', 'minLength:2'],
|
|
3786
|
+
},
|
|
3787
|
+
});
|
|
3788
|
+
|
|
3789
|
+
return (
|
|
3790
|
+
<form onSubmit={(e) => { e.preventDefault(); submit({ onSuccess: (res) => navigate(`/users/${res.id}`) }); }}>
|
|
3791
|
+
<input value={data.name} onChange={(e) => setData('name', e.target.value)} />
|
|
3792
|
+
{errors.name && <span>{errors.name[0]}</span>}
|
|
3793
|
+
<input value={data.email} onChange={(e) => setData('email', e.target.value)} />
|
|
3794
|
+
{errors.email && <span>{errors.email[0]}</span>}
|
|
3795
|
+
<button type="submit" disabled={isLoading}>Create</button>
|
|
3796
|
+
</form>
|
|
3797
|
+
);
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
// With data transformation
|
|
3801
|
+
const { submit, transform } = useFormFetch({ ... });
|
|
3802
|
+
transform((data) => ({ ...data, timestamp: Date.now() })).submit();
|
|
3803
|
+
```
|
|
3804
|
+
|
|
3805
|
+
**Notes**:
|
|
3806
|
+
|
|
3807
|
+
- Similar to `useForm` but for REST APIs instead of GraphQL
|
|
3808
|
+
- Supports all HTTP methods (POST, PUT, PATCH, DELETE)
|
|
3809
|
+
- `transform()` allows modifying data before submission
|
|
3810
|
+
- Prevents duplicate submissions while loading
|
|
3811
|
+
|
|
3812
|
+
---
|
|
3813
|
+
|
|
3814
|
+
## useFormState
|
|
3815
|
+
|
|
3816
|
+
Core form state management with Zod validation.
|
|
3817
|
+
|
|
3818
|
+
### Signature
|
|
3819
|
+
|
|
3820
|
+
```tsx
|
|
3821
|
+
function useFormState<T extends Record<string, unknown>>(options: {
|
|
3822
|
+
initialData: T; // Required (cannot contain 'root' key)
|
|
3823
|
+
validationRules?: ValidatorConfig;
|
|
3824
|
+
}): {
|
|
3825
|
+
data: T;
|
|
3826
|
+
setData: {
|
|
3827
|
+
(data: Partial<T>): void; // Set multiple fields
|
|
3828
|
+
(path: string, value: any): void; // Set single field by path
|
|
3829
|
+
};
|
|
3830
|
+
errors: Record<string, string | string[]>;
|
|
3831
|
+
hasErrors: boolean;
|
|
3832
|
+
isDirty: boolean;
|
|
3833
|
+
clearError: (field: string) => void;
|
|
3834
|
+
clearErrors: () => void;
|
|
3835
|
+
setError: (field: string, error: string | string[]) => void;
|
|
3836
|
+
hasError: (field: string) => boolean;
|
|
3837
|
+
reset: () => void;
|
|
3838
|
+
validate: () => boolean;
|
|
3839
|
+
};
|
|
3840
|
+
```
|
|
3841
|
+
|
|
3842
|
+
### Built-in Validation Rules
|
|
3843
|
+
|
|
3844
|
+
**Simple Rules**:
|
|
3845
|
+
|
|
3846
|
+
- `'nullable'`, `'required'`, `'string'`, `'email'`, `'url'`, `'number'`, `'integer'`, `'positive'`, `'negative'`, `'ipv4'`, `'boolean'`, `'date'`, `'array'`, `'object'`, `'file'`
|
|
3847
|
+
|
|
3848
|
+
**Parametrized Rules**:
|
|
3849
|
+
|
|
3850
|
+
- `'min:${number}'`, `'max:${number}'`, `'minLength:${number}'`, `'maxLength:${number}'`, `'pattern:${string}'`, `'fileSize:${string}'` (e.g., '5MB'), `'fileType:${string}'` (e.g., 'image/\*'), `'arrayMinLength:${number}'`, `'arrayMaxLength:${number}'`
|
|
3851
|
+
|
|
3852
|
+
**Array Rules**:
|
|
3853
|
+
|
|
3854
|
+
- `'arrayEach:string'`, `'arrayEach:number'`, `'arrayEach:email'`, `'arrayEach:url'`, `'arrayEach:file'`, `'arrayEach:fileSize:${string}'`, `'arrayEach:fileType:${string}'`
|
|
3855
|
+
|
|
3856
|
+
**Custom Validators**:
|
|
3857
|
+
|
|
3858
|
+
```tsx
|
|
3859
|
+
import { validator } from '@/hooks';
|
|
3860
|
+
|
|
3861
|
+
const customRule = validator((value) => value !== 'forbidden', 'This value is not allowed');
|
|
3862
|
+
```
|
|
3863
|
+
|
|
3864
|
+
### Example
|
|
3865
|
+
|
|
3866
|
+
```tsx
|
|
3867
|
+
function ContactForm() {
|
|
3868
|
+
const { data, setData, errors, validate, isDirty } = useFormState({
|
|
3869
|
+
initialData: { name: '', email: '', age: 0 },
|
|
3870
|
+
validationRules: {
|
|
3871
|
+
name: ['required', 'minLength:2'],
|
|
3872
|
+
email: ['required', 'email'],
|
|
3873
|
+
age: ['required', 'number', 'min:18'],
|
|
3874
|
+
},
|
|
3875
|
+
});
|
|
3876
|
+
|
|
3877
|
+
const handleSubmit = () => {
|
|
3878
|
+
if (validate()) {
|
|
3879
|
+
console.log('Valid data:', data);
|
|
3880
|
+
}
|
|
3881
|
+
};
|
|
3882
|
+
|
|
3883
|
+
return (
|
|
3884
|
+
<form>
|
|
3885
|
+
<input value={data.name} onChange={(e) => setData('name', e.target.value)} />
|
|
3886
|
+
{errors.name && <span>{errors.name}</span>}
|
|
3887
|
+
<button onClick={handleSubmit}>Submit</button>
|
|
3888
|
+
</form>
|
|
3889
|
+
);
|
|
3890
|
+
}
|
|
3891
|
+
```
|
|
3892
|
+
|
|
3893
|
+
**Notes**:
|
|
3894
|
+
|
|
3895
|
+
- Zod v4 validation
|
|
3896
|
+
- Nested path support (dot notation)
|
|
3897
|
+
- File size: B, KB, MB, GB, TB
|
|
3898
|
+
- File type wildcards: `image/*`
|
|
3899
|
+
|
|
3900
|
+
---
|
|
3901
|
+
|
|
3902
|
+
## useKeyboard
|
|
3903
|
+
|
|
3904
|
+
Keyboard event handling.
|
|
3905
|
+
|
|
3906
|
+
### Signature
|
|
3907
|
+
|
|
3908
|
+
```tsx
|
|
3909
|
+
function useKeyboard(options: {
|
|
3910
|
+
allowRepeat?: boolean; // Default: false
|
|
3911
|
+
enabled?: boolean; // Default: true
|
|
3912
|
+
onArrowDown?: () => void;
|
|
3913
|
+
onArrowUp?: () => void;
|
|
3914
|
+
onEnter?: () => void;
|
|
3915
|
+
onEscape?: () => void;
|
|
3916
|
+
onTab?: () => void;
|
|
3917
|
+
}): void;
|
|
3918
|
+
```
|
|
3919
|
+
|
|
3920
|
+
### Example
|
|
3921
|
+
|
|
3922
|
+
```tsx
|
|
3923
|
+
function SearchBox() {
|
|
3924
|
+
const [results, setResults] = useState([]);
|
|
3925
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
3926
|
+
|
|
3927
|
+
useKeyboard({
|
|
3928
|
+
onArrowDown: () => setSelectedIndex((i) => Math.min(i + 1, results.length - 1)),
|
|
3929
|
+
onArrowUp: () => setSelectedIndex((i) => Math.max(i - 1, 0)),
|
|
3930
|
+
onEnter: () => selectResult(results[selectedIndex]),
|
|
3931
|
+
onEscape: () => closeResults(),
|
|
3932
|
+
});
|
|
3933
|
+
|
|
3934
|
+
return <div>...</div>;
|
|
3935
|
+
}
|
|
3936
|
+
```
|
|
3937
|
+
|
|
3938
|
+
**Notes**:
|
|
3939
|
+
|
|
3940
|
+
- Document-level listeners
|
|
3941
|
+
- Prevents default for all keys except Tab
|
|
3942
|
+
- Key repeat control
|
|
3943
|
+
|
|
3944
|
+
---
|
|
3945
|
+
|
|
3946
|
+
## useOverflowDetection
|
|
3947
|
+
|
|
3948
|
+
Detects overflow and hides overflowing elements.
|
|
3949
|
+
|
|
3950
|
+
### Signature
|
|
3951
|
+
|
|
3952
|
+
```tsx
|
|
3953
|
+
function useOverflowDetection(options?: {
|
|
3954
|
+
selector?: string; // Default: '[data-ox-state="selected"]'
|
|
3955
|
+
}): {
|
|
3956
|
+
overflowAmount: number; // Number of hidden items
|
|
3957
|
+
checkOverflow: () => void;
|
|
3958
|
+
handleResize: (node: HTMLDivElement | null) => () => void; // Ref callback with cleanup
|
|
3959
|
+
};
|
|
3960
|
+
```
|
|
3961
|
+
|
|
3962
|
+
### Example
|
|
3963
|
+
|
|
3964
|
+
```tsx
|
|
3965
|
+
function TagList({ tags }) {
|
|
3966
|
+
const { overflowAmount, handleResize } = useOverflowDetection();
|
|
3967
|
+
|
|
3968
|
+
return (
|
|
3969
|
+
<div ref={handleResize}>
|
|
3970
|
+
{tags.map((tag) => (
|
|
3971
|
+
<span key={tag} data-ox-state="selected">
|
|
3972
|
+
{tag}
|
|
3973
|
+
</span>
|
|
3974
|
+
))}
|
|
3975
|
+
{overflowAmount > 0 && <span>+{overflowAmount} more</span>}
|
|
3976
|
+
</div>
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
```
|
|
3980
|
+
|
|
3981
|
+
**Notes**:
|
|
3982
|
+
|
|
3983
|
+
- ResizeObserver integration
|
|
3984
|
+
- Automatically hides overflow from end
|
|
3985
|
+
- Returns hidden element count
|
|
3986
|
+
|
|
3987
|
+
---
|
|
3988
|
+
|
|
3989
|
+
## useScrollSpy
|
|
3990
|
+
|
|
3991
|
+
Tracks active section based on scroll position using IntersectionObserver.
|
|
3992
|
+
|
|
3993
|
+
### Signature
|
|
3994
|
+
|
|
3995
|
+
```tsx
|
|
3996
|
+
function useScrollSpy(
|
|
3997
|
+
sectionIds: string[], // Array of element IDs to observe
|
|
3998
|
+
options?: UseScrollSpyOptions
|
|
3999
|
+
): UseScrollSpyReturn;
|
|
4000
|
+
|
|
4001
|
+
interface UseScrollSpyOptions {
|
|
4002
|
+
root?: Element | null; // Scroll container (default: viewport)
|
|
4003
|
+
rootMargin?: string; // Margin around root (default: '0px')
|
|
4004
|
+
threshold?: number | number[]; // Visibility threshold (default: 0)
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
interface UseScrollSpyReturn {
|
|
4008
|
+
activeId: string; // Currently visible section ID
|
|
4009
|
+
setActiveId: (id: string) => void; // Manually set active section
|
|
4010
|
+
}
|
|
4011
|
+
```
|
|
4012
|
+
|
|
4013
|
+
### Example
|
|
4014
|
+
|
|
4015
|
+
```tsx
|
|
4016
|
+
function TableOfContents() {
|
|
4017
|
+
const sectionIds = ['intro', 'features', 'installation', 'api'];
|
|
4018
|
+
const { activeId } = useScrollSpy(sectionIds, { rootMargin: '-100px 0px -50% 0px' });
|
|
4019
|
+
|
|
4020
|
+
return (
|
|
4021
|
+
<nav>
|
|
4022
|
+
{sectionIds.map(id => (
|
|
4023
|
+
<a
|
|
4024
|
+
key={id}
|
|
4025
|
+
href={`#${id}`}
|
|
4026
|
+
className={activeId === id ? 'active' : ''}
|
|
4027
|
+
>
|
|
4028
|
+
{id}
|
|
4029
|
+
</a>
|
|
4030
|
+
))}
|
|
4031
|
+
</nav>
|
|
4032
|
+
);
|
|
4033
|
+
}
|
|
4034
|
+
```
|
|
4035
|
+
|
|
4036
|
+
**Notes**:
|
|
4037
|
+
|
|
4038
|
+
- Uses IntersectionObserver for efficient scroll tracking
|
|
4039
|
+
- Automatically updates as user scrolls through sections
|
|
4040
|
+
- Commonly used with `OxPageOutline` component
|
|
4041
|
+
|
|
4042
|
+
---
|
|
4043
|
+
|
|
4044
|
+
## useSize
|
|
4045
|
+
|
|
4046
|
+
Tracks element dimensions using ResizeObserver.
|
|
4047
|
+
|
|
4048
|
+
### Signature
|
|
4049
|
+
|
|
4050
|
+
```tsx
|
|
4051
|
+
function useSize(element: Element | null): Size | undefined;
|
|
4052
|
+
|
|
4053
|
+
interface Size {
|
|
4054
|
+
height: number;
|
|
4055
|
+
width: number;
|
|
4056
|
+
}
|
|
4057
|
+
```
|
|
4058
|
+
|
|
4059
|
+
### Example
|
|
4060
|
+
|
|
4061
|
+
```tsx
|
|
4062
|
+
function ResponsiveComponent() {
|
|
4063
|
+
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
4064
|
+
const size = useSize(ref);
|
|
4065
|
+
|
|
4066
|
+
return (
|
|
4067
|
+
<div ref={setRef}>
|
|
4068
|
+
{size && (
|
|
4069
|
+
<p>Width: {size.width}px, Height: {size.height}px</p>
|
|
4070
|
+
)}
|
|
4071
|
+
{size && size.width < 400 ? <CompactView /> : <FullView />}
|
|
4072
|
+
</div>
|
|
4073
|
+
);
|
|
4074
|
+
}
|
|
4075
|
+
```
|
|
4076
|
+
|
|
4077
|
+
**Notes**:
|
|
4078
|
+
|
|
4079
|
+
- Returns `undefined` when element is null
|
|
4080
|
+
- Updates automatically on resize
|
|
4081
|
+
- Values are rounded to nearest integer
|
|
4082
|
+
|
|
4083
|
+
---
|
|
4084
|
+
|
|
4085
|
+
## useTheme
|
|
4086
|
+
|
|
4087
|
+
Theme management with localStorage persistence.
|
|
4088
|
+
|
|
4089
|
+
**Export alias**: `useOxTheme`
|
|
4090
|
+
|
|
4091
|
+
### Signature
|
|
4092
|
+
|
|
4093
|
+
```tsx
|
|
4094
|
+
function useTheme(options?: UseThemeOptions): UseThemeReturn;
|
|
4095
|
+
|
|
4096
|
+
type Theme = 'light' | 'dark';
|
|
4097
|
+
|
|
4098
|
+
interface UseThemeOptions {
|
|
4099
|
+
defaultTheme?: Theme; // Default: 'light'
|
|
4100
|
+
storageKey?: string; // localStorage key (default: 'ox-theme')
|
|
4101
|
+
}
|
|
4102
|
+
|
|
4103
|
+
interface UseThemeReturn {
|
|
4104
|
+
setTheme: (theme: Theme) => void;
|
|
4105
|
+
theme: Theme;
|
|
4106
|
+
toggleTheme: () => void;
|
|
4107
|
+
}
|
|
4108
|
+
```
|
|
4109
|
+
|
|
4110
|
+
### Example
|
|
4111
|
+
|
|
4112
|
+
```tsx
|
|
4113
|
+
function ThemeToggle() {
|
|
4114
|
+
const { theme, toggleTheme } = useTheme();
|
|
4115
|
+
|
|
4116
|
+
return (
|
|
4117
|
+
<button onClick={toggleTheme}>
|
|
4118
|
+
{theme === 'light' ? '🌙' : '☀️'}
|
|
4119
|
+
</button>
|
|
4120
|
+
);
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
// With custom storage key
|
|
4124
|
+
const { theme, setTheme } = useTheme({
|
|
4125
|
+
defaultTheme: 'dark',
|
|
4126
|
+
storageKey: 'my-app-theme',
|
|
4127
|
+
});
|
|
4128
|
+
```
|
|
4129
|
+
|
|
4130
|
+
**Notes**:
|
|
4131
|
+
|
|
4132
|
+
- Persists theme preference to localStorage
|
|
4133
|
+
- Syncs across browser tabs
|
|
4134
|
+
- Respects system preference as initial default
|
|
4135
|
+
|
|
4136
|
+
---
|
|
4137
|
+
|
|
4138
|
+
# Route Guards (3)
|
|
4139
|
+
|
|
4140
|
+
## OxGuestRoute
|
|
4141
|
+
|
|
4142
|
+
Protects routes for unauthenticated users only (e.g., login, signup).
|
|
4143
|
+
|
|
4144
|
+
### Props
|
|
4145
|
+
|
|
4146
|
+
```tsx
|
|
4147
|
+
interface OxGuestRouteProps {
|
|
4148
|
+
children: ReactNode; // Required - Shown when NOT authenticated
|
|
4149
|
+
onAuthenticated?: ReactNode; // Fallback when authenticated
|
|
4150
|
+
onLoading?: ReactNode; // Fallback during auth check
|
|
4151
|
+
}
|
|
4152
|
+
```
|
|
4153
|
+
|
|
4154
|
+
### Example
|
|
4155
|
+
|
|
4156
|
+
```tsx
|
|
4157
|
+
<OxGuestRoute onLoading={<Spinner />} onAuthenticated={<Navigate to="/dashboard" />}>
|
|
4158
|
+
<LoginPage />
|
|
4159
|
+
</OxGuestRoute>
|
|
4160
|
+
```
|
|
4161
|
+
|
|
4162
|
+
---
|
|
4163
|
+
|
|
4164
|
+
## OxProtectedRoute
|
|
4165
|
+
|
|
4166
|
+
Protects routes requiring authentication.
|
|
4167
|
+
|
|
4168
|
+
### Props
|
|
4169
|
+
|
|
4170
|
+
```tsx
|
|
4171
|
+
interface OxProtectedRouteProps {
|
|
4172
|
+
children: ReactNode; // Required - Shown when authenticated
|
|
4173
|
+
onLoading?: ReactNode; // Fallback during auth check
|
|
4174
|
+
onUnauthenticated?: ReactNode; // Fallback when NOT authenticated
|
|
4175
|
+
}
|
|
4176
|
+
```
|
|
4177
|
+
|
|
4178
|
+
### Example
|
|
4179
|
+
|
|
4180
|
+
```tsx
|
|
4181
|
+
<OxProtectedRoute onLoading={<Spinner />} onUnauthenticated={<Navigate to="/login" />}>
|
|
4182
|
+
<Dashboard />
|
|
4183
|
+
</OxProtectedRoute>
|
|
4184
|
+
```
|
|
4185
|
+
|
|
4186
|
+
---
|
|
4187
|
+
|
|
4188
|
+
## OxRoleRoute
|
|
4189
|
+
|
|
4190
|
+
Protects routes requiring specific user roles.
|
|
4191
|
+
|
|
4192
|
+
### Props
|
|
4193
|
+
|
|
4194
|
+
```tsx
|
|
4195
|
+
interface OxRoleRouteProps {
|
|
4196
|
+
children: ReactNode; // Required - Shown when user has role
|
|
4197
|
+
onLoading?: ReactNode; // Fallback during auth check
|
|
4198
|
+
onUnauthorized?: ReactNode; // Fallback when unauthorized or not authenticated
|
|
4199
|
+
roles?: string[]; // Default: ['admin'] - Allowed roles (OR logic)
|
|
4200
|
+
}
|
|
4201
|
+
```
|
|
4202
|
+
|
|
4203
|
+
### Example
|
|
4204
|
+
|
|
4205
|
+
```tsx
|
|
4206
|
+
{
|
|
4207
|
+
/* Admin only */
|
|
4208
|
+
}
|
|
4209
|
+
<OxRoleRoute>
|
|
4210
|
+
<AdminPanel />
|
|
4211
|
+
</OxRoleRoute>;
|
|
4212
|
+
|
|
4213
|
+
{
|
|
4214
|
+
/* Admin or moderator */
|
|
4215
|
+
}
|
|
4216
|
+
<OxRoleRoute roles={['admin', 'moderator']} onUnauthorized={<AccessDenied />}>
|
|
4217
|
+
<ModerationTools />
|
|
4218
|
+
</OxRoleRoute>;
|
|
4219
|
+
```
|
|
4220
|
+
|
|
4221
|
+
**Notes**:
|
|
4222
|
+
|
|
4223
|
+
- User must have at least ONE of the specified roles
|
|
4224
|
+
- Empty roles array always shows `onUnauthorized`
|
|
4225
|
+
- Default role: `'admin'`
|
|
4226
|
+
|
|
4227
|
+
---
|
|
4228
|
+
|
|
4229
|
+
# Utilities (9)
|
|
4230
|
+
|
|
4231
|
+
## Builder
|
|
4232
|
+
|
|
4233
|
+
URL and mailto link construction.
|
|
4234
|
+
|
|
4235
|
+
### Functions
|
|
4236
|
+
|
|
4237
|
+
```tsx
|
|
4238
|
+
// Build URL with query params
|
|
4239
|
+
function buildUrl(
|
|
4240
|
+
path: string,
|
|
4241
|
+
params?: Record<string, string | number | boolean | null | undefined>,
|
|
4242
|
+
options?: {
|
|
4243
|
+
baseUrl?: string;
|
|
4244
|
+
fragment?: string; // Auto-prefixes with # if needed
|
|
4245
|
+
}
|
|
4246
|
+
): string;
|
|
4247
|
+
|
|
4248
|
+
// Build mailto link
|
|
4249
|
+
function buildMailTo(
|
|
4250
|
+
email: string | string[],
|
|
4251
|
+
options?: {
|
|
4252
|
+
bcc?: string | string[];
|
|
4253
|
+
body?: string;
|
|
4254
|
+
cc?: string | string[];
|
|
4255
|
+
subject?: string;
|
|
4256
|
+
}
|
|
4257
|
+
): string;
|
|
4258
|
+
```
|
|
4259
|
+
|
|
4260
|
+
### Example
|
|
4261
|
+
|
|
4262
|
+
```tsx
|
|
4263
|
+
buildUrl(
|
|
4264
|
+
'/api/users',
|
|
4265
|
+
{ id: 123, active: true },
|
|
4266
|
+
{
|
|
4267
|
+
baseUrl: 'https://api.example.com',
|
|
4268
|
+
fragment: 'section-1',
|
|
4269
|
+
}
|
|
4270
|
+
);
|
|
4271
|
+
// "https://api.example.com/api/users?id=123&active=true#section-1"
|
|
4272
|
+
|
|
4273
|
+
buildMailTo(['john@example.com', 'jane@example.com'], {
|
|
4274
|
+
subject: 'Meeting Request',
|
|
4275
|
+
cc: 'manager@example.com',
|
|
4276
|
+
});
|
|
4277
|
+
// "mailto:john@example.com,jane@example.com?cc=manager@example.com&subject=Meeting+Request"
|
|
4278
|
+
```
|
|
4279
|
+
|
|
4280
|
+
---
|
|
4281
|
+
|
|
4282
|
+
## cn
|
|
4283
|
+
|
|
4284
|
+
Tailwind CSS class name merging.
|
|
4285
|
+
|
|
4286
|
+
### Function
|
|
4287
|
+
|
|
4288
|
+
```tsx
|
|
4289
|
+
function cn(...inputs: ClassValue[]): string;
|
|
4290
|
+
```
|
|
4291
|
+
|
|
4292
|
+
### Example
|
|
4293
|
+
|
|
4294
|
+
```tsx
|
|
4295
|
+
cn('px-4 py-2', 'bg-blue-500', { 'text-white': isActive });
|
|
4296
|
+
// "px-4 py-2 bg-blue-500 text-white" (if isActive)
|
|
4297
|
+
|
|
4298
|
+
cn('px-2', 'px-4');
|
|
4299
|
+
// "px-4" (tailwind-merge resolves conflicts)
|
|
4300
|
+
```
|
|
4301
|
+
|
|
4302
|
+
---
|
|
4303
|
+
|
|
4304
|
+
## copyToClipboard
|
|
4305
|
+
|
|
4306
|
+
Cross-browser clipboard copying.
|
|
4307
|
+
|
|
4308
|
+
### Function
|
|
4309
|
+
|
|
4310
|
+
```tsx
|
|
4311
|
+
async function copyToClipboard(text: string): Promise<boolean>;
|
|
4312
|
+
```
|
|
4313
|
+
|
|
4314
|
+
### Example
|
|
4315
|
+
|
|
4316
|
+
```tsx
|
|
4317
|
+
const success = await copyToClipboard('Hello, World!');
|
|
4318
|
+
if (success) {
|
|
4319
|
+
console.log('Copied!');
|
|
4320
|
+
}
|
|
4321
|
+
```
|
|
4322
|
+
|
|
4323
|
+
**Notes**:
|
|
4324
|
+
|
|
4325
|
+
- Modern Clipboard API with fallback
|
|
4326
|
+
- Never throws errors (returns false)
|
|
4327
|
+
- Requires HTTPS for modern API
|
|
4328
|
+
|
|
4329
|
+
---
|
|
4330
|
+
|
|
4331
|
+
## createId
|
|
4332
|
+
|
|
4333
|
+
Unique ID generation.
|
|
4334
|
+
|
|
4335
|
+
### Functions
|
|
4336
|
+
|
|
4337
|
+
```tsx
|
|
4338
|
+
// Overloaded function
|
|
4339
|
+
function createId(): string;
|
|
4340
|
+
function createId(prefix: string): string;
|
|
4341
|
+
function createId(prefix: string, options: { length?: number; separator?: string }): string;
|
|
4342
|
+
function createId(options: { length?: number; separator?: string }): string;
|
|
4343
|
+
|
|
4344
|
+
function generateRandomString(length: number): string;
|
|
4345
|
+
```
|
|
4346
|
+
|
|
4347
|
+
### Example
|
|
4348
|
+
|
|
4349
|
+
```tsx
|
|
4350
|
+
createId();
|
|
4351
|
+
// "ox_lz8k3xa1abc123de"
|
|
4352
|
+
|
|
4353
|
+
createId('user');
|
|
4354
|
+
// "user_lz8k3xb2xyz456gh"
|
|
4355
|
+
|
|
4356
|
+
createId('order', { length: 12, separator: '-' });
|
|
4357
|
+
// "order-lz8k3xc3abcdefgh1234"
|
|
4358
|
+
|
|
4359
|
+
generateRandomString(8);
|
|
4360
|
+
// "x7k2m9p4"
|
|
4361
|
+
```
|
|
4362
|
+
|
|
4363
|
+
**Notes**:
|
|
4364
|
+
|
|
4365
|
+
- Format: `{prefix}{separator}{timestamp}{counter}{randomString}`
|
|
4366
|
+
- Default prefix: `'ox'`, separator: `'_'`, length: `8`
|
|
4367
|
+
- Uses crypto.getRandomValues when available
|
|
4368
|
+
|
|
4369
|
+
---
|
|
4370
|
+
|
|
4371
|
+
## errorHelper
|
|
4372
|
+
|
|
4373
|
+
GraphQL and form error handling.
|
|
4374
|
+
|
|
4375
|
+
### Functions
|
|
4376
|
+
|
|
4377
|
+
```tsx
|
|
4378
|
+
function parseGraphQLErrors(errors: OxGraphQLError[]): OxParsedError[];
|
|
4379
|
+
|
|
4380
|
+
function handleFormErrors(errors: OxParsedError[], formState: OxFormState): void;
|
|
4381
|
+
|
|
4382
|
+
function handleGraphQLFormErrors(errors: OxGraphQLError[], formState: OxFormState): void;
|
|
4383
|
+
```
|
|
4384
|
+
|
|
4385
|
+
### Example
|
|
4386
|
+
|
|
4387
|
+
```tsx
|
|
4388
|
+
const graphqlErrors = [
|
|
4389
|
+
{
|
|
4390
|
+
message: 'Invalid email',
|
|
4391
|
+
extensions: { code: 'VALIDATION_ERROR', field: 'email' },
|
|
4392
|
+
},
|
|
4393
|
+
];
|
|
4394
|
+
|
|
4395
|
+
const parsed = parseGraphQLErrors(graphqlErrors);
|
|
4396
|
+
// [{ message: 'Invalid email', code: 'VALIDATION_ERROR', field: 'email' }]
|
|
4397
|
+
|
|
4398
|
+
handleFormErrors(parsed, formState);
|
|
4399
|
+
// Calls formState.setError('email', 'Invalid email')
|
|
4400
|
+
|
|
4401
|
+
// Or combined:
|
|
4402
|
+
handleGraphQLFormErrors(graphqlErrors, formState);
|
|
4403
|
+
```
|
|
4404
|
+
|
|
4405
|
+
---
|
|
4406
|
+
|
|
4407
|
+
## formatter
|
|
4408
|
+
|
|
4409
|
+
Data formatting utilities.
|
|
4410
|
+
|
|
4411
|
+
### Functions
|
|
4412
|
+
|
|
4413
|
+
```tsx
|
|
4414
|
+
// Bytes to human-readable
|
|
4415
|
+
function formatBytes(
|
|
4416
|
+
bytes: number,
|
|
4417
|
+
options?: {
|
|
4418
|
+
binary?: boolean; // Use 1024 base (default: false)
|
|
4419
|
+
decimals?: number; // Default: 1
|
|
4420
|
+
locale?: string; // Default: 'en-US'
|
|
4421
|
+
}
|
|
4422
|
+
): string;
|
|
4423
|
+
|
|
4424
|
+
// Currency formatting
|
|
4425
|
+
function formatCurrency(
|
|
4426
|
+
amount: number,
|
|
4427
|
+
options?: {
|
|
4428
|
+
currency?: string; // Default: 'USD'
|
|
4429
|
+
locale?: string; // Default: 'en-US'
|
|
4430
|
+
}
|
|
4431
|
+
): string;
|
|
4432
|
+
|
|
4433
|
+
// Date formatting
|
|
4434
|
+
function formatDate(
|
|
4435
|
+
date: Date | string | number,
|
|
4436
|
+
options?: {
|
|
4437
|
+
format?: 'short' | 'medium' | 'long' | 'full'; // Default: 'medium'
|
|
4438
|
+
locale?: string; // Default: 'en-US'
|
|
4439
|
+
}
|
|
4440
|
+
): string;
|
|
4441
|
+
|
|
4442
|
+
// Number formatting
|
|
4443
|
+
function formatNumber(
|
|
4444
|
+
number: number,
|
|
4445
|
+
options?: {
|
|
4446
|
+
compact?: boolean; // Compact notation (e.g., 1.2K)
|
|
4447
|
+
decimals?: number; // Default: 2
|
|
4448
|
+
locale?: string; // Default: 'en-US'
|
|
4449
|
+
}
|
|
4450
|
+
): string;
|
|
4451
|
+
|
|
4452
|
+
// Relative time
|
|
4453
|
+
function formatTimestamp(date: Date): string;
|
|
4454
|
+
```
|
|
4455
|
+
|
|
4456
|
+
### Example
|
|
4457
|
+
|
|
4458
|
+
```tsx
|
|
4459
|
+
formatBytes(1234567);
|
|
4460
|
+
// "1.2 MB"
|
|
4461
|
+
|
|
4462
|
+
formatCurrency(1234.56, { currency: 'EUR', locale: 'de-DE' });
|
|
4463
|
+
// "1.234,56 €"
|
|
4464
|
+
|
|
4465
|
+
formatDate(new Date('2024-01-15'), { format: 'long' });
|
|
4466
|
+
// "January 15, 2024"
|
|
4467
|
+
|
|
4468
|
+
formatNumber(1234567, { compact: true });
|
|
4469
|
+
// "1.23M"
|
|
4470
|
+
|
|
4471
|
+
formatTimestamp(new Date(Date.now() - 5 * 60 * 1000));
|
|
4472
|
+
// "5m ago"
|
|
4473
|
+
```
|
|
4474
|
+
|
|
4475
|
+
---
|
|
4476
|
+
|
|
4477
|
+
## hasDataSlot
|
|
4478
|
+
|
|
4479
|
+
React slot validation type guard.
|
|
4480
|
+
|
|
4481
|
+
### Function
|
|
4482
|
+
|
|
4483
|
+
```tsx
|
|
4484
|
+
function hasDataSlot(
|
|
4485
|
+
element: React.ReactNode,
|
|
4486
|
+
slot: string
|
|
4487
|
+
): element is React.ReactElement<{ 'data-ox-slot': string }>;
|
|
4488
|
+
```
|
|
4489
|
+
|
|
4490
|
+
### Example
|
|
4491
|
+
|
|
4492
|
+
```tsx
|
|
4493
|
+
const children = [
|
|
4494
|
+
<div data-ox-slot="header">Header</div>,
|
|
4495
|
+
<div data-ox-slot="content">Content</div>,
|
|
4496
|
+
];
|
|
4497
|
+
|
|
4498
|
+
const headerSlot = children.find((child) => hasDataSlot(child, 'header'));
|
|
4499
|
+
// TypeScript knows headerSlot has data-ox-slot prop
|
|
4500
|
+
```
|
|
4501
|
+
|
|
4502
|
+
**Notes**:
|
|
4503
|
+
|
|
4504
|
+
- Type guard for TypeScript narrowing
|
|
4505
|
+
- Validates React element and slot attribute
|
|
4506
|
+
|
|
4507
|
+
---
|
|
4508
|
+
|
|
4509
|
+
## scrollToElement
|
|
4510
|
+
|
|
4511
|
+
Promise-based smooth scrolling.
|
|
4512
|
+
|
|
4513
|
+
### Function
|
|
4514
|
+
|
|
4515
|
+
```tsx
|
|
4516
|
+
function scrollToElement(
|
|
4517
|
+
target: string | Element,
|
|
4518
|
+
options?: {
|
|
4519
|
+
behavior?: 'smooth' | 'instant' | 'auto';
|
|
4520
|
+
block?: 'start' | 'center' | 'end' | 'nearest';
|
|
4521
|
+
inline?: 'start' | 'center' | 'end' | 'nearest';
|
|
4522
|
+
offset?: number; // Pixels from element position
|
|
4523
|
+
timeout?: number; // Default: 5000ms
|
|
4524
|
+
}
|
|
4525
|
+
): Promise<void>;
|
|
4526
|
+
```
|
|
4527
|
+
|
|
4528
|
+
### Example
|
|
4529
|
+
|
|
4530
|
+
```tsx
|
|
4531
|
+
await scrollToElement('#my-section');
|
|
4532
|
+
await scrollToElement('.target', { offset: -100 }); // 100px above
|
|
4533
|
+
await scrollToElement(element, { behavior: 'smooth', block: 'center' });
|
|
4534
|
+
```
|
|
4535
|
+
|
|
4536
|
+
**Notes**:
|
|
4537
|
+
|
|
4538
|
+
- Smart selector parsing
|
|
4539
|
+
- Promise resolves when scroll completes
|
|
4540
|
+
- Rejects if element not found or timeout
|
|
4541
|
+
|
|
4542
|
+
---
|
|
4543
|
+
|
|
4544
|
+
## validation
|
|
4545
|
+
|
|
4546
|
+
Input validation utilities.
|
|
4547
|
+
|
|
4548
|
+
### Functions
|
|
4549
|
+
|
|
4550
|
+
```tsx
|
|
4551
|
+
// Email validation
|
|
4552
|
+
function validateEmail(
|
|
4553
|
+
email: string,
|
|
4554
|
+
options?: {
|
|
4555
|
+
allowLocalhost?: boolean;
|
|
4556
|
+
requireTld?: boolean; // Default: true
|
|
4557
|
+
}
|
|
4558
|
+
): boolean;
|
|
4559
|
+
|
|
4560
|
+
// URL validation
|
|
4561
|
+
function validateUrl(
|
|
4562
|
+
url: string,
|
|
4563
|
+
options?: {
|
|
4564
|
+
allowLocalhost?: boolean; // Default: false (blocks 127.0.0.1, 192.168.x.x, etc.)
|
|
4565
|
+
protocols?: string[]; // Default: ['http:', 'https:']
|
|
4566
|
+
requireProtocol?: boolean; // Default: false (auto-adds https://)
|
|
4567
|
+
}
|
|
4568
|
+
): boolean;
|
|
4569
|
+
|
|
4570
|
+
// Phone validation
|
|
4571
|
+
function validatePhone(
|
|
4572
|
+
phone: string,
|
|
4573
|
+
options?: {
|
|
4574
|
+
allowedCountries?: string[]; // e.g., ['US', 'CA', 'UK']
|
|
4575
|
+
strict?: boolean; // Default: false (requires E.164 format with +)
|
|
4576
|
+
}
|
|
4577
|
+
): boolean;
|
|
4578
|
+
```
|
|
4579
|
+
|
|
4580
|
+
### Example
|
|
4581
|
+
|
|
4582
|
+
```tsx
|
|
4583
|
+
validateEmail('user@example.com');
|
|
4584
|
+
// true
|
|
4585
|
+
|
|
4586
|
+
validateUrl('https://example.com');
|
|
4587
|
+
// true
|
|
4588
|
+
|
|
4589
|
+
validateUrl('example.com', { requireProtocol: false });
|
|
4590
|
+
// true (auto-adds https://)
|
|
4591
|
+
|
|
4592
|
+
validatePhone('+14155552671');
|
|
4593
|
+
// true
|
|
4594
|
+
|
|
4595
|
+
validatePhone('(415) 555-2671');
|
|
4596
|
+
// true (non-strict mode)
|
|
4597
|
+
|
|
4598
|
+
validatePhone('+14155552671', { strict: true });
|
|
4599
|
+
// true
|
|
4600
|
+
|
|
4601
|
+
validatePhone('(415) 555-2671', { strict: true });
|
|
4602
|
+
// false (strict requires E.164)
|
|
4603
|
+
|
|
4604
|
+
validatePhone('+14155552671', { allowedCountries: ['US', 'CA'] });
|
|
4605
|
+
// true
|
|
4606
|
+
```
|
|
4607
|
+
|
|
4608
|
+
**Notes**:
|
|
4609
|
+
|
|
4610
|
+
- Email: RFC 5321 compliant, max 254 chars
|
|
4611
|
+
- URL: Max 2083 chars (IE compatibility)
|
|
4612
|
+
- Phone: Supports 50+ country codes, 7-15 digits
|
|
4613
|
+
- Phone strict mode: E.164 format (+prefix, no formatting)
|
|
4614
|
+
|
|
4615
|
+
---
|
|
4616
|
+
|
|
4617
|
+
# Common Patterns
|
|
4618
|
+
|
|
4619
|
+
## asChild Pattern
|
|
4620
|
+
|
|
4621
|
+
The `asChild` prop enables polymorphic rendering using Radix Slot. When `asChild={true}`, the component renders as its child element while preserving the component's styling and functionality.
|
|
4622
|
+
|
|
4623
|
+
**Components with asChild**:
|
|
4624
|
+
|
|
4625
|
+
- `OxBreadcrumb.Link`
|
|
4626
|
+
- `OxButton`
|
|
4627
|
+
- `OxDropdown.Trigger`
|
|
4628
|
+
- `OxFloating.Trigger` _(deprecated - use OxPopper.Anchor)_
|
|
4629
|
+
- `OxHoverCard.Trigger`
|
|
4630
|
+
- `OxLink`
|
|
4631
|
+
- `OxPagination.Link`, `OxPagination.Next`, `OxPagination.Previous`
|
|
4632
|
+
- `OxPopover.Trigger`
|
|
4633
|
+
|
|
4634
|
+
**Example**:
|
|
4635
|
+
|
|
4636
|
+
```tsx
|
|
4637
|
+
{
|
|
4638
|
+
/* Render as Next.js Link while keeping button styling */
|
|
4639
|
+
}
|
|
4640
|
+
<OxButton asChild variant="primary">
|
|
4641
|
+
<Link to="/dashboard">Dashboard</Link>
|
|
4642
|
+
</OxButton>;
|
|
4643
|
+
```
|
|
4644
|
+
|
|
4645
|
+
---
|
|
4646
|
+
|
|
4647
|
+
## Compound Components
|
|
4648
|
+
|
|
4649
|
+
Components using the compound pattern provide sub-components accessed via dot notation. This allows flexible composition while maintaining type safety.
|
|
4650
|
+
|
|
4651
|
+
**Compound Components**:
|
|
4652
|
+
|
|
4653
|
+
- UI: Accordion, Alert, Breadcrumb, Card, DescriptionList, Dialog, Drawer, Dropdown, Floating, Icon, JsonField, Notifications, Pagination, Popover, Progress, Stepper, Table, Tabs, Timeline, Toggle
|
|
4654
|
+
- Forms: Checkbox, FileUpload, Form, Input (all variants), Radio, Select
|
|
4655
|
+
- Layouts: MainContent, Sidebar (+ Menu, Group)
|
|
4656
|
+
- Typography: (simple components)
|
|
4657
|
+
|
|
4658
|
+
---
|
|
4659
|
+
|
|
4660
|
+
## Controlled vs Uncontrolled
|
|
4661
|
+
|
|
4662
|
+
Many components support both controlled and uncontrolled modes:
|
|
4663
|
+
|
|
4664
|
+
**Controlled** (parent manages state):
|
|
4665
|
+
|
|
4666
|
+
```tsx
|
|
4667
|
+
<OxSlider value={value} onChange={setValue} />
|
|
4668
|
+
```
|
|
4669
|
+
|
|
4670
|
+
**Uncontrolled** (component manages state):
|
|
4671
|
+
|
|
4672
|
+
```tsx
|
|
4673
|
+
<OxSlider defaultValue={50} />
|
|
4674
|
+
```
|
|
4675
|
+
|
|
4676
|
+
**Components**:
|
|
4677
|
+
|
|
4678
|
+
- Accordion, Dialog, Drawer, Dropdown, Floating, Popover, Slider, Tabs, Toggle
|
|
4679
|
+
|
|
4680
|
+
---
|
|
4681
|
+
|
|
4682
|
+
## Loading States
|
|
4683
|
+
|
|
4684
|
+
Components with built-in loading states:
|
|
4685
|
+
|
|
4686
|
+
- Avatar (`isLoading`, `skeleton`)
|
|
4687
|
+
- Button (`isLoading`)
|
|
4688
|
+
- Card (`isLoading`)
|
|
4689
|
+
- Select (`isLoading`)
|
|
4690
|
+
- Table (`isLoading` with skeleton rows)
|
|
4691
|
+
|
|
4692
|
+
---
|
|
4693
|
+
|
|
4694
|
+
## Validation
|
|
4695
|
+
|
|
4696
|
+
Form components support validation via `useFormState` hook:
|
|
4697
|
+
|
|
4698
|
+
**Built-in Rules**: `'required'`, `'email'`, `'url'`, `'min:N'`, `'max:N'`, `'minLength:N'`, `'maxLength:N'`, `'pattern:regex'`, `'fileSize:5MB'`, `'fileType:image/*'`
|
|
4699
|
+
|
|
4700
|
+
**Example**:
|
|
4701
|
+
|
|
4702
|
+
```tsx
|
|
4703
|
+
const { data, errors, validate } = useFormState({
|
|
4704
|
+
initialData: { email: '' },
|
|
4705
|
+
validationRules: {
|
|
4706
|
+
email: ['required', 'email'],
|
|
4707
|
+
},
|
|
4708
|
+
});
|
|
4709
|
+
```
|
|
4710
|
+
|
|
4711
|
+
---
|
|
4712
|
+
|
|
4713
|
+
## Color Customization
|
|
4714
|
+
|
|
4715
|
+
Components accepting `ColorStyleName`:
|
|
4716
|
+
|
|
4717
|
+
- Tag, Card, Icon.Box, Progress, Skeleton, Spinner, Stepper, Timeline
|
|
4718
|
+
|
|
4719
|
+
**Available Colors**: purple, lime, red, yellow, green, blue, indigo, pink, amber, emerald, teal, cyan, sky, violet, fuchsia, orange, rose, gray
|
|
4720
|
+
|
|
4721
|
+
---
|
|
4722
|
+
|
|
4723
|
+
## Size Variants
|
|
4724
|
+
|
|
4725
|
+
Common size props:
|
|
4726
|
+
|
|
4727
|
+
- **XS-3XL**: Avatar, Icon, Spinner (`'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'`)
|
|
4728
|
+
- **SM-LG**: Button, Link, Toggle (`'sm' | 'md' | 'lg'`)
|
|
4729
|
+
- **SM-XL**: Dialog (`'sm' | 'md' | 'lg' | 'xl'`)
|
|
4730
|
+
|
|
4731
|
+
---
|
|
4732
|
+
|
|
4733
|
+
# Best Practices
|
|
4734
|
+
|
|
4735
|
+
## Import Optimization
|
|
4736
|
+
|
|
4737
|
+
Use specific imports for better tree-shaking:
|
|
4738
|
+
|
|
4739
|
+
```tsx
|
|
4740
|
+
// Good - specific imports
|
|
4741
|
+
import { OxButton, OxCard } from '@noxickon/onyx';
|
|
4742
|
+
import { useForm } from '@noxickon/onyx/hooks';
|
|
4743
|
+
|
|
4744
|
+
// Avoid - barrel imports everything
|
|
4745
|
+
import * as Onyx from '@noxickon/onyx';
|
|
4746
|
+
```
|
|
4747
|
+
|
|
4748
|
+
## Form Handling
|
|
4749
|
+
|
|
4750
|
+
Use `useForm` for GraphQL forms, `useFormState` for local forms:
|
|
4751
|
+
|
|
4752
|
+
```tsx
|
|
4753
|
+
// GraphQL form
|
|
4754
|
+
const { data, submit, errors } = useForm({
|
|
4755
|
+
operation: CREATE_USER_MUTATION,
|
|
4756
|
+
initialData: { name: '', email: '' },
|
|
4757
|
+
validationRules: { email: ['required', 'email'] },
|
|
4758
|
+
});
|
|
4759
|
+
|
|
4760
|
+
// Local form
|
|
4761
|
+
const { data, validate } = useFormState({
|
|
4762
|
+
initialData: { search: '' },
|
|
4763
|
+
validationRules: { search: ['minLength:3'] },
|
|
4764
|
+
});
|
|
4765
|
+
```
|
|
4766
|
+
|
|
4767
|
+
## Route Protection
|
|
4768
|
+
|
|
4769
|
+
Layer route guards for complex authorization:
|
|
4770
|
+
|
|
4771
|
+
```tsx
|
|
4772
|
+
{
|
|
4773
|
+
/* Guest route wrapping protected content */
|
|
4774
|
+
}
|
|
4775
|
+
<OxGuestRoute onAuthenticated={<Navigate to="/dashboard" />}>
|
|
4776
|
+
<LoginPage />
|
|
4777
|
+
</OxGuestRoute>;
|
|
4778
|
+
|
|
4779
|
+
{
|
|
4780
|
+
/* Protected route with role guard */
|
|
4781
|
+
}
|
|
4782
|
+
<OxProtectedRoute onUnauthenticated={<Navigate to="/login" />}>
|
|
4783
|
+
<OxRoleRoute roles={['admin', 'moderator']} onUnauthorized={<AccessDenied />}>
|
|
4784
|
+
<AdminPanel />
|
|
4785
|
+
</OxRoleRoute>
|
|
4786
|
+
</OxProtectedRoute>;
|
|
4787
|
+
```
|
|
4788
|
+
|
|
4789
|
+
## Accessibility
|
|
4790
|
+
|
|
4791
|
+
All components are WCAG compliant. Ensure proper labels and ARIA attributes:
|
|
4792
|
+
|
|
4793
|
+
```tsx
|
|
4794
|
+
<OxForm.Field>
|
|
4795
|
+
<OxForm.Label htmlFor="email" required>
|
|
4796
|
+
Email
|
|
4797
|
+
</OxForm.Label>
|
|
4798
|
+
<input id="email" aria-describedby="email-description email-error" />
|
|
4799
|
+
<OxForm.Description id="email-description">Your email address</OxForm.Description>
|
|
4800
|
+
<OxForm.Error id="email-error">Email is required</OxForm.Error>
|
|
4801
|
+
</OxForm.Field>
|
|
4802
|
+
```
|
|
4803
|
+
|
|
4804
|
+
---
|
|
4805
|
+
|
|
4806
|
+
# Type Definitions
|
|
4807
|
+
|
|
4808
|
+
## Common Types
|
|
4809
|
+
|
|
4810
|
+
```tsx
|
|
4811
|
+
// Lucide icon type
|
|
4812
|
+
import type { LucideIcon } from 'lucide-react';
|
|
4813
|
+
|
|
4814
|
+
// Color names from theme
|
|
4815
|
+
type ColorStyleName =
|
|
4816
|
+
| 'purple'
|
|
4817
|
+
| 'lime'
|
|
4818
|
+
| 'red'
|
|
4819
|
+
| 'yellow'
|
|
4820
|
+
| 'green'
|
|
4821
|
+
| 'blue'
|
|
4822
|
+
| 'indigo'
|
|
4823
|
+
| 'pink'
|
|
4824
|
+
| 'amber'
|
|
4825
|
+
| 'emerald'
|
|
4826
|
+
| 'teal'
|
|
4827
|
+
| 'cyan'
|
|
4828
|
+
| 'sky'
|
|
4829
|
+
| 'violet'
|
|
4830
|
+
| 'fuchsia'
|
|
4831
|
+
| 'orange'
|
|
4832
|
+
| 'rose'
|
|
4833
|
+
| 'gray';
|
|
4834
|
+
|
|
4835
|
+
type StatusType = 'default' | 'info' | 'success' | 'warning' | 'error';
|
|
4836
|
+
|
|
4837
|
+
// Validation errors
|
|
4838
|
+
type ValidationErrors = Record<string, string | string[]>;
|
|
4839
|
+
type ValidationError = string | string[];
|
|
4840
|
+
|
|
4841
|
+
// Form state
|
|
4842
|
+
interface OxFormState {
|
|
4843
|
+
setError: (field: string, error: string | string[]) => void;
|
|
4844
|
+
}
|
|
4845
|
+
|
|
4846
|
+
// User type (AuthContext)
|
|
4847
|
+
type OxUser = {
|
|
4848
|
+
id: string;
|
|
4849
|
+
role: string;
|
|
4850
|
+
email?: string;
|
|
4851
|
+
username?: string;
|
|
4852
|
+
} | null;
|
|
4853
|
+
```
|
|
4854
|
+
|
|
4855
|
+
---
|
|
4856
|
+
|
|
4857
|
+
# Maintenance Guide
|
|
4858
|
+
|
|
4859
|
+
## For Library Maintainers
|
|
4860
|
+
|
|
4861
|
+
This AI-README.md serves as the **single source of truth** for external API documentation. Update it whenever you make changes to the public API.
|
|
4862
|
+
|
|
4863
|
+
### When to Update
|
|
4864
|
+
|
|
4865
|
+
Update this document when:
|
|
4866
|
+
|
|
4867
|
+
- ✅ Adding/removing a component, hook, or utility
|
|
4868
|
+
- ✅ Changing public props or API signatures
|
|
4869
|
+
- ✅ Modifying component behavior or usage patterns
|
|
4870
|
+
- ✅ Releasing a new version
|
|
4871
|
+
|
|
4872
|
+
Do NOT update for:
|
|
4873
|
+
|
|
4874
|
+
- ❌ Internal refactoring without API changes
|
|
4875
|
+
- ❌ Documentation typo fixes (unless they affect accuracy)
|
|
4876
|
+
- ❌ Build/test configuration changes
|
|
4877
|
+
|
|
4878
|
+
### Update Workflow
|
|
4879
|
+
|
|
4880
|
+
1. **Make code changes** in the source files.
|
|
4881
|
+
2. **Update AI-README.md** immediately:
|
|
4882
|
+
- Update affected component/hook sections.
|
|
4883
|
+
- Update the version header (date, version if applicable, commit after commit).
|
|
4884
|
+
3. **Verify accuracy** by comparing with actual type definitions.
|
|
4885
|
+
4. **Commit together** with code changes.
|
|
4886
|
+
|
|
4887
|
+
### Version Header Maintenance
|
|
4888
|
+
|
|
4889
|
+
After making changes:
|
|
4890
|
+
|
|
4891
|
+
```bash
|
|
4892
|
+
# Update the header manually:
|
|
4893
|
+
# Version: Check package.json "version" field
|
|
4894
|
+
# Updated: Today's date (YYYY-MM-DD format)
|
|
4895
|
+
# Built from: Run `git rev-parse --short HEAD` after committing
|
|
4896
|
+
```
|
|
4897
|
+
|
|
4898
|
+
### Structure Consistency
|
|
4899
|
+
|
|
4900
|
+
Every component should follow this pattern:
|
|
4901
|
+
|
|
4902
|
+
````markdown
|
|
4903
|
+
## OxComponentName
|
|
4904
|
+
|
|
4905
|
+
Brief one-line description.
|
|
4906
|
+
|
|
4907
|
+
**Pattern**: Standalone | Compound Component
|
|
4908
|
+
**Sub-components**: `SubComp1`, `SubComp2` (if compound)
|
|
4909
|
+
|
|
4910
|
+
### Props
|
|
4911
|
+
|
|
4912
|
+
```tsx
|
|
4913
|
+
interface OxComponentNameProps {
|
|
4914
|
+
// Full TypeScript interface
|
|
4915
|
+
}
|
|
4916
|
+
```
|
|
4917
|
+
````
|
|
4918
|
+
|
|
4919
|
+
### Example
|
|
4920
|
+
|
|
4921
|
+
```tsx
|
|
4922
|
+
<OxComponentName prop="value">Content</OxComponentName>
|
|
4923
|
+
```
|
|
4924
|
+
|
|
4925
|
+
```
|
|
4926
|
+
|
|
4927
|
+
---
|
|
4928
|
+
|
|
4929
|
+
### Quality Checklist
|
|
4930
|
+
|
|
4931
|
+
Before releasing:
|
|
4932
|
+
- Version header matches package.json
|
|
4933
|
+
- All new components documented
|
|
4934
|
+
- All examples tested and accurate
|
|
4935
|
+
- TypeScript interfaces match source code
|
|
4936
|
+
- No internal file paths exposed
|
|
4937
|
+
- Only Ox-prefixed exports documented
|
|
4938
|
+
|
|
4939
|
+
---
|
|
4940
|
+
|
|
4941
|
+
This AI-README provides comprehensive reference documentation for all Onyx components, hooks, contexts, utilities, and patterns. All information is extracted from the actual source code with accurate prop definitions and usage examples.
|
|
4942
|
+
```
|