@rvx/ui 0.1.13 → 0.1.14
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/common/theme.d.ts +10 -0
- package/dist/components/button.js +3 -2
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.d.ts +1 -0
- package/dist/components/card.js +6 -3
- package/dist/components/card.js.map +1 -1
- package/dist/components/checkbox.js +6 -6
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/collapse.d.ts +16 -2
- package/dist/components/collapse.js +85 -4
- package/dist/components/collapse.js.map +1 -1
- package/dist/components/column.d.ts +1 -0
- package/dist/components/column.js +1 -0
- package/dist/components/column.js.map +1 -1
- package/dist/components/dialog.d.ts +1 -2
- package/dist/components/dialog.js +3 -5
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dropdown-input.js +2 -2
- package/dist/components/dropdown-input.js.map +1 -1
- package/dist/components/dropdown.js +2 -2
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/label.js +1 -1
- package/dist/components/label.js.map +1 -1
- package/dist/components/link.js +2 -1
- package/dist/components/link.js.map +1 -1
- package/dist/components/nav-list.js +2 -1
- package/dist/components/nav-list.js.map +1 -1
- package/dist/components/popout.d.ts +1 -2
- package/dist/components/popout.js +1 -2
- package/dist/components/popout.js.map +1 -1
- package/dist/components/popover.js +1 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/radio-buttons.js +11 -10
- package/dist/components/radio-buttons.js.map +1 -1
- package/dist/components/slider.js +1 -2
- package/dist/components/slider.js.map +1 -1
- package/dist/components/tabs.d.ts +10 -0
- package/dist/components/tabs.js +31 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/text-input.js +5 -4
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/validation-rules.d.ts +11 -0
- package/dist/components/validation-rules.js +37 -0
- package/dist/components/validation-rules.js.map +1 -0
- package/dist/components/validation.d.ts +53 -77
- package/dist/components/validation.js +117 -95
- package/dist/components/validation.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/theme.module.css +75 -0
- package/dist/theme.module.css.map +1 -1
- package/package.json +3 -3
- package/src/common/theme.tsx +11 -0
- package/src/components/button.tsx +3 -2
- package/src/components/card.tsx +10 -5
- package/src/components/checkbox.tsx +6 -6
- package/src/components/collapse.tsx +127 -5
- package/src/components/column.tsx +2 -0
- package/src/components/dialog.tsx +3 -5
- package/src/components/dropdown-input.tsx +2 -2
- package/src/components/dropdown.tsx +2 -2
- package/src/components/label.tsx +1 -3
- package/src/components/link.tsx +2 -1
- package/src/components/nav-list.tsx +2 -1
- package/src/components/popout.tsx +1 -2
- package/src/components/popover.tsx +1 -2
- package/src/components/radio-buttons.tsx +21 -20
- package/src/components/slider.tsx +1 -2
- package/src/components/tabs.tsx +67 -0
- package/src/components/text-input.tsx +5 -4
- package/src/components/validation-rules.tsx +50 -0
- package/src/components/validation.tsx +175 -177
- package/src/index.tsx +2 -3
- package/src/theme/base.scss +7 -0
- package/src/theme/components/card.scss +4 -1
- package/src/theme/components/checkbox.scss +5 -0
- package/src/theme/components/column.scss +5 -0
- package/src/theme/components/radio-buttons.scss +5 -0
- package/src/theme/components/slider.scss +1 -0
- package/src/theme/components/tabs.scss +72 -0
- package/src/theme/theme.scss +1 -0
- package/dist/common/debounce.d.ts +0 -12
- package/dist/common/debounce.js +0 -23
- package/dist/common/debounce.js.map +0 -1
- package/dist/common/parsers.d.ts +0 -88
- package/dist/common/parsers.js +0 -62
- package/dist/common/parsers.js.map +0 -1
- package/dist/common/trim.d.ts +0 -12
- package/dist/common/trim.js +0 -16
- package/dist/common/trim.js.map +0 -1
- package/src/common/debounce.tsx +0 -36
- package/src/common/parsers.tsx +0 -167
- package/src/common/trim.tsx +0 -30
package/dist/common/parsers.d.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Signal } from "rvx";
|
|
2
|
-
export interface ValidParseResult<S> {
|
|
3
|
-
type: "valid";
|
|
4
|
-
/**
|
|
5
|
-
* The parsed value.
|
|
6
|
-
*/
|
|
7
|
-
value: S;
|
|
8
|
-
}
|
|
9
|
-
export interface InvalidParseResult<M> {
|
|
10
|
-
type: "invalid";
|
|
11
|
-
/**
|
|
12
|
-
* The validation message key.
|
|
13
|
-
*/
|
|
14
|
-
value: M;
|
|
15
|
-
}
|
|
16
|
-
export type ParseResult<S, M> = ValidParseResult<S> | InvalidParseResult<M>;
|
|
17
|
-
export declare class ParserFormatError extends TypeError {
|
|
18
|
-
}
|
|
19
|
-
export interface Parser<S, I, M extends string> {
|
|
20
|
-
/**
|
|
21
|
-
* Parse the specified input.
|
|
22
|
-
*/
|
|
23
|
-
parse(input: I): ParseResult<S, M>;
|
|
24
|
-
/**
|
|
25
|
-
* Format the specified source.
|
|
26
|
-
*
|
|
27
|
-
* @throws A {@link ParserFormatError} if the source isn't currently formattable.
|
|
28
|
-
*/
|
|
29
|
-
format(source: S): I;
|
|
30
|
-
/**
|
|
31
|
-
* An object with validation message keys and content to display.
|
|
32
|
-
*/
|
|
33
|
-
messages: Record<M, unknown>;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Utility to get the source type from a parser.
|
|
37
|
-
*/
|
|
38
|
-
export type ParserSource<P extends Parser<any, any, any>> = P extends Parser<infer S, any, any> ? S : never;
|
|
39
|
-
/**
|
|
40
|
-
* Utility to get the input type from a parser.
|
|
41
|
-
*/
|
|
42
|
-
export type ParserInput<P extends Parser<any, any, any>> = P extends Parser<any, infer I, any> ? I : never;
|
|
43
|
-
/**
|
|
44
|
-
* Utility to get the message keys type from a parser.
|
|
45
|
-
*/
|
|
46
|
-
export type ParserMessages<P extends Parser<any, any, any>> = P extends Parser<any, any, infer M> ? M : never;
|
|
47
|
-
/**
|
|
48
|
-
* Create a bi-directional input signal with validation.
|
|
49
|
-
*
|
|
50
|
-
* @param source The source signal.
|
|
51
|
-
* @param parser The parser to use.
|
|
52
|
-
* @param input The initial input signal to use. If not specified, the parser is used to format the current source value.
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```tsx
|
|
56
|
-
* const value = $(0);
|
|
57
|
-
*
|
|
58
|
-
* // Format the current value as initial value:
|
|
59
|
-
* <TextInput value={
|
|
60
|
-
* value
|
|
61
|
-
* .pipe(parse, intParser({ format: "Enter a valid number." }))
|
|
62
|
-
* .pipe(trim)
|
|
63
|
-
* } />
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
export declare function parse<S, P extends Parser<S, any, any>>(source: Signal<S>, parser: P, input?: Signal<ParserInput<P>>): Signal<ParserInput<P>>;
|
|
67
|
-
export interface IntParserOptions {
|
|
68
|
-
/**
|
|
69
|
-
* A function to check if the parsed value is in a valid range.
|
|
70
|
-
*
|
|
71
|
-
* By default, the value must be a safe integer.
|
|
72
|
-
*/
|
|
73
|
-
testRange?: (value: number) => boolean;
|
|
74
|
-
/**
|
|
75
|
-
* A validation message if the format is invalid.
|
|
76
|
-
*/
|
|
77
|
-
format: unknown;
|
|
78
|
-
/**
|
|
79
|
-
* A validation message if the parsed value is out of range.
|
|
80
|
-
*
|
|
81
|
-
* By default, the format validation message is used.
|
|
82
|
-
*/
|
|
83
|
-
range?: unknown;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Create a parser for integers for use with {@link parse}.
|
|
87
|
-
*/
|
|
88
|
-
export declare function intParser(options: IntParserOptions): Parser<number, string, "format" | "range">;
|
package/dist/common/parsers.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { $, watchUpdates } from "rvx";
|
|
2
|
-
import { Validator } from "../components/validation.js";
|
|
3
|
-
export class ParserFormatError extends TypeError {
|
|
4
|
-
}
|
|
5
|
-
export function parse(source, parser, input) {
|
|
6
|
-
if (input === undefined) {
|
|
7
|
-
input = $(parser.format(source.value));
|
|
8
|
-
}
|
|
9
|
-
const invalid = $(undefined);
|
|
10
|
-
watchUpdates(source, value => {
|
|
11
|
-
try {
|
|
12
|
-
input.value = parser.format(value);
|
|
13
|
-
}
|
|
14
|
-
catch (error) {
|
|
15
|
-
if (!(error instanceof ParserFormatError)) {
|
|
16
|
-
throw error;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
watchUpdates(input, value => {
|
|
21
|
-
const result = parser.parse(value);
|
|
22
|
-
if (result.type === "valid") {
|
|
23
|
-
source.value = result.value;
|
|
24
|
-
invalid.value = undefined;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
invalid.value = result.value;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
const validator = Validator.attach(source);
|
|
31
|
-
validator.attach(input);
|
|
32
|
-
for (const key in parser.messages) {
|
|
33
|
-
validator.prependRule({
|
|
34
|
-
validate: () => invalid.value !== key,
|
|
35
|
-
message: parser.messages[key],
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return input;
|
|
39
|
-
}
|
|
40
|
-
export function intParser(options) {
|
|
41
|
-
const range = options.testRange ?? Number.isSafeInteger;
|
|
42
|
-
return {
|
|
43
|
-
parse(input) {
|
|
44
|
-
if (!/^-?\d+$/.test(input)) {
|
|
45
|
-
return { type: "invalid", value: "format" };
|
|
46
|
-
}
|
|
47
|
-
const value = Number(input);
|
|
48
|
-
if (!range(value)) {
|
|
49
|
-
return { type: "invalid", value: "range" };
|
|
50
|
-
}
|
|
51
|
-
return { type: "valid", value };
|
|
52
|
-
},
|
|
53
|
-
format(source) {
|
|
54
|
-
return String(source);
|
|
55
|
-
},
|
|
56
|
-
messages: {
|
|
57
|
-
format: options.format,
|
|
58
|
-
range: options.range ?? options.format,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
//# sourceMappingURL=parsers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parsers.js","sourceRoot":"","sources":["../../src/common/parsers.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAU,YAAY,EAAE,MAAM,KAAK,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAsBxD,MAAM,OAAO,iBAAkB,SAAQ,SAAS;CAAG;AAuDnD,MAAM,UAAU,KAAK,CAAmC,MAAiB,EAAE,MAAS,EAAE,KAA8B;IACnH,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,CAAgC,SAAS,CAAC,CAAC;IAE5D,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;QAC5B,IAAI,CAAC;YACJ,KAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAmB,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,CAAC,KAAK,YAAY,iBAAiB,CAAC,EAAE,CAAC;gBAC3C,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAC5B,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;QAC3B,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,KAA0B,CAAC;QACnD,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3C,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAExB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACnC,SAAS,CAAC,WAAW,CAAC;YACrB,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,GAAG;YACrC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;SAC7B,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AA0BD,MAAM,UAAU,SAAS,CAAC,OAAyB;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,aAAa,CAAC;IACxD,OAAO;QACN,KAAK,CAAC,KAAK;YACV,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;YAC7C,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YAC5C,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACjC,CAAC;QAED,MAAM,CAAC,MAAM;YACZ,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QAED,QAAQ,EAAE;YACT,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM;SACtC;KACD,CAAC;AACH,CAAC"}
|
package/dist/common/trim.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Signal } from "rvx";
|
|
2
|
-
/**
|
|
3
|
-
* Create a signal that trims input.
|
|
4
|
-
*
|
|
5
|
-
* This supports validation.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```tsx
|
|
9
|
-
* <TextInput value={someSignal.pipe(trim)} />;
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
export declare function trim(source: Signal<string>): Signal<string>;
|
package/dist/common/trim.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { $, watchUpdates } from "rvx";
|
|
2
|
-
import { validatorFor } from "../components/validation.js";
|
|
3
|
-
export function trim(source) {
|
|
4
|
-
const input = $(source.value);
|
|
5
|
-
watchUpdates(input, value => {
|
|
6
|
-
source.value = value.trim();
|
|
7
|
-
});
|
|
8
|
-
watchUpdates(source, value => {
|
|
9
|
-
if (value !== input.value.trim()) {
|
|
10
|
-
input.value = value;
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
validatorFor(source)?.attach(input);
|
|
14
|
-
return input;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=trim.js.map
|
package/dist/common/trim.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trim.js","sourceRoot":"","sources":["../../src/common/trim.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAU,YAAY,EAAE,MAAM,KAAK,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAY3D,MAAM,UAAU,IAAI,CAAC,MAAsB;IAC1C,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE9B,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE;QAC3B,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;QAC5B,IAAI,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC;AACd,CAAC"}
|
package/src/common/debounce.tsx
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { $, Signal, teardown, watchUpdates } from "rvx";
|
|
2
|
-
import { validatorFor } from "../components/validation.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create a signal that debounces input changes.
|
|
6
|
-
*
|
|
7
|
-
* This supports validation.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```tsx
|
|
11
|
-
* <TextInput value={someSignal.pipe(debounce, 500)} />
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export function debounce<T>(source: Signal<T>, delay: number): Signal<T> {
|
|
15
|
-
const input = $(source.value);
|
|
16
|
-
let timer: number | undefined;
|
|
17
|
-
|
|
18
|
-
watchUpdates(input, value => {
|
|
19
|
-
clearTimeout(timer);
|
|
20
|
-
if (source.value !== value) {
|
|
21
|
-
timer = (setTimeout as typeof window.setTimeout)(() => {
|
|
22
|
-
source.value = value;
|
|
23
|
-
}, delay);
|
|
24
|
-
teardown(() => {
|
|
25
|
-
clearTimeout(timer);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
watchUpdates(source, value => {
|
|
31
|
-
input.value = value;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
validatorFor(source)?.attach(input);
|
|
35
|
-
return input;
|
|
36
|
-
}
|
package/src/common/parsers.tsx
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { $, Signal, watchUpdates } from "rvx";
|
|
2
|
-
|
|
3
|
-
import { Validator } from "../components/validation.js";
|
|
4
|
-
|
|
5
|
-
export interface ValidParseResult<S> {
|
|
6
|
-
type: "valid";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The parsed value.
|
|
10
|
-
*/
|
|
11
|
-
value: S;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface InvalidParseResult<M> {
|
|
15
|
-
type: "invalid";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The validation message key.
|
|
19
|
-
*/
|
|
20
|
-
value: M;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type ParseResult<S, M> = ValidParseResult<S> | InvalidParseResult<M>;
|
|
24
|
-
|
|
25
|
-
export class ParserFormatError extends TypeError {}
|
|
26
|
-
|
|
27
|
-
export interface Parser<S, I, M extends string> {
|
|
28
|
-
/**
|
|
29
|
-
* Parse the specified input.
|
|
30
|
-
*/
|
|
31
|
-
parse(input: I): ParseResult<S, M>;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Format the specified source.
|
|
35
|
-
*
|
|
36
|
-
* @throws A {@link ParserFormatError} if the source isn't currently formattable.
|
|
37
|
-
*/
|
|
38
|
-
format(source: S): I;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* An object with validation message keys and content to display.
|
|
42
|
-
*/
|
|
43
|
-
messages: Record<M, unknown>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Utility to get the source type from a parser.
|
|
48
|
-
*/
|
|
49
|
-
export type ParserSource<P extends Parser<any, any, any>> = P extends Parser<infer S, any, any> ? S : never;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Utility to get the input type from a parser.
|
|
53
|
-
*/
|
|
54
|
-
export type ParserInput<P extends Parser<any, any, any>> = P extends Parser<any, infer I, any> ? I : never;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Utility to get the message keys type from a parser.
|
|
58
|
-
*/
|
|
59
|
-
export type ParserMessages<P extends Parser<any, any, any>> = P extends Parser<any, any, infer M> ? M : never;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Create a bi-directional input signal with validation.
|
|
63
|
-
*
|
|
64
|
-
* @param source The source signal.
|
|
65
|
-
* @param parser The parser to use.
|
|
66
|
-
* @param input The initial input signal to use. If not specified, the parser is used to format the current source value.
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* ```tsx
|
|
70
|
-
* const value = $(0);
|
|
71
|
-
*
|
|
72
|
-
* // Format the current value as initial value:
|
|
73
|
-
* <TextInput value={
|
|
74
|
-
* value
|
|
75
|
-
* .pipe(parse, intParser({ format: "Enter a valid number." }))
|
|
76
|
-
* .pipe(trim)
|
|
77
|
-
* } />
|
|
78
|
-
* ```
|
|
79
|
-
*/
|
|
80
|
-
export function parse<S, P extends Parser<S, any, any>>(source: Signal<S>, parser: P, input?: Signal<ParserInput<P>>): Signal<ParserInput<P>> {
|
|
81
|
-
if (input === undefined) {
|
|
82
|
-
input = $(parser.format(source.value));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const invalid = $<undefined | ParserMessages<P>>(undefined);
|
|
86
|
-
|
|
87
|
-
watchUpdates(source, value => {
|
|
88
|
-
try {
|
|
89
|
-
input!.value = parser.format(value) as ParserInput<P>;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
if (!(error instanceof ParserFormatError)) {
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
watchUpdates(input, value => {
|
|
98
|
-
const result = parser.parse(value);
|
|
99
|
-
if (result.type === "valid") {
|
|
100
|
-
source.value = result.value;
|
|
101
|
-
invalid.value = undefined;
|
|
102
|
-
} else {
|
|
103
|
-
invalid.value = result.value as ParserMessages<P>;
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const validator = Validator.attach(source);
|
|
108
|
-
validator.attach(input);
|
|
109
|
-
|
|
110
|
-
for (const key in parser.messages) {
|
|
111
|
-
validator.prependRule({
|
|
112
|
-
validate: () => invalid.value !== key,
|
|
113
|
-
message: parser.messages[key],
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return input;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface IntParserOptions {
|
|
121
|
-
/**
|
|
122
|
-
* A function to check if the parsed value is in a valid range.
|
|
123
|
-
*
|
|
124
|
-
* By default, the value must be a safe integer.
|
|
125
|
-
*/
|
|
126
|
-
testRange?: (value: number) => boolean;
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* A validation message if the format is invalid.
|
|
130
|
-
*/
|
|
131
|
-
format: unknown;
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* A validation message if the parsed value is out of range.
|
|
135
|
-
*
|
|
136
|
-
* By default, the format validation message is used.
|
|
137
|
-
*/
|
|
138
|
-
range?: unknown;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Create a parser for integers for use with {@link parse}.
|
|
143
|
-
*/
|
|
144
|
-
export function intParser(options: IntParserOptions): Parser<number, string, "format" | "range"> {
|
|
145
|
-
const range = options.testRange ?? Number.isSafeInteger;
|
|
146
|
-
return {
|
|
147
|
-
parse(input) {
|
|
148
|
-
if (!/^-?\d+$/.test(input)) {
|
|
149
|
-
return { type: "invalid", value: "format" };
|
|
150
|
-
}
|
|
151
|
-
const value = Number(input);
|
|
152
|
-
if (!range(value)) {
|
|
153
|
-
return { type: "invalid", value: "range" };
|
|
154
|
-
}
|
|
155
|
-
return { type: "valid", value };
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
format(source) {
|
|
159
|
-
return String(source);
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
messages: {
|
|
163
|
-
format: options.format,
|
|
164
|
-
range: options.range ?? options.format,
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
}
|
package/src/common/trim.tsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { $, Signal, watchUpdates } from "rvx";
|
|
2
|
-
|
|
3
|
-
import { validatorFor } from "../components/validation.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Create a signal that trims input.
|
|
7
|
-
*
|
|
8
|
-
* This supports validation.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```tsx
|
|
12
|
-
* <TextInput value={someSignal.pipe(trim)} />;
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export function trim(source: Signal<string>): Signal<string> {
|
|
16
|
-
const input = $(source.value);
|
|
17
|
-
|
|
18
|
-
watchUpdates(input, value => {
|
|
19
|
-
source.value = value.trim();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
watchUpdates(source, value => {
|
|
23
|
-
if (value !== input.value.trim()) {
|
|
24
|
-
input.value = value;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
validatorFor(source)?.attach(input);
|
|
29
|
-
return input;
|
|
30
|
-
}
|