@cube-dev/ui-kit 0.180.0 → 0.181.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/dist/CHANGELOG.md +14 -0
- package/dist/_internal/hooks/use-debounced-value.js +1 -1
- package/dist/_internal/hooks/use-deprecation-warning.js +1 -1
- package/dist/_internal/hooks/use-event.js +1 -1
- package/dist/_internal/hooks/use-is-first-render.js +1 -1
- package/dist/_internal/hooks/use-sync-ref.js +1 -1
- package/dist/_internal/hooks/use-timer/timer.js +1 -1
- package/dist/_internal/hooks/use-timer/use-timer.js +1 -1
- package/dist/_internal/hooks/use-warn.js +1 -1
- package/dist/components/Block.js +1 -1
- package/dist/components/CollectionItem.js +1 -1
- package/dist/components/GlobalStyles.js +1 -1
- package/dist/components/GridProvider.js +1 -1
- package/dist/components/HiddenInput.js +1 -1
- package/dist/components/Root.js +1 -1
- package/dist/components/actions/Action/Action.js +1 -1
- package/dist/components/actions/Banner/Banner.js +1 -1
- package/dist/components/actions/Button/Button.js +3 -3
- package/dist/components/actions/ButtonGroup/ButtonGroup.js +1 -1
- package/dist/components/actions/ButtonSplit/ButtonSplit.js +1 -1
- package/dist/components/actions/ButtonSplit/context.js +1 -1
- package/dist/components/actions/CommandMenu/CommandMenu.js +1 -1
- package/dist/components/actions/CommandMenu/styled.js +1 -1
- package/dist/components/actions/ItemAction/ItemAction.js +1 -1
- package/dist/components/actions/ItemActionContext.js +1 -1
- package/dist/components/actions/ItemActionsWrapper.js +1 -1
- package/dist/components/actions/ItemButton/ItemButton.js +1 -1
- package/dist/components/actions/Link/Link.js +1 -1
- package/dist/components/actions/Menu/Menu.js +1 -1
- package/dist/components/actions/Menu/MenuItem.js +1 -1
- package/dist/components/actions/Menu/MenuSection.js +1 -1
- package/dist/components/actions/Menu/MenuTrigger.js +1 -1
- package/dist/components/actions/Menu/SubMenuTrigger.js +1 -1
- package/dist/components/actions/Menu/SubmenuTriggerContext.js +1 -1
- package/dist/components/actions/Menu/context.js +1 -1
- package/dist/components/actions/Menu/styled.js +1 -1
- package/dist/components/actions/actions-run.js +1 -1
- package/dist/components/actions/index.js +1 -1
- package/dist/components/actions/use-action.js +1 -1
- package/dist/components/actions/use-anchored-menu.js +1 -1
- package/dist/components/actions/use-context-menu.js +1 -1
- package/dist/components/content/ActiveZone/ActiveZone.js +1 -1
- package/dist/components/content/Alert/Alert.js +1 -1
- package/dist/components/content/Alert/use-alert.js +1 -1
- package/dist/components/content/Avatar/Avatar.js +1 -1
- package/dist/components/content/Badge/Badge.js +1 -1
- package/dist/components/content/Card/Card.js +1 -1
- package/dist/components/content/Content.js +1 -1
- package/dist/components/content/CopyPasteBlock/CopyPasteBlock.js +1 -1
- package/dist/components/content/CopySnippet/CopySnippet.js +1 -1
- package/dist/components/content/Disclosure/Disclosure.js +1 -1
- package/dist/components/content/Divider.js +1 -1
- package/dist/components/content/Footer.js +1 -1
- package/dist/components/content/Header.js +1 -1
- package/dist/components/content/HotKeys/HotKeys.js +1 -1
- package/dist/components/content/InfoBadge/InfoBadge.js +1 -1
- package/dist/components/content/InlineInput/InlineInput.js +1 -1
- package/dist/components/content/Item/Item.js +2 -2
- package/dist/components/content/ItemBadge/ItemBadge.js +1 -1
- package/dist/components/content/ItemCard/ItemCard.js +1 -1
- package/dist/components/content/Layout/GridLayout.js +1 -1
- package/dist/components/content/Layout/Layout.js +1 -1
- package/dist/components/content/Layout/LayoutBlock.js +1 -1
- package/dist/components/content/Layout/LayoutCenter.js +1 -1
- package/dist/components/content/Layout/LayoutContainer.js +1 -1
- package/dist/components/content/Layout/LayoutContent.js +1 -1
- package/dist/components/content/Layout/LayoutContext.js +1 -1
- package/dist/components/content/Layout/LayoutFlex.js +1 -1
- package/dist/components/content/Layout/LayoutFooter.js +1 -1
- package/dist/components/content/Layout/LayoutGrid.js +1 -1
- package/dist/components/content/Layout/LayoutHeader.js +1 -1
- package/dist/components/content/Layout/LayoutPane.js +1 -1
- package/dist/components/content/Layout/LayoutPanel.js +2 -2
- package/dist/components/content/Layout/LayoutPanelHeader.js +1 -1
- package/dist/components/content/Layout/LayoutToolbar.js +1 -1
- package/dist/components/content/Layout/hooks/useTinyScrollbar.js +1 -1
- package/dist/components/content/Layout/index.js +1 -1
- package/dist/components/content/Layout/utils.js +1 -1
- package/dist/components/content/Paragraph.js +1 -1
- package/dist/components/content/Placeholder/Placeholder.js +1 -1
- package/dist/components/content/PrismCode/PrismCode.js +1 -1
- package/dist/components/content/PrismCode/prismSetup.js +1 -1
- package/dist/components/content/PrismDiffCode/PrismDiffCode.js +1 -1
- package/dist/components/content/Result/Result.js +1 -1
- package/dist/components/content/Skeleton/Skeleton.js +1 -1
- package/dist/components/content/Tag/Tag.js +1 -1
- package/dist/components/content/Text.js +1 -1
- package/dist/components/content/TextItem/TextItem.js +1 -1
- package/dist/components/content/Title.js +1 -1
- package/dist/components/content/Tree/Tree.js +1 -1
- package/dist/components/content/Tree/TreeNode.js +1 -1
- package/dist/components/content/Tree/styled.js +1 -1
- package/dist/components/content/Tree/tree-index.js +1 -1
- package/dist/components/content/Tree/use-checkbox-tree.js +1 -1
- package/dist/components/content/Tree/use-load-data.js +1 -1
- package/dist/components/content/highlightText.js +1 -1
- package/dist/components/content/use-auto-tooltip.js +1 -1
- package/dist/components/data/DataTable/DataTable.js +1 -1
- package/dist/components/data/ItemTable/ItemTable.js +1 -1
- package/dist/components/data/ItemTable/ItemTableBulkBar.js +1 -1
- package/dist/components/data/ItemTable/ItemTableDragPreview.js +1 -1
- package/dist/components/data/ItemTable/ItemTableFooter.js +1 -1
- package/dist/components/data/ItemTable/ItemTableToolbar.js +1 -1
- package/dist/components/data/TableBase/ColumnResizer.js +1 -1
- package/dist/components/data/TableBase/RowCollection.js +1 -1
- package/dist/components/data/TableBase/TableHeaderCell.js +1 -1
- package/dist/components/data/TableBase/TableRow.js +1 -1
- package/dist/components/data/TableBase/TableView.js +1 -1
- package/dist/components/data/TableBase/column-menu.js +1 -1
- package/dist/components/data/TableBase/column-tint.js +1 -1
- package/dist/components/data/TableBase/row-menu.js +1 -1
- package/dist/components/data/TableBase/styled.js +1 -1
- package/dist/components/data/TableBase/table-tree.js +1 -1
- package/dist/components/data/TableBase/types.js +1 -1
- package/dist/components/data/TableBase/use-cell-selection.js +0 -0
- package/dist/components/data/TableBase/use-column-order.js +1 -1
- package/dist/components/data/TableBase/use-container-width.js +1 -1
- package/dist/components/data/TableBase/use-row-move-animation.js +1 -1
- package/dist/components/data/TableBase/use-scrollability.js +1 -1
- package/dist/components/data/TableBase/use-table-columns.js +1 -1
- package/dist/components/data/TableBase/use-table-search.js +1 -1
- package/dist/components/data/TableBase/use-table-selection.js +1 -1
- package/dist/components/data/TableBase/use-table-sort.js +1 -1
- package/dist/components/data/TableBase/use-table-sorts.js +1 -1
- package/dist/components/data/TableBase/use-table-storage.js +1 -1
- package/dist/components/data/TableBase/use-table-tree-state.js +1 -1
- package/dist/components/fields/Checkbox/Checkbox.d.ts +3 -1
- package/dist/components/fields/Checkbox/Checkbox.js +4 -5
- package/dist/components/fields/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/fields/Checkbox/CheckboxGroup.d.ts +1 -1
- package/dist/components/fields/Checkbox/CheckboxGroup.js +1 -1
- package/dist/components/fields/Checkbox/CheckboxGroup.js.map +1 -1
- package/dist/components/fields/Checkbox/context.js +1 -1
- package/dist/components/fields/ColorInput/ColorInput.d.ts +1 -1
- package/dist/components/fields/ColorInput/ColorInput.js +1 -1
- package/dist/components/fields/ColorInput/ColorInput.js.map +1 -1
- package/dist/components/fields/ColorPicker/ColorPicker.d.ts +1 -1
- package/dist/components/fields/ColorPicker/ColorPicker.js +1 -1
- package/dist/components/fields/ColorPicker/ColorPicker.js.map +1 -1
- package/dist/components/fields/ColorSwatch/ColorSwatch.js +1 -1
- package/dist/components/fields/ColorSwatchGroup/ColorSwatchGroup.d.ts +1 -1
- package/dist/components/fields/ColorSwatchGroup/ColorSwatchGroup.js +1 -1
- package/dist/components/fields/ColorSwatchGroup/ColorSwatchGroup.js.map +1 -1
- package/dist/components/fields/ComboBox/ComboBox.d.ts +1 -1
- package/dist/components/fields/ComboBox/ComboBox.js +8 -2
- package/dist/components/fields/ComboBox/ComboBox.js.map +1 -1
- package/dist/components/fields/CommandTextArea/CommandTextArea.js +1 -1
- package/dist/components/fields/CommandTextArea/caretPosition.js +1 -1
- package/dist/components/fields/CommandTextArea/useCaretAnchor.js +1 -1
- package/dist/components/fields/DatePicker/DateInput.d.ts +1 -1
- package/dist/components/fields/DatePicker/DateInput.js +1 -1
- package/dist/components/fields/DatePicker/DateInput.js.map +1 -1
- package/dist/components/fields/DatePicker/DateInputBase.js +1 -1
- package/dist/components/fields/DatePicker/DatePicker.d.ts +1 -1
- package/dist/components/fields/DatePicker/DatePicker.js +1 -1
- package/dist/components/fields/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/fields/DatePicker/DatePickerButton.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerElement.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerInput.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerSegment.js +1 -1
- package/dist/components/fields/DatePicker/DateRangePicker.d.ts +4 -1
- package/dist/components/fields/DatePicker/DateRangePicker.js +1 -1
- package/dist/components/fields/DatePicker/DateRangePicker.js.map +1 -1
- package/dist/components/fields/DatePicker/DateRangeSeparatedPicker.d.ts +4 -1
- package/dist/components/fields/DatePicker/DateRangeSeparatedPicker.js +1 -1
- package/dist/components/fields/DatePicker/DateRangeSeparatedPicker.js.map +1 -1
- package/dist/components/fields/DatePicker/MonthPicker.js +1 -1
- package/dist/components/fields/DatePicker/PeriodPicker.d.ts +1 -1
- package/dist/components/fields/DatePicker/PeriodPicker.js +1 -1
- package/dist/components/fields/DatePicker/PeriodPicker.js.map +1 -1
- package/dist/components/fields/DatePicker/QuarterPicker.js +1 -1
- package/dist/components/fields/DatePicker/TimeInput.d.ts +1 -1
- package/dist/components/fields/DatePicker/TimeInput.js +1 -1
- package/dist/components/fields/DatePicker/TimeInput.js.map +1 -1
- package/dist/components/fields/DatePicker/WeekPicker.js +1 -1
- package/dist/components/fields/DatePicker/YearPicker.js +1 -1
- package/dist/components/fields/DatePicker/parseDate.js +1 -1
- package/dist/components/fields/DatePicker/period.js +1 -1
- package/dist/components/fields/DatePicker/props.js +1 -1
- package/dist/components/fields/DatePicker/utils.js +1 -1
- package/dist/components/fields/FileInput/FileInput.d.ts +4 -1
- package/dist/components/fields/FileInput/FileInput.js +38 -29
- package/dist/components/fields/FileInput/FileInput.js.map +1 -1
- package/dist/components/fields/FilterListBox/FilterListBox.d.ts +1 -1
- package/dist/components/fields/FilterListBox/FilterListBox.js +1 -1
- package/dist/components/fields/FilterListBox/FilterListBox.js.map +1 -1
- package/dist/components/fields/FilterPicker/FilterPicker.d.ts +1 -1
- package/dist/components/fields/FilterPicker/FilterPicker.js +8 -5
- package/dist/components/fields/FilterPicker/FilterPicker.js.map +1 -1
- package/dist/components/fields/Input/Input.js +1 -1
- package/dist/components/fields/ListBox/DraggableListBox.js +1 -1
- package/dist/components/fields/ListBox/ListBox.d.ts +1 -1
- package/dist/components/fields/ListBox/ListBox.js +5 -3
- package/dist/components/fields/ListBox/ListBox.js.map +1 -1
- package/dist/components/fields/ListBoxPopover/ListBoxPopover.js +1 -1
- package/dist/components/fields/ListBoxPopover/listNavigation.js +1 -1
- package/dist/components/fields/ListBoxPopover/useCompositeFocus.js +1 -1
- package/dist/components/fields/NumberInput/NumberInput.d.ts +3 -1
- package/dist/components/fields/NumberInput/NumberInput.js +1 -1
- package/dist/components/fields/NumberInput/NumberInput.js.map +1 -1
- package/dist/components/fields/NumberInput/StepButton.js +1 -1
- package/dist/components/fields/PasswordInput/PasswordInput.js +1 -1
- package/dist/components/fields/Picker/Picker.d.ts +1 -1
- package/dist/components/fields/Picker/Picker.js +8 -5
- package/dist/components/fields/Picker/Picker.js.map +1 -1
- package/dist/components/fields/RadioGroup/Radio.js +1 -1
- package/dist/components/fields/RadioGroup/RadioGroup.d.ts +1 -1
- package/dist/components/fields/RadioGroup/RadioGroup.js +1 -1
- package/dist/components/fields/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/components/fields/RadioGroup/context.js +1 -1
- package/dist/components/fields/SearchComboBox/SearchComboBox.js +1 -1
- package/dist/components/fields/SearchInput/SearchInput.js +1 -1
- package/dist/components/fields/Select/Select.d.ts +1 -1
- package/dist/components/fields/Select/Select.js +16 -2
- package/dist/components/fields/Select/Select.js.map +1 -1
- package/dist/components/fields/Slider/Gradation.js +1 -1
- package/dist/components/fields/Slider/HueSlider.js +1 -1
- package/dist/components/fields/Slider/RangeSlider.js +1 -1
- package/dist/components/fields/Slider/Slider.js +1 -1
- package/dist/components/fields/Slider/SliderBase.js +1 -1
- package/dist/components/fields/Slider/SliderThumb.js +1 -1
- package/dist/components/fields/Slider/SliderTrack.js +1 -1
- package/dist/components/fields/Slider/elements.js +1 -1
- package/dist/components/fields/Slider/index.js +1 -1
- package/dist/components/fields/Slider/types.d.ts +1 -1
- package/dist/components/fields/Switch/Switch.d.ts +3 -1
- package/dist/components/fields/Switch/Switch.js +1 -1
- package/dist/components/fields/Switch/Switch.js.map +1 -1
- package/dist/components/fields/TextArea/TextArea.d.ts +2 -0
- package/dist/components/fields/TextArea/TextArea.js +1 -1
- package/dist/components/fields/TextArea/TextArea.js.map +1 -1
- package/dist/components/fields/TextInput/TextInput.d.ts +4 -1
- package/dist/components/fields/TextInput/TextInput.js +1 -1
- package/dist/components/fields/TextInput/TextInput.js.map +1 -1
- package/dist/components/fields/TextInput/TextInputBase.d.ts +1 -1
- package/dist/components/fields/TextInput/TextInputBase.js +1 -1
- package/dist/components/fields/TextInput/TextInputBase.js.map +1 -1
- package/dist/components/fields/TextInput/useAutoSizeTextArea.js +1 -1
- package/dist/components/fields/TextInputMapper/TextInputMapper.d.ts +1 -1
- package/dist/components/fields/TextInputMapper/TextInputMapper.js +1 -1
- package/dist/components/fields/TextInputMapper/TextInputMapper.js.map +1 -1
- package/dist/components/fields/TriggerActions.js +1 -1
- package/dist/components/fields/color/ColorPanel.js +1 -1
- package/dist/components/fields/color/channels.js +1 -1
- package/dist/components/fields/color/color.js +1 -1
- package/dist/components/fields/color/context.js +1 -1
- package/dist/components/form/FieldWrapper/FieldWrapper.js +1 -1
- package/dist/components/form/Form/Field.d.ts +2 -0
- package/dist/components/form/Form/Field.js +1 -1
- package/dist/components/form/Form/Field.js.map +1 -1
- package/dist/components/form/Form/Form.js +1 -1
- package/dist/components/form/Form/ModernFormRoot.d.ts +9 -4
- package/dist/components/form/Form/ModernFormRoot.js +13 -6
- package/dist/components/form/Form/ModernFormRoot.js.map +1 -1
- package/dist/components/form/Form/ResetButton/ResetButton.d.ts +1 -1
- package/dist/components/form/Form/ResetButton/ResetButton.js +1 -1
- package/dist/components/form/Form/SubmitButton/SubmitButton.d.ts +3 -1
- package/dist/components/form/Form/SubmitButton/SubmitButton.js +2 -2
- package/dist/components/form/Form/SubmitButton/SubmitButton.js.map +1 -1
- package/dist/components/form/Form/SubmitError.d.ts +8 -2
- package/dist/components/form/Form/SubmitError.js +11 -4
- package/dist/components/form/Form/SubmitError.js.map +1 -1
- package/dist/components/form/Form/backend.js +1 -1
- package/dist/components/form/Form/index.d.ts +12 -8
- package/dist/components/form/Form/index.js +4 -2
- package/dist/components/form/Form/index.js.map +1 -1
- package/dist/components/form/Form/modern/actions.js +27 -9
- package/dist/components/form/Form/modern/actions.js.map +1 -1
- package/dist/components/form/Form/modern/context.d.ts +3 -0
- package/dist/components/form/Form/modern/context.js +1 -1
- package/dist/components/form/Form/modern/controller.d.ts +19 -8
- package/dist/components/form/Form/modern/controller.js +15 -4
- package/dist/components/form/Form/modern/controller.js.map +1 -1
- package/dist/components/form/Form/modern/field-binding.js +23 -5
- package/dist/components/form/Form/modern/field-binding.js.map +1 -1
- package/dist/components/form/Form/modern/field.d.ts +30 -0
- package/dist/components/form/Form/modern/field.js +23 -0
- package/dist/components/form/Form/modern/field.js.map +1 -0
- package/dist/components/form/Form/modern/path-types.d.ts +5 -1
- package/dist/components/form/Form/modern/react.d.ts +7 -2
- package/dist/components/form/Form/modern/react.js +21 -3
- package/dist/components/form/Form/modern/react.js.map +1 -1
- package/dist/components/form/Form/modern/read-types.d.ts +11 -0
- package/dist/components/form/Form/modern/store.js +82 -19
- package/dist/components/form/Form/modern/store.js.map +1 -1
- package/dist/components/form/Form/modern/types.d.ts +39 -23
- package/dist/components/form/Form/modern/validation.d.ts +2 -1
- package/dist/components/form/Form/modern/validation.js +4 -4
- package/dist/components/form/Form/modern/validation.js.map +1 -1
- package/dist/components/form/Form/modern/values.js +16 -2
- package/dist/components/form/Form/modern/values.js.map +1 -1
- package/dist/components/form/Form/use-field/types.d.ts +1 -1
- package/dist/components/form/Form/use-field/use-field-binding.js +12 -8
- package/dist/components/form/Form/use-field/use-field-binding.js.map +1 -1
- package/dist/components/form/Form/use-field/use-field-props.d.ts +3 -1
- package/dist/components/form/Form/use-field/use-field-props.js +16 -5
- package/dist/components/form/Form/use-field/use-field-props.js.map +1 -1
- package/dist/components/form/Form/use-field/use-field.d.ts +3 -1
- package/dist/components/form/Form/use-field/use-field.js +1 -1
- package/dist/components/form/Form/use-field/use-field.js.map +1 -1
- package/dist/components/form/Form/use-form.d.ts +1 -1
- package/dist/components/form/Form/use-form.js +1 -1
- package/dist/components/form/Form/validation.js +1 -1
- package/dist/components/form/Label.js +1 -1
- package/dist/components/form/index.d.ts +6 -5
- package/dist/components/form/validation/ValidationIndicator.js +1 -1
- package/dist/components/form/validation/resolve-validation-props.js +1 -1
- package/dist/components/form/validation/use-validation-props.js +1 -1
- package/dist/components/form/wrapper.js +1 -1
- package/dist/components/helpers/DisplayTransition/DisplayTransition.js +1 -1
- package/dist/components/helpers/IconSwitch/IconSwitch.js +1 -1
- package/dist/components/layout/Board/Board.js +1 -1
- package/dist/components/layout/Board/BoardProvider.js +1 -1
- package/dist/components/layout/Board/BoardResponsive.js +1 -1
- package/dist/components/layout/Board/Widget.js +1 -1
- package/dist/components/layout/Board/WidgetHost.js +1 -1
- package/dist/components/layout/Board/board-context.js +1 -1
- package/dist/components/layout/Board/board-store.js +1 -1
- package/dist/components/layout/Board/grid-core/calculate.js +1 -1
- package/dist/components/layout/Board/grid-core/collision-modes.js +1 -1
- package/dist/components/layout/Board/grid-core/collision.js +1 -1
- package/dist/components/layout/Board/grid-core/compactors.js +1 -1
- package/dist/components/layout/Board/grid-core/constraints.js +1 -1
- package/dist/components/layout/Board/grid-core/group-move.js +1 -1
- package/dist/components/layout/Board/grid-core/layout.js +1 -1
- package/dist/components/layout/Board/grid-core/placement.js +1 -1
- package/dist/components/layout/Board/grid-core/sort.js +1 -1
- package/dist/components/layout/Board/index.js +1 -1
- package/dist/components/layout/Board/responsive-utils.js +1 -1
- package/dist/components/layout/Board/use-board-layout.js +1 -1
- package/dist/components/layout/Board/use-board-registry.js +1 -1
- package/dist/components/layout/Board/use-board-select-modifier-key.js +1 -1
- package/dist/components/layout/Board/use-board-selection.js +1 -1
- package/dist/components/layout/Flex.js +1 -1
- package/dist/components/layout/Flow.js +1 -1
- package/dist/components/layout/Grid.js +1 -1
- package/dist/components/layout/Panel.js +1 -1
- package/dist/components/layout/Prefix.js +1 -1
- package/dist/components/layout/ResizablePanel.js +1 -1
- package/dist/components/layout/Space.js +1 -1
- package/dist/components/layout/Suffix.js +1 -1
- package/dist/components/navigation/Pagination/Pagination.js +1 -1
- package/dist/components/navigation/Pagination/use-pagination.js +1 -1
- package/dist/components/navigation/Tabs/DraggableTabList.js +1 -1
- package/dist/components/navigation/Tabs/TabButton.js +1 -1
- package/dist/components/navigation/Tabs/TabDropIndicator.js +1 -1
- package/dist/components/navigation/Tabs/TabPanel.js +1 -1
- package/dist/components/navigation/Tabs/TabPicker.js +1 -1
- package/dist/components/navigation/Tabs/Tabs.js +1 -1
- package/dist/components/navigation/Tabs/TabsAction.js +1 -1
- package/dist/components/navigation/Tabs/TabsContext.js +1 -1
- package/dist/components/navigation/Tabs/popover-placement.js +1 -1
- package/dist/components/navigation/Tabs/styled.js +1 -1
- package/dist/components/navigation/Tabs/types.js +1 -1
- package/dist/components/navigation/Tabs/use-tab-editing.js +1 -1
- package/dist/components/navigation/Tabs/use-tab-indicator.js +1 -1
- package/dist/components/organisms/StatsCard/StatsCard.js +1 -1
- package/dist/components/other/Calendar/Calendar.js +1 -1
- package/dist/components/other/Calendar/CalendarCell.js +1 -1
- package/dist/components/other/Calendar/CalendarGrid.js +1 -1
- package/dist/components/other/Calendar/CalendarHeader.js +1 -1
- package/dist/components/other/Calendar/CalendarPanel.js +1 -1
- package/dist/components/other/Calendar/PeriodCalendar.js +1 -1
- package/dist/components/other/Calendar/PeriodGrid.js +1 -1
- package/dist/components/other/Calendar/RangeCalendar.js +1 -1
- package/dist/components/other/Calendar/styled.js +1 -1
- package/dist/components/other/CubeLogo/CubeLogo.js +1 -1
- package/dist/components/other/NoDataIcon/NoDataIcon.js +1 -1
- package/dist/components/overlays/AlertDialog/AlertDialog.js +1 -1
- package/dist/components/overlays/AlertDialog/AlertDialogApiProvider.js +1 -1
- package/dist/components/overlays/AlertDialog/AlertDialogZone.js +1 -1
- package/dist/components/overlays/Dialog/Dialog.js +1 -1
- package/dist/components/overlays/Dialog/DialogContainer.js +1 -1
- package/dist/components/overlays/Dialog/DialogForm.js +1 -1
- package/dist/components/overlays/Dialog/DialogTrigger.js +1 -1
- package/dist/components/overlays/Dialog/context.js +1 -1
- package/dist/components/overlays/Dialog/use-dialog-container.js +1 -1
- package/dist/components/overlays/Modal/Modal.js +1 -1
- package/dist/components/overlays/Modal/OpenTransitionContext.js +1 -1
- package/dist/components/overlays/Modal/Overlay.js +8 -2
- package/dist/components/overlays/Modal/Overlay.js.map +1 -1
- package/dist/components/overlays/Modal/Popover.js +4 -2
- package/dist/components/overlays/Modal/Popover.js.map +1 -1
- package/dist/components/overlays/Modal/Tray.js +1 -1
- package/dist/components/overlays/Modal/Underlay.js +1 -1
- package/dist/components/overlays/Notifications/Notification.js +1 -1
- package/dist/components/overlays/Notifications/NotificationAction.js +1 -1
- package/dist/components/overlays/Notifications/NotificationCard.js +1 -1
- package/dist/components/overlays/Notifications/NotificationContext.js +1 -1
- package/dist/components/overlays/Notifications/NotificationItem.js +1 -1
- package/dist/components/overlays/Notifications/OverlayContainer.js +1 -1
- package/dist/components/overlays/Notifications/OverlayProvider.js +1 -1
- package/dist/components/overlays/Notifications/PersistentNotificationsList.js +1 -1
- package/dist/components/overlays/Notifications/dismissed-storage.js +1 -1
- package/dist/components/overlays/Notifications/format-relative-time.js +1 -1
- package/dist/components/overlays/Notifications/index.js +1 -1
- package/dist/components/overlays/Notifications/use-notification-state.js +1 -1
- package/dist/components/overlays/Notifications/use-notifications.js +1 -1
- package/dist/components/overlays/Notifications/use-overlay-timers.js +1 -1
- package/dist/components/overlays/Notifications/use-persistent-notifications.js +1 -1
- package/dist/components/overlays/Notifications/use-persistent-state.js +1 -1
- package/dist/components/overlays/Notifications/use-toast-state.js +1 -1
- package/dist/components/overlays/Toast/ToastItem.js +1 -1
- package/dist/components/overlays/Toast/index.js +1 -1
- package/dist/components/overlays/Toast/useProgressToast.js +1 -1
- package/dist/components/overlays/Toast/useToast.js +1 -1
- package/dist/components/overlays/Tooltip/Tooltip.js +1 -1
- package/dist/components/overlays/Tooltip/TooltipProvider.js +1 -1
- package/dist/components/overlays/Tooltip/TooltipTrigger.js +12 -2
- package/dist/components/overlays/Tooltip/TooltipTrigger.js.map +1 -1
- package/dist/components/overlays/Tooltip/context.js +1 -1
- package/dist/components/portal/Portal.js +1 -1
- package/dist/components/portal/PortalProvider.js +1 -1
- package/dist/components/portal/usePortal.js +1 -1
- package/dist/components/shared/DraggableCollection.js +1 -1
- package/dist/components/shared/InvalidIcon.js +1 -1
- package/dist/components/shared/ValidIcon.js +1 -1
- package/dist/components/status/LoadingAnimation/LoadingAnimation.js +1 -1
- package/dist/components/status/Spin/Cube.js +1 -1
- package/dist/components/status/Spin/InternalSpinner.js +1 -1
- package/dist/components/status/Spin/Spin.js +1 -1
- package/dist/components/status/Spin/SpinsContainer.js +1 -1
- package/dist/data/item-themes.js +1 -1
- package/dist/data/themes.js +1 -1
- package/dist/eslint-plugin/defaults.generated.js +1 -1
- package/dist/eslint-plugin/index.js +1 -1
- package/dist/eslint-plugin/rules/no-redundant-default-prop.js +1 -1
- package/dist/i18n/I18nProvider.js +1 -1
- package/dist/i18n/createFormatter.js +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/instance.js +1 -1
- package/dist/i18n/locales/de-DE/uikit.js +1 -1
- package/dist/i18n/locales/en-US/uikit.js +1 -1
- package/dist/i18n/locales/es-ES/uikit.js +1 -1
- package/dist/i18n/locales/es-MX/uikit.js +1 -1
- package/dist/i18n/locales/fr-FR/uikit.js +1 -1
- package/dist/i18n/locales/it-IT/uikit.js +1 -1
- package/dist/i18n/locales/ja-JP/uikit.js +1 -1
- package/dist/i18n/locales/nb-NO/uikit.js +1 -1
- package/dist/i18n/locales/pt-BR/uikit.js +1 -1
- package/dist/i18n/locales/pt-PT/uikit.js +1 -1
- package/dist/i18n/locales/sv-SE/uikit.js +1 -1
- package/dist/i18n/locales/vi-VN/uikit.js +1 -1
- package/dist/i18n/locales.js +1 -1
- package/dist/i18n/useFormatter.js +1 -1
- package/dist/i18n/useI18n.js +1 -1
- package/dist/icons/AdjustmentsHorizontalIcon.js +1 -1
- package/dist/icons/AdjustmentsIcon.js +1 -1
- package/dist/icons/AiIcon.js +1 -1
- package/dist/icons/AreaChartIcon.js +1 -1
- package/dist/icons/ArrowNarrowDownIcon.js +1 -1
- package/dist/icons/ArrowNarrowUpIcon.js +1 -1
- package/dist/icons/BackwardIcon.js +1 -1
- package/dist/icons/BarChartIcon.js +1 -1
- package/dist/icons/BellFilledIcon.js +1 -1
- package/dist/icons/BellIcon.js +1 -1
- package/dist/icons/BooleanIcon.js +1 -1
- package/dist/icons/CalendarEditIcon.js +1 -1
- package/dist/icons/CalendarIcon.js +1 -1
- package/dist/icons/CaretDownIcon.js +1 -1
- package/dist/icons/CaretUpIcon.js +1 -1
- package/dist/icons/ChartAreaStackedIcon.js +1 -1
- package/dist/icons/ChartAreaStackedPercentageIcon.js +1 -1
- package/dist/icons/ChartBarGroupedHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarGroupedIcon.js +1 -1
- package/dist/icons/ChartBarHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarLineIcon.js +1 -1
- package/dist/icons/ChartBarStackedHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarStackedIcon.js +1 -1
- package/dist/icons/ChartBarStackedPercentageHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarStackedPercentageIcon.js +1 -1
- package/dist/icons/ChartBoxPlot2Icon.js +1 -1
- package/dist/icons/ChartBoxPlotIcon.js +1 -1
- package/dist/icons/ChartBubbleIcon.js +1 -1
- package/dist/icons/ChartDonut2Icon.js +1 -1
- package/dist/icons/ChartFunnelIcon.js +1 -1
- package/dist/icons/ChartHeatmapIcon.js +1 -1
- package/dist/icons/ChartKPIIcon.js +1 -1
- package/dist/icons/ChartPie2Icon.js +1 -1
- package/dist/icons/ChartScatterIcon.js +1 -1
- package/dist/icons/CheckCircleFilledIcon.js +1 -1
- package/dist/icons/CheckCircleIcon.js +1 -1
- package/dist/icons/CheckIcon.js +1 -1
- package/dist/icons/CircleFilledIcon.js +1 -1
- package/dist/icons/ClearIcon.js +1 -1
- package/dist/icons/CloseCircleFilledIcon.js +1 -1
- package/dist/icons/CloseCircleIcon.js +1 -1
- package/dist/icons/CloseIcon.js +1 -1
- package/dist/icons/CodeIcon.js +1 -1
- package/dist/icons/ColumnTotalIcon.js +1 -1
- package/dist/icons/CopyIcon.js +1 -1
- package/dist/icons/CountIcon.js +1 -1
- package/dist/icons/CubeIcon.js +1 -1
- package/dist/icons/CubePauseIcon.js +1 -1
- package/dist/icons/CubePlayIcon.js +1 -1
- package/dist/icons/CurrencyDollarIcon.js +1 -1
- package/dist/icons/DangerIcon.js +1 -1
- package/dist/icons/DashboardIcon.js +1 -1
- package/dist/icons/DatabaseIcon.js +1 -1
- package/dist/icons/DecimalDecreaseIcon.js +1 -1
- package/dist/icons/DecimalIncreaseIcon.js +1 -1
- package/dist/icons/DirectionIcon.js +1 -1
- package/dist/icons/DonutIcon.js +1 -1
- package/dist/icons/DownIcon.js +1 -1
- package/dist/icons/EditIcon.js +1 -1
- package/dist/icons/ExclamationCircleFilledIcon.js +1 -1
- package/dist/icons/ExclamationCircleIcon.js +1 -1
- package/dist/icons/ExclamationIcon.js +1 -1
- package/dist/icons/EyeIcon.js +1 -1
- package/dist/icons/EyeInvisibleIcon.js +1 -1
- package/dist/icons/FilterIcon.js +1 -1
- package/dist/icons/FolderFilledIcon.js +1 -1
- package/dist/icons/FolderIcon.js +1 -1
- package/dist/icons/FolderOpenFilledIcon.js +1 -1
- package/dist/icons/FolderOpenIcon.js +1 -1
- package/dist/icons/ForwardIcon.js +1 -1
- package/dist/icons/GripVerticalIcon.js +1 -1
- package/dist/icons/HierarchyIcon.js +1 -1
- package/dist/icons/HierarchyOpenIcon.js +1 -1
- package/dist/icons/Icon.js +1 -1
- package/dist/icons/InfoCircleIcon.js +1 -1
- package/dist/icons/InfoIcon.js +1 -1
- package/dist/icons/KeyIcon.js +1 -1
- package/dist/icons/LeftIcon.js +1 -1
- package/dist/icons/LineChartIcon.js +1 -1
- package/dist/icons/LoadingIcon.js +1 -1
- package/dist/icons/LockFilledIcon.js +1 -1
- package/dist/icons/LockIcon.js +1 -1
- package/dist/icons/MoreIcon.js +1 -1
- package/dist/icons/NotAllowedIcon.js +1 -1
- package/dist/icons/Number123Icon.js +1 -1
- package/dist/icons/NumberIcon.js +1 -1
- package/dist/icons/PauseCircleFilledIcon.js +1 -1
- package/dist/icons/PauseCircleIcon.js +1 -1
- package/dist/icons/PauseIcon.js +1 -1
- package/dist/icons/PercentageIcon.js +1 -1
- package/dist/icons/PieChartIcon.js +1 -1
- package/dist/icons/PipetteIcon.js +1 -1
- package/dist/icons/PlayCircleIcon.js +1 -1
- package/dist/icons/PlayIcon.js +1 -1
- package/dist/icons/PlusIcon.js +1 -1
- package/dist/icons/ProgressBarIcon.js +1 -1
- package/dist/icons/ReloadIcon.js +1 -1
- package/dist/icons/ReportIcon.js +1 -1
- package/dist/icons/ReturnIcon.js +1 -1
- package/dist/icons/RightIcon.js +1 -1
- package/dist/icons/RowTotalsIcon.js +1 -1
- package/dist/icons/SchemeIcon.js +1 -1
- package/dist/icons/SearchIcon.js +1 -1
- package/dist/icons/SemanticQueryIcon.js +1 -1
- package/dist/icons/SettingsIcon.js +1 -1
- package/dist/icons/ShieldFilledIcon.js +1 -1
- package/dist/icons/ShieldIcon.js +1 -1
- package/dist/icons/SlashIcon.js +1 -1
- package/dist/icons/SparklesIcon.js +1 -1
- package/dist/icons/SqlIcon.js +1 -1
- package/dist/icons/StatsIcon.js +1 -1
- package/dist/icons/StopIcon.js +1 -1
- package/dist/icons/StringIcon.js +1 -1
- package/dist/icons/SubtotalsIcon.js +1 -1
- package/dist/icons/SwitchIcon.js +1 -1
- package/dist/icons/TableIcon.js +1 -1
- package/dist/icons/ThumbsDownIcon.js +1 -1
- package/dist/icons/ThumbsUpIcon.js +1 -1
- package/dist/icons/ThunderboltCrossedIcon.js +1 -1
- package/dist/icons/ThunderboltFilledIcon.js +1 -1
- package/dist/icons/ThunderboltIcon.js +1 -1
- package/dist/icons/TimeIcon.js +1 -1
- package/dist/icons/TrashIcon.js +1 -1
- package/dist/icons/UnlockIcon.js +1 -1
- package/dist/icons/UpIcon.js +1 -1
- package/dist/icons/UserGroupIcon.js +1 -1
- package/dist/icons/UserIcon.js +1 -1
- package/dist/icons/UserLockIcon.js +1 -1
- package/dist/icons/ViewIcon.js +1 -1
- package/dist/icons/WarningFilledIcon.js +1 -1
- package/dist/icons/WarningIcon.js +1 -1
- package/dist/icons/wrap-icon.js +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +2 -2
- package/dist/probe/canonicalize.js +1 -1
- package/dist/probe/css.js +1 -1
- package/dist/probe/index.js +1 -1
- package/dist/provider.js +1 -1
- package/dist/providers/TrackingProvider.js +1 -1
- package/dist/providers/navigationAdapter.default.js +1 -1
- package/dist/shared/form.d.ts +13 -5
- package/dist/tokens/all-tokens.js +1 -1
- package/dist/tokens/base.js +1 -1
- package/dist/tokens/color-seed.js +1 -1
- package/dist/tokens/color-theme.js +1 -1
- package/dist/tokens/colors.js +1 -1
- package/dist/tokens/layout.js +1 -1
- package/dist/tokens/lazy-styles.js +1 -1
- package/dist/tokens/legacy-color.js +1 -1
- package/dist/tokens/palette-config.js +1 -1
- package/dist/tokens/palette.js +1 -1
- package/dist/tokens/resolve.js +1 -1
- package/dist/tokens/shadows.js +1 -1
- package/dist/tokens/sizes.js +1 -1
- package/dist/tokens/spacing.js +1 -1
- package/dist/tokens/typography.js +1 -1
- package/dist/utils/ResizeSensor.js +1 -1
- package/dist/utils/colors.js +1 -1
- package/dist/utils/dotize.js +1 -1
- package/dist/utils/is-dev-env.js +1 -1
- package/dist/utils/modules.js +1 -1
- package/dist/utils/promise.js +1 -1
- package/dist/utils/raf.js +1 -1
- package/dist/utils/random.js +1 -1
- package/dist/utils/range.js +1 -1
- package/dist/utils/react/RenderCache.js +1 -1
- package/dist/utils/react/Slots.js +1 -1
- package/dist/utils/react/chain.js +1 -1
- package/dist/utils/react/disabledProps.js +1 -1
- package/dist/utils/react/escapePropagation.js +39 -0
- package/dist/utils/react/escapePropagation.js.map +1 -0
- package/dist/utils/react/forwardRefWithGenerics.js +1 -1
- package/dist/utils/react/index.js +1 -1
- package/dist/utils/react/interactions.js +1 -1
- package/dist/utils/react/isTextOnly.js +1 -1
- package/dist/utils/react/mapProps.js +1 -1
- package/dist/utils/react/mergeProps.js +1 -1
- package/dist/utils/react/nullableValue.js +1 -1
- package/dist/utils/react/programmaticFocus.js +58 -0
- package/dist/utils/react/programmaticFocus.js.map +1 -0
- package/dist/utils/react/resolveIcon.js +1 -1
- package/dist/utils/react/sharedStore.js +1 -1
- package/dist/utils/react/useBufferedValue.js +1 -1
- package/dist/utils/react/useCombinedRefs.js +1 -1
- package/dist/utils/react/useControlledFocusVisible.js +1 -1
- package/dist/utils/react/useEventBus.js +1 -1
- package/dist/utils/react/useId.js +1 -1
- package/dist/utils/react/useIsDarwin.js +1 -1
- package/dist/utils/react/useKeySymbols.js +1 -1
- package/dist/utils/react/useLayoutEffect.js +1 -1
- package/dist/utils/react/useLocalStorage.js +1 -1
- package/dist/utils/react/useMergeStyles.js +1 -1
- package/dist/utils/react/useOverlayEscapeGuard.js +41 -0
- package/dist/utils/react/useOverlayEscapeGuard.js.map +1 -0
- package/dist/utils/react/usePopoverSync.js +1 -1
- package/dist/utils/react/useQaProps.js +1 -1
- package/dist/utils/react/useScheme.js +1 -1
- package/dist/utils/react/useViewportSize.js +1 -1
- package/dist/utils/react/wrapNodeIfPlain.js +1 -1
- package/dist/utils/selection.js +1 -1
- package/dist/utils/styles.js +1 -1
- package/dist/utils/tree.js +1 -1
- package/dist/utils/warnings.js +1 -1
- package/dist/version.js +2 -2
- package/docs/CreateComponent.md +1 -1
- package/docs/FieldProperties.md +25 -13
- package/docs/Usage.md +17 -31
- package/docs/components/fields/FileInput.md +4 -0
- package/docs/components/form/Field.md +2 -2
- package/docs/components/form/Form.md +13 -39
- package/docs/components/form/FormInstance.md +5 -9
- package/docs/components/form/ModernForm.md +269 -0
- package/docs/modern-form-guide.md +269 -0
- package/docs/modern-form-migration.md +20 -59
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# FormInstance
|
|
1
|
+
# FormInstance (legacy)
|
|
2
|
+
|
|
3
|
+
This page describes `Form.useForm()` and `CubeFormInstance`. New modern forms use `Form.useController<Model>()`; see [Modern Form](./ModernForm.md) for its commands and recipes. The methods and dot notation below are legacy-only.
|
|
2
4
|
|
|
3
5
|
The `CubeFormInstance` is the core form management class that provides programmatic control over form state, validation, and data handling. It serves as the central API for interacting with form fields, managing validation states, and handling form submission workflows.
|
|
4
6
|
|
|
@@ -16,13 +18,7 @@ function MyComponent() {
|
|
|
16
18
|
}
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
import { CubeFormInstance } from '@cube-dev/ui-kit';
|
|
23
|
-
|
|
24
|
-
const formInstance = new CubeFormInstance();
|
|
25
|
-
```
|
|
21
|
+
`CubeFormInstance` is exported as a type for wrapper props and references. Use the hook to create a React form; the package does not export its constructor as a runtime API.
|
|
26
22
|
|
|
27
23
|
## Field Management
|
|
28
24
|
|
|
@@ -295,7 +291,7 @@ type CubeFieldData<Name extends string, Value> = {
|
|
|
295
291
|
|
|
296
292
|
## Best Practices
|
|
297
293
|
|
|
298
|
-
1. **
|
|
294
|
+
1. **For legacy forms, use the `useForm` hook** in React components rather than creating instances manually
|
|
299
295
|
2. **Handle async validation errors** appropriately using try-catch blocks
|
|
300
296
|
3. **Set initial values early** in the component lifecycle to avoid field state inconsistencies
|
|
301
297
|
4. **Use dot notation** for nested object structures instead of managing complex object hierarchies manually
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Modern Form: choose the API for the job
|
|
2
|
+
|
|
3
|
+
For a new modern form, start with `Form.useController<Model>()`, bind inputs with `name="email"` or a nested tuple such as `name={['user', 'email']}`, and submit through `<Form onSubmit={...}>` and `<Form.Submit>`. Add subscriptions only where other UI needs to read form state. The input components already subscribe to their own values and errors. `field={form.field(path)}` is an optional alternative when you want model-checked paths, input value compatibility checks, or inferred custom-validator types.
|
|
4
|
+
|
|
5
|
+
Existing `Form.useForm()` forms and roots without a modern controller use the legacy backend. Follow the [migration checklist](https://github.com/cube-js/cube-ui-kit/blob/main/docs/modern-form-migration.md) when converting one. `DialogForm` still requires a legacy instance; do not pass a modern controller through a cast.
|
|
6
|
+
|
|
7
|
+
Recipes: [API choices](#pick-one-binding-and-one-owner-for-each-concern), [reactive UI](#read-a-value-show-status-or-reveal-a-section), [server data](#load-server-data-refresh-defaults-or-discard-edits), [programmatic edits](#edit-values-from-an-event-handler), [validation](#validate-simple-rules-sibling-fields-or-an-api-response), [conditional fields and wizards](#hide-fields-use-nested-data-or-build-a-wizard), [submission](#submit-display-server-errors-and-reset), and [custom controls](#implement-a-reusable-custom-control).
|
|
8
|
+
|
|
9
|
+
## Start with an ordinary edit form
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { Form, Switch, TextInput } from '@cube-dev/ui-kit';
|
|
13
|
+
import type { FormValues } from '@cube-dev/ui-kit';
|
|
14
|
+
|
|
15
|
+
interface Profile {
|
|
16
|
+
email: string | null;
|
|
17
|
+
notifications: boolean | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ProfileForm({
|
|
21
|
+
initialProfile,
|
|
22
|
+
save,
|
|
23
|
+
}: {
|
|
24
|
+
initialProfile: Profile;
|
|
25
|
+
save: (values: FormValues<Profile>, signal: AbortSignal) => Promise<void>;
|
|
26
|
+
}) {
|
|
27
|
+
const form = Form.useController<Profile>({ defaultValues: initialProfile });
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Form form={form} onSubmit={(values, { signal }) => save(values, signal)}>
|
|
31
|
+
<TextInput
|
|
32
|
+
name="email"
|
|
33
|
+
rules={[{ type: 'email', message: 'Enter a valid email' }]}
|
|
34
|
+
label="Email"
|
|
35
|
+
isRequired
|
|
36
|
+
/>
|
|
37
|
+
<Switch name="notifications" label="Notifications" />
|
|
38
|
+
<Form.SubmitError />
|
|
39
|
+
<Form.Submit>Save</Form.Submit>
|
|
40
|
+
<Form.Reset>Reset</Form.Reset>
|
|
41
|
+
</Form>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The model describes possible values, including API `null`s. A text input displays `null` as empty and a switch displays it as unchecked; neither writes that display fallback into the form. Editing uses the control's normal change payload, and reset restores the nullable default. `FormValues<Model>` describes readonly, potentially incomplete data: registration and validation do not prove that every model property is present. Map to your API's request type explicitly where it requires complete data.
|
|
47
|
+
|
|
48
|
+
`defaultValues` initializes this controller once. A later `initialProfile` prop does not overwrite edits. Use the loading commands below, or remount the form with a record key when switching records should start a new editing session. Modern roots do not accept `defaultValues`.
|
|
49
|
+
|
|
50
|
+
## Pick one binding and one owner for each concern
|
|
51
|
+
|
|
52
|
+
| Concern | Start here | Use the alternative when… |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| Connect an input | `name="email"` for a literal name, or `name={['user', 'email']}` for a nested modern path | `field={form.field(path, options)}` adds model-checked paths, input value compatibility, and validator inference. Neither binding requires the other. |
|
|
55
|
+
| Locate the controller | Use the controller you created, or pass it as a prop | `Form.useControllerContext<Model>()` avoids prop threading in a descendant. It reads context without subscribing. |
|
|
56
|
+
| Configure a field | With `name`, put `rules`, dependencies, and retention on the input | With `field`, put these options and typed `validate` on the descriptor. Avoid configuring the same option in both places. |
|
|
57
|
+
| Present a field | Input props such as `label`, `description`, `isRequired` | `rules: [{ required: true }]` is useful when required validation needs a custom message or no visible required marker. |
|
|
58
|
+
| Handle submit/change/failure | Callbacks on the owning `<Form>` | Hook callbacks let a controller own behavior independently of a root, or provide defaults to a wrapper. Choose one location per callback. |
|
|
59
|
+
| Initialize values | Controller `defaultValues` | Field `defaultValue` supplies a fallback for a reusable field missing from the controller's data. |
|
|
60
|
+
|
|
61
|
+
`Form.Submit`, `Form.Reset`, and `Form.SubmitError` are aliases of the exported `SubmitButton`, `ResetButton`, and `SubmitError` components. This guide uses the `Form.*` names consistently; importing a standalone name does not select a different implementation.
|
|
62
|
+
|
|
63
|
+
`name` uses the surrounding form, or an explicit `form={form}` outside it. Tuple names accept string and numeric segments and readonly arrays; inline tuples do not need memoization. Names are not checked against the surrounding controller's model because React context cannot infer its type. Tuple names require a modern controller; legacy forms retain string names and dot notation.
|
|
64
|
+
|
|
65
|
+
A descriptor is pure configuration, so creating it inline is expected. It neither reads state nor registers a field; the mounted input registers after commit. It supplies the controller and path even outside the root, so adding `form` or `name` to the same input is unnecessary. If both are present, the descriptor wins. Descriptor options override only matching options they supply. In particular, descriptor `rules` or `validate` replaces input-level `rules`; put built-in rules and a custom validator together on the descriptor to run both.
|
|
66
|
+
|
|
67
|
+
Controller values and defaults take precedence over field defaults, including explicit `null` and `undefined`. Root callbacks override the corresponding hook callback while mounted; omitted or undefined root callbacks fall back to the hook callback. Business callbacks update after every committed render. Defaults, `errorPolicy`, and diagnostic handlers are creation options; update values through commands and override error policy per field when needed.
|
|
68
|
+
|
|
69
|
+
Use one owning root per controller. Omitted `form` props use context; explicitly passing `form={undefined}` detaches an input. Legacy roots and `FormScopeMask` hide the surrounding modern context.
|
|
70
|
+
|
|
71
|
+
## Read a value, show status, or reveal a section
|
|
72
|
+
|
|
73
|
+
| UI needs | Use | Why it exists |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| One field's value | `Form.useValue(form, path)` | Checked path and inferred value without writing a selector. |
|
|
76
|
+
| A field's value, errors, or metadata | `Form.useFieldState(form, path)` | Checked path with typed value/default, errors, status, dirty, touched, and active state. |
|
|
77
|
+
| A derived result or form-wide flag | `Form.useSelector(form, selector)` | General selection, such as `state.isDirty` or `state.canReset`. |
|
|
78
|
+
| A small inline region that reacts independently | `<Form.Subscribe form={form} selector={...}>` | Keeps the subscription in that subtree without extracting a component. |
|
|
79
|
+
| A value in an event handler or effect | `form.getValue(path)` / `form.getValues()` | One-time imperative read; it does not subscribe React. |
|
|
80
|
+
|
|
81
|
+
The value and field-state hooks are conveniences over selectors. The choice between a hook and `Subscribe` is where rerenders should happen: hooks rerender their component, while `Subscribe` rerenders its children region. Creating a controller does not subscribe its owner. Prefer a leaf component for reusable reactive UI:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
function EmailPreview() {
|
|
85
|
+
const form = Form.useControllerContext<Profile>();
|
|
86
|
+
const email = Form.useValue(form, 'email');
|
|
87
|
+
return <output>{email ?? 'No email set'}</output>;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use `Subscribe` for a conditional section directly inside the form:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<Form.Subscribe form={form} selector={(state) => state.values.notifications}>
|
|
95
|
+
{(enabled) =>
|
|
96
|
+
enabled ? <TextInput field={form.field('email')} label="Email" /> : null
|
|
97
|
+
}
|
|
98
|
+
</Form.Subscribe>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
These are alternative placements of the email input, not an instruction to mount it twice. Pass `form` to `Subscribe` for inferred model types. It can also use modern context when `form` is omitted, but React context cannot infer the ancestor's model. The generic on `useControllerContext<Profile>()` is your assertion that the ancestor uses `Profile`.
|
|
102
|
+
|
|
103
|
+
Selectors and equality functions must be pure. Select a primitive or a stable snapshot branch; allocating a fresh object compares unequal under the default `Object.is`. Supply `isEqual` when such an object should compare by its contents. `useFieldState` can return `undefined` before a path is tracked. Getter reads in JSX will not stay current. Plain objects and arrays in snapshots are deeply readonly and potentially incomplete; use commands for writes. Date-control values retain their methods; treat them and other non-plain values as immutable.
|
|
104
|
+
|
|
105
|
+
## Load server data, refresh defaults, or discard edits
|
|
106
|
+
|
|
107
|
+
Most forms need only initialization, adoption for incoming data, and reset for a new editing session. The other modes let integrations change the reset baseline independently of user interaction.
|
|
108
|
+
|
|
109
|
+
| Situation | Command | Effect |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| Data is available before mounting | `Form.useController({ defaultValues })` | Seeds values and the reset baseline once. |
|
|
112
|
+
| A response arrives while the user may be typing | `adoptDefaultValues(response, { when: 'untouched' })` | Replaces the baseline and adopts untouched fields, preserving touched values. This is the default adoption policy. |
|
|
113
|
+
| Refresh fields that still match their previous defaults | `adoptDefaultValues(response, { when: 'clean' })` | Preserves dirty values, including programmatic edits; touched-but-clean fields can refresh. |
|
|
114
|
+
| Discard edits and start from a different record | `reset({ values: response })` | Replaces baseline and values, clears interaction/errors, and cancels pending submission. |
|
|
115
|
+
| Discard edits using the existing baseline | `reset()` / `<Form.Reset>` | Restores defaults and clears interaction/errors. |
|
|
116
|
+
| Change what Reset will restore without changing the draft | `setDefaultValues(response)` | Replaces only the baseline; dirtiness is recomputed against it. |
|
|
117
|
+
|
|
118
|
+
All defaults commands replace the baseline object; omitted keys are removed from it. Adoption also starts from the incoming object, then restores protected paths. It is not a partial patch. Use `setValue` or `setValues` for edits to current data.
|
|
119
|
+
|
|
120
|
+
### Advanced defaults policies
|
|
121
|
+
|
|
122
|
+
Use these only when the ordinary adoption or reset behavior above does not fit the interaction:
|
|
123
|
+
|
|
124
|
+
| Situation | Command | Effect |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| Protect both touched and dirty fields | `adoptDefaultValues(response, { when: 'untouched', preserveDirty: true })` | Adds dirty-value protection to the touched check. |
|
|
127
|
+
| Replace values but keep touched state and submission state | `adoptDefaultValues(response, { when: 'always' })` | Replaces values and baseline; invalidates affected field validation. |
|
|
128
|
+
| Replace values and clear field interaction, keeping submission state | `setDefaultValues(response, { currentValues: 'replace' })` | Replaces values and baseline; clears touched/field validation, but does not cancel submission or clear its error. |
|
|
129
|
+
|
|
130
|
+
### Asynchronous loading
|
|
131
|
+
|
|
132
|
+
For asynchronous loading, protect both user edits and request ordering:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
const request = new AbortController();
|
|
137
|
+
void load(request.signal)
|
|
138
|
+
.then((profile) => {
|
|
139
|
+
if (!request.signal.aborted) {
|
|
140
|
+
setLoadError(undefined);
|
|
141
|
+
form.adoptDefaultValues(profile, { when: 'untouched' });
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
.catch((error: unknown) => {
|
|
145
|
+
if (!request.signal.aborted) setLoadError(error);
|
|
146
|
+
});
|
|
147
|
+
return () => request.abort();
|
|
148
|
+
}, [form, load]);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Here `load` is a stable loader for the current record and `setLoadError` is local UI state; import `useEffect` from React. Gate editing on loading if a partially loaded record must never be submitted. When switching records, remount or reset the editing session: adopting an unrelated record while preserving old edits would mix the two.
|
|
152
|
+
|
|
153
|
+
## Edit values from an event handler
|
|
154
|
+
|
|
155
|
+
Use `setValue(path, value)` for one path and `setValues(partial)` for several top-level keys. Nested objects supplied to `setValues` replace those objects; use tuple paths to update an individual nested leaf. Neither command changes the reset baseline.
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
<button
|
|
159
|
+
type="button"
|
|
160
|
+
onClick={() => form.setValue('notifications', true, { source: 'user' })}
|
|
161
|
+
>
|
|
162
|
+
Enable notifications
|
|
163
|
+
</button>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`source: 'user'` defaults to touching the field and notifying `onValuesChange` when values change. Programmatic writes default to neither; override `touch` and `notify` explicitly when needed. `validate` defaults to `auto`; use `always` to force validation or `never` to invalidate without revalidating. `onValuesChange(values, change)` receives retained values and `{ names, source, kind }`. Use it for user-edit integrations such as a debounced draft saver; decide separately whether programmatic writes should trigger that saver. Defaults adoption, value replacement, and reset also notify this callback when they report changed paths; filter `change.kind` or `change.source` if only direct edits should be saved.
|
|
167
|
+
|
|
168
|
+
`batch(() => { ... })` combines synchronous commands into one publication. It is useful for several tuple writes; it is not a rollback transaction and must not wrap async work. `subscribe(listener)` is for imperative integrations that need every publication; React UI should use the hooks above and observers must call the returned unsubscribe function.
|
|
169
|
+
|
|
170
|
+
## Validate simple rules, sibling fields, or an API response
|
|
171
|
+
|
|
172
|
+
| Validation case | Start here |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| Required field with a visible marker | Input `isRequired`; it adds required validation too. |
|
|
175
|
+
| Email, length, range, pattern, or enum | Input `rules` using the built-in constraints, or descriptor `rules` when using `field`. |
|
|
176
|
+
| Custom domain check | Descriptor `validate(value, context)` for an inferred field value and checked reads. |
|
|
177
|
+
| Several custom rules or existing shared rule objects | `rules: [{ validator(rule, value, context) { ... } }]`; use `ModernValidationRule` for strict modern rule typing. |
|
|
178
|
+
| Validation depends on another form value | `dependsOn: ['password']`, or nested tuples such as `dependsOn: [['account', 'password']]`. |
|
|
179
|
+
| Validation captures props or other external values | `deps: [organizationId, checkName]`. Include changing functions too. |
|
|
180
|
+
| An integration explicitly versions function behavior | `rulesKey: revision`; ordinary forms can leave it out. |
|
|
181
|
+
|
|
182
|
+
`deps` and `dependsOn` deliberately have separate jobs. A string in `deps` is an external value; the same string in `dependsOn` names a form field. Use both when a validator reads both sources. `rulesKey` is an advanced function revision, not another place to list ordinary dependencies.
|
|
183
|
+
|
|
184
|
+
A password confirmation can declare its sibling dependency even when an early return skips the read:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<TextInput
|
|
188
|
+
field={form.field('confirmation', {
|
|
189
|
+
dependsOn: ['password'],
|
|
190
|
+
validate: (value, { getValue }) => {
|
|
191
|
+
if (!value) return;
|
|
192
|
+
return value === getValue('password') ? undefined : 'Passwords must match';
|
|
193
|
+
},
|
|
194
|
+
})}
|
|
195
|
+
label="Confirm password"
|
|
196
|
+
type="password"
|
|
197
|
+
isRequired
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
This example uses a controller whose model contains `password` and `confirmation`. A declared dependency change revalidates fields that were already validated or validating, unless the write requests `validate: 'never'`. Reads through validator `getValue`/`getValues` use one captured snapshot and cancel in-flight work if that data changes; reads alone do not schedule another run.
|
|
202
|
+
|
|
203
|
+
For a remote check, pass the cancellation signal and declare captured inputs:
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
<TextInput
|
|
207
|
+
field={form.field('name', {
|
|
208
|
+
deps: [organizationId, checkName],
|
|
209
|
+
validateTrigger: 'onChange',
|
|
210
|
+
validationDelay: 200,
|
|
211
|
+
validate: async (value, { signal }) => {
|
|
212
|
+
if (!value) return;
|
|
213
|
+
const available = await checkName(organizationId, value, signal);
|
|
214
|
+
return available ? undefined : 'Name is already in use';
|
|
215
|
+
},
|
|
216
|
+
})}
|
|
217
|
+
label="Name"
|
|
218
|
+
isRequired
|
|
219
|
+
/>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Here the model contains a string `name`, and `checkName` returns `Promise<boolean>`. A validator succeeds by returning `undefined`, `null`, or an empty string. Return a ReactNode error, or throw/reject, to fail. Adapt legacy validators that resolve data objects; those are failures in the modern contract. Superseded results cannot overwrite current errors even if the validator ignores its signal.
|
|
223
|
+
|
|
224
|
+
Automatic validation honors `validateTrigger` and `validationDelay`. An `auto` write revalidates on-change fields and fields already showing errors; otherwise on-blur fields wait for blur. Explicit `form.validate()` and submission validate immediately; `form.validate(['email'])` selects a literal path, and `form.validate([['rows', 0, 'email']])` selects a nested path. Inspect `{ isValid, stale, fields }` before advancing a wizard step. Validation covers active fields only. `errorPolicy: 'first' | 'all'` controls rule-error collection, with `first` as the default.
|
|
225
|
+
|
|
226
|
+
Rule constraints and function source are compared automatically so equivalent inline validators do not restart pending work. Captures require `deps`; different functions with identical source, including bound/native functions, also need an explicit dependency or revision. `rulesKey` skips function-source comparison only; constraints such as `min`, `pattern`, and `required` are still compared. Update the key or dependencies when function behavior changes. `deps` alone retains source comparison.
|
|
227
|
+
|
|
228
|
+
## Hide fields, use nested data, or build a wizard
|
|
229
|
+
|
|
230
|
+
Unmounting the last input at a path removes it from `activeValues` immediately and retains its draft by default. Remounting restores that draft. Use `field={form.field('details', { preserve: false })}` when leaving a section should discard its current value. Cleanup removes the value, but its default baseline remains available to reset. Visually hiding an input while leaving it mounted does not unregister it.
|
|
231
|
+
|
|
232
|
+
| Payload | How to choose it | Validation |
|
|
233
|
+
| --- | --- | --- |
|
|
234
|
+
| Mounted fields | `<Form>` / `form.submit()` defaults | Active registered fields only. |
|
|
235
|
+
| All retained data, including hidden sections | `<Form submitValues="all">` or `form.submit({ include: 'all' })` | Still only active registered fields. |
|
|
236
|
+
|
|
237
|
+
For a wizard, validate a step before unmounting it and choose `submitValues="all"` if previous steps belong in the final payload. That does not revalidate unmounted steps. If final submission must validate every step against the latest data, keep the relevant inputs mounted or perform whole-payload validation explicitly. A form with no active fields is invalid.
|
|
238
|
+
|
|
239
|
+
Bind nested leaves directly with `name={['rows', index, 'email']}`. The optional typed equivalent is `field={form.field(['rows', index, 'email'])}`. A string such as `'user.email'` is one literal key, not dot notation. Numeric indices and numeric string segments address the same path. Removing an array path does not shift the other indices. Registering a parent object makes its complete value active; register leaf paths when only selected children belong in the active payload. Descriptor and controller-command literal paths are checked against the model; `name` accepts dynamic paths without those checks. The declaration surface requires TypeScript 5.4 or newer.
|
|
240
|
+
|
|
241
|
+
`getValues()` / `state.values` contains all retained data; `getActiveValues()` / `state.activeValues` contains registered paths. Dirty/touched metadata includes retained fields, while validity concerns active fields.
|
|
242
|
+
|
|
243
|
+
## Submit, display server errors, and reset
|
|
244
|
+
|
|
245
|
+
Use `<Form.Submit>` for normal submission, including Enter in the form. It handles loading and stays enabled after validation errors so users can request feedback again. `disableOnInvalid` opts into disabling it on invalid state. Use `form.submit()` for a workflow controlled from an event handler; inspect its result (`submitted`, `invalid`, `failed`, `ignored`, or `stale`) before continuing. Choose the payload independently for each call: imperative `submit()` defaults to active values even if the root has `submitValues="all"`.
|
|
246
|
+
|
|
247
|
+
To place actions or errors outside the root, pass the controller:
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
<Form.Submit form={form}>Save</Form.Submit>
|
|
251
|
+
<Form.Reset form={form}>Reset</Form.Reset>
|
|
252
|
+
<Form.SubmitError form={form} />
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
An explicit-controller Submit targets that controller's owning form even inside another DOM form. Return or await the request in `onSubmit(values, { signal, include })`; concurrent submissions are ignored. Throwing or rejecting sets `submitError`, which `Form.SubmitError` displays with a safe generic fallback. Supply `renderError` to format application errors. Ordinary edits keep that error; another submit or reset clears it.
|
|
256
|
+
|
|
257
|
+
Use `setFieldErrors('email', ['Email is already registered'])` for a server error attached to an input and `clearFieldErrors('email')` to clear it. `setSubmitError` / `clearSubmitError` manage a form-level failure. `onSubmitFailed` receives `{ status: 'invalid', errors }` for field validation or `{ status: 'failed', error }` for a rejected submit callback; field validation does not set `submitError`.
|
|
258
|
+
|
|
259
|
+
`Form.Reset` is enabled when `state.canReset` is true: edits, touched/validation state, or a submit error can be cleared, and no submission is running. Use that selector for a custom reset UI. Direct `form.reset()` can also cancel an active submission; unmounting the owning root cancels it too. Action buttons preserve `onPress`; use `onClick` and `event.preventDefault()` to cancel their action. Root `onReset` can cancel before reset runs; `onResetCapture` is reserved for internal reset handling.
|
|
260
|
+
|
|
261
|
+
Native forms with `action`/`method` use browser submission and bypass the controller's submit pipeline. Use that path only when the browser should own the request.
|
|
262
|
+
|
|
263
|
+
## Implement a reusable custom control
|
|
264
|
+
|
|
265
|
+
Use the same field integration as built-in inputs: `FieldBaseProps<Value>`, `useFieldProps`, then `wrapWithField`. Map the control's value/event API and forward the generated id, blur, disabled, read-only, and invalid state to the interactive element. Include `null | undefined` in the accepted model type when empty values are supported and normalize only for display. Do not spread controller props onto a DOM element.
|
|
266
|
+
|
|
267
|
+
The [compiled CustomControl example](https://github.com/cube-js/cube-ui-kit/blob/main/typecheck/consumer/modern-form-examples.tsx) shows the complete pattern. The [component creation guide](https://cube-ui-kit.vercel.app/?path=/docs/getting-started-create-component--docs) explains the shared field wrapper. `Form.Item` / `Field` belongs to the legacy backend; it is not a second way to connect a modern custom input.
|
|
268
|
+
|
|
269
|
+
For SSR, seed the controller with the same serializable defaults on server and client. Initial hydration uses the creation snapshot before subscribers catch up with client state. Each mounted creator owns its controller; no manual disposal is needed.
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Modern Form: choose the API for the job
|
|
2
|
+
|
|
3
|
+
For a new modern form, start with `Form.useController<Model>()`, bind inputs with `name="email"` or a nested tuple such as `name={['user', 'email']}`, and submit through `<Form onSubmit={...}>` and `<Form.Submit>`. Add subscriptions only where other UI needs to read form state. The input components already subscribe to their own values and errors. `field={form.field(path)}` is an optional alternative when you want model-checked paths, input value compatibility checks, or inferred custom-validator types.
|
|
4
|
+
|
|
5
|
+
Existing `Form.useForm()` forms and roots without a modern controller use the legacy backend. Follow the [migration checklist](https://github.com/cube-js/cube-ui-kit/blob/main/docs/modern-form-migration.md) when converting one. `DialogForm` still requires a legacy instance; do not pass a modern controller through a cast.
|
|
6
|
+
|
|
7
|
+
Recipes: [API choices](#pick-one-binding-and-one-owner-for-each-concern), [reactive UI](#read-a-value-show-status-or-reveal-a-section), [server data](#load-server-data-refresh-defaults-or-discard-edits), [programmatic edits](#edit-values-from-an-event-handler), [validation](#validate-simple-rules-sibling-fields-or-an-api-response), [conditional fields and wizards](#hide-fields-use-nested-data-or-build-a-wizard), [submission](#submit-display-server-errors-and-reset), and [custom controls](#implement-a-reusable-custom-control).
|
|
8
|
+
|
|
9
|
+
## Start with an ordinary edit form
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { Form, Switch, TextInput } from '@cube-dev/ui-kit';
|
|
13
|
+
import type { FormValues } from '@cube-dev/ui-kit';
|
|
14
|
+
|
|
15
|
+
interface Profile {
|
|
16
|
+
email: string | null;
|
|
17
|
+
notifications: boolean | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ProfileForm({
|
|
21
|
+
initialProfile,
|
|
22
|
+
save,
|
|
23
|
+
}: {
|
|
24
|
+
initialProfile: Profile;
|
|
25
|
+
save: (values: FormValues<Profile>, signal: AbortSignal) => Promise<void>;
|
|
26
|
+
}) {
|
|
27
|
+
const form = Form.useController<Profile>({ defaultValues: initialProfile });
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Form form={form} onSubmit={(values, { signal }) => save(values, signal)}>
|
|
31
|
+
<TextInput
|
|
32
|
+
name="email"
|
|
33
|
+
rules={[{ type: 'email', message: 'Enter a valid email' }]}
|
|
34
|
+
label="Email"
|
|
35
|
+
isRequired
|
|
36
|
+
/>
|
|
37
|
+
<Switch name="notifications" label="Notifications" />
|
|
38
|
+
<Form.SubmitError />
|
|
39
|
+
<Form.Submit>Save</Form.Submit>
|
|
40
|
+
<Form.Reset>Reset</Form.Reset>
|
|
41
|
+
</Form>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The model describes possible values, including API `null`s. A text input displays `null` as empty and a switch displays it as unchecked; neither writes that display fallback into the form. Editing uses the control's normal change payload, and reset restores the nullable default. `FormValues<Model>` describes readonly, potentially incomplete data: registration and validation do not prove that every model property is present. Map to your API's request type explicitly where it requires complete data.
|
|
47
|
+
|
|
48
|
+
`defaultValues` initializes this controller once. A later `initialProfile` prop does not overwrite edits. Use the loading commands below, or remount the form with a record key when switching records should start a new editing session. Modern roots do not accept `defaultValues`.
|
|
49
|
+
|
|
50
|
+
## Pick one binding and one owner for each concern
|
|
51
|
+
|
|
52
|
+
| Concern | Start here | Use the alternative when… |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| Connect an input | `name="email"` for a literal name, or `name={['user', 'email']}` for a nested modern path | `field={form.field(path, options)}` adds model-checked paths, input value compatibility, and validator inference. Neither binding requires the other. |
|
|
55
|
+
| Locate the controller | Use the controller you created, or pass it as a prop | `Form.useControllerContext<Model>()` avoids prop threading in a descendant. It reads context without subscribing. |
|
|
56
|
+
| Configure a field | With `name`, put `rules`, dependencies, and retention on the input | With `field`, put these options and typed `validate` on the descriptor. Avoid configuring the same option in both places. |
|
|
57
|
+
| Present a field | Input props such as `label`, `description`, `isRequired` | `rules: [{ required: true }]` is useful when required validation needs a custom message or no visible required marker. |
|
|
58
|
+
| Handle submit/change/failure | Callbacks on the owning `<Form>` | Hook callbacks let a controller own behavior independently of a root, or provide defaults to a wrapper. Choose one location per callback. |
|
|
59
|
+
| Initialize values | Controller `defaultValues` | Field `defaultValue` supplies a fallback for a reusable field missing from the controller's data. |
|
|
60
|
+
|
|
61
|
+
`Form.Submit`, `Form.Reset`, and `Form.SubmitError` are aliases of the exported `SubmitButton`, `ResetButton`, and `SubmitError` components. This guide uses the `Form.*` names consistently; importing a standalone name does not select a different implementation.
|
|
62
|
+
|
|
63
|
+
`name` uses the surrounding form, or an explicit `form={form}` outside it. Tuple names accept string and numeric segments and readonly arrays; inline tuples do not need memoization. Names are not checked against the surrounding controller's model because React context cannot infer its type. Tuple names require a modern controller; legacy forms retain string names and dot notation.
|
|
64
|
+
|
|
65
|
+
A descriptor is pure configuration, so creating it inline is expected. It neither reads state nor registers a field; the mounted input registers after commit. It supplies the controller and path even outside the root, so adding `form` or `name` to the same input is unnecessary. If both are present, the descriptor wins. Descriptor options override only matching options they supply. In particular, descriptor `rules` or `validate` replaces input-level `rules`; put built-in rules and a custom validator together on the descriptor to run both.
|
|
66
|
+
|
|
67
|
+
Controller values and defaults take precedence over field defaults, including explicit `null` and `undefined`. Root callbacks override the corresponding hook callback while mounted; omitted or undefined root callbacks fall back to the hook callback. Business callbacks update after every committed render. Defaults, `errorPolicy`, and diagnostic handlers are creation options; update values through commands and override error policy per field when needed.
|
|
68
|
+
|
|
69
|
+
Use one owning root per controller. Omitted `form` props use context; explicitly passing `form={undefined}` detaches an input. Legacy roots and `FormScopeMask` hide the surrounding modern context.
|
|
70
|
+
|
|
71
|
+
## Read a value, show status, or reveal a section
|
|
72
|
+
|
|
73
|
+
| UI needs | Use | Why it exists |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| One field's value | `Form.useValue(form, path)` | Checked path and inferred value without writing a selector. |
|
|
76
|
+
| A field's value, errors, or metadata | `Form.useFieldState(form, path)` | Checked path with typed value/default, errors, status, dirty, touched, and active state. |
|
|
77
|
+
| A derived result or form-wide flag | `Form.useSelector(form, selector)` | General selection, such as `state.isDirty` or `state.canReset`. |
|
|
78
|
+
| A small inline region that reacts independently | `<Form.Subscribe form={form} selector={...}>` | Keeps the subscription in that subtree without extracting a component. |
|
|
79
|
+
| A value in an event handler or effect | `form.getValue(path)` / `form.getValues()` | One-time imperative read; it does not subscribe React. |
|
|
80
|
+
|
|
81
|
+
The value and field-state hooks are conveniences over selectors. The choice between a hook and `Subscribe` is where rerenders should happen: hooks rerender their component, while `Subscribe` rerenders its children region. Creating a controller does not subscribe its owner. Prefer a leaf component for reusable reactive UI:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
function EmailPreview() {
|
|
85
|
+
const form = Form.useControllerContext<Profile>();
|
|
86
|
+
const email = Form.useValue(form, 'email');
|
|
87
|
+
return <output>{email ?? 'No email set'}</output>;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use `Subscribe` for a conditional section directly inside the form:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<Form.Subscribe form={form} selector={(state) => state.values.notifications}>
|
|
95
|
+
{(enabled) =>
|
|
96
|
+
enabled ? <TextInput field={form.field('email')} label="Email" /> : null
|
|
97
|
+
}
|
|
98
|
+
</Form.Subscribe>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
These are alternative placements of the email input, not an instruction to mount it twice. Pass `form` to `Subscribe` for inferred model types. It can also use modern context when `form` is omitted, but React context cannot infer the ancestor's model. The generic on `useControllerContext<Profile>()` is your assertion that the ancestor uses `Profile`.
|
|
102
|
+
|
|
103
|
+
Selectors and equality functions must be pure. Select a primitive or a stable snapshot branch; allocating a fresh object compares unequal under the default `Object.is`. Supply `isEqual` when such an object should compare by its contents. `useFieldState` can return `undefined` before a path is tracked. Getter reads in JSX will not stay current. Plain objects and arrays in snapshots are deeply readonly and potentially incomplete; use commands for writes. Date-control values retain their methods; treat them and other non-plain values as immutable.
|
|
104
|
+
|
|
105
|
+
## Load server data, refresh defaults, or discard edits
|
|
106
|
+
|
|
107
|
+
Most forms need only initialization, adoption for incoming data, and reset for a new editing session. The other modes let integrations change the reset baseline independently of user interaction.
|
|
108
|
+
|
|
109
|
+
| Situation | Command | Effect |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| Data is available before mounting | `Form.useController({ defaultValues })` | Seeds values and the reset baseline once. |
|
|
112
|
+
| A response arrives while the user may be typing | `adoptDefaultValues(response, { when: 'untouched' })` | Replaces the baseline and adopts untouched fields, preserving touched values. This is the default adoption policy. |
|
|
113
|
+
| Refresh fields that still match their previous defaults | `adoptDefaultValues(response, { when: 'clean' })` | Preserves dirty values, including programmatic edits; touched-but-clean fields can refresh. |
|
|
114
|
+
| Discard edits and start from a different record | `reset({ values: response })` | Replaces baseline and values, clears interaction/errors, and cancels pending submission. |
|
|
115
|
+
| Discard edits using the existing baseline | `reset()` / `<Form.Reset>` | Restores defaults and clears interaction/errors. |
|
|
116
|
+
| Change what Reset will restore without changing the draft | `setDefaultValues(response)` | Replaces only the baseline; dirtiness is recomputed against it. |
|
|
117
|
+
|
|
118
|
+
All defaults commands replace the baseline object; omitted keys are removed from it. Adoption also starts from the incoming object, then restores protected paths. It is not a partial patch. Use `setValue` or `setValues` for edits to current data.
|
|
119
|
+
|
|
120
|
+
### Advanced defaults policies
|
|
121
|
+
|
|
122
|
+
Use these only when the ordinary adoption or reset behavior above does not fit the interaction:
|
|
123
|
+
|
|
124
|
+
| Situation | Command | Effect |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| Protect both touched and dirty fields | `adoptDefaultValues(response, { when: 'untouched', preserveDirty: true })` | Adds dirty-value protection to the touched check. |
|
|
127
|
+
| Replace values but keep touched state and submission state | `adoptDefaultValues(response, { when: 'always' })` | Replaces values and baseline; invalidates affected field validation. |
|
|
128
|
+
| Replace values and clear field interaction, keeping submission state | `setDefaultValues(response, { currentValues: 'replace' })` | Replaces values and baseline; clears touched/field validation, but does not cancel submission or clear its error. |
|
|
129
|
+
|
|
130
|
+
### Asynchronous loading
|
|
131
|
+
|
|
132
|
+
For asynchronous loading, protect both user edits and request ordering:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
const request = new AbortController();
|
|
137
|
+
void load(request.signal)
|
|
138
|
+
.then((profile) => {
|
|
139
|
+
if (!request.signal.aborted) {
|
|
140
|
+
setLoadError(undefined);
|
|
141
|
+
form.adoptDefaultValues(profile, { when: 'untouched' });
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
.catch((error: unknown) => {
|
|
145
|
+
if (!request.signal.aborted) setLoadError(error);
|
|
146
|
+
});
|
|
147
|
+
return () => request.abort();
|
|
148
|
+
}, [form, load]);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Here `load` is a stable loader for the current record and `setLoadError` is local UI state; import `useEffect` from React. Gate editing on loading if a partially loaded record must never be submitted. When switching records, remount or reset the editing session: adopting an unrelated record while preserving old edits would mix the two.
|
|
152
|
+
|
|
153
|
+
## Edit values from an event handler
|
|
154
|
+
|
|
155
|
+
Use `setValue(path, value)` for one path and `setValues(partial)` for several top-level keys. Nested objects supplied to `setValues` replace those objects; use tuple paths to update an individual nested leaf. Neither command changes the reset baseline.
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
<button
|
|
159
|
+
type="button"
|
|
160
|
+
onClick={() => form.setValue('notifications', true, { source: 'user' })}
|
|
161
|
+
>
|
|
162
|
+
Enable notifications
|
|
163
|
+
</button>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`source: 'user'` defaults to touching the field and notifying `onValuesChange` when values change. Programmatic writes default to neither; override `touch` and `notify` explicitly when needed. `validate` defaults to `auto`; use `always` to force validation or `never` to invalidate without revalidating. `onValuesChange(values, change)` receives retained values and `{ names, source, kind }`. Use it for user-edit integrations such as a debounced draft saver; decide separately whether programmatic writes should trigger that saver. Defaults adoption, value replacement, and reset also notify this callback when they report changed paths; filter `change.kind` or `change.source` if only direct edits should be saved.
|
|
167
|
+
|
|
168
|
+
`batch(() => { ... })` combines synchronous commands into one publication. It is useful for several tuple writes; it is not a rollback transaction and must not wrap async work. `subscribe(listener)` is for imperative integrations that need every publication; React UI should use the hooks above and observers must call the returned unsubscribe function.
|
|
169
|
+
|
|
170
|
+
## Validate simple rules, sibling fields, or an API response
|
|
171
|
+
|
|
172
|
+
| Validation case | Start here |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| Required field with a visible marker | Input `isRequired`; it adds required validation too. |
|
|
175
|
+
| Email, length, range, pattern, or enum | Input `rules` using the built-in constraints, or descriptor `rules` when using `field`. |
|
|
176
|
+
| Custom domain check | Descriptor `validate(value, context)` for an inferred field value and checked reads. |
|
|
177
|
+
| Several custom rules or existing shared rule objects | `rules: [{ validator(rule, value, context) { ... } }]`; use `ModernValidationRule` for strict modern rule typing. |
|
|
178
|
+
| Validation depends on another form value | `dependsOn: ['password']`, or nested tuples such as `dependsOn: [['account', 'password']]`. |
|
|
179
|
+
| Validation captures props or other external values | `deps: [organizationId, checkName]`. Include changing functions too. |
|
|
180
|
+
| An integration explicitly versions function behavior | `rulesKey: revision`; ordinary forms can leave it out. |
|
|
181
|
+
|
|
182
|
+
`deps` and `dependsOn` deliberately have separate jobs. A string in `deps` is an external value; the same string in `dependsOn` names a form field. Use both when a validator reads both sources. `rulesKey` is an advanced function revision, not another place to list ordinary dependencies.
|
|
183
|
+
|
|
184
|
+
A password confirmation can declare its sibling dependency even when an early return skips the read:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<TextInput
|
|
188
|
+
field={form.field('confirmation', {
|
|
189
|
+
dependsOn: ['password'],
|
|
190
|
+
validate: (value, { getValue }) => {
|
|
191
|
+
if (!value) return;
|
|
192
|
+
return value === getValue('password') ? undefined : 'Passwords must match';
|
|
193
|
+
},
|
|
194
|
+
})}
|
|
195
|
+
label="Confirm password"
|
|
196
|
+
type="password"
|
|
197
|
+
isRequired
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
This example uses a controller whose model contains `password` and `confirmation`. A declared dependency change revalidates fields that were already validated or validating, unless the write requests `validate: 'never'`. Reads through validator `getValue`/`getValues` use one captured snapshot and cancel in-flight work if that data changes; reads alone do not schedule another run.
|
|
202
|
+
|
|
203
|
+
For a remote check, pass the cancellation signal and declare captured inputs:
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
<TextInput
|
|
207
|
+
field={form.field('name', {
|
|
208
|
+
deps: [organizationId, checkName],
|
|
209
|
+
validateTrigger: 'onChange',
|
|
210
|
+
validationDelay: 200,
|
|
211
|
+
validate: async (value, { signal }) => {
|
|
212
|
+
if (!value) return;
|
|
213
|
+
const available = await checkName(organizationId, value, signal);
|
|
214
|
+
return available ? undefined : 'Name is already in use';
|
|
215
|
+
},
|
|
216
|
+
})}
|
|
217
|
+
label="Name"
|
|
218
|
+
isRequired
|
|
219
|
+
/>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Here the model contains a string `name`, and `checkName` returns `Promise<boolean>`. A validator succeeds by returning `undefined`, `null`, or an empty string. Return a ReactNode error, or throw/reject, to fail. Adapt legacy validators that resolve data objects; those are failures in the modern contract. Superseded results cannot overwrite current errors even if the validator ignores its signal.
|
|
223
|
+
|
|
224
|
+
Automatic validation honors `validateTrigger` and `validationDelay`. An `auto` write revalidates on-change fields and fields already showing errors; otherwise on-blur fields wait for blur. Explicit `form.validate()` and submission validate immediately; `form.validate(['email'])` selects a literal path, and `form.validate([['rows', 0, 'email']])` selects a nested path. Inspect `{ isValid, stale, fields }` before advancing a wizard step. Validation covers active fields only. `errorPolicy: 'first' | 'all'` controls rule-error collection, with `first` as the default.
|
|
225
|
+
|
|
226
|
+
Rule constraints and function source are compared automatically so equivalent inline validators do not restart pending work. Captures require `deps`; different functions with identical source, including bound/native functions, also need an explicit dependency or revision. `rulesKey` skips function-source comparison only; constraints such as `min`, `pattern`, and `required` are still compared. Update the key or dependencies when function behavior changes. `deps` alone retains source comparison.
|
|
227
|
+
|
|
228
|
+
## Hide fields, use nested data, or build a wizard
|
|
229
|
+
|
|
230
|
+
Unmounting the last input at a path removes it from `activeValues` immediately and retains its draft by default. Remounting restores that draft. Use `field={form.field('details', { preserve: false })}` when leaving a section should discard its current value. Cleanup removes the value, but its default baseline remains available to reset. Visually hiding an input while leaving it mounted does not unregister it.
|
|
231
|
+
|
|
232
|
+
| Payload | How to choose it | Validation |
|
|
233
|
+
| --- | --- | --- |
|
|
234
|
+
| Mounted fields | `<Form>` / `form.submit()` defaults | Active registered fields only. |
|
|
235
|
+
| All retained data, including hidden sections | `<Form submitValues="all">` or `form.submit({ include: 'all' })` | Still only active registered fields. |
|
|
236
|
+
|
|
237
|
+
For a wizard, validate a step before unmounting it and choose `submitValues="all"` if previous steps belong in the final payload. That does not revalidate unmounted steps. If final submission must validate every step against the latest data, keep the relevant inputs mounted or perform whole-payload validation explicitly. A form with no active fields is invalid.
|
|
238
|
+
|
|
239
|
+
Bind nested leaves directly with `name={['rows', index, 'email']}`. The optional typed equivalent is `field={form.field(['rows', index, 'email'])}`. A string such as `'user.email'` is one literal key, not dot notation. Numeric indices and numeric string segments address the same path. Removing an array path does not shift the other indices. Registering a parent object makes its complete value active; register leaf paths when only selected children belong in the active payload. Descriptor and controller-command literal paths are checked against the model; `name` accepts dynamic paths without those checks. The declaration surface requires TypeScript 5.4 or newer.
|
|
240
|
+
|
|
241
|
+
`getValues()` / `state.values` contains all retained data; `getActiveValues()` / `state.activeValues` contains registered paths. Dirty/touched metadata includes retained fields, while validity concerns active fields.
|
|
242
|
+
|
|
243
|
+
## Submit, display server errors, and reset
|
|
244
|
+
|
|
245
|
+
Use `<Form.Submit>` for normal submission, including Enter in the form. It handles loading and stays enabled after validation errors so users can request feedback again. `disableOnInvalid` opts into disabling it on invalid state. Use `form.submit()` for a workflow controlled from an event handler; inspect its result (`submitted`, `invalid`, `failed`, `ignored`, or `stale`) before continuing. Choose the payload independently for each call: imperative `submit()` defaults to active values even if the root has `submitValues="all"`.
|
|
246
|
+
|
|
247
|
+
To place actions or errors outside the root, pass the controller:
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
<Form.Submit form={form}>Save</Form.Submit>
|
|
251
|
+
<Form.Reset form={form}>Reset</Form.Reset>
|
|
252
|
+
<Form.SubmitError form={form} />
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
An explicit-controller Submit targets that controller's owning form even inside another DOM form. Return or await the request in `onSubmit(values, { signal, include })`; concurrent submissions are ignored. Throwing or rejecting sets `submitError`, which `Form.SubmitError` displays with a safe generic fallback. Supply `renderError` to format application errors. Ordinary edits keep that error; another submit or reset clears it.
|
|
256
|
+
|
|
257
|
+
Use `setFieldErrors('email', ['Email is already registered'])` for a server error attached to an input and `clearFieldErrors('email')` to clear it. `setSubmitError` / `clearSubmitError` manage a form-level failure. `onSubmitFailed` receives `{ status: 'invalid', errors }` for field validation or `{ status: 'failed', error }` for a rejected submit callback; field validation does not set `submitError`.
|
|
258
|
+
|
|
259
|
+
`Form.Reset` is enabled when `state.canReset` is true: edits, touched/validation state, or a submit error can be cleared, and no submission is running. Use that selector for a custom reset UI. Direct `form.reset()` can also cancel an active submission; unmounting the owning root cancels it too. Action buttons preserve `onPress`; use `onClick` and `event.preventDefault()` to cancel their action. Root `onReset` can cancel before reset runs; `onResetCapture` is reserved for internal reset handling.
|
|
260
|
+
|
|
261
|
+
Native forms with `action`/`method` use browser submission and bypass the controller's submit pipeline. Use that path only when the browser should own the request.
|
|
262
|
+
|
|
263
|
+
## Implement a reusable custom control
|
|
264
|
+
|
|
265
|
+
Use the same field integration as built-in inputs: `FieldBaseProps<Value>`, `useFieldProps`, then `wrapWithField`. Map the control's value/event API and forward the generated id, blur, disabled, read-only, and invalid state to the interactive element. Include `null | undefined` in the accepted model type when empty values are supported and normalize only for display. Do not spread controller props onto a DOM element.
|
|
266
|
+
|
|
267
|
+
The [compiled CustomControl example](https://github.com/cube-js/cube-ui-kit/blob/main/typecheck/consumer/modern-form-examples.tsx) shows the complete pattern. The [component creation guide](https://cube-ui-kit.vercel.app/?path=/docs/getting-started-create-component--docs) explains the shared field wrapper. `Form.Item` / `Field` belongs to the legacy backend; it is not a second way to connect a modern custom input.
|
|
268
|
+
|
|
269
|
+
For SSR, seed the controller with the same serializable defaults on server and client. Initial hydration uses the creation snapshot before subscribers catch up with client state. Each mounted creator owns its controller; no manual disposal is needed.
|