@octanejs/radix 0.1.32 → 0.1.33
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/cjs/AccessibleIcon.cjs +56 -0
- package/dist/cjs/Accordion.cjs +272 -0
- package/dist/cjs/AlertDialog.cjs +144 -0
- package/dist/cjs/Arrow.cjs +43 -0
- package/dist/cjs/AspectRatio.cjs +56 -0
- package/dist/cjs/Avatar.cjs +150 -0
- package/dist/cjs/Checkbox.cjs +281 -0
- package/dist/cjs/Collapsible.cjs +176 -0
- package/dist/cjs/ContextMenu.cjs +315 -0
- package/dist/cjs/Dialog.cjs +291 -0
- package/dist/cjs/DismissableLayer.cjs +382 -0
- package/dist/cjs/DropdownMenu.cjs +296 -0
- package/dist/cjs/FocusScope.cjs +257 -0
- package/dist/cjs/Form.cjs +537 -0
- package/dist/cjs/HoverCard.cjs +309 -0
- package/dist/cjs/Label.cjs +42 -0
- package/dist/cjs/Menu.cjs +925 -0
- package/dist/cjs/Menubar.cjs +475 -0
- package/dist/cjs/NavigationMenu.cjs +942 -0
- package/dist/cjs/OneTimePasswordField.cjs +729 -0
- package/dist/cjs/PasswordToggleField.cjs +377 -0
- package/dist/cjs/Popover.cjs +340 -0
- package/dist/cjs/Popper.cjs +368 -0
- package/dist/cjs/Portal.cjs +40 -0
- package/dist/cjs/Presence.cjs +183 -0
- package/dist/cjs/Primitive.cjs +63 -0
- package/dist/cjs/Progress.cjs +111 -0
- package/dist/cjs/Radio.cjs +240 -0
- package/dist/cjs/RadioGroup.cjs +216 -0
- package/dist/cjs/RovingFocusGroup.cjs +253 -0
- package/dist/cjs/ScrollArea.cjs +828 -0
- package/dist/cjs/Select.cjs +1429 -0
- package/dist/cjs/Separator.cjs +51 -0
- package/dist/cjs/Slider.cjs +634 -0
- package/dist/cjs/Slot.cjs +97 -0
- package/dist/cjs/Switch.cjs +253 -0
- package/dist/cjs/Tabs.cjs +195 -0
- package/dist/cjs/Toast.cjs +733 -0
- package/dist/cjs/Toggle.cjs +54 -0
- package/dist/cjs/ToggleGroup.cjs +174 -0
- package/dist/cjs/Toolbar.cjs +165 -0
- package/dist/cjs/Tooltip.cjs +628 -0
- package/dist/cjs/VisuallyHidden.cjs +52 -0
- package/dist/cjs/collection.cjs +95 -0
- package/dist/cjs/compose-event-handlers.cjs +35 -0
- package/dist/cjs/compose-refs.cjs +68 -0
- package/dist/cjs/context.cjs +96 -0
- package/dist/cjs/direction.cjs +41 -0
- package/dist/cjs/focus-guards.cjs +76 -0
- package/dist/cjs/index.cjs +174 -0
- package/dist/cjs/internal.cjs +51 -0
- package/dist/cjs/scroll-lock.cjs +67 -0
- package/dist/cjs/use-callback-ref.cjs +47 -0
- package/dist/cjs/use-effect-event.cjs +52 -0
- package/dist/cjs/use-is-hydrated.cjs +43 -0
- package/dist/cjs/use-previous.cjs +47 -0
- package/dist/cjs/use-size.cjs +69 -0
- package/dist/cjs/useControllableState.cjs +80 -0
- package/dist/cjs/useId.cjs +36 -0
- package/package.json +14 -7
|
@@ -0,0 +1,729 @@
|
|
|
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
|
+
var OneTimePasswordField_exports = {};
|
|
30
|
+
__export(OneTimePasswordField_exports, {
|
|
31
|
+
HiddenInput: () => OneTimePasswordFieldHiddenInput,
|
|
32
|
+
Input: () => OneTimePasswordFieldInput,
|
|
33
|
+
OneTimePasswordField: () => OneTimePasswordField,
|
|
34
|
+
OneTimePasswordFieldHiddenInput: () => OneTimePasswordFieldHiddenInput,
|
|
35
|
+
OneTimePasswordFieldInput: () => OneTimePasswordFieldInput,
|
|
36
|
+
Root: () => OneTimePasswordField,
|
|
37
|
+
createOneTimePasswordFieldScope: () => createOneTimePasswordFieldScope
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(OneTimePasswordField_exports);
|
|
40
|
+
var import_octane = require("octane");
|
|
41
|
+
var import_compose_event_handlers = require("./compose-event-handlers.cjs");
|
|
42
|
+
var import_compose_refs = require("./compose-refs.cjs");
|
|
43
|
+
var import_context = require("./context.cjs");
|
|
44
|
+
var import_direction = require("./direction.cjs");
|
|
45
|
+
var import_internal = require("./internal.cjs");
|
|
46
|
+
var import_Primitive = require("./Primitive.cjs");
|
|
47
|
+
var RovingFocusGroup = __toESM(require("./RovingFocusGroup.cjs"), 1);
|
|
48
|
+
var import_RovingFocusGroup = require("./RovingFocusGroup.cjs");
|
|
49
|
+
var import_useControllableState = require("./useControllableState.cjs");
|
|
50
|
+
var import_use_is_hydrated = require("./use-is-hydrated.cjs");
|
|
51
|
+
const INPUT_VALIDATION_MAP = {
|
|
52
|
+
numeric: {
|
|
53
|
+
type: "numeric",
|
|
54
|
+
regexp: /[^\d]/g,
|
|
55
|
+
pattern: "\\d{1}",
|
|
56
|
+
inputMode: "numeric"
|
|
57
|
+
},
|
|
58
|
+
alpha: {
|
|
59
|
+
type: "alpha",
|
|
60
|
+
regexp: /[^a-zA-Z]/g,
|
|
61
|
+
pattern: "[a-zA-Z]{1}",
|
|
62
|
+
inputMode: "text"
|
|
63
|
+
},
|
|
64
|
+
alphanumeric: {
|
|
65
|
+
type: "alphanumeric",
|
|
66
|
+
regexp: /[^a-zA-Z0-9]/g,
|
|
67
|
+
pattern: "[a-zA-Z0-9]{1}",
|
|
68
|
+
inputMode: "text"
|
|
69
|
+
},
|
|
70
|
+
none: null
|
|
71
|
+
};
|
|
72
|
+
const ONE_TIME_PASSWORD_FIELD_NAME = "OneTimePasswordField";
|
|
73
|
+
const [createOneTimePasswordFieldContext, createOneTimePasswordFieldScope] = (0, import_context.createContextScope)(
|
|
74
|
+
ONE_TIME_PASSWORD_FIELD_NAME,
|
|
75
|
+
[import_RovingFocusGroup.createRovingFocusGroupScope]
|
|
76
|
+
);
|
|
77
|
+
const useRovingFocusGroupScope = (0, import_RovingFocusGroup.createRovingFocusGroupScope)();
|
|
78
|
+
const [OneTimePasswordFieldContext, useOneTimePasswordFieldContext] = createOneTimePasswordFieldContext(ONE_TIME_PASSWORD_FIELD_NAME);
|
|
79
|
+
const OTP_INPUT_ATTR = "data-radix-otp-input";
|
|
80
|
+
function createCollectionApi(getRoot) {
|
|
81
|
+
const elements = () => {
|
|
82
|
+
const root = getRoot();
|
|
83
|
+
if (!root) return [];
|
|
84
|
+
return Array.from(root.querySelectorAll(`input[${OTP_INPUT_ATTR}]`));
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
get size() {
|
|
88
|
+
return elements().length;
|
|
89
|
+
},
|
|
90
|
+
at(index) {
|
|
91
|
+
const element = elements().at(index);
|
|
92
|
+
return element ? { element } : void 0;
|
|
93
|
+
},
|
|
94
|
+
from(element, offset) {
|
|
95
|
+
const els = elements();
|
|
96
|
+
const index = els.indexOf(element);
|
|
97
|
+
if (index === -1) return void 0;
|
|
98
|
+
let dest = index + offset;
|
|
99
|
+
if (dest < 0) dest = 0;
|
|
100
|
+
if (dest >= els.length) dest = els.length - 1;
|
|
101
|
+
const target = els[dest];
|
|
102
|
+
return target ? { element: target } : void 0;
|
|
103
|
+
},
|
|
104
|
+
indexOf(element) {
|
|
105
|
+
return elements().indexOf(element);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function OneTimePasswordField(props) {
|
|
110
|
+
const slot = (0, import_internal.S)("OneTimePasswordField.Root");
|
|
111
|
+
const {
|
|
112
|
+
__scopeOneTimePasswordField,
|
|
113
|
+
defaultValue,
|
|
114
|
+
value: valueProp,
|
|
115
|
+
onValueChange,
|
|
116
|
+
autoSubmit = false,
|
|
117
|
+
children,
|
|
118
|
+
onPaste,
|
|
119
|
+
onAutoSubmit,
|
|
120
|
+
disabled = false,
|
|
121
|
+
readOnly = false,
|
|
122
|
+
autoComplete = "one-time-code",
|
|
123
|
+
autoFocus = false,
|
|
124
|
+
form,
|
|
125
|
+
name,
|
|
126
|
+
placeholder,
|
|
127
|
+
type = "text",
|
|
128
|
+
// TODO (source): Change default to vertical when inputs use vertical writing mode
|
|
129
|
+
orientation = "horizontal",
|
|
130
|
+
dir,
|
|
131
|
+
validationType = "numeric",
|
|
132
|
+
sanitizeValue: sanitizeValueProp,
|
|
133
|
+
ref: forwardedRef,
|
|
134
|
+
...domProps
|
|
135
|
+
} = props ?? {};
|
|
136
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(
|
|
137
|
+
__scopeOneTimePasswordField,
|
|
138
|
+
(0, import_internal.subSlot)(slot, "rfs")
|
|
139
|
+
);
|
|
140
|
+
const direction = (0, import_direction.useDirection)(dir);
|
|
141
|
+
const rootRef = (0, import_octane.useRef)(null, (0, import_internal.subSlot)(slot, "root"));
|
|
142
|
+
const collection = (0, import_octane.useMemo)(
|
|
143
|
+
() => createCollectionApi(() => rootRef.current),
|
|
144
|
+
[],
|
|
145
|
+
(0, import_internal.subSlot)(slot, "coll")
|
|
146
|
+
);
|
|
147
|
+
const [collectionVersion, setCollectionVersion] = (0, import_octane.useState)(0, (0, import_internal.subSlot)(slot, "collVer"));
|
|
148
|
+
const registerInput = (0, import_octane.useCallback)(
|
|
149
|
+
() => {
|
|
150
|
+
setCollectionVersion((v) => v + 1);
|
|
151
|
+
return () => setCollectionVersion((v) => v + 1);
|
|
152
|
+
},
|
|
153
|
+
[],
|
|
154
|
+
(0, import_internal.subSlot)(slot, "register")
|
|
155
|
+
);
|
|
156
|
+
const validation = INPUT_VALIDATION_MAP[validationType] ?? null;
|
|
157
|
+
const sanitizeValue = (0, import_octane.useCallback)(
|
|
158
|
+
(value2) => {
|
|
159
|
+
let str;
|
|
160
|
+
if (Array.isArray(value2)) {
|
|
161
|
+
str = value2.map(removeWhitespace).join("");
|
|
162
|
+
} else {
|
|
163
|
+
str = removeWhitespace(value2);
|
|
164
|
+
}
|
|
165
|
+
if (validation) {
|
|
166
|
+
const regexp = new RegExp(validation.regexp);
|
|
167
|
+
str = str.replace(regexp, "");
|
|
168
|
+
} else if (sanitizeValueProp) {
|
|
169
|
+
str = sanitizeValueProp(str);
|
|
170
|
+
}
|
|
171
|
+
return str.split("");
|
|
172
|
+
},
|
|
173
|
+
[validation, sanitizeValueProp],
|
|
174
|
+
(0, import_internal.subSlot)(slot, "sanitize")
|
|
175
|
+
);
|
|
176
|
+
const controlledValue = (0, import_octane.useMemo)(
|
|
177
|
+
() => {
|
|
178
|
+
return valueProp != null ? sanitizeValue(valueProp) : void 0;
|
|
179
|
+
},
|
|
180
|
+
[valueProp, sanitizeValue],
|
|
181
|
+
(0, import_internal.subSlot)(slot, "controlled")
|
|
182
|
+
);
|
|
183
|
+
const handleValueChange = (0, import_octane.useCallback)(
|
|
184
|
+
(value2) => onValueChange?.(value2.join("")),
|
|
185
|
+
[onValueChange],
|
|
186
|
+
(0, import_internal.subSlot)(slot, "onChange")
|
|
187
|
+
);
|
|
188
|
+
const [value, setValue] = (0, import_useControllableState.useControllableState)(
|
|
189
|
+
{
|
|
190
|
+
prop: controlledValue,
|
|
191
|
+
defaultProp: defaultValue != null ? sanitizeValue(defaultValue) : [],
|
|
192
|
+
onChange: handleValueChange
|
|
193
|
+
},
|
|
194
|
+
(0, import_internal.subSlot)(slot, "value")
|
|
195
|
+
);
|
|
196
|
+
const latestValueRef = (0, import_octane.useRef)(value, (0, import_internal.subSlot)(slot, "latest"));
|
|
197
|
+
latestValueRef.current = value;
|
|
198
|
+
const dispatch = (0, import_octane.useCallback)(
|
|
199
|
+
(action) => {
|
|
200
|
+
const value2 = latestValueRef.current;
|
|
201
|
+
switch (action.type) {
|
|
202
|
+
case "SET_CHAR": {
|
|
203
|
+
const { index, char } = action;
|
|
204
|
+
const currentTarget = collection.at(index)?.element;
|
|
205
|
+
if (value2[index] === char) {
|
|
206
|
+
const next = currentTarget && collection.from(currentTarget, 1)?.element;
|
|
207
|
+
focusInput(next);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (char === "") {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (validation) {
|
|
214
|
+
const regexp = new RegExp(validation.regexp);
|
|
215
|
+
const clean = char.replace(regexp, "");
|
|
216
|
+
if (clean !== char) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (value2.length >= collection.size) {
|
|
221
|
+
const newValue2 = [...value2];
|
|
222
|
+
newValue2[index] = char;
|
|
223
|
+
(0, import_octane.flushSync)(() => setValue(newValue2));
|
|
224
|
+
const next = currentTarget && collection.from(currentTarget, 1)?.element;
|
|
225
|
+
focusInput(next);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const newValue = [...value2];
|
|
229
|
+
newValue[index] = char;
|
|
230
|
+
const lastElement = collection.at(-1)?.element;
|
|
231
|
+
(0, import_octane.flushSync)(() => setValue(newValue));
|
|
232
|
+
if (currentTarget !== lastElement) {
|
|
233
|
+
const next = currentTarget && collection.from(currentTarget, 1)?.element;
|
|
234
|
+
focusInput(next);
|
|
235
|
+
} else {
|
|
236
|
+
currentTarget?.select();
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
case "CLEAR_CHAR": {
|
|
241
|
+
const { index, reason } = action;
|
|
242
|
+
if (!value2[index]) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const newValue = value2.filter((_, i) => i !== index);
|
|
246
|
+
const currentTarget = collection.at(index)?.element;
|
|
247
|
+
const previous = currentTarget && collection.from(currentTarget, -1)?.element;
|
|
248
|
+
(0, import_octane.flushSync)(() => setValue(newValue));
|
|
249
|
+
if (reason === "Backspace") {
|
|
250
|
+
focusInput(previous);
|
|
251
|
+
} else if (reason === "Delete" || reason === "Cut") {
|
|
252
|
+
focusInput(currentTarget);
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
case "CLEAR": {
|
|
257
|
+
if (value2.length === 0) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (action.reason === "Backspace" || action.reason === "Delete") {
|
|
261
|
+
(0, import_octane.flushSync)(() => setValue([]));
|
|
262
|
+
focusInput(collection.at(0)?.element);
|
|
263
|
+
} else {
|
|
264
|
+
setValue([]);
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
case "PASTE": {
|
|
269
|
+
const { value: pastedValue } = action;
|
|
270
|
+
const sanitizedValue = sanitizeValue(pastedValue);
|
|
271
|
+
if (!sanitizedValue) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const value3 = sanitizedValue.slice(0, collection.size);
|
|
275
|
+
(0, import_octane.flushSync)(() => setValue(value3));
|
|
276
|
+
focusInput(collection.at(value3.length - 1)?.element);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
[collection, sanitizeValue, setValue, validation],
|
|
282
|
+
(0, import_internal.subSlot)(slot, "dispatch")
|
|
283
|
+
);
|
|
284
|
+
const validationTypeRef = (0, import_octane.useRef)(validation, (0, import_internal.subSlot)(slot, "vtype"));
|
|
285
|
+
(0, import_octane.useEffect)(
|
|
286
|
+
() => {
|
|
287
|
+
if (!validation) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (validationTypeRef.current?.type !== validation.type) {
|
|
291
|
+
validationTypeRef.current = validation;
|
|
292
|
+
setValue(sanitizeValue(value.join("")));
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
[sanitizeValue, setValue, validation, value],
|
|
296
|
+
(0, import_internal.subSlot)(slot, "e:revalidate")
|
|
297
|
+
);
|
|
298
|
+
const hiddenInputRef = (0, import_octane.useRef)(null, (0, import_internal.subSlot)(slot, "hidden"));
|
|
299
|
+
const userActionRef = (0, import_octane.useRef)(null, (0, import_internal.subSlot)(slot, "action"));
|
|
300
|
+
const composedRefs = (0, import_compose_refs.useComposedRefs)(forwardedRef, rootRef, (0, import_internal.subSlot)(slot, "refs"));
|
|
301
|
+
const locateForm = (0, import_octane.useCallback)(
|
|
302
|
+
() => {
|
|
303
|
+
let formElement;
|
|
304
|
+
if (form) {
|
|
305
|
+
const associatedElement = (rootRef.current?.ownerDocument ?? document).getElementById(form);
|
|
306
|
+
if (isFormElement(associatedElement)) {
|
|
307
|
+
formElement = associatedElement;
|
|
308
|
+
}
|
|
309
|
+
} else if (hiddenInputRef.current) {
|
|
310
|
+
formElement = hiddenInputRef.current.form;
|
|
311
|
+
} else {
|
|
312
|
+
const firstInput = collection.at(0)?.element;
|
|
313
|
+
if (firstInput) {
|
|
314
|
+
formElement = firstInput.form;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return formElement ?? null;
|
|
318
|
+
},
|
|
319
|
+
[form, collection],
|
|
320
|
+
(0, import_internal.subSlot)(slot, "locate")
|
|
321
|
+
);
|
|
322
|
+
const attemptSubmit = (0, import_octane.useCallback)(
|
|
323
|
+
() => {
|
|
324
|
+
const formElement = locateForm();
|
|
325
|
+
formElement?.requestSubmit();
|
|
326
|
+
},
|
|
327
|
+
[locateForm],
|
|
328
|
+
(0, import_internal.subSlot)(slot, "submit")
|
|
329
|
+
);
|
|
330
|
+
(0, import_octane.useEffect)(
|
|
331
|
+
() => {
|
|
332
|
+
const form2 = locateForm();
|
|
333
|
+
if (form2) {
|
|
334
|
+
const reset = () => dispatch({ type: "CLEAR", reason: "Reset" });
|
|
335
|
+
form2.addEventListener("reset", reset);
|
|
336
|
+
return () => form2.removeEventListener("reset", reset);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
[dispatch, locateForm],
|
|
340
|
+
(0, import_internal.subSlot)(slot, "e:reset")
|
|
341
|
+
);
|
|
342
|
+
const currentValue = value.join("");
|
|
343
|
+
const valueRef = (0, import_octane.useRef)(currentValue, (0, import_internal.subSlot)(slot, "valueRef"));
|
|
344
|
+
const length = collection.size;
|
|
345
|
+
(0, import_octane.useEffect)(
|
|
346
|
+
() => {
|
|
347
|
+
const previousValue = valueRef.current;
|
|
348
|
+
valueRef.current = currentValue;
|
|
349
|
+
if (previousValue === currentValue) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (autoSubmit && value.every((char) => char !== "") && value.length === length) {
|
|
353
|
+
onAutoSubmit?.(value.join(""));
|
|
354
|
+
attemptSubmit();
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
[attemptSubmit, autoSubmit, currentValue, length, onAutoSubmit, value],
|
|
358
|
+
(0, import_internal.subSlot)(slot, "e:autosubmit")
|
|
359
|
+
);
|
|
360
|
+
const isHydrated = (0, import_use_is_hydrated.useIsHydrated)((0, import_internal.subSlot)(slot, "hydrated"));
|
|
361
|
+
return (0, import_octane.createElement)(OneTimePasswordFieldContext, {
|
|
362
|
+
scope: __scopeOneTimePasswordField,
|
|
363
|
+
value,
|
|
364
|
+
attemptSubmit,
|
|
365
|
+
collection,
|
|
366
|
+
collectionVersion,
|
|
367
|
+
registerInput,
|
|
368
|
+
disabled,
|
|
369
|
+
readOnly,
|
|
370
|
+
autoComplete,
|
|
371
|
+
autoFocus,
|
|
372
|
+
form,
|
|
373
|
+
name,
|
|
374
|
+
placeholder,
|
|
375
|
+
type,
|
|
376
|
+
hiddenInputRef,
|
|
377
|
+
userActionRef,
|
|
378
|
+
dispatch,
|
|
379
|
+
validationType,
|
|
380
|
+
orientation,
|
|
381
|
+
isHydrated,
|
|
382
|
+
sanitizeValue,
|
|
383
|
+
children: (0, import_octane.createElement)(RovingFocusGroup.Root, {
|
|
384
|
+
asChild: true,
|
|
385
|
+
...rovingFocusGroupScope,
|
|
386
|
+
orientation,
|
|
387
|
+
dir: direction,
|
|
388
|
+
children: (0, import_octane.createElement)(import_Primitive.Primitive.div, {
|
|
389
|
+
...domProps,
|
|
390
|
+
role: "group",
|
|
391
|
+
ref: composedRefs,
|
|
392
|
+
onPaste: (0, import_compose_event_handlers.composeEventHandlers)(onPaste, (event) => {
|
|
393
|
+
event.preventDefault();
|
|
394
|
+
const pastedValue = event.clipboardData?.getData("text/plain") ?? "";
|
|
395
|
+
dispatch({ type: "PASTE", value: pastedValue });
|
|
396
|
+
}),
|
|
397
|
+
children
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function OneTimePasswordFieldHiddenInput(props) {
|
|
403
|
+
const slot = (0, import_internal.S)("OneTimePasswordField.HiddenInput");
|
|
404
|
+
const { __scopeOneTimePasswordField, ref: forwardedRef, ...hiddenProps } = props ?? {};
|
|
405
|
+
const { value, hiddenInputRef, name } = useOneTimePasswordFieldContext(
|
|
406
|
+
"OneTimePasswordFieldHiddenInput",
|
|
407
|
+
__scopeOneTimePasswordField
|
|
408
|
+
);
|
|
409
|
+
const ref = (0, import_compose_refs.useComposedRefs)(hiddenInputRef, forwardedRef, (0, import_internal.subSlot)(slot, "refs"));
|
|
410
|
+
return (0, import_octane.createElement)("input", {
|
|
411
|
+
ref,
|
|
412
|
+
name,
|
|
413
|
+
value: value.join("").trim(),
|
|
414
|
+
autoComplete: "off",
|
|
415
|
+
autoCapitalize: "off",
|
|
416
|
+
autoCorrect: "off",
|
|
417
|
+
autoSave: "off",
|
|
418
|
+
// octane: pass the string 'false' — a false-valued attribute would be removed,
|
|
419
|
+
// but React renders spellcheck="false".
|
|
420
|
+
spellCheck: "false",
|
|
421
|
+
...hiddenProps,
|
|
422
|
+
type: "hidden",
|
|
423
|
+
readOnly: true
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function OneTimePasswordFieldInput(props) {
|
|
427
|
+
const slot = (0, import_internal.S)("OneTimePasswordField.Input");
|
|
428
|
+
const {
|
|
429
|
+
__scopeOneTimePasswordField,
|
|
430
|
+
onInvalidChange,
|
|
431
|
+
index: indexProp,
|
|
432
|
+
// props users should pass on the Root instead (the source strips + TODO-warns)
|
|
433
|
+
value: _value,
|
|
434
|
+
defaultValue: _defaultValue,
|
|
435
|
+
disabled: _disabled,
|
|
436
|
+
readOnly: _readOnly,
|
|
437
|
+
autoComplete: _autoComplete,
|
|
438
|
+
autoFocus: _autoFocus,
|
|
439
|
+
form: _form,
|
|
440
|
+
name: _name,
|
|
441
|
+
placeholder: _placeholder,
|
|
442
|
+
type: _type,
|
|
443
|
+
// octane adaptation: React's onChange on a text input is the native `input` event —
|
|
444
|
+
// both user handlers fold into the single composed onInput binding below, so they
|
|
445
|
+
// must not stay in domProps (a native `change` binding would mean something else).
|
|
446
|
+
onInput: onInputProp,
|
|
447
|
+
onChange: onChangeProp,
|
|
448
|
+
ref: forwardedRef,
|
|
449
|
+
...domProps
|
|
450
|
+
} = props ?? {};
|
|
451
|
+
const context = useOneTimePasswordFieldContext(
|
|
452
|
+
"OneTimePasswordFieldInput",
|
|
453
|
+
__scopeOneTimePasswordField
|
|
454
|
+
);
|
|
455
|
+
const { dispatch, userActionRef, validationType, isHydrated, disabled, collection } = context;
|
|
456
|
+
const registerInput = context.registerInput;
|
|
457
|
+
(0, import_octane.useLayoutEffect)(() => registerInput(), [registerInput], (0, import_internal.subSlot)(slot, "e:register"));
|
|
458
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(
|
|
459
|
+
__scopeOneTimePasswordField,
|
|
460
|
+
(0, import_internal.subSlot)(slot, "rfs")
|
|
461
|
+
);
|
|
462
|
+
const inputRef = (0, import_octane.useRef)(null, (0, import_internal.subSlot)(slot, "input"));
|
|
463
|
+
const [element, setElement] = (0, import_octane.useState)(null, (0, import_internal.subSlot)(slot, "element"));
|
|
464
|
+
const index = indexProp ?? (element ? collection.indexOf(element) : -1);
|
|
465
|
+
const canSetPlaceholder = indexProp != null || isHydrated;
|
|
466
|
+
let placeholder;
|
|
467
|
+
if (canSetPlaceholder && context.placeholder && context.value.length === 0) {
|
|
468
|
+
placeholder = context.placeholder[index];
|
|
469
|
+
}
|
|
470
|
+
const composedInputRef = (0, import_compose_refs.useComposedRefs)(
|
|
471
|
+
forwardedRef,
|
|
472
|
+
inputRef,
|
|
473
|
+
setElement,
|
|
474
|
+
(0, import_internal.subSlot)(slot, "refs")
|
|
475
|
+
);
|
|
476
|
+
const char = context.value[index] ?? "";
|
|
477
|
+
const keyboardActionTimeoutRef = (0, import_octane.useRef)(null, (0, import_internal.subSlot)(slot, "timeout"));
|
|
478
|
+
(0, import_octane.useEffect)(
|
|
479
|
+
() => {
|
|
480
|
+
return () => {
|
|
481
|
+
if (keyboardActionTimeoutRef.current) {
|
|
482
|
+
window.clearTimeout(keyboardActionTimeoutRef.current);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
},
|
|
486
|
+
[],
|
|
487
|
+
(0, import_internal.subSlot)(slot, "e:timeout")
|
|
488
|
+
);
|
|
489
|
+
const totalValue = context.value.join("").trim();
|
|
490
|
+
const lastSelectableIndex = clamp(totalValue.length, [0, collection.size - 1]);
|
|
491
|
+
const isFocusable = index <= lastSelectableIndex;
|
|
492
|
+
const validation = INPUT_VALIDATION_MAP[validationType] ?? void 0;
|
|
493
|
+
const handleInput = (event) => {
|
|
494
|
+
const value = event.currentTarget.value;
|
|
495
|
+
if (value.length > 1) {
|
|
496
|
+
event.preventDefault();
|
|
497
|
+
userActionRef.current = { type: "autocomplete-paste" };
|
|
498
|
+
dispatch({ type: "PASTE", value });
|
|
499
|
+
keyboardActionTimeoutRef.current = window.setTimeout(() => {
|
|
500
|
+
userActionRef.current = null;
|
|
501
|
+
}, 10);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
const handleChange = (event) => {
|
|
505
|
+
const target = event.target;
|
|
506
|
+
const value = target.value;
|
|
507
|
+
event.preventDefault();
|
|
508
|
+
const action = userActionRef.current;
|
|
509
|
+
userActionRef.current = null;
|
|
510
|
+
if (action) {
|
|
511
|
+
switch (action.type) {
|
|
512
|
+
case "cut":
|
|
513
|
+
dispatch({ type: "CLEAR_CHAR", index, reason: "Cut" });
|
|
514
|
+
return;
|
|
515
|
+
case "autocomplete-paste":
|
|
516
|
+
return;
|
|
517
|
+
case "keydown": {
|
|
518
|
+
if (action.key === "Char") {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const isClearing = action.key === "Backspace" && (action.metaKey || action.ctrlKey);
|
|
522
|
+
if (action.key === "Clear" || isClearing) {
|
|
523
|
+
dispatch({ type: "CLEAR", reason: "Backspace" });
|
|
524
|
+
} else {
|
|
525
|
+
dispatch({ type: "CLEAR_CHAR", index, reason: action.key });
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
default:
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (target.validity.valid) {
|
|
534
|
+
if (value === "") {
|
|
535
|
+
let reason = "Backspace";
|
|
536
|
+
const inputType = event.inputType;
|
|
537
|
+
if (inputType === "deleteContentBackward") {
|
|
538
|
+
reason = "Backspace";
|
|
539
|
+
} else if (inputType === "deleteByCut") {
|
|
540
|
+
reason = "Cut";
|
|
541
|
+
}
|
|
542
|
+
dispatch({ type: "CLEAR_CHAR", index, reason });
|
|
543
|
+
} else {
|
|
544
|
+
dispatch({ type: "SET_CHAR", char: value, index, event });
|
|
545
|
+
}
|
|
546
|
+
} else {
|
|
547
|
+
onInvalidChange?.(target.value);
|
|
548
|
+
requestAnimationFrame(() => {
|
|
549
|
+
if (target.ownerDocument.activeElement === target) {
|
|
550
|
+
target.select();
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
return (0, import_octane.createElement)(RovingFocusGroup.Item, {
|
|
556
|
+
...rovingFocusGroupScope,
|
|
557
|
+
asChild: true,
|
|
558
|
+
focusable: !context.disabled && isFocusable,
|
|
559
|
+
active: index === lastSelectableIndex,
|
|
560
|
+
children: ({
|
|
561
|
+
hasTabStop,
|
|
562
|
+
isCurrentTabStop
|
|
563
|
+
}) => {
|
|
564
|
+
const supportsAutoComplete = hasTabStop ? isCurrentTabStop : index === 0;
|
|
565
|
+
return (0, import_octane.createElement)(import_Primitive.Primitive.input, {
|
|
566
|
+
ref: composedInputRef,
|
|
567
|
+
type: context.type,
|
|
568
|
+
disabled,
|
|
569
|
+
"aria-label": `Character ${index + 1} of ${collection.size}`,
|
|
570
|
+
autoComplete: supportsAutoComplete ? context.autoComplete : "off",
|
|
571
|
+
"data-1p-ignore": supportsAutoComplete ? void 0 : "true",
|
|
572
|
+
"data-lpignore": supportsAutoComplete ? void 0 : "true",
|
|
573
|
+
"data-protonpass-ignore": supportsAutoComplete ? void 0 : "true",
|
|
574
|
+
"data-bwignore": supportsAutoComplete ? void 0 : "true",
|
|
575
|
+
inputMode: validation?.inputMode,
|
|
576
|
+
maxLength: supportsAutoComplete ? collection.size : 1,
|
|
577
|
+
pattern: validation?.pattern,
|
|
578
|
+
readOnly: context.readOnly,
|
|
579
|
+
value: char,
|
|
580
|
+
placeholder,
|
|
581
|
+
[OTP_INPUT_ATTR]: "",
|
|
582
|
+
"data-radix-index": index,
|
|
583
|
+
...domProps,
|
|
584
|
+
onFocus: (0, import_compose_event_handlers.composeEventHandlers)(props?.onFocus, (event) => {
|
|
585
|
+
event.currentTarget.select();
|
|
586
|
+
}),
|
|
587
|
+
onCut: (0, import_compose_event_handlers.composeEventHandlers)(props?.onCut, (event) => {
|
|
588
|
+
const currentValue = event.currentTarget.value;
|
|
589
|
+
if (currentValue !== "") {
|
|
590
|
+
userActionRef.current = { type: "cut" };
|
|
591
|
+
keyboardActionTimeoutRef.current = window.setTimeout(() => {
|
|
592
|
+
userActionRef.current = null;
|
|
593
|
+
}, 10);
|
|
594
|
+
}
|
|
595
|
+
}),
|
|
596
|
+
// octane adaptation: ONE native `input` binding runs the source's onInput
|
|
597
|
+
// logic then its onChange logic (React's order for a native input event).
|
|
598
|
+
// React's controlled-input restoration is the runtime's: after the event
|
|
599
|
+
// flush, the controlled `value` snaps the DOM back to the rendered char.
|
|
600
|
+
onInput: (event) => {
|
|
601
|
+
(0, import_compose_event_handlers.composeEventHandlers)(onInputProp, handleInput)(event);
|
|
602
|
+
(0, import_compose_event_handlers.composeEventHandlers)(onChangeProp, handleChange)(event);
|
|
603
|
+
},
|
|
604
|
+
onKeyDown: (0, import_compose_event_handlers.composeEventHandlers)(props?.onKeyDown, (event) => {
|
|
605
|
+
switch (event.key) {
|
|
606
|
+
case "Clear":
|
|
607
|
+
case "Delete":
|
|
608
|
+
case "Backspace": {
|
|
609
|
+
const currentTarget = event.currentTarget;
|
|
610
|
+
const currentValue = currentTarget.value;
|
|
611
|
+
if (currentValue === "") {
|
|
612
|
+
if (event.key === "Delete") return;
|
|
613
|
+
const isClearing = event.key === "Clear" || event.metaKey || event.ctrlKey;
|
|
614
|
+
if (isClearing) {
|
|
615
|
+
dispatch({ type: "CLEAR", reason: "Backspace" });
|
|
616
|
+
} else {
|
|
617
|
+
const element2 = currentTarget;
|
|
618
|
+
requestAnimationFrame(() => {
|
|
619
|
+
focusInput(collection.from(element2, -1)?.element);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
} else {
|
|
623
|
+
userActionRef.current = {
|
|
624
|
+
type: "keydown",
|
|
625
|
+
key: event.key,
|
|
626
|
+
metaKey: event.metaKey,
|
|
627
|
+
ctrlKey: event.ctrlKey
|
|
628
|
+
};
|
|
629
|
+
keyboardActionTimeoutRef.current = window.setTimeout(() => {
|
|
630
|
+
userActionRef.current = null;
|
|
631
|
+
}, 10);
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
case "Enter": {
|
|
636
|
+
event.preventDefault();
|
|
637
|
+
context.attemptSubmit();
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
case "ArrowDown":
|
|
641
|
+
case "ArrowUp": {
|
|
642
|
+
if (context.orientation === "horizontal") {
|
|
643
|
+
event.preventDefault();
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
// TODO (source): Handle left/right arrow keys in vertical writing mode
|
|
648
|
+
default: {
|
|
649
|
+
const currentTarget = event.currentTarget;
|
|
650
|
+
if (currentTarget.value === event.key) {
|
|
651
|
+
event.preventDefault();
|
|
652
|
+
focusInput(collection.from(currentTarget, 1)?.element);
|
|
653
|
+
return;
|
|
654
|
+
} else if (
|
|
655
|
+
// input already has a value, but...
|
|
656
|
+
currentTarget.value && // the value is not selected
|
|
657
|
+
!(currentTarget.selectionStart === 0 && currentTarget.selectionEnd != null && currentTarget.selectionEnd > 0)
|
|
658
|
+
) {
|
|
659
|
+
const attemptedValue = event.key;
|
|
660
|
+
if (event.key.length > 1 || event.key === " ") {
|
|
661
|
+
return;
|
|
662
|
+
} else {
|
|
663
|
+
const nextInput = collection.from(currentTarget, 1)?.element;
|
|
664
|
+
const lastInput = collection.at(-1)?.element;
|
|
665
|
+
if (nextInput !== lastInput && currentTarget !== lastInput) {
|
|
666
|
+
if (currentTarget.selectionStart === 0) {
|
|
667
|
+
dispatch({ type: "SET_CHAR", char: attemptedValue, index, event });
|
|
668
|
+
} else {
|
|
669
|
+
dispatch({
|
|
670
|
+
type: "SET_CHAR",
|
|
671
|
+
char: attemptedValue,
|
|
672
|
+
index: index + 1,
|
|
673
|
+
event
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
userActionRef.current = {
|
|
677
|
+
type: "keydown",
|
|
678
|
+
key: "Char",
|
|
679
|
+
metaKey: event.metaKey,
|
|
680
|
+
ctrlKey: event.ctrlKey
|
|
681
|
+
};
|
|
682
|
+
keyboardActionTimeoutRef.current = window.setTimeout(() => {
|
|
683
|
+
userActionRef.current = null;
|
|
684
|
+
}, 10);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}),
|
|
691
|
+
onPointerDown: (0, import_compose_event_handlers.composeEventHandlers)(props?.onPointerDown, (event) => {
|
|
692
|
+
event.preventDefault();
|
|
693
|
+
const indexToFocus = Math.min(index, lastSelectableIndex);
|
|
694
|
+
const element2 = collection.at(indexToFocus)?.element;
|
|
695
|
+
focusInput(element2);
|
|
696
|
+
})
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
function isFormElement(element) {
|
|
702
|
+
return element?.tagName === "FORM";
|
|
703
|
+
}
|
|
704
|
+
function removeWhitespace(value) {
|
|
705
|
+
return value.replace(/\s/g, "");
|
|
706
|
+
}
|
|
707
|
+
function focusInput(element) {
|
|
708
|
+
if (!element) return;
|
|
709
|
+
if (element.ownerDocument.activeElement === element) {
|
|
710
|
+
window.requestAnimationFrame(() => {
|
|
711
|
+
element.select?.();
|
|
712
|
+
});
|
|
713
|
+
} else {
|
|
714
|
+
element.focus();
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function clamp(value, [min, max]) {
|
|
718
|
+
return Math.min(max, Math.max(min, value));
|
|
719
|
+
}
|
|
720
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
721
|
+
0 && (module.exports = {
|
|
722
|
+
HiddenInput,
|
|
723
|
+
Input,
|
|
724
|
+
OneTimePasswordField,
|
|
725
|
+
OneTimePasswordFieldHiddenInput,
|
|
726
|
+
OneTimePasswordFieldInput,
|
|
727
|
+
Root,
|
|
728
|
+
createOneTimePasswordFieldScope
|
|
729
|
+
});
|