@dr.pogodin/react-utils 1.43.33 → 1.44.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.
@@ -9,7 +9,7 @@
9
9
  * Server-side helpers for error handling.
10
10
  */
11
11
  import { StatusCodes as CODES, ReasonPhrases as ERRORS, getReasonPhrase as getErrorForCode } from 'http-status-codes';
12
- import joi from 'joi';
12
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
13
13
  /**
14
14
  * @static
15
15
  * @const CODES
@@ -47,20 +47,6 @@ export { ERRORS };
47
47
  * console.log(server.errors.getErrorForCode(400)); // Prints: Bad Request
48
48
  */
49
49
  export { getErrorForCode };
50
- /**
51
- * @static
52
- * @const joi
53
- * @desc An alias for [Joi library](https://joi.dev/api/?v=17.4.0),
54
- * which provides tooling for HTTP request validation. You can use it in any
55
- * way you would use that library import.
56
- * @example
57
- * import { server } from '@dr.pogodin/react-utils';
58
- * const { joi } = server.errors;
59
- * const requestBodySchema = joi.object({
60
- * sampleKey: joi.string().max(16).required(),
61
- * });
62
- */
63
- export { joi };
64
50
  declare class ErrorWithStatus extends Error {
65
51
  status: number;
66
52
  }
@@ -94,4 +80,4 @@ export declare function fail(message: string, statusCode?: CODES): Error;
94
80
  * @param [statusCode=500] HTTP status code. Defaults to 400 (Bad
95
81
  * Request).
96
82
  */
97
- export declare function assert(value: unknown, schema: joi.AnySchema, message?: string, statusCode?: CODES): void;
83
+ export declare function assert<T extends StandardSchemaV1>(value: StandardSchemaV1.InferInput<T>, schema: T, message?: string, statusCode?: CODES): Promise<StandardSchemaV1.InferOutput<T>>;
@@ -1,6 +1,6 @@
1
1
  import { type Ref } from 'react';
2
2
  import { type Theme } from '@dr.pogodin/react-themes';
