@koobiq/react-primitives 0.7.1 → 0.9.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.
@@ -68,11 +68,11 @@ const Button = polymorphicForwardRef(
68
68
  },
69
69
  ...buttonProps,
70
70
  ...renderProps,
71
- "data-hovered": isHovered,
72
- "data-pressed": isPressed,
73
- "data-focused": isFocused,
74
- "data-disabled": isDisabled,
75
- "data-focus-visible": isFocusVisible,
71
+ "data-hovered": isHovered || void 0,
72
+ "data-pressed": isPressed || void 0,
73
+ "data-focused": isFocused || void 0,
74
+ "data-disabled": isDisabled || void 0,
75
+ "data-focus-visible": isFocusVisible || void 0,
76
76
  tabIndex: buttonProps.tabIndex,
77
77
  ..."tabIndex" in props && { tabIndex },
78
78
  "aria-disabled": isLoading ? "true" : buttonProps["aria-disabled"],
@@ -35,7 +35,6 @@ export type ButtonBaseProps = RenderProps<ButtonRenderProps> & {
35
35
  slot?: string;
36
36
  /**
37
37
  * Whether this button is loading.
38
- * @default false
39
38
  */
40
39
  isLoading?: boolean;
41
40
  } & Omit<UseButtonProps<never>, 'elementType' | 'href'>;
