@devtools-ui/input 0.0.2--canary.11.588

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.
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ Input: () => Input2,
34
+ InputComponent: () => InputComponent,
35
+ inputTransform: () => inputTransform
36
+ });
37
+ module.exports = __toCommonJS(src_exports);
38
+
39
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx
40
+ var import_react2 = __toESM(require("react"));
41
+ var import_react3 = require("@chakra-ui/react");
42
+ var import_react4 = require("@player-ui/react");
43
+
44
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/hooks.ts
45
+ var import_react = __toESM(require("react"));
46
+ var getConfig = (userConfig = {}) => {
47
+ return {
48
+ decimalSymbol: ".",
49
+ formatDelay: 200,
50
+ prefix: "",
51
+ suffix: "",
52
+ ...userConfig
53
+ };
54
+ };
55
+ var useInputAssetProps = (props, config) => {
56
+ const [localValue, setLocalValue] = import_react.default.useState(props.value ?? "");
57
+ const formatTimerRef = import_react.default.useRef(void 0);
58
+ const { formatDelay, decimalSymbol, prefix, suffix } = getConfig(config);
59
+ function clearPending() {
60
+ if (formatTimerRef.current) {
61
+ clearTimeout(formatTimerRef.current);
62
+ formatTimerRef.current = void 0;
63
+ }
64
+ }
65
+ function handleAffixOnFocus(target) {
66
+ let val = target.value;
67
+ if (suffix)
68
+ val = val.substring(0, val.indexOf(suffix));
69
+ if (prefix && !val.includes(prefix)) {
70
+ val = `${prefix}${val}`;
71
+ }
72
+ return val;
73
+ }
74
+ function handlePrefixEdgeCases(e) {
75
+ const target = e.target;
76
+ const start = target.selectionStart;
77
+ const end = target.selectionEnd;
78
+ const pl = prefix.length;
79
+ const atStart = start === pl;
80
+ const atEnd = end === pl;
81
+ if (start && end && start < pl) {
82
+ e.preventDefault();
83
+ target.setSelectionRange(pl, end - start + pl);
84
+ } else if (e.key === "ArrowLeft" && atStart || e.key === "Backspace" && atStart && atEnd || e.key === "Home") {
85
+ e.preventDefault();
86
+ target.setSelectionRange(prefix.length, prefix.length);
87
+ }
88
+ }
89
+ function formatValueWithAffix(value) {
90
+ if (!value)
91
+ return "";
92
+ return `${prefix}${value}${suffix}`;
93
+ }
94
+ const onKeyDownHandler = (currentValue) => {
95
+ const symbolPosition = currentValue.indexOf(decimalSymbol);
96
+ const newValue = props.format(currentValue) ?? "";
97
+ const newSymbolPosition = newValue.indexOf(decimalSymbol);
98
+ if ((symbolPosition === -1 || symbolPosition === 0) && newSymbolPosition > 0) {
99
+ return {
100
+ newValue: newValue.includes(prefix) ? `${newValue}` : `${prefix}${newValue}`,
101
+ newCursorPosition: newValue.includes(prefix) ? newSymbolPosition : newSymbolPosition + prefix.length
102
+ };
103
+ }
104
+ return {
105
+ newValue: newValue.includes(prefix) ? `${newValue}` : `${prefix}${newValue}`
106
+ };
107
+ };
108
+ const onBlur = (e) => {
109
+ clearPending();
110
+ const formatted = (prefix ? e.target.value.replace(prefix, "") : props.format(e.target.value)) ?? "";
111
+ if (formatted) {
112
+ props.set(formatted);
113
+ setLocalValue(formatValueWithAffix(formatted));
114
+ } else {
115
+ props.set("");
116
+ setLocalValue("");
117
+ }
118
+ };
119
+ const onChange = (e) => {
120
+ setLocalValue(e.target.value);
121
+ };
122
+ const onKeyDown = (e) => {
123
+ clearPending();
124
+ if (prefix)
125
+ handlePrefixEdgeCases(e);
126
+ const target = e.target;
127
+ formatTimerRef.current = setTimeout(() => {
128
+ const cursorPosition = target.selectionStart;
129
+ const currentValue = target.value;
130
+ if (cursorPosition !== currentValue.length) {
131
+ return;
132
+ }
133
+ const obj = onKeyDownHandler(currentValue);
134
+ setLocalValue(obj.newValue);
135
+ target.selectionStart = obj.newCursorPosition ?? target.selectionStart;
136
+ target.selectionEnd = obj.newCursorPosition ?? target.selectionEnd;
137
+ }, formatDelay);
138
+ };
139
+ const onFocus = (e) => {
140
+ const target = e.target;
141
+ const inputEmpty = target.value === "";
142
+ if (!inputEmpty && suffix || inputEmpty && prefix) {
143
+ setLocalValue(handleAffixOnFocus(target));
144
+ }
145
+ };
146
+ const propsValue = props.value;
147
+ import_react.default.useEffect(() => {
148
+ setLocalValue(formatValueWithAffix(propsValue));
149
+ }, [propsValue]);
150
+ import_react.default.useEffect(() => clearPending, []);
151
+ return {
152
+ onBlur,
153
+ onChange,
154
+ onKeyDown,
155
+ onFocus,
156
+ value: localValue
157
+ };
158
+ };
159
+
160
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx
161
+ var InputComponent = (props) => {
162
+ const { validation, label, id, note, size, maxLength, placeholder } = props;
163
+ const inputProps = useInputAssetProps(props);
164
+ return /* @__PURE__ */ import_react2.default.createElement(import_react3.FormControl, { isInvalid: Boolean(validation) }, label && /* @__PURE__ */ import_react2.default.createElement(import_react3.FormLabel, { htmlFor: id }, /* @__PURE__ */ import_react2.default.createElement(import_react4.ReactAsset, { ...label.asset })), /* @__PURE__ */ import_react2.default.createElement(
165
+ import_react3.Input,
166
+ {
167
+ id,
168
+ ...inputProps,
169
+ size,
170
+ maxLength,
171
+ placeholder
172
+ }
173
+ ), validation && /* @__PURE__ */ import_react2.default.createElement(import_react3.FormErrorMessage, null, validation.message), note && /* @__PURE__ */ import_react2.default.createElement(import_react3.FormHelperText, null, /* @__PURE__ */ import_react2.default.createElement(import_react4.ReactAsset, { ...note.asset })));
174
+ };
175
+
176
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/dsl/Input.tsx
177
+ var import_react5 = __toESM(require("react"));
178
+ var import_dsl = require("@player-tools/dsl");
179
+ var import_text = require("@devtools-ui/text");
180
+ var Input2 = (props) => {
181
+ const { children, binding, ...rest } = props;
182
+ return /* @__PURE__ */ import_react5.default.createElement(import_dsl.Asset, { type: "input", ...rest }, /* @__PURE__ */ import_react5.default.createElement("property", { name: "binding" }, binding.toValue()), children);
183
+ };
184
+ Input2.Label = (0, import_dsl.createSlot)({
185
+ name: "label",
186
+ TextComp: import_text.Text,
187
+ isArray: false,
188
+ wrapInAsset: true
189
+ });
190
+ Input2.Note = (0, import_dsl.createSlot)({
191
+ name: "note",
192
+ TextComp: import_text.Text,
193
+ isArray: false,
194
+ wrapInAsset: true
195
+ });
196
+
197
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/transform/index.ts
198
+ var inputTransform = (asset, options) => {
199
+ return {
200
+ ...asset,
201
+ format(val) {
202
+ if (asset.binding === void 0) {
203
+ return val;
204
+ }
205
+ return options.data.format(asset.binding, val);
206
+ },
207
+ set(val) {
208
+ if (asset.binding === void 0) {
209
+ return;
210
+ }
211
+ return options.data.model.set([[asset.binding, val]], {
212
+ formatted: true
213
+ });
214
+ },
215
+ value: asset.binding === void 0 ? "" : options.data.model.get(asset.binding, {
216
+ includeInvalid: true,
217
+ formatted: true
218
+ }),
219
+ validation: asset.binding === void 0 ? void 0 : options.validation?.get(asset.binding, { track: true }),
220
+ dataType: asset.binding === void 0 ? void 0 : options.validation?.type(asset.binding)
221
+ };
222
+ };
223
+ // Annotate the CommonJS export names for ESM import in node:
224
+ 0 && (module.exports = {
225
+ Input,
226
+ InputComponent,
227
+ inputTransform
228
+ });
229
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/index.ts","../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx","../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/hooks.ts","../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/dsl/Input.tsx","../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/transform/index.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./components\";\nexport * from \"./dsl\";\nexport * from \"./transform\";\n","import React from \"react\";\nimport {\n Input,\n FormControl,\n FormLabel,\n FormHelperText,\n FormErrorMessage,\n} from \"@chakra-ui/react\";\nimport { TransformedInput } from \"../types\";\nimport { ReactAsset } from \"@player-ui/react\";\nimport { useInputAssetProps } from \"./hooks\";\n\nexport const InputComponent = (props: TransformedInput) => {\n const { validation, label, id, note, size, maxLength, placeholder } = props;\n const inputProps = useInputAssetProps(props);\n\n return (\n <FormControl isInvalid={Boolean(validation)}>\n {label && (\n <FormLabel htmlFor={id}>\n <ReactAsset {...label.asset} />\n </FormLabel>\n )}\n <Input\n id={id}\n {...inputProps}\n size={size}\n maxLength={maxLength}\n placeholder={placeholder}\n />\n {validation && <FormErrorMessage>{validation.message}</FormErrorMessage>}\n {note && (\n <FormHelperText>\n <ReactAsset {...note.asset} />\n </FormHelperText>\n )}\n </FormControl>\n );\n};\n","import React from \"react\";\nimport type { TransformedInput } from \"../types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KeyDownHandler = (currentValue: string, props?: TransformedInput) => any;\n\nexport interface InputHookConfig {\n /** Time in ms to wait before formatting the user input for normal keys */\n formatDelay?: number;\n\n /** Symbol to be used for decimal point */\n decimalSymbol?: string;\n\n /** Affix to append to value - does not save to model and is only for display on input */\n prefix?: string;\n\n /** Affix to prepend to value - does not save to model and is only for display on input */\n suffix?: string;\n}\n\n/** Create a valid config mixing in defaults and user overrides */\nexport const getConfig = (\n userConfig: InputHookConfig = {}\n): Required<InputHookConfig> => {\n return {\n decimalSymbol: \".\",\n formatDelay: 200,\n prefix: \"\",\n suffix: \"\",\n ...userConfig,\n };\n};\n\n/**\n * A hook to manage an input html element as an asset.\n * The hook returns an object containing props that are expected to reside on any html input.\n * It will handle formatting, setting values, beaconing, aria-labels, etc.\n *\n * @param props - The output of the input transform\n * @param config - Local config to manage user interaction overrides\n */\nexport const useInputAssetProps = (\n props: TransformedInput,\n config?: InputHookConfig\n) => {\n const [localValue, setLocalValue] = React.useState(props.value ?? \"\");\n const formatTimerRef = React.useRef<NodeJS.Timeout | undefined>(undefined);\n\n const { formatDelay, decimalSymbol, prefix, suffix } = getConfig(config);\n\n /** Reset and pending format timers */\n function clearPending() {\n if (formatTimerRef.current) {\n clearTimeout(formatTimerRef.current);\n formatTimerRef.current = undefined;\n }\n }\n\n /** Affix handling logic on focus */\n function handleAffixOnFocus(target: HTMLInputElement) {\n let val = target.value;\n\n if (suffix) val = val.substring(0, val.indexOf(suffix));\n\n if (prefix && !val.includes(prefix)) {\n val = `${prefix}${val}`;\n }\n\n return val;\n }\n\n /** Edge cases handling for prefix */\n function handlePrefixEdgeCases(e: React.KeyboardEvent<HTMLInputElement>) {\n const target = e.target as HTMLInputElement;\n const start = target.selectionStart;\n const end = target.selectionEnd;\n const pl = prefix.length;\n const atStart = start === pl;\n const atEnd = end === pl;\n\n if (start && end && start < pl) {\n e.preventDefault();\n target.setSelectionRange(pl, end - start + pl);\n } else if (\n (e.key === \"ArrowLeft\" && atStart) ||\n (e.key === \"Backspace\" && atStart && atEnd) ||\n e.key === \"Home\"\n ) {\n e.preventDefault();\n target.setSelectionRange(prefix.length, prefix.length);\n }\n }\n\n /** Helper to add affixes to value where appropriate */\n function formatValueWithAffix(value: string | undefined) {\n if (!value) return \"\";\n\n return `${prefix}${value}${suffix}`;\n }\n\n /** Value handling logic on key down */\n const onKeyDownHandler: KeyDownHandler = (currentValue: string) => {\n const symbolPosition = currentValue.indexOf(decimalSymbol);\n const newValue = props.format(currentValue) ?? \"\";\n const newSymbolPosition = newValue.indexOf(decimalSymbol);\n\n if (\n (symbolPosition === -1 || symbolPosition === 0) &&\n newSymbolPosition > 0\n ) {\n // formatting added dot, so set cursor before dot\n return {\n newValue: newValue.includes(prefix)\n ? `${newValue}`\n : `${prefix}${newValue}`,\n newCursorPosition: newValue.includes(prefix)\n ? newSymbolPosition\n : newSymbolPosition + prefix.length,\n };\n }\n\n return {\n newValue: newValue.includes(prefix)\n ? `${newValue}`\n : `${prefix}${newValue}`,\n };\n };\n\n /** On blur, commit the value to the model */\n const onBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {\n clearPending();\n\n const formatted =\n (prefix\n ? e.target.value.replace(prefix, \"\")\n : props.format(e.target.value)) ?? \"\";\n\n if (formatted) {\n props.set(formatted);\n setLocalValue(formatValueWithAffix(formatted));\n } else {\n props.set(\"\");\n setLocalValue(\"\");\n }\n };\n\n /** Keep track of any user changes */\n const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {\n setLocalValue(e.target.value);\n };\n\n /** Schedule a format of the current input in the future */\n const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (e) => {\n clearPending();\n\n if (prefix) handlePrefixEdgeCases(e);\n\n const target = e.target as HTMLInputElement;\n\n formatTimerRef.current = setTimeout(() => {\n const cursorPosition = target.selectionStart;\n const currentValue = target.value;\n\n /** Skip formatting if we're in the middle of the input */\n if (cursorPosition !== currentValue.length) {\n return;\n }\n\n const obj = onKeyDownHandler(currentValue);\n\n setLocalValue(obj.newValue);\n target.selectionStart = obj.newCursorPosition ?? target.selectionStart;\n target.selectionEnd = obj.newCursorPosition ?? target.selectionEnd;\n }, formatDelay);\n };\n\n /** Format value onFocus if affixes exist */\n const onFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {\n const target = e.target as HTMLInputElement;\n const inputEmpty = target.value === \"\";\n\n if ((!inputEmpty && suffix) || (inputEmpty && prefix)) {\n setLocalValue(handleAffixOnFocus(target));\n }\n };\n\n // Update the stored value if data changes\n const propsValue = props.value;\n React.useEffect(() => {\n setLocalValue(formatValueWithAffix(propsValue));\n }, [propsValue]);\n\n /** clear anything pending on unmount of input */\n React.useEffect(() => clearPending, []);\n\n return {\n onBlur,\n onChange,\n onKeyDown,\n onFocus,\n value: localValue,\n };\n};\n","import React from \"react\";\nimport {\n AssetPropsWithChildren,\n Asset,\n createSlot,\n BindingTemplateInstance,\n} from \"@player-tools/dsl\";\nimport { InputAsset } from \"../types\";\nimport { Text } from \"@devtools-ui/text\";\n\nexport const Input = (\n props: Omit<AssetPropsWithChildren<InputAsset>, \"binding\"> & {\n /** The binding */\n binding: BindingTemplateInstance;\n }\n) => {\n const { children, binding, ...rest } = props;\n return (\n <Asset type=\"input\" {...rest}>\n <property name=\"binding\">{binding.toValue()}</property>\n {children}\n </Asset>\n );\n};\n\nInput.Label = createSlot({\n name: \"label\",\n TextComp: Text,\n isArray: false,\n wrapInAsset: true,\n});\n\nInput.Note = createSlot({\n name: \"note\",\n TextComp: Text,\n isArray: false,\n wrapInAsset: true,\n});\n","import type { TransformFunction } from \"@player-ui/player\";\nimport { InputAsset, TransformedInput } from \"../types\";\n\nexport const inputTransform: TransformFunction<InputAsset, TransformedInput> = (\n asset,\n options\n) => {\n return {\n ...asset,\n format(val) {\n if (asset.binding === undefined) {\n return val;\n }\n\n return options.data.format(asset.binding, val);\n },\n set(val) {\n if (asset.binding === undefined) {\n return;\n }\n\n return options.data.model.set([[asset.binding, val]], {\n formatted: true,\n });\n },\n value:\n asset.binding === undefined\n ? \"\"\n : options.data.model.get(asset.binding, {\n includeInvalid: true,\n formatted: true,\n }),\n validation:\n asset.binding === undefined\n ? undefined\n : options.validation?.get(asset.binding, { track: true }),\n dataType:\n asset.binding === undefined\n ? undefined\n : options.validation?.type(asset.binding),\n };\n};\n"],"mappings":"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","names":["Input","import_react","React","React","import_react","Input","React"]}
@@ -0,0 +1,199 @@
1
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx
2
+ import React2 from "react";
3
+ import {
4
+ Input,
5
+ FormControl,
6
+ FormLabel,
7
+ FormHelperText,
8
+ FormErrorMessage
9
+ } from "@chakra-ui/react";
10
+ import { ReactAsset } from "@player-ui/react";
11
+
12
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/hooks.ts
13
+ import React from "react";
14
+ var getConfig = (userConfig = {}) => {
15
+ return {
16
+ decimalSymbol: ".",
17
+ formatDelay: 200,
18
+ prefix: "",
19
+ suffix: "",
20
+ ...userConfig
21
+ };
22
+ };
23
+ var useInputAssetProps = (props, config) => {
24
+ const [localValue, setLocalValue] = React.useState(props.value ?? "");
25
+ const formatTimerRef = React.useRef(void 0);
26
+ const { formatDelay, decimalSymbol, prefix, suffix } = getConfig(config);
27
+ function clearPending() {
28
+ if (formatTimerRef.current) {
29
+ clearTimeout(formatTimerRef.current);
30
+ formatTimerRef.current = void 0;
31
+ }
32
+ }
33
+ function handleAffixOnFocus(target) {
34
+ let val = target.value;
35
+ if (suffix)
36
+ val = val.substring(0, val.indexOf(suffix));
37
+ if (prefix && !val.includes(prefix)) {
38
+ val = `${prefix}${val}`;
39
+ }
40
+ return val;
41
+ }
42
+ function handlePrefixEdgeCases(e) {
43
+ const target = e.target;
44
+ const start = target.selectionStart;
45
+ const end = target.selectionEnd;
46
+ const pl = prefix.length;
47
+ const atStart = start === pl;
48
+ const atEnd = end === pl;
49
+ if (start && end && start < pl) {
50
+ e.preventDefault();
51
+ target.setSelectionRange(pl, end - start + pl);
52
+ } else if (e.key === "ArrowLeft" && atStart || e.key === "Backspace" && atStart && atEnd || e.key === "Home") {
53
+ e.preventDefault();
54
+ target.setSelectionRange(prefix.length, prefix.length);
55
+ }
56
+ }
57
+ function formatValueWithAffix(value) {
58
+ if (!value)
59
+ return "";
60
+ return `${prefix}${value}${suffix}`;
61
+ }
62
+ const onKeyDownHandler = (currentValue) => {
63
+ const symbolPosition = currentValue.indexOf(decimalSymbol);
64
+ const newValue = props.format(currentValue) ?? "";
65
+ const newSymbolPosition = newValue.indexOf(decimalSymbol);
66
+ if ((symbolPosition === -1 || symbolPosition === 0) && newSymbolPosition > 0) {
67
+ return {
68
+ newValue: newValue.includes(prefix) ? `${newValue}` : `${prefix}${newValue}`,
69
+ newCursorPosition: newValue.includes(prefix) ? newSymbolPosition : newSymbolPosition + prefix.length
70
+ };
71
+ }
72
+ return {
73
+ newValue: newValue.includes(prefix) ? `${newValue}` : `${prefix}${newValue}`
74
+ };
75
+ };
76
+ const onBlur = (e) => {
77
+ clearPending();
78
+ const formatted = (prefix ? e.target.value.replace(prefix, "") : props.format(e.target.value)) ?? "";
79
+ if (formatted) {
80
+ props.set(formatted);
81
+ setLocalValue(formatValueWithAffix(formatted));
82
+ } else {
83
+ props.set("");
84
+ setLocalValue("");
85
+ }
86
+ };
87
+ const onChange = (e) => {
88
+ setLocalValue(e.target.value);
89
+ };
90
+ const onKeyDown = (e) => {
91
+ clearPending();
92
+ if (prefix)
93
+ handlePrefixEdgeCases(e);
94
+ const target = e.target;
95
+ formatTimerRef.current = setTimeout(() => {
96
+ const cursorPosition = target.selectionStart;
97
+ const currentValue = target.value;
98
+ if (cursorPosition !== currentValue.length) {
99
+ return;
100
+ }
101
+ const obj = onKeyDownHandler(currentValue);
102
+ setLocalValue(obj.newValue);
103
+ target.selectionStart = obj.newCursorPosition ?? target.selectionStart;
104
+ target.selectionEnd = obj.newCursorPosition ?? target.selectionEnd;
105
+ }, formatDelay);
106
+ };
107
+ const onFocus = (e) => {
108
+ const target = e.target;
109
+ const inputEmpty = target.value === "";
110
+ if (!inputEmpty && suffix || inputEmpty && prefix) {
111
+ setLocalValue(handleAffixOnFocus(target));
112
+ }
113
+ };
114
+ const propsValue = props.value;
115
+ React.useEffect(() => {
116
+ setLocalValue(formatValueWithAffix(propsValue));
117
+ }, [propsValue]);
118
+ React.useEffect(() => clearPending, []);
119
+ return {
120
+ onBlur,
121
+ onChange,
122
+ onKeyDown,
123
+ onFocus,
124
+ value: localValue
125
+ };
126
+ };
127
+
128
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx
129
+ var InputComponent = (props) => {
130
+ const { validation, label, id, note, size, maxLength, placeholder } = props;
131
+ const inputProps = useInputAssetProps(props);
132
+ return /* @__PURE__ */ React2.createElement(FormControl, { isInvalid: Boolean(validation) }, label && /* @__PURE__ */ React2.createElement(FormLabel, { htmlFor: id }, /* @__PURE__ */ React2.createElement(ReactAsset, { ...label.asset })), /* @__PURE__ */ React2.createElement(
133
+ Input,
134
+ {
135
+ id,
136
+ ...inputProps,
137
+ size,
138
+ maxLength,
139
+ placeholder
140
+ }
141
+ ), validation && /* @__PURE__ */ React2.createElement(FormErrorMessage, null, validation.message), note && /* @__PURE__ */ React2.createElement(FormHelperText, null, /* @__PURE__ */ React2.createElement(ReactAsset, { ...note.asset })));
142
+ };
143
+
144
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/dsl/Input.tsx
145
+ import React3 from "react";
146
+ import {
147
+ Asset,
148
+ createSlot
149
+ } from "@player-tools/dsl";
150
+ import { Text } from "@devtools-ui/text";
151
+ var Input2 = (props) => {
152
+ const { children, binding, ...rest } = props;
153
+ return /* @__PURE__ */ React3.createElement(Asset, { type: "input", ...rest }, /* @__PURE__ */ React3.createElement("property", { name: "binding" }, binding.toValue()), children);
154
+ };
155
+ Input2.Label = createSlot({
156
+ name: "label",
157
+ TextComp: Text,
158
+ isArray: false,
159
+ wrapInAsset: true
160
+ });
161
+ Input2.Note = createSlot({
162
+ name: "note",
163
+ TextComp: Text,
164
+ isArray: false,
165
+ wrapInAsset: true
166
+ });
167
+
168
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/transform/index.ts
169
+ var inputTransform = (asset, options) => {
170
+ return {
171
+ ...asset,
172
+ format(val) {
173
+ if (asset.binding === void 0) {
174
+ return val;
175
+ }
176
+ return options.data.format(asset.binding, val);
177
+ },
178
+ set(val) {
179
+ if (asset.binding === void 0) {
180
+ return;
181
+ }
182
+ return options.data.model.set([[asset.binding, val]], {
183
+ formatted: true
184
+ });
185
+ },
186
+ value: asset.binding === void 0 ? "" : options.data.model.get(asset.binding, {
187
+ includeInvalid: true,
188
+ formatted: true
189
+ }),
190
+ validation: asset.binding === void 0 ? void 0 : options.validation?.get(asset.binding, { track: true }),
191
+ dataType: asset.binding === void 0 ? void 0 : options.validation?.type(asset.binding)
192
+ };
193
+ };
194
+ export {
195
+ Input2 as Input,
196
+ InputComponent,
197
+ inputTransform
198
+ };
199
+ //# sourceMappingURL=index.mjs.map
package/dist/index.mjs ADDED
@@ -0,0 +1,199 @@
1
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx
2
+ import React2 from "react";
3
+ import {
4
+ Input,
5
+ FormControl,
6
+ FormLabel,
7
+ FormHelperText,
8
+ FormErrorMessage
9
+ } from "@chakra-ui/react";
10
+ import { ReactAsset } from "@player-ui/react";
11
+
12
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/hooks.ts
13
+ import React from "react";
14
+ var getConfig = (userConfig = {}) => {
15
+ return {
16
+ decimalSymbol: ".",
17
+ formatDelay: 200,
18
+ prefix: "",
19
+ suffix: "",
20
+ ...userConfig
21
+ };
22
+ };
23
+ var useInputAssetProps = (props, config) => {
24
+ const [localValue, setLocalValue] = React.useState(props.value ?? "");
25
+ const formatTimerRef = React.useRef(void 0);
26
+ const { formatDelay, decimalSymbol, prefix, suffix } = getConfig(config);
27
+ function clearPending() {
28
+ if (formatTimerRef.current) {
29
+ clearTimeout(formatTimerRef.current);
30
+ formatTimerRef.current = void 0;
31
+ }
32
+ }
33
+ function handleAffixOnFocus(target) {
34
+ let val = target.value;
35
+ if (suffix)
36
+ val = val.substring(0, val.indexOf(suffix));
37
+ if (prefix && !val.includes(prefix)) {
38
+ val = `${prefix}${val}`;
39
+ }
40
+ return val;
41
+ }
42
+ function handlePrefixEdgeCases(e) {
43
+ const target = e.target;
44
+ const start = target.selectionStart;
45
+ const end = target.selectionEnd;
46
+ const pl = prefix.length;
47
+ const atStart = start === pl;
48
+ const atEnd = end === pl;
49
+ if (start && end && start < pl) {
50
+ e.preventDefault();
51
+ target.setSelectionRange(pl, end - start + pl);
52
+ } else if (e.key === "ArrowLeft" && atStart || e.key === "Backspace" && atStart && atEnd || e.key === "Home") {
53
+ e.preventDefault();
54
+ target.setSelectionRange(prefix.length, prefix.length);
55
+ }
56
+ }
57
+ function formatValueWithAffix(value) {
58
+ if (!value)
59
+ return "";
60
+ return `${prefix}${value}${suffix}`;
61
+ }
62
+ const onKeyDownHandler = (currentValue) => {
63
+ const symbolPosition = currentValue.indexOf(decimalSymbol);
64
+ const newValue = props.format(currentValue) ?? "";
65
+ const newSymbolPosition = newValue.indexOf(decimalSymbol);
66
+ if ((symbolPosition === -1 || symbolPosition === 0) && newSymbolPosition > 0) {
67
+ return {
68
+ newValue: newValue.includes(prefix) ? `${newValue}` : `${prefix}${newValue}`,
69
+ newCursorPosition: newValue.includes(prefix) ? newSymbolPosition : newSymbolPosition + prefix.length
70
+ };
71
+ }
72
+ return {
73
+ newValue: newValue.includes(prefix) ? `${newValue}` : `${prefix}${newValue}`
74
+ };
75
+ };
76
+ const onBlur = (e) => {
77
+ clearPending();
78
+ const formatted = (prefix ? e.target.value.replace(prefix, "") : props.format(e.target.value)) ?? "";
79
+ if (formatted) {
80
+ props.set(formatted);
81
+ setLocalValue(formatValueWithAffix(formatted));
82
+ } else {
83
+ props.set("");
84
+ setLocalValue("");
85
+ }
86
+ };
87
+ const onChange = (e) => {
88
+ setLocalValue(e.target.value);
89
+ };
90
+ const onKeyDown = (e) => {
91
+ clearPending();
92
+ if (prefix)
93
+ handlePrefixEdgeCases(e);
94
+ const target = e.target;
95
+ formatTimerRef.current = setTimeout(() => {
96
+ const cursorPosition = target.selectionStart;
97
+ const currentValue = target.value;
98
+ if (cursorPosition !== currentValue.length) {
99
+ return;
100
+ }
101
+ const obj = onKeyDownHandler(currentValue);
102
+ setLocalValue(obj.newValue);
103
+ target.selectionStart = obj.newCursorPosition ?? target.selectionStart;
104
+ target.selectionEnd = obj.newCursorPosition ?? target.selectionEnd;
105
+ }, formatDelay);
106
+ };
107
+ const onFocus = (e) => {
108
+ const target = e.target;
109
+ const inputEmpty = target.value === "";
110
+ if (!inputEmpty && suffix || inputEmpty && prefix) {
111
+ setLocalValue(handleAffixOnFocus(target));
112
+ }
113
+ };
114
+ const propsValue = props.value;
115
+ React.useEffect(() => {
116
+ setLocalValue(formatValueWithAffix(propsValue));
117
+ }, [propsValue]);
118
+ React.useEffect(() => clearPending, []);
119
+ return {
120
+ onBlur,
121
+ onChange,
122
+ onKeyDown,
123
+ onFocus,
124
+ value: localValue
125
+ };
126
+ };
127
+
128
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx
129
+ var InputComponent = (props) => {
130
+ const { validation, label, id, note, size, maxLength, placeholder } = props;
131
+ const inputProps = useInputAssetProps(props);
132
+ return /* @__PURE__ */ React2.createElement(FormControl, { isInvalid: Boolean(validation) }, label && /* @__PURE__ */ React2.createElement(FormLabel, { htmlFor: id }, /* @__PURE__ */ React2.createElement(ReactAsset, { ...label.asset })), /* @__PURE__ */ React2.createElement(
133
+ Input,
134
+ {
135
+ id,
136
+ ...inputProps,
137
+ size,
138
+ maxLength,
139
+ placeholder
140
+ }
141
+ ), validation && /* @__PURE__ */ React2.createElement(FormErrorMessage, null, validation.message), note && /* @__PURE__ */ React2.createElement(FormHelperText, null, /* @__PURE__ */ React2.createElement(ReactAsset, { ...note.asset })));
142
+ };
143
+
144
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/dsl/Input.tsx
145
+ import React3 from "react";
146
+ import {
147
+ Asset,
148
+ createSlot
149
+ } from "@player-tools/dsl";
150
+ import { Text } from "@devtools-ui/text";
151
+ var Input2 = (props) => {
152
+ const { children, binding, ...rest } = props;
153
+ return /* @__PURE__ */ React3.createElement(Asset, { type: "input", ...rest }, /* @__PURE__ */ React3.createElement("property", { name: "binding" }, binding.toValue()), children);
154
+ };
155
+ Input2.Label = createSlot({
156
+ name: "label",
157
+ TextComp: Text,
158
+ isArray: false,
159
+ wrapInAsset: true
160
+ });
161
+ Input2.Note = createSlot({
162
+ name: "note",
163
+ TextComp: Text,
164
+ isArray: false,
165
+ wrapInAsset: true
166
+ });
167
+
168
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/transform/index.ts
169
+ var inputTransform = (asset, options) => {
170
+ return {
171
+ ...asset,
172
+ format(val) {
173
+ if (asset.binding === void 0) {
174
+ return val;
175
+ }
176
+ return options.data.format(asset.binding, val);
177
+ },
178
+ set(val) {
179
+ if (asset.binding === void 0) {
180
+ return;
181
+ }
182
+ return options.data.model.set([[asset.binding, val]], {
183
+ formatted: true
184
+ });
185
+ },
186
+ value: asset.binding === void 0 ? "" : options.data.model.get(asset.binding, {
187
+ includeInvalid: true,
188
+ formatted: true
189
+ }),
190
+ validation: asset.binding === void 0 ? void 0 : options.validation?.get(asset.binding, { track: true }),
191
+ dataType: asset.binding === void 0 ? void 0 : options.validation?.type(asset.binding)
192
+ };
193
+ };
194
+ export {
195
+ Input2 as Input,
196
+ InputComponent,
197
+ inputTransform
198
+ };
199
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/InputComponent.tsx","../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/components/hooks.ts","../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/dsl/Input.tsx","../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/input/src/transform/index.ts"],"sourcesContent":["import React from \"react\";\nimport {\n Input,\n FormControl,\n FormLabel,\n FormHelperText,\n FormErrorMessage,\n} from \"@chakra-ui/react\";\nimport { TransformedInput } from \"../types\";\nimport { ReactAsset } from \"@player-ui/react\";\nimport { useInputAssetProps } from \"./hooks\";\n\nexport const InputComponent = (props: TransformedInput) => {\n const { validation, label, id, note, size, maxLength, placeholder } = props;\n const inputProps = useInputAssetProps(props);\n\n return (\n <FormControl isInvalid={Boolean(validation)}>\n {label && (\n <FormLabel htmlFor={id}>\n <ReactAsset {...label.asset} />\n </FormLabel>\n )}\n <Input\n id={id}\n {...inputProps}\n size={size}\n maxLength={maxLength}\n placeholder={placeholder}\n />\n {validation && <FormErrorMessage>{validation.message}</FormErrorMessage>}\n {note && (\n <FormHelperText>\n <ReactAsset {...note.asset} />\n </FormHelperText>\n )}\n </FormControl>\n );\n};\n","import React from \"react\";\nimport type { TransformedInput } from \"../types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KeyDownHandler = (currentValue: string, props?: TransformedInput) => any;\n\nexport interface InputHookConfig {\n /** Time in ms to wait before formatting the user input for normal keys */\n formatDelay?: number;\n\n /** Symbol to be used for decimal point */\n decimalSymbol?: string;\n\n /** Affix to append to value - does not save to model and is only for display on input */\n prefix?: string;\n\n /** Affix to prepend to value - does not save to model and is only for display on input */\n suffix?: string;\n}\n\n/** Create a valid config mixing in defaults and user overrides */\nexport const getConfig = (\n userConfig: InputHookConfig = {}\n): Required<InputHookConfig> => {\n return {\n decimalSymbol: \".\",\n formatDelay: 200,\n prefix: \"\",\n suffix: \"\",\n ...userConfig,\n };\n};\n\n/**\n * A hook to manage an input html element as an asset.\n * The hook returns an object containing props that are expected to reside on any html input.\n * It will handle formatting, setting values, beaconing, aria-labels, etc.\n *\n * @param props - The output of the input transform\n * @param config - Local config to manage user interaction overrides\n */\nexport const useInputAssetProps = (\n props: TransformedInput,\n config?: InputHookConfig\n) => {\n const [localValue, setLocalValue] = React.useState(props.value ?? \"\");\n const formatTimerRef = React.useRef<NodeJS.Timeout | undefined>(undefined);\n\n const { formatDelay, decimalSymbol, prefix, suffix } = getConfig(config);\n\n /** Reset and pending format timers */\n function clearPending() {\n if (formatTimerRef.current) {\n clearTimeout(formatTimerRef.current);\n formatTimerRef.current = undefined;\n }\n }\n\n /** Affix handling logic on focus */\n function handleAffixOnFocus(target: HTMLInputElement) {\n let val = target.value;\n\n if (suffix) val = val.substring(0, val.indexOf(suffix));\n\n if (prefix && !val.includes(prefix)) {\n val = `${prefix}${val}`;\n }\n\n return val;\n }\n\n /** Edge cases handling for prefix */\n function handlePrefixEdgeCases(e: React.KeyboardEvent<HTMLInputElement>) {\n const target = e.target as HTMLInputElement;\n const start = target.selectionStart;\n const end = target.selectionEnd;\n const pl = prefix.length;\n const atStart = start === pl;\n const atEnd = end === pl;\n\n if (start && end && start < pl) {\n e.preventDefault();\n target.setSelectionRange(pl, end - start + pl);\n } else if (\n (e.key === \"ArrowLeft\" && atStart) ||\n (e.key === \"Backspace\" && atStart && atEnd) ||\n e.key === \"Home\"\n ) {\n e.preventDefault();\n target.setSelectionRange(prefix.length, prefix.length);\n }\n }\n\n /** Helper to add affixes to value where appropriate */\n function formatValueWithAffix(value: string | undefined) {\n if (!value) return \"\";\n\n return `${prefix}${value}${suffix}`;\n }\n\n /** Value handling logic on key down */\n const onKeyDownHandler: KeyDownHandler = (currentValue: string) => {\n const symbolPosition = currentValue.indexOf(decimalSymbol);\n const newValue = props.format(currentValue) ?? \"\";\n const newSymbolPosition = newValue.indexOf(decimalSymbol);\n\n if (\n (symbolPosition === -1 || symbolPosition === 0) &&\n newSymbolPosition > 0\n ) {\n // formatting added dot, so set cursor before dot\n return {\n newValue: newValue.includes(prefix)\n ? `${newValue}`\n : `${prefix}${newValue}`,\n newCursorPosition: newValue.includes(prefix)\n ? newSymbolPosition\n : newSymbolPosition + prefix.length,\n };\n }\n\n return {\n newValue: newValue.includes(prefix)\n ? `${newValue}`\n : `${prefix}${newValue}`,\n };\n };\n\n /** On blur, commit the value to the model */\n const onBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {\n clearPending();\n\n const formatted =\n (prefix\n ? e.target.value.replace(prefix, \"\")\n : props.format(e.target.value)) ?? \"\";\n\n if (formatted) {\n props.set(formatted);\n setLocalValue(formatValueWithAffix(formatted));\n } else {\n props.set(\"\");\n setLocalValue(\"\");\n }\n };\n\n /** Keep track of any user changes */\n const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {\n setLocalValue(e.target.value);\n };\n\n /** Schedule a format of the current input in the future */\n const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (e) => {\n clearPending();\n\n if (prefix) handlePrefixEdgeCases(e);\n\n const target = e.target as HTMLInputElement;\n\n formatTimerRef.current = setTimeout(() => {\n const cursorPosition = target.selectionStart;\n const currentValue = target.value;\n\n /** Skip formatting if we're in the middle of the input */\n if (cursorPosition !== currentValue.length) {\n return;\n }\n\n const obj = onKeyDownHandler(currentValue);\n\n setLocalValue(obj.newValue);\n target.selectionStart = obj.newCursorPosition ?? target.selectionStart;\n target.selectionEnd = obj.newCursorPosition ?? target.selectionEnd;\n }, formatDelay);\n };\n\n /** Format value onFocus if affixes exist */\n const onFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {\n const target = e.target as HTMLInputElement;\n const inputEmpty = target.value === \"\";\n\n if ((!inputEmpty && suffix) || (inputEmpty && prefix)) {\n setLocalValue(handleAffixOnFocus(target));\n }\n };\n\n // Update the stored value if data changes\n const propsValue = props.value;\n React.useEffect(() => {\n setLocalValue(formatValueWithAffix(propsValue));\n }, [propsValue]);\n\n /** clear anything pending on unmount of input */\n React.useEffect(() => clearPending, []);\n\n return {\n onBlur,\n onChange,\n onKeyDown,\n onFocus,\n value: localValue,\n };\n};\n","import React from \"react\";\nimport {\n AssetPropsWithChildren,\n Asset,\n createSlot,\n BindingTemplateInstance,\n} from \"@player-tools/dsl\";\nimport { InputAsset } from \"../types\";\nimport { Text } from \"@devtools-ui/text\";\n\nexport const Input = (\n props: Omit<AssetPropsWithChildren<InputAsset>, \"binding\"> & {\n /** The binding */\n binding: BindingTemplateInstance;\n }\n) => {\n const { children, binding, ...rest } = props;\n return (\n <Asset type=\"input\" {...rest}>\n <property name=\"binding\">{binding.toValue()}</property>\n {children}\n </Asset>\n );\n};\n\nInput.Label = createSlot({\n name: \"label\",\n TextComp: Text,\n isArray: false,\n wrapInAsset: true,\n});\n\nInput.Note = createSlot({\n name: \"note\",\n TextComp: Text,\n isArray: false,\n wrapInAsset: true,\n});\n","import type { TransformFunction } from \"@player-ui/player\";\nimport { InputAsset, TransformedInput } from \"../types\";\n\nexport const inputTransform: TransformFunction<InputAsset, TransformedInput> = (\n asset,\n options\n) => {\n return {\n ...asset,\n format(val) {\n if (asset.binding === undefined) {\n return val;\n }\n\n return options.data.format(asset.binding, val);\n },\n set(val) {\n if (asset.binding === undefined) {\n return;\n }\n\n return options.data.model.set([[asset.binding, val]], {\n formatted: true,\n });\n },\n value:\n asset.binding === undefined\n ? \"\"\n : options.data.model.get(asset.binding, {\n includeInvalid: true,\n formatted: true,\n }),\n validation:\n asset.binding === undefined\n ? undefined\n : options.validation?.get(asset.binding, { track: true }),\n dataType:\n asset.binding === undefined\n ? undefined\n : options.validation?.type(asset.binding),\n };\n};\n"],"mappings":"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","names":["React","React","React","Input"]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@devtools-ui/input",
3
+ "version": "0.0.2--canary.11.588",
4
+ "main": "dist/cjs/index.cjs",
5
+ "dependencies": {
6
+ "@devtools-ui/text": "0.0.2--canary.11.588",
7
+ "@chakra-ui/react": "^2.8.2",
8
+ "@emotion/react": "^11.11.4",
9
+ "@emotion/styled": "^11.11.0",
10
+ "@player-tools/dsl": "0.5.2--canary.87.2261",
11
+ "@player-ui/types": "0.7.1",
12
+ "@player-ui/player": "0.7.1",
13
+ "@player-ui/react": "^0.7.1",
14
+ "@player-ui/asset-transform-plugin": "^0.7.1",
15
+ "@types/react": "^18.2.51",
16
+ "framer-motion": "^11.0.8",
17
+ "react": "^18.2.0",
18
+ "dlv": "^1.1.3",
19
+ "eslint-plugin-storybook": "^0.8.0",
20
+ "tslib": "^2.6.2"
21
+ },
22
+ "module": "dist/index.legacy-esm.js",
23
+ "types": "types/index.d.ts",
24
+ "sideEffects": false,
25
+ "exports": {
26
+ "./package.json": "./package.json",
27
+ ".": {
28
+ "types": "./types/index.d.ts",
29
+ "import": "./dist/index.mjs",
30
+ "default": "./dist/cjs/index.cjs"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "src",
36
+ "types"
37
+ ],
38
+ "peerDependencies": {}
39
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import {
3
+ Input,
4
+ FormControl,
5
+ FormLabel,
6
+ FormHelperText,
7
+ FormErrorMessage,
8
+ } from "@chakra-ui/react";
9
+ import { TransformedInput } from "../types";
10
+ import { ReactAsset } from "@player-ui/react";
11
+ import { useInputAssetProps } from "./hooks";
12
+
13
+ export const InputComponent = (props: TransformedInput) => {
14
+ const { validation, label, id, note, size, maxLength, placeholder } = props;
15
+ const inputProps = useInputAssetProps(props);
16
+
17
+ return (
18
+ <FormControl isInvalid={Boolean(validation)}>
19
+ {label && (
20
+ <FormLabel htmlFor={id}>
21
+ <ReactAsset {...label.asset} />
22
+ </FormLabel>
23
+ )}
24
+ <Input
25
+ id={id}
26
+ {...inputProps}
27
+ size={size}
28
+ maxLength={maxLength}
29
+ placeholder={placeholder}
30
+ />
31
+ {validation && <FormErrorMessage>{validation.message}</FormErrorMessage>}
32
+ {note && (
33
+ <FormHelperText>
34
+ <ReactAsset {...note.asset} />
35
+ </FormHelperText>
36
+ )}
37
+ </FormControl>
38
+ );
39
+ };
@@ -0,0 +1,203 @@
1
+ import React from "react";
2
+ import type { TransformedInput } from "../types";
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ type KeyDownHandler = (currentValue: string, props?: TransformedInput) => any;
6
+
7
+ export interface InputHookConfig {
8
+ /** Time in ms to wait before formatting the user input for normal keys */
9
+ formatDelay?: number;
10
+
11
+ /** Symbol to be used for decimal point */
12
+ decimalSymbol?: string;
13
+
14
+ /** Affix to append to value - does not save to model and is only for display on input */
15
+ prefix?: string;
16
+
17
+ /** Affix to prepend to value - does not save to model and is only for display on input */
18
+ suffix?: string;
19
+ }
20
+
21
+ /** Create a valid config mixing in defaults and user overrides */
22
+ export const getConfig = (
23
+ userConfig: InputHookConfig = {}
24
+ ): Required<InputHookConfig> => {
25
+ return {
26
+ decimalSymbol: ".",
27
+ formatDelay: 200,
28
+ prefix: "",
29
+ suffix: "",
30
+ ...userConfig,
31
+ };
32
+ };
33
+
34
+ /**
35
+ * A hook to manage an input html element as an asset.
36
+ * The hook returns an object containing props that are expected to reside on any html input.
37
+ * It will handle formatting, setting values, beaconing, aria-labels, etc.
38
+ *
39
+ * @param props - The output of the input transform
40
+ * @param config - Local config to manage user interaction overrides
41
+ */
42
+ export const useInputAssetProps = (
43
+ props: TransformedInput,
44
+ config?: InputHookConfig
45
+ ) => {
46
+ const [localValue, setLocalValue] = React.useState(props.value ?? "");
47
+ const formatTimerRef = React.useRef<NodeJS.Timeout | undefined>(undefined);
48
+
49
+ const { formatDelay, decimalSymbol, prefix, suffix } = getConfig(config);
50
+
51
+ /** Reset and pending format timers */
52
+ function clearPending() {
53
+ if (formatTimerRef.current) {
54
+ clearTimeout(formatTimerRef.current);
55
+ formatTimerRef.current = undefined;
56
+ }
57
+ }
58
+
59
+ /** Affix handling logic on focus */
60
+ function handleAffixOnFocus(target: HTMLInputElement) {
61
+ let val = target.value;
62
+
63
+ if (suffix) val = val.substring(0, val.indexOf(suffix));
64
+
65
+ if (prefix && !val.includes(prefix)) {
66
+ val = `${prefix}${val}`;
67
+ }
68
+
69
+ return val;
70
+ }
71
+
72
+ /** Edge cases handling for prefix */
73
+ function handlePrefixEdgeCases(e: React.KeyboardEvent<HTMLInputElement>) {
74
+ const target = e.target as HTMLInputElement;
75
+ const start = target.selectionStart;
76
+ const end = target.selectionEnd;
77
+ const pl = prefix.length;
78
+ const atStart = start === pl;
79
+ const atEnd = end === pl;
80
+
81
+ if (start && end && start < pl) {
82
+ e.preventDefault();
83
+ target.setSelectionRange(pl, end - start + pl);
84
+ } else if (
85
+ (e.key === "ArrowLeft" && atStart) ||
86
+ (e.key === "Backspace" && atStart && atEnd) ||
87
+ e.key === "Home"
88
+ ) {
89
+ e.preventDefault();
90
+ target.setSelectionRange(prefix.length, prefix.length);
91
+ }
92
+ }
93
+
94
+ /** Helper to add affixes to value where appropriate */
95
+ function formatValueWithAffix(value: string | undefined) {
96
+ if (!value) return "";
97
+
98
+ return `${prefix}${value}${suffix}`;
99
+ }
100
+
101
+ /** Value handling logic on key down */
102
+ const onKeyDownHandler: KeyDownHandler = (currentValue: string) => {
103
+ const symbolPosition = currentValue.indexOf(decimalSymbol);
104
+ const newValue = props.format(currentValue) ?? "";
105
+ const newSymbolPosition = newValue.indexOf(decimalSymbol);
106
+
107
+ if (
108
+ (symbolPosition === -1 || symbolPosition === 0) &&
109
+ newSymbolPosition > 0
110
+ ) {
111
+ // formatting added dot, so set cursor before dot
112
+ return {
113
+ newValue: newValue.includes(prefix)
114
+ ? `${newValue}`
115
+ : `${prefix}${newValue}`,
116
+ newCursorPosition: newValue.includes(prefix)
117
+ ? newSymbolPosition
118
+ : newSymbolPosition + prefix.length,
119
+ };
120
+ }
121
+
122
+ return {
123
+ newValue: newValue.includes(prefix)
124
+ ? `${newValue}`
125
+ : `${prefix}${newValue}`,
126
+ };
127
+ };
128
+
129
+ /** On blur, commit the value to the model */
130
+ const onBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
131
+ clearPending();
132
+
133
+ const formatted =
134
+ (prefix
135
+ ? e.target.value.replace(prefix, "")
136
+ : props.format(e.target.value)) ?? "";
137
+
138
+ if (formatted) {
139
+ props.set(formatted);
140
+ setLocalValue(formatValueWithAffix(formatted));
141
+ } else {
142
+ props.set("");
143
+ setLocalValue("");
144
+ }
145
+ };
146
+
147
+ /** Keep track of any user changes */
148
+ const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
149
+ setLocalValue(e.target.value);
150
+ };
151
+
152
+ /** Schedule a format of the current input in the future */
153
+ const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (e) => {
154
+ clearPending();
155
+
156
+ if (prefix) handlePrefixEdgeCases(e);
157
+
158
+ const target = e.target as HTMLInputElement;
159
+
160
+ formatTimerRef.current = setTimeout(() => {
161
+ const cursorPosition = target.selectionStart;
162
+ const currentValue = target.value;
163
+
164
+ /** Skip formatting if we're in the middle of the input */
165
+ if (cursorPosition !== currentValue.length) {
166
+ return;
167
+ }
168
+
169
+ const obj = onKeyDownHandler(currentValue);
170
+
171
+ setLocalValue(obj.newValue);
172
+ target.selectionStart = obj.newCursorPosition ?? target.selectionStart;
173
+ target.selectionEnd = obj.newCursorPosition ?? target.selectionEnd;
174
+ }, formatDelay);
175
+ };
176
+
177
+ /** Format value onFocus if affixes exist */
178
+ const onFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
179
+ const target = e.target as HTMLInputElement;
180
+ const inputEmpty = target.value === "";
181
+
182
+ if ((!inputEmpty && suffix) || (inputEmpty && prefix)) {
183
+ setLocalValue(handleAffixOnFocus(target));
184
+ }
185
+ };
186
+
187
+ // Update the stored value if data changes
188
+ const propsValue = props.value;
189
+ React.useEffect(() => {
190
+ setLocalValue(formatValueWithAffix(propsValue));
191
+ }, [propsValue]);
192
+
193
+ /** clear anything pending on unmount of input */
194
+ React.useEffect(() => clearPending, []);
195
+
196
+ return {
197
+ onBlur,
198
+ onChange,
199
+ onKeyDown,
200
+ onFocus,
201
+ value: localValue,
202
+ };
203
+ };
@@ -0,0 +1 @@
1
+ export { InputComponent } from "./InputComponent";
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import {
3
+ AssetPropsWithChildren,
4
+ Asset,
5
+ createSlot,
6
+ BindingTemplateInstance,
7
+ } from "@player-tools/dsl";
8
+ import { InputAsset } from "../types";
9
+ import { Text } from "@devtools-ui/text";
10
+
11
+ export const Input = (
12
+ props: Omit<AssetPropsWithChildren<InputAsset>, "binding"> & {
13
+ /** The binding */
14
+ binding: BindingTemplateInstance;
15
+ }
16
+ ) => {
17
+ const { children, binding, ...rest } = props;
18
+ return (
19
+ <Asset type="input" {...rest}>
20
+ <property name="binding">{binding.toValue()}</property>
21
+ {children}
22
+ </Asset>
23
+ );
24
+ };
25
+
26
+ Input.Label = createSlot({
27
+ name: "label",
28
+ TextComp: Text,
29
+ isArray: false,
30
+ wrapInAsset: true,
31
+ });
32
+
33
+ Input.Note = createSlot({
34
+ name: "note",
35
+ TextComp: Text,
36
+ isArray: false,
37
+ wrapInAsset: true,
38
+ });
@@ -0,0 +1,65 @@
1
+ import React from "react";
2
+ import { describe, expect, test } from "vitest";
3
+ import { render, binding as b } from "@player-tools/dsl";
4
+ import { Input } from "../Input";
5
+
6
+ describe("DSL: Input", () => {
7
+ test("Renders default input", async () => {
8
+ const rendered = await render(<Input binding={b`binding`} />);
9
+
10
+ expect(rendered.jsonValue).toStrictEqual({
11
+ id: "root",
12
+ type: "input",
13
+ binding: "binding",
14
+ });
15
+ });
16
+
17
+ test("Renders input with size, placeholder and maxLength", async () => {
18
+ const rendered = await render(
19
+ <Input
20
+ size={"md"}
21
+ placeholder={"User input"}
22
+ maxLength={10}
23
+ binding={b`binding`}
24
+ />
25
+ );
26
+
27
+ expect(rendered.jsonValue).toStrictEqual({
28
+ id: "root",
29
+ type: "input",
30
+ placeholder: "User input",
31
+ size: "md",
32
+ maxLength: 10,
33
+ binding: "binding",
34
+ });
35
+ });
36
+
37
+ test("Renders input with label, binding and note", async () => {
38
+ const rendered = await render(
39
+ <Input binding={b`binding`}>
40
+ <Input.Label>Label</Input.Label>
41
+ <Input.Note>Some note</Input.Note>
42
+ </Input>
43
+ );
44
+
45
+ expect(rendered.jsonValue).toStrictEqual({
46
+ id: "root",
47
+ type: "input",
48
+ label: {
49
+ asset: {
50
+ id: "label",
51
+ type: "text",
52
+ value: "Label",
53
+ },
54
+ },
55
+ note: {
56
+ asset: {
57
+ id: "note",
58
+ type: "text",
59
+ value: "Some note",
60
+ },
61
+ },
62
+ binding: "binding",
63
+ });
64
+ });
65
+ });
@@ -0,0 +1 @@
1
+ export { Input } from "./Input";
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./types";
2
+ export * from "./components";
3
+ export * from "./dsl";
4
+ export * from "./transform";
@@ -0,0 +1,42 @@
1
+ import type { TransformFunction } from "@player-ui/player";
2
+ import { InputAsset, TransformedInput } from "../types";
3
+
4
+ export const inputTransform: TransformFunction<InputAsset, TransformedInput> = (
5
+ asset,
6
+ options
7
+ ) => {
8
+ return {
9
+ ...asset,
10
+ format(val) {
11
+ if (asset.binding === undefined) {
12
+ return val;
13
+ }
14
+
15
+ return options.data.format(asset.binding, val);
16
+ },
17
+ set(val) {
18
+ if (asset.binding === undefined) {
19
+ return;
20
+ }
21
+
22
+ return options.data.model.set([[asset.binding, val]], {
23
+ formatted: true,
24
+ });
25
+ },
26
+ value:
27
+ asset.binding === undefined
28
+ ? ""
29
+ : options.data.model.get(asset.binding, {
30
+ includeInvalid: true,
31
+ formatted: true,
32
+ }),
33
+ validation:
34
+ asset.binding === undefined
35
+ ? undefined
36
+ : options.validation?.get(asset.binding, { track: true }),
37
+ dataType:
38
+ asset.binding === undefined
39
+ ? undefined
40
+ : options.validation?.type(asset.binding),
41
+ };
42
+ };
@@ -0,0 +1,44 @@
1
+ import type { TextAsset } from "@devtools-ui/text";
2
+ import { Asset, Schema, AssetWrapper } from "@player-ui/types";
3
+ import { ValidationResponse } from "@player-ui/player";
4
+
5
+ export type InputSize = "xs" | "sm" | "md" | "lg";
6
+
7
+ type ValueType = string | undefined;
8
+
9
+ export interface InputAsset extends Asset<"input"> {
10
+ /** The location in the data-model to store the data */
11
+ binding: ValueType;
12
+
13
+ /** A text asset for the action's label */
14
+ label?: AssetWrapper<TextAsset>;
15
+
16
+ /** Asset container for a note. */
17
+ note?: AssetWrapper<TextAsset>;
18
+
19
+ /** Size of the Input height */
20
+ size?: InputSize;
21
+
22
+ /** Placeholder of the Input */
23
+ placeholder?: string;
24
+
25
+ /** Max character length in the Input */
26
+ maxLength?: number;
27
+ }
28
+
29
+ export interface TransformedInput extends InputAsset {
30
+ /** A function to commit the new value to the data-model */
31
+ set: (newValue: ValueType) => void;
32
+
33
+ /** A function to format a value */
34
+ format: (newValue: ValueType) => ValueType;
35
+
36
+ /** The current value of the input from the data-model */
37
+ value: ValueType;
38
+
39
+ /** Any validation associated with the current input's value */
40
+ validation?: ValidationResponse;
41
+
42
+ /** The dataType defined from the schema */
43
+ dataType?: Schema.DataType;
44
+ }
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import { TransformedInput } from "../types";
3
+ export declare const InputComponent: (props: TransformedInput) => React.JSX.Element;
4
+ //# sourceMappingURL=InputComponent.d.ts.map
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import type { TransformedInput } from "../types";
3
+ export interface InputHookConfig {
4
+ /** Time in ms to wait before formatting the user input for normal keys */
5
+ formatDelay?: number;
6
+ /** Symbol to be used for decimal point */
7
+ decimalSymbol?: string;
8
+ /** Affix to append to value - does not save to model and is only for display on input */
9
+ prefix?: string;
10
+ /** Affix to prepend to value - does not save to model and is only for display on input */
11
+ suffix?: string;
12
+ }
13
+ /** Create a valid config mixing in defaults and user overrides */
14
+ export declare const getConfig: (userConfig?: InputHookConfig) => Required<InputHookConfig>;
15
+ /**
16
+ * A hook to manage an input html element as an asset.
17
+ * The hook returns an object containing props that are expected to reside on any html input.
18
+ * It will handle formatting, setting values, beaconing, aria-labels, etc.
19
+ *
20
+ * @param props - The output of the input transform
21
+ * @param config - Local config to manage user interaction overrides
22
+ */
23
+ export declare const useInputAssetProps: (props: TransformedInput, config?: InputHookConfig) => {
24
+ onBlur: React.FocusEventHandler<HTMLInputElement>;
25
+ onChange: React.ChangeEventHandler<HTMLInputElement>;
26
+ onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
27
+ onFocus: React.FocusEventHandler<HTMLInputElement>;
28
+ value: string;
29
+ };
30
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { InputComponent } from "./InputComponent";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { AssetPropsWithChildren, BindingTemplateInstance } from "@player-tools/dsl";
3
+ import { InputAsset } from "../types";
4
+ export declare const Input: {
5
+ (props: Omit<AssetPropsWithChildren<InputAsset>, "binding"> & {
6
+ /** The binding */
7
+ binding: BindingTemplateInstance;
8
+ }): React.JSX.Element;
9
+ Label: (props: {
10
+ children?: React.ReactNode;
11
+ }) => React.JSX.Element;
12
+ Note: (props: {
13
+ children?: React.ReactNode;
14
+ }) => React.JSX.Element;
15
+ };
16
+ //# sourceMappingURL=Input.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { Input } from "./Input";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,5 @@
1
+ export * from "./types";
2
+ export * from "./components";
3
+ export * from "./dsl";
4
+ export * from "./transform";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ import type { TransformFunction } from "@player-ui/player";
2
+ import { InputAsset, TransformedInput } from "../types";
3
+ export declare const inputTransform: TransformFunction<InputAsset, TransformedInput>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,33 @@
1
+ import type { TextAsset } from "@devtools-ui/text";
2
+ import { Asset, Schema, AssetWrapper } from "@player-ui/types";
3
+ import { ValidationResponse } from "@player-ui/player";
4
+ export type InputSize = "xs" | "sm" | "md" | "lg";
5
+ type ValueType = string | undefined;
6
+ export interface InputAsset extends Asset<"input"> {
7
+ /** The location in the data-model to store the data */
8
+ binding: ValueType;
9
+ /** A text asset for the action's label */
10
+ label?: AssetWrapper<TextAsset>;
11
+ /** Asset container for a note. */
12
+ note?: AssetWrapper<TextAsset>;
13
+ /** Size of the Input height */
14
+ size?: InputSize;
15
+ /** Placeholder of the Input */
16
+ placeholder?: string;
17
+ /** Max character length in the Input */
18
+ maxLength?: number;
19
+ }
20
+ export interface TransformedInput extends InputAsset {
21
+ /** A function to commit the new value to the data-model */
22
+ set: (newValue: ValueType) => void;
23
+ /** A function to format a value */
24
+ format: (newValue: ValueType) => ValueType;
25
+ /** The current value of the input from the data-model */
26
+ value: ValueType;
27
+ /** Any validation associated with the current input's value */
28
+ validation?: ValidationResponse;
29
+ /** The dataType defined from the schema */
30
+ dataType?: Schema.DataType;
31
+ }
32
+ export {};
33
+ //# sourceMappingURL=index.d.ts.map