3
- type ThemeKeyT = 'container' | 'empty' | 'input' | 'label';
3
+ type ThemeKeyT = 'container' | 'empty' | 'focused' | 'input' | 'label';
4
4
  type PropsT = React.InputHTMLAttributes<HTMLInputElement> & {
5
5
  label?: React.ReactNode;
6
6
  ref?: Ref<HTMLInputElement>;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.43.33",
2
+ "version": "1.44.0",
3
3
  "bin": {
4
4
  "react-utils-build": "bin/build.js",
5
5
  "react-utils-setup": "bin/setup.js"
@@ -12,7 +12,7 @@
12
12
  "@dr.pogodin/babel-plugin-react-css-modules": "^6.13.7",
13
13
  "@dr.pogodin/csurf": "^1.16.5",
14
14
  "@dr.pogodin/js-utils": "^0.1.3",
15
- "@dr.pogodin/react-global-state": "^0.19.3",
15
+ "@dr.pogodin/react-global-state": "^0.19.4",
16
16
  "@dr.pogodin/react-helmet": "^3.0.2",
17
17
  "@dr.pogodin/react-themes": "^1.9.2",
18
18
  "@jest/environment": "^30.0.5",
@@ -27,7 +27,6 @@
27
27
  "express": "^5.1.0",
28
28
  "helmet": "^8.1.0",
29
29
  "http-status-codes": "^2.3.0",
30
- "joi": "^18.0.0",
31
30
  "lodash": "^4.17.21",
32
31
  "morgan": "^1.10.1",
33
32
  "node-forge": "^1.3.1",
@@ -58,6 +57,7 @@
58
57
  "@dr.pogodin/babel-preset-svgr": "^1.9.2",
59
58
  "@dr.pogodin/eslint-configs": "^0.0.11",
60
59
  "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
60
+ "@standard-schema/spec": "^1.0.0",
61
61
  "@testing-library/dom": "^10.4.1",
62
62
  "@testing-library/react": "^16.3.0",
63
63
  "@testing-library/user-event": "^14.6.1",
@@ -72,14 +72,14 @@
72
72
  "@types/morgan": "^1.9.10",
73
73
  "@types/node-forge": "^1.3.13",
74
74
  "@types/pretty": "^2.0.3",
75
- "@types/react": "^19.1.9",
75
+ "@types/react": "^19.1.10",
76
76
  "@types/react-dom": "^19.1.7",
77
77
  "@types/request-ip": "^0.0.41",
78
78
  "@types/serialize-javascript": "^5.0.4",
79
79
  "@types/serve-favicon": "^2.5.7",
80
80
  "@types/supertest": "^6.0.3",
81
81
  "@types/webpack": "^5.28.5",
82
- "@types/webpack-hot-middleware": "^2.25.9",
82
+ "@types/webpack-hot-middleware": "^2.25.10",
83
83
  "autoprefixer": "^10.4.21",
84
84
  "babel-jest": "^30.0.5",
85
85
  "babel-loader": "^10.0.0",
@@ -91,7 +91,7 @@
91
91
  "jest": "^30.0.5",
92
92
  "jest-environment-jsdom": "^30.0.5",
93
93
  "memfs": "^4.36.0",
94
- "mini-css-extract-plugin": "^2.9.3",
94
+ "mini-css-extract-plugin": "^2.9.4",
95
95
  "mockdate": "^3.0.5",
96
96
  "nodelist-foreach-polyfill": "^1.2.0",
97
97
  "postcss": "^8.5.6",
@@ -112,7 +112,7 @@
112
112
  "tstyche": "^4.3.0",
113
113
  "typed-scss-modules": "^8.1.1",
114
114
  "typescript": "^5.9.2",
115
- "webpack": "^5.101.0",
115
+ "webpack": "^5.101.1",
116
116
  "webpack-dev-middleware": "^7.4.2",
117
117
  "webpack-hot-middleware": "^2.26.1",
118
118
  "webpack-merge": "^6.0.1",
@@ -15,7 +15,7 @@ import {
15
15
  getReasonPhrase as getErrorForCode,
16
16
  } from 'http-status-codes';
17
17
 
18
- import joi from 'joi';
18
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
19
19
 
20
20
  /**
21
21
  * @static
@@ -57,21 +57,6 @@ export { ERRORS };
57
57
  */
58
58
  export { getErrorForCode };
59
59
 
60
- /**
61
- * @static
62
- * @const joi
63
- * @desc An alias for [Joi library](https://joi.dev/api/?v=17.4.0),
64
- * which provides tooling for HTTP request validation. You can use it in any
65
- * way you would use that library import.
66
- * @example
67
- * import { server } from '@dr.pogodin/react-utils';
68
- * const { joi } = server.errors;
69
- * const requestBodySchema = joi.object({
70
- * sampleKey: joi.string().max(16).required(),
71
- * });
72
- */
73
- export { joi };
74
-
75
60
  // TODO: It could accept the status code as a constructor argument.
76
61
  class ErrorWithStatus extends Error {
77
62
  status: number = CODES.INTERNAL_SERVER_ERROR;
@@ -121,14 +106,20 @@ export function fail(
121
106
  * @param [statusCode=500] HTTP status code. Defaults to 400 (Bad
122
107
  * Request).
123
108
  */
124
- export function assert(
125
- value: unknown,
126
- schema: joi.AnySchema,
109
+ export async function assert<T extends StandardSchemaV1>(
110
+ value: StandardSchemaV1.InferInput<T>,
111
+ schema: T,
127
112
  message = '',
128
113
  statusCode = CODES.BAD_REQUEST,
129
- ): void {
130
- const { error } = schema.validate(value, { abortEarly: false });
131
- if (error) {
132
- fail(message.concat(message ? '\n' : '', error.message), statusCode);
114
+ ): Promise<StandardSchemaV1.InferOutput<T>> {
115
+ let result = schema['~standard'].validate(value);
116
+ if (result instanceof Promise) result = await result;
117
+
118
+ if (result.issues) {
119
+ let error = JSON.stringify(result.issues, null, 2);
120
+ if (message) error = `${message}\n\n${error}`;
121
+ throw fail(error, statusCode);
133
122
  }
123
+
124
+ return result.value;
134
125
  }
@@ -1,10 +1,15 @@
1
- import { type FunctionComponent, type Ref, useRef } from 'react';
1
+ import {
2
+ type FunctionComponent,
3
+ type Ref,
4
+ useRef,
5
+ useState,
6
+ } from 'react';
2
7
 
3
8
  import themed, { type Theme } from '@dr.pogodin/react-themes';
4
9
 
5
10
  import defaultTheme from './theme.scss';
6
11
 
7
- type ThemeKeyT = 'container' | 'empty' | 'input' | 'label';
12
+ type ThemeKeyT = 'container' | 'empty' | 'focused' | 'input' | 'label';
8
13
 
9
14
  type PropsT = React.InputHTMLAttributes<HTMLInputElement> & {
10
15
  label?: React.ReactNode;
@@ -28,9 +33,18 @@ const Input: FunctionComponent<PropsT> = ({
28
33
  theme,
29
34
  ...rest
30
35
  }) => {
36
+ // NOTE: As of now, it is only updated when "theme.focused" is defined,
37
+ // as otherwise its value is not used.
38
+ const [focused, setFocused] = useState(false);
39
+
31
40
  const localRef = useRef<HTMLInputElement>(null);
32
41
 
33
42
  let containerClassName = theme.container;
43
+
44
+ // NOTE: As of now, "focused" can be true only when "theme.focused"
45
+ // is provided.
46
+ if (focused /* && theme.focused */) containerClassName += ` ${theme.focused}`;
47
+
34
48
  if (!rest.value && theme.empty) containerClassName += ` ${theme.empty}`;
35
49
 
36
50
  return (
@@ -53,6 +67,15 @@ const Input: FunctionComponent<PropsT> = ({
53
67
  // TODO: Avoid the spreading later.
54
68
  // eslint-disable-next-line react/jsx-props-no-spreading
55
69
  {...rest}
70
+
71
+ onBlur={theme.focused ? (e) => {
72
+ setFocused(false);
73
+ rest.onBlur?.(e);
74
+ } : rest.onBlur}
75
+ onFocus={theme.focused ? (e) => {
76
+ setFocused(true);
77
+ rest.onFocus?.(e);
78
+ } : rest.onFocus}
56
79
  />
57
80
  </span>
58
81
  );