@@ -47,14 +47,14 @@ const Checkbox = forwardRef(
47
47
  return /* @__PURE__ */ jsxs(
48
48
  "label",
49
49
  {
50
- "data-hovered": isHovered,
51
- "data-pressed": isPressed,
52
- "data-focused": isFocused,
53
- "data-invalid": isInvalid,
54
- "data-selected": isSelected,
55
- "data-disabled": isDisabled,
56
- "data-read-only": isReadOnly,
57
- "data-focus-visible": isFocusVisible,
50
+ "data-hovered": isHovered || void 0,
51
+ "data-pressed": isPressed || void 0,
52
+ "data-focused": isFocused || void 0,
53
+ "data-invalid": isInvalid || void 0,
54
+ "data-selected": isSelected || void 0,
55
+ "data-disabled": isDisabled || void 0,
56
+ "data-read-only": isReadOnly || void 0,
57
+ "data-focus-visible": isFocusVisible || void 0,
58
58
  ...mergeProps(DOMProps, labelProps, renderProps),
59
59
  ref,
60
60
  children: [
@@ -0,0 +1,8 @@
1
+ import { type ComponentPropsWithRef, type ElementType } from 'react';
2
+ import type { ValidationResult } from '@koobiq/react-core';
3
+ import { type RenderProps } from '../../utils';
4
+ export declare const FieldErrorContext: import("react").Context<ValidationResult | null>;
5
+ export type FieldErrorRenderProps = ValidationResult;
6
+ export type FieldErrorBaseProps = RenderProps<FieldErrorRenderProps>;
7
+ export declare const FieldError: import("@koobiq/react-core").PolyForwardComponent<"p", FieldErrorBaseProps, ElementType>;
8
+ export type FieldErrorProps<As extends ElementType = 'p'> = ComponentPropsWithRef<typeof FieldError<As>>;
@@ -0,0 +1,23 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ import { polymorphicForwardRef, isNotNil } from "@koobiq/react-core";
4
+ import { useRenderProps } from "../../utils/index.js";
5
+ import { Text } from "../Text/Text.js";
6
+ const FieldErrorContext = createContext(null);
7
+ const FieldError = polymorphicForwardRef(
8
+ (props, ref) => {
9
+ const { as = "p" } = props;
10
+ const validation = useContext(FieldErrorContext);
11
+ const renderProps = useRenderProps({
12
+ ...props,
13
+ values: validation,
14
+ defaultChildren: validation.validationErrors.length === 0 ? void 0 : validation.validationErrors.join(" ")
15
+ });
16
+ return isNotNil(renderProps.children) && validation.isInvalid ? /* @__PURE__ */ jsx(Text, { as, slot: "errorMessage", ...renderProps, ref, children: renderProps.children }) : null;
17
+ }
18
+ );
19
+ FieldError.displayName = "FieldError";
20
+ export {
21
+ FieldError,
22
+ FieldErrorContext
23
+ };
@@ -0,0 +1 @@
1
+ export * from './FieldError';
@@ -1 +1 @@
1
- export declare const NumberField: import("react").ForwardRefExoticComponent<Omit<import("@react-types/numberfield").AriaNumberFieldProps, keyof import("../..").RenderProps<import("./types").NumberFieldRenderProps>> & import("../..").RenderProps<import("./types").NumberFieldRenderProps> & import("react").RefAttributes<HTMLDivElement>>;
1
+ export declare const NumberField: import("react").ForwardRefExoticComponent<Omit<import("@react-types/numberfield").AriaNumberFieldProps & Pick<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "form" | "name" | "disabled">, keyof import("../..").RenderProps<import("./types").NumberFieldRenderProps>> & import("../..").RenderProps<import("./types").NumberFieldRenderProps> & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,18 +1,23 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import { forwardRef, useRef } from "react";
4
4
  import { filterDOMProps } from "@koobiq/react-core";
5
+ import { useLocale } from "@react-aria/i18n";
6
+ import { useNumberField } from "@react-aria/numberfield";
7
+ import { useNumberFieldState } from "@react-stately/numberfield";
5
8
  import { removeDataAttributes, useRenderProps, Provider } from "../../utils/index.js";
6
- import { useNumberField } from "../../behaviors/useNumberField.js";
7
9
  import { GroupContext } from "../Group/GroupContext.js";
8
10
  import { ButtonContext } from "../Button/ButtonContext.js";
9
11
  import { LabelContext } from "../Label/LabelContext.js";
10
12
  import { InputContext } from "../Input/InputContext.js";
11
13
  import { TextContext } from "../Text/TextContext.js";
14
+ import { FieldErrorContext } from "../FieldError/FieldError.js";
12
15
  const NumberField = forwardRef(
13
16
  (props, ref) => {
14
- const { isDisabled, isReadOnly, isRequired, isInvalid } = props;
17
+ const { isDisabled, isReadOnly, isRequired } = props;
15
18
  const inputRef = useRef(null);
19
+ const { locale } = useLocale();
20
+ const state = useNumberFieldState({ ...props, locale });
16
21
  const {
17
22
  labelProps,
18
23
  inputProps,
@@ -20,64 +25,77 @@ const NumberField = forwardRef(
20
25
  descriptionProps,
21
26
  errorMessageProps,
22
27
  incrementButtonProps,
23
- decrementButtonProps
24
- } = useNumberField({ ...removeDataAttributes(props) }, inputRef);
28
+ decrementButtonProps,
29
+ ...validation
30
+ } = useNumberField(removeDataAttributes(props), state, inputRef);
25
31
  const DOMProps = filterDOMProps(props);
26
32
  delete DOMProps.id;
27
33
  const renderProps = useRenderProps({
28
34
  ...props,
29
35
  values: {
30
- isInvalid: isInvalid || false,
36
+ isInvalid: validation.isInvalid || false,
31
37
  isDisabled: isDisabled || false,
32
38
  isReadonly: isReadOnly || false,
33
39
  isRequired: isRequired || false
34
40
  }
35
41
  });
36
- return /* @__PURE__ */ jsx(
37
- "div",
42
+ return /* @__PURE__ */ jsxs(
43
+ Provider,
38
44
  {
39
- ...DOMProps,
40
- ...renderProps,
41
- "data-invalid": isInvalid || void 0,
42
- "data-readonly": isReadOnly || void 0,
43
- "data-required": isRequired || void 0,
44
- "data-disabled": isDisabled || void 0,
45
- ref,
46
- children: /* @__PURE__ */ jsx(
47
- Provider,
48
- {
49
- values: [
50
- [LabelContext, labelProps],
51
- [InputContext, { ...inputProps, ref: inputRef }],
52
- [GroupContext, groupProps],
53
- [
54
- ButtonContext,
55
- {
56
- slots: {
57
- increment: {
58
- ...incrementButtonProps,
59
- disabled: incrementButtonProps.isDisabled
60
- },
61
- decrement: {
62
- ...decrementButtonProps,
63
- disabled: decrementButtonProps.isDisabled
64
- }
65
- }
45
+ values: [
46
+ [LabelContext, labelProps],
47
+ [InputContext, { ...inputProps, ref: inputRef }],
48
+ [GroupContext, groupProps],
49
+ [
50
+ ButtonContext,
51
+ {
52
+ slots: {
53
+ increment: {
54
+ ...incrementButtonProps,
55
+ disabled: incrementButtonProps.isDisabled
56
+ },
57
+ decrement: {
58
+ ...decrementButtonProps,
59
+ disabled: decrementButtonProps.isDisabled
66
60
  }
67
- ],
68
- [
69
- TextContext,
70
- {
71
- slots: {
72
- description: descriptionProps,
73
- errorMessage: errorMessageProps
74
- }
75
- }
76
- ]
77
- ],
78
- children: renderProps.children
79
- }
80
- )
61
+ }
62
+ }
63
+ ],
64
+ [
65
+ TextContext,
66
+ {
67
+ slots: {
68
+ description: descriptionProps,
69
+ errorMessage: errorMessageProps
70
+ }
71
+ }
72
+ ],
73
+ [FieldErrorContext, validation]
74
+ ],
75
+ children: [
76
+ /* @__PURE__ */ jsx(
77
+ "div",
78
+ {
79
+ ...DOMProps,
80
+ ...renderProps,
81
+ "data-invalid": validation.isInvalid || void 0,
82
+ "data-readonly": isReadOnly || void 0,
83
+ "data-required": isRequired || void 0,
84
+ "data-disabled": isDisabled || void 0,
85
+ ref
86
+ }
87
+ ),
88
+ props.name && /* @__PURE__ */ jsx(
89
+ "input",
90
+ {
91
+ type: "hidden",
92
+ name: props.name,
93
+ form: props.form,
94
+ value: Number.isNaN(state.numberValue) ? "" : state.numberValue,
95
+ disabled: props.isDisabled || void 0
96
+ }
97
+ )
98
+ ]
81
99
  }
82
100
  );
83
101
  }
@@ -1,6 +1,6 @@
1
- import type { ComponentRef } from 'react';
1
+ import type { ComponentPropsWithoutRef, ComponentRef } from 'react';
2
2
  import type { ExtendableProps } from '@koobiq/react-core';
3
- import type { UseNumberFieldProps } from '../../behaviors';
3
+ import type { AriaNumberFieldProps } from '@react-aria/numberfield';
4
4
  import type { RenderProps } from '../../utils';
5
5
  export type NumberFieldRenderProps = {
6
6
  /**
@@ -25,6 +25,6 @@ export type NumberFieldRenderProps = {
25
25
  isRequired: boolean;
26
26
  };
27
27
  type NumberFieldBaseProps = RenderProps<NumberFieldRenderProps>;
28
- export type NumberFieldProps = ExtendableProps<NumberFieldBaseProps, UseNumberFieldProps>;
28
+ export type NumberFieldProps = ExtendableProps<NumberFieldBaseProps, AriaNumberFieldProps & Pick<ComponentPropsWithoutRef<'input'>, 'name' | 'form' | 'disabled'>>;
29
29
  export type NumberFieldRef = ComponentRef<'div'>;
30
30
  export {};
@@ -11,7 +11,7 @@ const ProgressBar = polymorphicForwardRef(
11
11
  minValue = 0,
12
12
  maxValue = 100,
13
13
  as: Tag = "div",
14
- isIndeterminate = false,
14
+ isIndeterminate,
15
15
  ...other
16
16
  } = props;
17
17
  const { progressBarProps, labelProps } = useProgressBar(props);
@@ -1,19 +1,24 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { forwardRef } from "react";
4
- import { useRenderProps, Provider } from "../../utils/index.js";
4
+ import { filterDOMProps } from "@koobiq/react-core";
5
+ import { removeDataAttributes, useRenderProps, Provider } from "../../utils/index.js";
5
6
  import { useRadioGroupState } from "../../behaviors/useRadioGroupState.js";
6
7
  import { useRadioGroup } from "../../behaviors/useRadioGroup.js";
8
+ import { FieldErrorContext } from "../FieldError/FieldError.js";
7
9
  import { RadioContext } from "./RadioContext.js";
8
10
  import { LabelContext } from "../Label/LabelContext.js";
9
11
  import { TextContext } from "../Text/TextContext.js";
10
12
  const RadioGroup = forwardRef(
11
13
  (props, ref) => {
12
14
  const state = useRadioGroupState(props);
13
- const { radioGroupProps, labelProps, descriptionProps } = useRadioGroup(
14
- props,
15
- state
16
- );
15
+ const {
16
+ radioGroupProps,
17
+ labelProps,
18
+ descriptionProps,
19
+ errorMessageProps,
20
+ ...validation
21
+ } = useRadioGroup(removeDataAttributes(props), state);
17
22
  const renderProps = useRenderProps({
18
23
  ...props,
19
24
  values: {
@@ -25,24 +30,41 @@ const RadioGroup = forwardRef(
25
30
  state
26
31
  }
27
32
  });
28
- return /* @__PURE__ */ jsx("div", { ...radioGroupProps, ...renderProps, ref, children: /* @__PURE__ */ jsx(
29
- Provider,
33
+ const DOMProps = filterDOMProps(props);
34
+ delete DOMProps.id;
35
+ return /* @__PURE__ */ jsx(
36
+ "div",
30
37
  {
31
- values: [
32
- [RadioContext, state],
33
- [LabelContext, labelProps],
34
- [
35
- TextContext,
36
- {
37
- slots: {
38
- description: descriptionProps
39
- }
40
- }
41
- ]
42
- ],
43
- children: renderProps.children
38
+ ...DOMProps,
39
+ "data-invalid": validation.isInvalid || void 0,
40
+ "data-readonly": state.isInvalid || void 0,
41
+ "data-required": state.isRequired || void 0,
42
+ "data-disabled": state.isDisabled || void 0,
43
+ ...radioGroupProps,
44
+ ...renderProps,
45
+ ref,
46
+ children: /* @__PURE__ */ jsx(
47
+ Provider,
48
+ {
49
+ values: [
50
+ [RadioContext, state],
51
+ [LabelContext, labelProps],
52
+ [
53
+ TextContext,
54
+ {
55
+ slots: {
56
+ description: descriptionProps,
57
+ errorMessage: errorMessageProps
58
+ }
59
+ }
60
+ ],
61
+ [FieldErrorContext, validation]
62
+ ],
63
+ children: renderProps.children
64
+ }
65
+ )
44
66
  }
45
- ) });
67
+ );
46
68
  }
47
69
  );
48
70
  RadioGroup.displayName = "RadioGroup";
@@ -8,17 +8,20 @@ import { InputContext } from "../Input/InputContext.js";
8
8
  import { TextareaContext } from "../Textarea/TextareaContext.js";
9
9
  import { LabelContext } from "../Label/LabelContext.js";
10
10
  import { TextContext } from "../Text/TextContext.js";
11
+ import { FieldErrorContext } from "../FieldError/FieldError.js";
11
12
  function TextFieldRender(props, ref) {
12
13
  const { isDisabled, isReadOnly, isRequired } = props;
13
14
  const inputRef = useRef(null);
14
15
  const {
15
- isInvalid,
16
16
  labelProps,
17
17
  inputProps,
18
18
  descriptionProps,
19
- errorMessageProps
19
+ errorMessageProps,
20
+ ...validation
20
21
  } = useTextField(
21
- { ...removeDataAttributes(props) },
22
+ {
23
+ ...removeDataAttributes(props)
24
+ },
22
25
  inputRef
23
26
  );
24
27
  const DOMProps = filterDOMProps(props);
@@ -26,7 +29,7 @@ function TextFieldRender(props, ref) {
26
29
  const renderProps = useRenderProps({
27
30
  ...props,
28
31
  values: {
29
- isInvalid: isInvalid || false,
32
+ isInvalid: validation.isInvalid || false,
30
33
  isDisabled: isDisabled || false,
31
34
  isReadOnly: isReadOnly || false,
32
35
  isRequired: isRequired || false
@@ -37,7 +40,7 @@ function TextFieldRender(props, ref) {
37
40
  {
38
41
  ...DOMProps,
39
42
  ...renderProps,
40
- "data-invalid": isInvalid || void 0,
43
+ "data-invalid": validation.isInvalid || void 0,
41
44
  "data-readonly": isReadOnly || void 0,
42
45
  "data-required": isRequired || void 0,
43
46
  "data-disabled": isDisabled || void 0,
@@ -57,7 +60,8 @@ function TextFieldRender(props, ref) {
57
60
  errorMessage: errorMessageProps
58
61
  }
59
62
  }
60
- ]
63
+ ],
64
+ [FieldErrorContext, validation]
61
65
  ],
62
66
  children: renderProps.children
63
67
  }
@@ -11,3 +11,4 @@ export * from './Textarea';
11
11
  export * from './ProgressBar';
12
12
  export * from './TextField';
13
13
  export * from './NumberField';
14
+ export * from './FieldError';
package/dist/index.js CHANGED
@@ -63,10 +63,13 @@ import { TextareaContext, useTextareaContext } from "./components/Textarea/Texta
63
63
  import { ProgressBar } from "./components/ProgressBar/ProgressBar.js";
64
64
  import { TextField } from "./components/TextField/TextField.js";
65
65
  import { NumberField } from "./components/NumberField/NumberField.js";
66
+ import { FieldError, FieldErrorContext } from "./components/FieldError/FieldError.js";
66
67
  export {
67
68
  Button,
68
69
  ButtonContext,
69
70
  Checkbox,
71
+ FieldError,
72
+ FieldErrorContext,
70
73
  Group,
71
74
  GroupContext,
72
75
  Input,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koobiq/react-primitives",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -64,8 +64,8 @@
64
64
  "@react-stately/toggle": "^3.7.0",
65
65
  "@react-stately/tooltip": "^3.5.5",
66
66
  "@react-stately/tree": "^3.8.9",
67
- "@koobiq/logger": "0.7.1",
68
- "@koobiq/react-core": "0.7.1"
67
+ "@koobiq/logger": "0.9.0",
68
+ "@koobiq/react-core": "0.9.0"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "react": "18.x || 19.x",