@lynx-js/lynx-ui-input-otp 0.0.0-oidc-bootstrap.0 → 3.136.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # @lynx-js/lynx-ui-input-otp
2
+
3
+ ## 3.136.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add the headless InputOTP and InputOTPSlot primitives with four- and six-character ([#241](https://github.com/lynx-family/lynx-ui/pull/241))
8
+ input, controlled and uncontrolled state, input filtering, completion callbacks,
9
+ state styling hooks, examples, and skill references. InputOTP requires Lynx SDK
10
+ 4.0 or newer.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`0257f8b`](https://github.com/lynx-family/lynx-ui/commit/0257f8bc9fbe6b8a51df00663c7860827ce468b2), [`4214d92`](https://github.com/lynx-family/lynx-ui/commit/4214d923421d0a61ecd0b7cbf9a7a44acaff8d2a)]:
15
+ - @lynx-js/lynx-ui-common@3.136.0
16
+ - @lynx-js/lynx-ui-input@3.136.0
package/README.md CHANGED
@@ -1,9 +1,40 @@
1
1
  # @lynx-js/lynx-ui-input-otp
2
2
 
3
- > Placeholder package published only to enable npm Trusted Publishing (OIDC) setup.
3
+ A headless, configurable-length OTP and PIN input for ReactLynx. InputOTP owns
4
+ keyboard, focus, deletion, paste, filtering, and value state through one hidden
5
+ native input. Consumers compose the visible field with InputOTPSlot.
4
6
 
5
- This is a placeholder package published only to enable npm Trusted Publishing (OIDC) setup.
7
+ ## Requirements
6
8
 
7
- It contains no functional code and should not be installed.
9
+ - **Lynx SDK**: >= 4.0
8
10
 
9
- A proper release will replace this package once OIDC is configured.
11
+ ## Installation
12
+
13
+ ```bash
14
+ pnpm add @lynx-js/lynx-ui
15
+ ```
16
+
17
+ The standalone package is also available as
18
+ `@lynx-js/lynx-ui-input-otp`.
19
+
20
+ ## Component structure
21
+
22
+ ```tsx
23
+ <InputOTP length={4}>
24
+ <InputOTPSlot index={0} />
25
+ <InputOTPSlot index={1} />
26
+ <InputOTPSlot index={2} />
27
+ <InputOTPSlot index={3} />
28
+ </InputOTP>
29
+ ```
30
+
31
+ - `InputOTP` owns the normalized controlled or uncontrolled value and the
32
+ hidden native input.
33
+ - `InputOTPSlot` reads one character and its focused, filled, complete,
34
+ disabled, and invalid states from context.
35
+
36
+ InputOTP and InputOTPSlot ship without visible styles. Use `className`,
37
+ `style`, injected `ui-*` variants, and render props to compose the
38
+ presentation.
39
+
40
+ [View examples](https://github.com/lynx-family/lynx-ui/tree/main/apps/examples/InputOTP)
@@ -0,0 +1,3 @@
1
+ import type { InputOTP as InputOTPType } from './types';
2
+ export declare const InputOTP: InputOTPType;
3
+ //# sourceMappingURL=InputOTP.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputOTP.d.ts","sourceRoot":"","sources":["../src/InputOTP.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAIV,QAAQ,IAAI,YAAY,EACzB,MAAM,SAAS,CAAA;AAqBhB,eAAO,MAAM,QAAQ,EAAqC,YAAY,CAAA"}
@@ -0,0 +1,150 @@
1
+ import { forwardRef, memo, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "@lynx-js/react";
2
+ import { InvokeRejectError, useMemoizedFn } from "@lynx-js/lynx-ui-common";
3
+ import { KeyboardAwareTriggerContext } from "@lynx-js/lynx-ui-input";
4
+ import { clsx } from "clsx";
5
+ import { InputOTPContext } from "./InputOTPContext.jsx";
6
+ import { nativeInputFilters, normalizeInputOTPLength, normalizeOTPValue } from "./utils.jsx";
7
+ const hiddenInputStyle = {
8
+ position: 'absolute',
9
+ top: '0px',
10
+ left: '0px',
11
+ width: '1px',
12
+ height: '1px',
13
+ padding: '0px',
14
+ borderWidth: '0px',
15
+ opacity: 0,
16
+ color: 'transparent',
17
+ caretColor: 'transparent',
18
+ backgroundColor: 'transparent'
19
+ };
20
+ const InputOTP = memo(forwardRef(InputOTPImpl));
21
+ function reportInputError(error) {
22
+ console.warn(`[lynx-ui-input-otp][InputOTP] native input command failed: ${String(error)}`);
23
+ }
24
+ function InputOTPImpl(props, ref) {
25
+ const { length = 6, inputType = 'numeric', value, defaultValue = '', autoFocus = false, disabled = false, invalid = false, className, style, children, onChange, onComplete, onFocus, onBlur } = props;
26
+ const fieldLength = normalizeInputOTPLength(length);
27
+ const isControlled = void 0 !== value;
28
+ const [uncontrolledValue, setUncontrolledValue] = useState(normalizeOTPValue(defaultValue, fieldLength, inputType));
29
+ const [focused, setFocused] = useState(false);
30
+ const inputRef = useRef(null);
31
+ const { onInputBlurred, onInputFocused } = useContext(KeyboardAwareTriggerContext);
32
+ const displayedValue = normalizeOTPValue(isControlled ? value : uncontrolledValue, fieldLength, inputType);
33
+ const invokeInput = useMemoizedFn((method, nextValue)=>new Promise((resolve, reject)=>{
34
+ if (!inputRef.current) return void reject(new Error('InputOTP input is not mounted.'));
35
+ inputRef.current.invoke({
36
+ method,
37
+ ...'setValue' === method ? {
38
+ params: {
39
+ value: nextValue ?? ''
40
+ }
41
+ } : {},
42
+ success: ()=>{
43
+ resolve();
44
+ },
45
+ fail: (result)=>{
46
+ reject(new InvokeRejectError(result.code, result.data));
47
+ }
48
+ }).exec();
49
+ }));
50
+ const focus = useMemoizedFn(()=>disabled ? Promise.resolve() : invokeInput('focus'));
51
+ const blur = useMemoizedFn(()=>invokeInput('blur'));
52
+ const syncNativeValue = useMemoizedFn((nextValue)=>invokeInput('setValue', nextValue));
53
+ const emitValue = useMemoizedFn((nextValue, previousValue)=>{
54
+ if (nextValue === previousValue) return;
55
+ onChange?.(nextValue);
56
+ if (nextValue.length === fieldLength && previousValue.length !== fieldLength) onComplete?.(nextValue);
57
+ });
58
+ const setValue = useMemoizedFn((nextRawValue)=>{
59
+ const nextValue = normalizeOTPValue(nextRawValue, fieldLength, inputType);
60
+ if (!isControlled) setUncontrolledValue(nextValue);
61
+ emitValue(nextValue, displayedValue);
62
+ return syncNativeValue(nextValue);
63
+ });
64
+ useImperativeHandle(ref, ()=>({
65
+ focus,
66
+ blur,
67
+ setValue,
68
+ clear: ()=>setValue(''),
69
+ getValue: ()=>displayedValue
70
+ }), [
71
+ blur,
72
+ displayedValue,
73
+ focus,
74
+ setValue
75
+ ]);
76
+ useEffect(()=>{
77
+ if (isControlled) return;
78
+ const nextValue = normalizeOTPValue(uncontrolledValue, fieldLength, inputType);
79
+ if (nextValue !== uncontrolledValue) setUncontrolledValue(nextValue);
80
+ }, [
81
+ fieldLength,
82
+ inputType,
83
+ isControlled,
84
+ uncontrolledValue
85
+ ]);
86
+ useEffect(()=>{
87
+ syncNativeValue(displayedValue).catch(reportInputError);
88
+ }, [
89
+ displayedValue,
90
+ syncNativeValue
91
+ ]);
92
+ useEffect(()=>{
93
+ if (autoFocus && !disabled) focus().catch(reportInputError);
94
+ }, [
95
+ autoFocus,
96
+ disabled,
97
+ focus
98
+ ]);
99
+ const handleInput = useMemoizedFn((event)=>{
100
+ const rawValue = event.detail.value;
101
+ const nextValue = normalizeOTPValue(rawValue, fieldLength, inputType);
102
+ if (!isControlled) setUncontrolledValue(nextValue);
103
+ emitValue(nextValue, displayedValue);
104
+ if (rawValue !== nextValue) syncNativeValue(nextValue).catch(reportInputError);
105
+ });
106
+ const handleFocus = useMemoizedFn((_event)=>{
107
+ setFocused(true);
108
+ onInputFocused?.();
109
+ onFocus?.();
110
+ });
111
+ const handleBlur = useMemoizedFn((_event)=>{
112
+ setFocused(false);
113
+ onInputBlurred?.();
114
+ onBlur?.();
115
+ });
116
+ const handleTap = useMemoizedFn(()=>{
117
+ if (!disabled) focus().catch(reportInputError);
118
+ });
119
+ const contextValue = useMemo(()=>({
120
+ value: displayedValue,
121
+ length: fieldLength,
122
+ inputType,
123
+ focused,
124
+ complete: displayedValue.length === fieldLength,
125
+ disabled,
126
+ invalid
127
+ }), [
128
+ disabled,
129
+ displayedValue,
130
+ fieldLength,
131
+ focused,
132
+ inputType,
133
+ invalid
134
+ ]);
135
+ const renderedChildren = 'function' == typeof children ? children(contextValue) : children;
136
+ return <InputOTPContext.Provider value={contextValue}>
137
+ <view bindtap={handleTap} className={clsx(className, {
138
+ 'ui-focused': focused,
139
+ 'ui-complete': contextValue.complete,
140
+ 'ui-disabled': disabled,
141
+ 'ui-invalid': invalid
142
+ })} flatten={false} style={style}>
143
+ {renderedChildren}
144
+ <input ref={inputRef} android-fullscreen-mode={false} bindblur={handleBlur} bindfocus={handleFocus} bindinput={handleInput} confirm-type='done' disabled={disabled} ignore-focus={true} input-filter={nativeInputFilters[inputType]} ios-auto-correct={false} ios-spell-check={false} maxlength={fieldLength} show-soft-input-on-focus={true} style={hiddenInputStyle} type={'numeric' === inputType ? 'number' : 'text'}/>
145
+ </view>
146
+ </InputOTPContext.Provider>;
147
+ }
148
+ export { InputOTP };
149
+
150
+ //# sourceMappingURL=InputOTP.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputOTP.jsx","sources":["../src/InputOTP.tsx"],"sourcesContent":["// Copyright 2026 The Lynx Authors. All rights reserved.\n// Licensed under the Apache License Version 2.0 that can be found in the\n// LICENSE file in the root directory of this source tree.\n\nimport {\n forwardRef,\n memo,\n useContext,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from '@lynx-js/react'\nimport type { ForwardedRef } from '@lynx-js/react'\n\nimport { InvokeRejectError, useMemoizedFn } from '@lynx-js/lynx-ui-common'\nimport { KeyboardAwareTriggerContext } from '@lynx-js/lynx-ui-input'\nimport type {\n BaseEvent,\n CSSProperties,\n InputBlurEvent,\n InputFocusEvent,\n InputInputEvent,\n NodesRef,\n} from '@lynx-js/types'\nimport { clsx } from 'clsx'\n\nimport { InputOTPContext } from './InputOTPContext'\nimport type {\n InputOTPProps,\n InputOTPRef,\n InputOTPRenderProps,\n InputOTP as InputOTPType,\n} from './types'\nimport {\n nativeInputFilters,\n normalizeInputOTPLength,\n normalizeOTPValue,\n} from './utils'\n\nconst hiddenInputStyle: CSSProperties = {\n position: 'absolute',\n top: '0px',\n left: '0px',\n width: '1px',\n height: '1px',\n padding: '0px',\n borderWidth: '0px',\n opacity: 0,\n color: 'transparent',\n caretColor: 'transparent',\n backgroundColor: 'transparent',\n}\n\nexport const InputOTP = memo(forwardRef(InputOTPImpl)) as InputOTPType\n\nfunction reportInputError(error: unknown) {\n console.warn(\n `[lynx-ui-input-otp][InputOTP] native input command failed: ${\n String(error)\n }`,\n )\n}\n\nfunction InputOTPImpl(\n props: InputOTPProps,\n ref: ForwardedRef<InputOTPRef>,\n) {\n const {\n length = 6,\n inputType = 'numeric',\n value,\n defaultValue = '',\n autoFocus = false,\n disabled = false,\n invalid = false,\n className,\n style,\n children,\n onChange,\n onComplete,\n onFocus,\n onBlur,\n } = props\n\n const fieldLength = normalizeInputOTPLength(length)\n const isControlled = value !== undefined\n const [uncontrolledValue, setUncontrolledValue] = useState(\n normalizeOTPValue(defaultValue, fieldLength, inputType),\n )\n const [focused, setFocused] = useState(false)\n const inputRef = useRef<NodesRef>(null)\n const { onInputBlurred, onInputFocused } = useContext(\n KeyboardAwareTriggerContext,\n )\n\n const displayedValue = normalizeOTPValue(\n isControlled ? value : uncontrolledValue,\n fieldLength,\n inputType,\n )\n\n const invokeInput = useMemoizedFn(\n (\n method: 'focus' | 'blur' | 'setValue',\n nextValue?: string,\n ): Promise<void> =>\n new Promise((resolve, reject) => {\n if (!inputRef.current) {\n reject(new Error('InputOTP input is not mounted.'))\n return\n }\n\n inputRef.current\n .invoke({\n method,\n ...(method === 'setValue'\n ? {\n params: {\n value: nextValue ?? '',\n },\n }\n : {}),\n success: () => {\n resolve()\n },\n fail: (result: { code: number, data: string }) => {\n reject(new InvokeRejectError(result.code, result.data))\n },\n })\n .exec()\n }),\n )\n\n const focus = useMemoizedFn(() =>\n disabled ? Promise.resolve() : invokeInput('focus')\n )\n\n const blur = useMemoizedFn(() => invokeInput('blur'))\n\n const syncNativeValue = useMemoizedFn(\n (nextValue: string) => invokeInput('setValue', nextValue),\n )\n\n const emitValue = useMemoizedFn(\n (nextValue: string, previousValue: string) => {\n if (nextValue === previousValue) {\n return\n }\n\n onChange?.(nextValue)\n\n if (\n nextValue.length === fieldLength\n && previousValue.length !== fieldLength\n ) {\n onComplete?.(nextValue)\n }\n },\n )\n\n const setValue = useMemoizedFn((nextRawValue: string) => {\n const nextValue = normalizeOTPValue(\n nextRawValue,\n fieldLength,\n inputType,\n )\n\n if (!isControlled) {\n setUncontrolledValue(nextValue)\n }\n\n emitValue(nextValue, displayedValue)\n\n return syncNativeValue(nextValue)\n })\n\n useImperativeHandle(\n ref,\n () => ({\n focus,\n blur,\n setValue,\n clear: () => setValue(''),\n getValue: () => displayedValue,\n }),\n [blur, displayedValue, focus, setValue],\n )\n\n useEffect(() => {\n if (isControlled) {\n return\n }\n\n const nextValue = normalizeOTPValue(\n uncontrolledValue,\n fieldLength,\n inputType,\n )\n\n if (nextValue !== uncontrolledValue) {\n setUncontrolledValue(nextValue)\n }\n }, [fieldLength, inputType, isControlled, uncontrolledValue])\n\n useEffect(() => {\n void syncNativeValue(displayedValue).catch(reportInputError)\n }, [displayedValue, syncNativeValue])\n\n useEffect(() => {\n if (autoFocus && !disabled) {\n void focus().catch(reportInputError)\n }\n }, [autoFocus, disabled, focus])\n\n const handleInput = useMemoizedFn(\n (event: BaseEvent<'bindinput', InputInputEvent>) => {\n const rawValue = event.detail.value\n const nextValue = normalizeOTPValue(rawValue, fieldLength, inputType)\n\n if (!isControlled) {\n setUncontrolledValue(nextValue)\n }\n\n emitValue(nextValue, displayedValue)\n\n if (rawValue !== nextValue) {\n void syncNativeValue(nextValue).catch(reportInputError)\n }\n },\n )\n\n const handleFocus = useMemoizedFn(\n (_event: BaseEvent<'bindfocus', InputFocusEvent>) => {\n setFocused(true)\n onInputFocused?.()\n onFocus?.()\n },\n )\n\n const handleBlur = useMemoizedFn(\n (_event: BaseEvent<'bindblur', InputBlurEvent>) => {\n setFocused(false)\n onInputBlurred?.()\n onBlur?.()\n },\n )\n\n const handleTap = useMemoizedFn(() => {\n if (!disabled) {\n void focus().catch(reportInputError)\n }\n })\n\n const contextValue = useMemo<InputOTPRenderProps>(() => ({\n value: displayedValue,\n length: fieldLength,\n inputType,\n focused,\n complete: displayedValue.length === fieldLength,\n disabled,\n invalid,\n }), [\n disabled,\n displayedValue,\n fieldLength,\n focused,\n inputType,\n invalid,\n ])\n\n const renderedChildren = typeof children === 'function'\n ? children(contextValue)\n : children\n\n return (\n <InputOTPContext.Provider value={contextValue}>\n <view\n bindtap={handleTap}\n className={clsx(className, {\n 'ui-focused': focused,\n 'ui-complete': contextValue.complete,\n 'ui-disabled': disabled,\n 'ui-invalid': invalid,\n })}\n flatten={false}\n style={style}\n >\n {renderedChildren}\n <input\n ref={inputRef}\n android-fullscreen-mode={false}\n bindblur={handleBlur}\n bindfocus={handleFocus}\n bindinput={handleInput}\n confirm-type='done'\n disabled={disabled}\n ignore-focus={true}\n input-filter={nativeInputFilters[inputType]}\n ios-auto-correct={false}\n ios-spell-check={false}\n maxlength={fieldLength}\n show-soft-input-on-focus={true}\n style={hiddenInputStyle}\n type={inputType === 'numeric' ? 'number' : 'text'}\n />\n </view>\n </InputOTPContext.Provider>\n )\n}\n"],"names":["hiddenInputStyle","InputOTP","memo","forwardRef","InputOTPImpl","reportInputError","error","console","String","props","ref","length","inputType","value","defaultValue","autoFocus","disabled","invalid","className","style","children","onChange","onComplete","onFocus","onBlur","fieldLength","normalizeInputOTPLength","isControlled","undefined","uncontrolledValue","setUncontrolledValue","useState","normalizeOTPValue","focused","setFocused","inputRef","useRef","onInputBlurred","onInputFocused","useContext","KeyboardAwareTriggerContext","displayedValue","invokeInput","useMemoizedFn","method","nextValue","Promise","resolve","reject","Error","result","InvokeRejectError","focus","blur","syncNativeValue","emitValue","previousValue","setValue","nextRawValue","useImperativeHandle","useEffect","handleInput","event","rawValue","handleFocus","_event","handleBlur","handleTap","contextValue","useMemo","renderedChildren","InputOTPContext","view","clsx","input","nativeInputFilters"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { InputOTPRenderProps } from './types';
2
+ export declare const InputOTPContext: import("react").Context<InputOTPRenderProps | null>;
3
+ export declare function useInputOTPContext(): InputOTPRenderProps;
4
+ //# sourceMappingURL=InputOTPContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputOTPContext.d.ts","sourceRoot":"","sources":["../src/InputOTPContext.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAElD,eAAO,MAAM,eAAe,qDAAkD,CAAA;AAE9E,wBAAgB,kBAAkB,IAAI,mBAAmB,CAQxD"}
@@ -0,0 +1,10 @@
1
+ import { createContext, useContext } from "@lynx-js/react";
2
+ const InputOTPContext = createContext(null);
3
+ function useInputOTPContext() {
4
+ const context = useContext(InputOTPContext);
5
+ if (!context) throw new Error('InputOTPSlot must be used within InputOTP.');
6
+ return context;
7
+ }
8
+ export { InputOTPContext, useInputOTPContext };
9
+
10
+ //# sourceMappingURL=InputOTPContext.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputOTPContext.jsx","sources":["../src/InputOTPContext.ts"],"sourcesContent":["// Copyright 2026 The Lynx Authors. All rights reserved.\n// Licensed under the Apache License Version 2.0 that can be found in the\n// LICENSE file in the root directory of this source tree.\n\nimport { createContext, useContext } from '@lynx-js/react'\n\nimport type { InputOTPRenderProps } from './types'\n\nexport const InputOTPContext = createContext<InputOTPRenderProps | null>(null)\n\nexport function useInputOTPContext(): InputOTPRenderProps {\n const context = useContext(InputOTPContext)\n\n if (!context) {\n throw new Error('InputOTPSlot must be used within InputOTP.')\n }\n\n return context\n}\n"],"names":["InputOTPContext","createContext","useInputOTPContext","context","useContext","Error"],"mappings":";AAQO,MAAMA,kBAAkBC,cAA0C;AAElE,SAASC;IACd,MAAMC,UAAUC,WAAWJ;IAE3B,IAAI,CAACG,SACH,MAAM,IAAIE,MAAM;IAGlB,OAAOF;AACT"}
@@ -0,0 +1,3 @@
1
+ import type { InputOTPSlot as InputOTPSlotType } from './types';
2
+ export declare const InputOTPSlot: InputOTPSlotType;
3
+ //# sourceMappingURL=InputOTPSlot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputOTPSlot.d.ts","sourceRoot":"","sources":["../src/InputOTPSlot.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAGV,YAAY,IAAI,gBAAgB,EACjC,MAAM,SAAS,CAAA;AAEhB,eAAO,MAAM,YAAY,EAA6B,gBAAgB,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { memo } from "@lynx-js/react";
2
+ import { clsx } from "clsx";
3
+ import { useInputOTPContext } from "./InputOTPContext.jsx";
4
+ const InputOTPSlot = memo(InputOTPSlotImpl);
5
+ function InputOTPSlotImpl(props) {
6
+ const { index, className, style, children } = props;
7
+ const context = useInputOTPContext();
8
+ const { value, length, focused, complete, disabled, invalid } = context;
9
+ const char = value[index];
10
+ const isFilled = void 0 !== char;
11
+ const hasFakeCaret = focused && !disabled && value.length < length && index === value.length;
12
+ const slotState = {
13
+ index,
14
+ char,
15
+ focused: hasFakeCaret,
16
+ filled: isFilled,
17
+ complete,
18
+ disabled,
19
+ invalid
20
+ };
21
+ const renderedChildren = 'function' == typeof children ? children(slotState) : children;
22
+ return <view className={clsx(className, {
23
+ 'ui-focused': hasFakeCaret,
24
+ 'ui-filled': isFilled,
25
+ 'ui-complete': complete,
26
+ 'ui-disabled': disabled,
27
+ 'ui-invalid': invalid
28
+ })} style={style}>
29
+ {void 0 === children ? <>
30
+ {void 0 === char ? null : <text>{char}</text>}
31
+ {hasFakeCaret ? <view/> : null}
32
+ </> : renderedChildren}
33
+ </view>;
34
+ }
35
+ export { InputOTPSlot };
36
+
37
+ //# sourceMappingURL=InputOTPSlot.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputOTPSlot.jsx","sources":["../src/InputOTPSlot.tsx"],"sourcesContent":["// Copyright 2026 The Lynx Authors. All rights reserved.\n// Licensed under the Apache License Version 2.0 that can be found in the\n// LICENSE file in the root directory of this source tree.\n\nimport { memo } from '@lynx-js/react'\n\nimport { clsx } from 'clsx'\n\nimport { useInputOTPContext } from './InputOTPContext'\nimport type {\n InputOTPSlotProps,\n InputOTPSlotRenderProps,\n InputOTPSlot as InputOTPSlotType,\n} from './types'\n\nexport const InputOTPSlot = memo(InputOTPSlotImpl) as InputOTPSlotType\n\nfunction InputOTPSlotImpl(props: InputOTPSlotProps) {\n const {\n index,\n className,\n style,\n children,\n } = props\n const context = useInputOTPContext()\n\n const {\n value,\n length,\n focused,\n complete,\n disabled,\n invalid,\n } = context\n const char = value[index]\n const isFilled = char !== undefined\n const hasFakeCaret = focused\n && !disabled\n && value.length < length\n && index === value.length\n const slotState: InputOTPSlotRenderProps = {\n index,\n char,\n focused: hasFakeCaret,\n filled: isFilled,\n complete,\n disabled,\n invalid,\n }\n const renderedChildren = typeof children === 'function'\n ? children(slotState)\n : children\n\n return (\n <view\n className={clsx(className, {\n 'ui-focused': hasFakeCaret,\n 'ui-filled': isFilled,\n 'ui-complete': complete,\n 'ui-disabled': disabled,\n 'ui-invalid': invalid,\n })}\n style={style}\n >\n {children === undefined\n ? (\n <>\n {char === undefined\n ? null\n : <text>{char}</text>}\n {hasFakeCaret\n ? <view />\n : null}\n </>\n )\n : renderedChildren}\n </view>\n )\n}\n"],"names":["InputOTPSlot","memo","InputOTPSlotImpl","props","index","className","style","children","context","useInputOTPContext","value","length","focused","complete","disabled","invalid","char","isFilled","undefined","hasFakeCaret","slotState","renderedChildren","view","clsx","text"],"mappings":";;;AAeO,MAAMA,eAAeC,KAAKC;AAEjC,SAASA,iBAAiBC,KAAwB;IAChD,MAAM,EACJC,KAAK,EACLC,SAAS,EACTC,KAAK,EACLC,QAAQ,EACT,GAAGJ;IACJ,MAAMK,UAAUC;IAEhB,MAAM,EACJC,KAAK,EACLC,MAAM,EACNC,OAAO,EACPC,QAAQ,EACRC,QAAQ,EACRC,OAAO,EACR,GAAGP;IACJ,MAAMQ,OAAON,KAAK,CAACN,MAAM;IACzB,MAAMa,WAAWD,AAASE,WAATF;IACjB,MAAMG,eAAeP,WAChB,CAACE,YACDJ,MAAM,MAAM,GAAGC,UACfP,UAAUM,MAAM,MAAM;IAC3B,MAAMU,YAAqC;QACzChB;QACAY;QACA,SAASG;QACT,QAAQF;QACRJ;QACAC;QACAC;IACF;IACA,MAAMM,mBAAmB,AAAoB,cAApB,OAAOd,WAC5BA,SAASa,aACTb;IAEJ,QACGe,KACC,WAAWC,KAAKlB,WAAW;QACzB,cAAcc;QACd,aAAaF;QACb,eAAeJ;QACf,eAAeC;QACf,cAAcC;IAChB,IACA,OAAOT,OACR;MACC,CAACC,AAAaW,WAAbX,aAEK;YACA,CAACS,AAASE,WAATF,OACG,QACCQ,MAAMR,OAAOQ,MAAM;YACxB,CAACL,gBACIG,SACD,KAAK;UACX,MAEAD,iBAAiB;IACvB,EAAEC;AAEN"}
@@ -0,0 +1,4 @@
1
+ export { InputOTP } from './InputOTP';
2
+ export { InputOTPSlot } from './InputOTPSlot';
3
+ export type { InputOTPInputType, InputOTPLength, InputOTPProps, InputOTPRef, InputOTPRenderProps, InputOTPSlotProps, InputOTPSlotRenderProps, InputOTPSlotUIVariants, InputOTPUIVariants, } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,SAAS,CAAA"}
package/dist/index.jsx ADDED
@@ -0,0 +1,2 @@
1
+ export { InputOTP } from "./InputOTP.jsx";
2
+ export { InputOTPSlot } from "./InputOTPSlot.jsx";
@@ -0,0 +1,2 @@
1
+ export type * from './index.docs';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAIA,mBAAmB,cAAc,CAAA"}