@xsolla/xui-input-edit 0.176.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.
@@ -0,0 +1,930 @@
1
+ // src/InputEdit.tsx
2
+ import React3, {
3
+ useState,
4
+ useRef,
5
+ useEffect,
6
+ forwardRef as forwardRef2
7
+ } from "react";
8
+
9
+ // ../../foundation/primitives-native/src/Box.tsx
10
+ import {
11
+ View,
12
+ Pressable,
13
+ Image
14
+ } from "react-native";
15
+ import { jsx } from "react/jsx-runtime";
16
+ var Box = ({
17
+ children,
18
+ onPress,
19
+ onLayout,
20
+ onMoveShouldSetResponder,
21
+ onResponderGrant,
22
+ onResponderMove,
23
+ onResponderRelease,
24
+ onResponderTerminate,
25
+ backgroundColor,
26
+ borderColor,
27
+ borderWidth,
28
+ borderBottomWidth,
29
+ borderBottomColor,
30
+ borderTopWidth,
31
+ borderTopColor,
32
+ borderLeftWidth,
33
+ borderLeftColor,
34
+ borderRightWidth,
35
+ borderRightColor,
36
+ borderRadius,
37
+ borderStyle,
38
+ height,
39
+ padding,
40
+ paddingHorizontal,
41
+ paddingVertical,
42
+ margin,
43
+ marginTop,
44
+ marginBottom,
45
+ marginLeft,
46
+ marginRight,
47
+ flexDirection,
48
+ alignItems,
49
+ justifyContent,
50
+ position,
51
+ top,
52
+ bottom,
53
+ left,
54
+ right,
55
+ width,
56
+ minWidth,
57
+ minHeight,
58
+ maxWidth,
59
+ maxHeight,
60
+ flex,
61
+ overflow,
62
+ zIndex,
63
+ hoverStyle,
64
+ pressStyle,
65
+ style,
66
+ "data-testid": dataTestId,
67
+ testID,
68
+ as,
69
+ src,
70
+ alt,
71
+ ...rest
72
+ }) => {
73
+ const getContainerStyle = (pressed) => ({
74
+ backgroundColor: pressed && pressStyle?.backgroundColor ? pressStyle.backgroundColor : backgroundColor,
75
+ borderColor,
76
+ borderWidth,
77
+ borderBottomWidth,
78
+ borderBottomColor,
79
+ borderTopWidth,
80
+ borderTopColor,
81
+ borderLeftWidth,
82
+ borderLeftColor,
83
+ borderRightWidth,
84
+ borderRightColor,
85
+ borderRadius,
86
+ borderStyle,
87
+ overflow,
88
+ zIndex,
89
+ height,
90
+ width,
91
+ minWidth,
92
+ minHeight,
93
+ maxWidth,
94
+ maxHeight,
95
+ padding,
96
+ paddingHorizontal,
97
+ paddingVertical,
98
+ margin,
99
+ marginTop,
100
+ marginBottom,
101
+ marginLeft,
102
+ marginRight,
103
+ flexDirection,
104
+ alignItems,
105
+ justifyContent,
106
+ position,
107
+ top,
108
+ bottom,
109
+ left,
110
+ right,
111
+ flex,
112
+ ...style
113
+ });
114
+ const finalTestID = dataTestId || testID;
115
+ const {
116
+ role,
117
+ tabIndex,
118
+ onKeyDown,
119
+ onKeyUp,
120
+ "aria-label": _ariaLabel,
121
+ "aria-labelledby": _ariaLabelledBy,
122
+ "aria-current": _ariaCurrent,
123
+ "aria-disabled": _ariaDisabled,
124
+ "aria-live": _ariaLive,
125
+ className,
126
+ "data-testid": _dataTestId,
127
+ ...nativeRest
128
+ } = rest;
129
+ if (as === "img" && src) {
130
+ const imageStyle = {
131
+ width,
132
+ height,
133
+ borderRadius,
134
+ position,
135
+ top,
136
+ bottom,
137
+ left,
138
+ right,
139
+ ...style
140
+ };
141
+ return /* @__PURE__ */ jsx(
142
+ Image,
143
+ {
144
+ source: { uri: src },
145
+ style: imageStyle,
146
+ testID: finalTestID,
147
+ resizeMode: "cover",
148
+ ...nativeRest
149
+ }
150
+ );
151
+ }
152
+ if (onPress) {
153
+ return /* @__PURE__ */ jsx(
154
+ Pressable,
155
+ {
156
+ onPress,
157
+ onLayout,
158
+ onMoveShouldSetResponder,
159
+ onResponderGrant,
160
+ onResponderMove,
161
+ onResponderRelease,
162
+ onResponderTerminate,
163
+ style: ({ pressed }) => getContainerStyle(pressed),
164
+ testID: finalTestID,
165
+ ...nativeRest,
166
+ children
167
+ }
168
+ );
169
+ }
170
+ return /* @__PURE__ */ jsx(
171
+ View,
172
+ {
173
+ style: getContainerStyle(),
174
+ testID: finalTestID,
175
+ onLayout,
176
+ onMoveShouldSetResponder,
177
+ onResponderGrant,
178
+ onResponderMove,
179
+ onResponderRelease,
180
+ onResponderTerminate,
181
+ ...nativeRest,
182
+ children
183
+ }
184
+ );
185
+ };
186
+
187
+ // ../../foundation/primitives-native/src/Text.tsx
188
+ import {
189
+ Text as RNText,
190
+ StyleSheet
191
+ } from "react-native";
192
+ import { jsx as jsx2 } from "react/jsx-runtime";
193
+ var roleMap = {
194
+ alert: "alert",
195
+ heading: "header",
196
+ button: "button",
197
+ link: "link",
198
+ text: "text"
199
+ };
200
+ var parseNumericValue = (value) => {
201
+ if (value === void 0) return void 0;
202
+ if (typeof value === "number") return value;
203
+ const parsed = parseFloat(value);
204
+ return isNaN(parsed) ? void 0 : parsed;
205
+ };
206
+ var Text = ({
207
+ children,
208
+ color,
209
+ fontSize,
210
+ fontWeight,
211
+ fontFamily,
212
+ textAlign,
213
+ lineHeight,
214
+ numberOfLines,
215
+ id,
216
+ role,
217
+ testID,
218
+ "data-testid": dataTestId,
219
+ style: styleProp,
220
+ ...props
221
+ }) => {
222
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
223
+ if (resolvedFontFamily === "Pilat Wide" || resolvedFontFamily === "Pilat Wide Bold" || resolvedFontFamily === "Aktiv Grotesk") {
224
+ resolvedFontFamily = void 0;
225
+ }
226
+ const incomingStyle = StyleSheet.flatten(styleProp);
227
+ const baseStyle = {
228
+ color: color ?? incomingStyle?.color,
229
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
230
+ fontWeight,
231
+ fontFamily: resolvedFontFamily,
232
+ textDecorationLine: props.textDecoration,
233
+ textAlign: textAlign ?? incomingStyle?.textAlign,
234
+ lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),
235
+ marginTop: parseNumericValue(
236
+ incomingStyle?.marginTop
237
+ ),
238
+ marginBottom: parseNumericValue(
239
+ incomingStyle?.marginBottom
240
+ )
241
+ };
242
+ const accessibilityRole = role ? roleMap[role] : void 0;
243
+ return /* @__PURE__ */ jsx2(
244
+ RNText,
245
+ {
246
+ style: baseStyle,
247
+ numberOfLines,
248
+ testID: dataTestId || testID || id,
249
+ accessibilityRole,
250
+ children
251
+ }
252
+ );
253
+ };
254
+
255
+ // ../../foundation/primitives-native/src/Icon.tsx
256
+ import React from "react";
257
+ import { View as View2 } from "react-native";
258
+ import { jsx as jsx3 } from "react/jsx-runtime";
259
+ var Icon = ({
260
+ children,
261
+ color,
262
+ size,
263
+ testID,
264
+ "data-testid": dataTestId
265
+ }) => {
266
+ const style = {
267
+ width: typeof size === "number" ? size : void 0,
268
+ height: typeof size === "number" ? size : void 0,
269
+ alignItems: "center",
270
+ justifyContent: "center"
271
+ };
272
+ const childrenWithProps = React.Children.map(children, (child) => {
273
+ if (React.isValidElement(child)) {
274
+ return React.cloneElement(child, {
275
+ color: child.props.color || color,
276
+ // Also pass size if child seems to be an icon that needs it
277
+ size: child.props.size || size
278
+ });
279
+ }
280
+ return child;
281
+ });
282
+ return /* @__PURE__ */ jsx3(View2, { style, testID: dataTestId || testID, children: childrenWithProps });
283
+ };
284
+
285
+ // ../../foundation/primitives-native/src/TextArea.tsx
286
+ import { forwardRef } from "react";
287
+ import { TextInput as RNTextInput } from "react-native";
288
+ import { jsx as jsx4 } from "react/jsx-runtime";
289
+ var TextAreaPrimitive = forwardRef(
290
+ ({
291
+ value,
292
+ placeholder,
293
+ onChange,
294
+ onChangeText,
295
+ onFocus,
296
+ onBlur,
297
+ onKeyDown,
298
+ disabled,
299
+ style,
300
+ color,
301
+ fontSize,
302
+ fontFamily,
303
+ placeholderTextColor,
304
+ maxLength,
305
+ rows,
306
+ id,
307
+ "aria-describedby": ariaDescribedBy,
308
+ "aria-label": ariaLabel,
309
+ "aria-disabled": ariaDisabled,
310
+ "data-testid": dataTestId,
311
+ testID
312
+ }, ref) => {
313
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
314
+ if (resolvedFontFamily === "Pilat Wide" || resolvedFontFamily === "Pilat Wide Bold" || resolvedFontFamily === "Aktiv Grotesk") {
315
+ resolvedFontFamily = void 0;
316
+ }
317
+ const handleChangeText = (text) => {
318
+ onChangeText?.(text);
319
+ if (onChange) {
320
+ const syntheticEvent = {
321
+ target: { value: text },
322
+ currentTarget: { value: text },
323
+ type: "change",
324
+ nativeEvent: { text },
325
+ preventDefault: () => {
326
+ },
327
+ stopPropagation: () => {
328
+ },
329
+ isTrusted: false
330
+ };
331
+ onChange(syntheticEvent);
332
+ }
333
+ };
334
+ return /* @__PURE__ */ jsx4(
335
+ RNTextInput,
336
+ {
337
+ ref,
338
+ value,
339
+ placeholder,
340
+ onChangeText: handleChangeText,
341
+ onFocus,
342
+ onBlur,
343
+ onKeyPress: (e) => {
344
+ if (onKeyDown) {
345
+ onKeyDown({
346
+ key: e.nativeEvent.key,
347
+ preventDefault: () => {
348
+ }
349
+ });
350
+ }
351
+ },
352
+ editable: !disabled,
353
+ multiline: true,
354
+ numberOfLines: rows || 4,
355
+ style: [
356
+ {
357
+ color,
358
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
359
+ fontFamily: resolvedFontFamily,
360
+ flex: 1,
361
+ padding: 0,
362
+ textAlignVertical: "top",
363
+ textAlign: style?.textAlign || "left"
364
+ },
365
+ style
366
+ ],
367
+ placeholderTextColor,
368
+ maxLength,
369
+ testID: dataTestId || testID || id,
370
+ accessibilityLabel: ariaLabel,
371
+ accessibilityHint: ariaDescribedBy,
372
+ accessibilityState: {
373
+ disabled: disabled || ariaDisabled
374
+ },
375
+ accessible: true
376
+ }
377
+ );
378
+ }
379
+ );
380
+ TextAreaPrimitive.displayName = "TextAreaPrimitive";
381
+
382
+ // ../../foundation/primitives-native/src/index.tsx
383
+ var isWeb = false;
384
+
385
+ // src/InputEdit.tsx
386
+ import {
387
+ useResolvedTheme,
388
+ useId
389
+ } from "@xsolla/xui-core";
390
+ import { Edit, Check, Remove } from "@xsolla/xui-icons-base";
391
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
392
+ var FIELD = {
393
+ fontSize: 16,
394
+ lineHeight: 18,
395
+ framePadding: 11,
396
+ iconSize: 16
397
+ };
398
+ var stripHtml = (html) => html.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ");
399
+ var CONTROL_ICON_SIZE = 18;
400
+ var POPOVER_SHADOW = "0px 6px 10px 4px rgba(7, 7, 8, 0.1), 0px 2px 3px 0px rgba(7, 7, 8, 0.2)";
401
+ var POPOVER_DROP_SHADOW = "drop-shadow(0 6px 10px rgba(7, 7, 8, 0.1)) drop-shadow(0 2px 3px rgba(7, 7, 8, 0.2))";
402
+ var InputEdit = forwardRef2(
403
+ ({
404
+ value,
405
+ defaultValue,
406
+ placeholder = "Placeholder",
407
+ onChangeText,
408
+ onConfirm,
409
+ onCancel,
410
+ onKeyDown,
411
+ editControl = true,
412
+ icon = true,
413
+ editIcon,
414
+ tools,
415
+ rows,
416
+ mode = "inline",
417
+ textStyle,
418
+ richText = false,
419
+ disabled = false,
420
+ error = false,
421
+ errorMessage,
422
+ id: providedId,
423
+ "aria-label": ariaLabel,
424
+ "aria-labelledby": ariaLabelledBy,
425
+ testID,
426
+ className,
427
+ themeMode,
428
+ themeProductContext,
429
+ ...rest
430
+ }, ref) => {
431
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
432
+ const isControlled = value !== void 0;
433
+ const [internalValue, setInternalValue] = useState(
434
+ value ?? defaultValue ?? ""
435
+ );
436
+ const currentValue = isControlled ? value : internalValue;
437
+ const isFormMode = mode === "form";
438
+ const [isEditing, setIsEditing] = useState(false);
439
+ const editing = isFormMode || isEditing;
440
+ const [focused, setFocused] = useState(false);
441
+ const active = isFormMode ? focused : isEditing;
442
+ const richEnabled = richText && isWeb;
443
+ const inputRef = useRef(null);
444
+ const editableRef = useRef(null);
445
+ const focusField = () => {
446
+ if (richEnabled) editableRef.current?.focus();
447
+ else inputRef.current?.focus();
448
+ };
449
+ const committedValue = useRef(currentValue);
450
+ const [fieldHeight, setFieldHeight] = useState(null);
451
+ const rawId = useId();
452
+ const safeId = rawId.replace(/:/g, "");
453
+ const inputId = providedId || `input-edit-${safeId}`;
454
+ const errorId = `${inputId}-error`;
455
+ const triggerId = `${inputId}-trigger`;
456
+ const focusTrigger = () => {
457
+ if (isWeb) {
458
+ setTimeout(() => document.getElementById(triggerId)?.focus(), 0);
459
+ }
460
+ };
461
+ React3.useImperativeHandle(
462
+ ref,
463
+ () => richEnabled ? editableRef.current : inputRef.current,
464
+ [richEnabled]
465
+ );
466
+ useEffect(() => {
467
+ if (value !== void 0) {
468
+ setInternalValue(value);
469
+ committedValue.current = value;
470
+ }
471
+ }, [value]);
472
+ const autoSize = () => {
473
+ if (!isWeb) return;
474
+ if (richEnabled) {
475
+ requestAnimationFrame(() => {
476
+ const el2 = editableRef.current;
477
+ if (el2) setFieldHeight(el2.offsetHeight + FIELD.framePadding * 2);
478
+ });
479
+ return;
480
+ }
481
+ const el = inputRef.current;
482
+ if (!el) return;
483
+ el.style.height = "auto";
484
+ el.style.height = `${el.scrollHeight}px`;
485
+ setFieldHeight(el.scrollHeight + FIELD.framePadding * 2);
486
+ };
487
+ useEffect(() => {
488
+ if (!editing) return;
489
+ if (richEnabled) {
490
+ const el = editableRef.current;
491
+ if (el && el.innerHTML !== currentValue) el.innerHTML = currentValue;
492
+ }
493
+ autoSize();
494
+ }, [editing, currentValue, richEnabled]);
495
+ const isError = !!(error || errorMessage);
496
+ const isFilled = richText ? stripHtml(currentValue).trim().length > 0 : currentValue.length > 0;
497
+ const plainText = richText ? stripHtml(currentValue) : currentValue;
498
+ const inputColors = theme.colors.control.input;
499
+ const borderRadius = theme.shape.input.md.borderRadius;
500
+ const typo = theme.typographyTokens;
501
+ const textStyleToken = textStyle ? typo.heading[textStyle] ?? typo.basic[textStyle] : null;
502
+ const fontSize = textStyleToken ? textStyleToken.fontSize : FIELD.fontSize;
503
+ const lineHeight = textStyleToken ? parseInt(String(textStyleToken.lineHeight), 10) : FIELD.lineHeight;
504
+ const fontWeight = textStyleToken ? textStyleToken.fontWeight : 400;
505
+ const fontFamily = textStyleToken && textStyleToken.lineHeightCategory === "display" ? theme.fonts.heading : theme.fonts.body;
506
+ const controlsTop = (fieldHeight ?? lineHeight + FIELD.framePadding * 2) - FIELD.framePadding + 4;
507
+ const enterEdit = () => {
508
+ if (disabled || isEditing) return;
509
+ setIsEditing(true);
510
+ setTimeout(focusField, 0);
511
+ };
512
+ const handleRichInput = (e) => {
513
+ handleChangeText(e.currentTarget.innerHTML);
514
+ };
515
+ const handlePaste = (e) => {
516
+ e.preventDefault();
517
+ const text = e.clipboardData.getData("text/plain");
518
+ document.execCommand("insertText", false, text);
519
+ };
520
+ const handleChangeText = (next) => {
521
+ onChangeText?.(next);
522
+ if (!isControlled) {
523
+ setInternalValue(next);
524
+ }
525
+ };
526
+ const commitExplicit = () => {
527
+ onConfirm?.(currentValue);
528
+ committedValue.current = currentValue;
529
+ };
530
+ const commitOnBlur = () => {
531
+ if (currentValue !== committedValue.current) {
532
+ onConfirm?.(currentValue);
533
+ committedValue.current = currentValue;
534
+ }
535
+ };
536
+ const exitEdit = () => {
537
+ if (!isFormMode) {
538
+ setIsEditing(false);
539
+ setFocused(false);
540
+ focusTrigger();
541
+ }
542
+ };
543
+ const handleConfirm = () => {
544
+ commitExplicit();
545
+ exitEdit();
546
+ };
547
+ const handleCancel = () => {
548
+ if (!isControlled) {
549
+ setInternalValue(committedValue.current);
550
+ }
551
+ onCancel?.();
552
+ exitEdit();
553
+ };
554
+ const handleKeyDown = (e) => {
555
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
556
+ e.preventDefault();
557
+ handleConfirm();
558
+ return;
559
+ }
560
+ if (e.key === "Escape") {
561
+ e.preventDefault();
562
+ handleCancel();
563
+ return;
564
+ }
565
+ onKeyDown?.(e);
566
+ };
567
+ const handleFocus = () => setFocused(true);
568
+ const handleBlur = () => {
569
+ setFocused(false);
570
+ commitOnBlur();
571
+ if (!isFormMode) {
572
+ setIsEditing(false);
573
+ }
574
+ };
575
+ let fieldBg = "transparent";
576
+ let fieldBorder = "transparent";
577
+ if (disabled) {
578
+ fieldBg = inputColors.bgDisable;
579
+ fieldBorder = inputColors.borderDisable;
580
+ } else if (isError) {
581
+ fieldBg = inputColors.bg;
582
+ fieldBorder = theme.colors.border.alert;
583
+ } else if (active) {
584
+ fieldBg = theme.colors.control.focus.bg;
585
+ fieldBorder = theme.colors.border.brand;
586
+ } else if (isFormMode) {
587
+ fieldBg = inputColors.bg;
588
+ fieldBorder = inputColors.border;
589
+ }
590
+ const textColor = disabled ? inputColors.textDisable : inputColors.text;
591
+ const displayColor = isFilled ? textColor : inputColors.placeholder;
592
+ const preventBlur = isWeb ? { onMouseDown: (e) => e.preventDefault() } : {};
593
+ const controlBg = theme.colors.control.mono.primary.bg;
594
+ const controlBgHover = theme.colors.control.mono.primary.bgHover;
595
+ const controlIconColor = theme.colors.control.mono.text.primary;
596
+ const isTrigger = !editing && !disabled;
597
+ const triggerProps = isTrigger ? {
598
+ as: "button",
599
+ type: "button",
600
+ id: triggerId,
601
+ onPress: enterEdit,
602
+ "aria-label": ariaLabel,
603
+ "aria-labelledby": ariaLabelledBy
604
+ } : {
605
+ "aria-disabled": disabled || void 0
606
+ };
607
+ return /* @__PURE__ */ jsxs(
608
+ Box,
609
+ {
610
+ position: "relative",
611
+ width: "100%",
612
+ testID,
613
+ className,
614
+ children: [
615
+ /* @__PURE__ */ jsx5(Box, { position: "relative", width: "100%", style: { minHeight: lineHeight }, children: /* @__PURE__ */ jsxs(
616
+ Box,
617
+ {
618
+ ...triggerProps,
619
+ position: "absolute",
620
+ flexDirection: "row",
621
+ alignItems: editing || isError || disabled ? "flex-start" : "center",
622
+ gap: 8,
623
+ paddingVertical: FIELD.framePadding,
624
+ paddingHorizontal: FIELD.framePadding,
625
+ backgroundColor: fieldBg,
626
+ borderColor: fieldBorder,
627
+ borderWidth: 1,
628
+ cursor: disabled ? "not-allowed" : editing ? "text" : "pointer",
629
+ hoverStyle: !disabled && !editing && !isError ? {
630
+ backgroundColor: inputColors.bgHover,
631
+ borderColor: inputColors.borderHover
632
+ } : void 0,
633
+ style: {
634
+ // Pin top/left/right to the anchor (inflated outward); leave the bottom
635
+ // free so the field grows downward with multi-line content.
636
+ top: -FIELD.framePadding,
637
+ left: -FIELD.framePadding,
638
+ right: -FIELD.framePadding,
639
+ minHeight: lineHeight + FIELD.framePadding * 2,
640
+ borderRadius,
641
+ boxSizing: "border-box",
642
+ textAlign: "left",
643
+ width: "auto"
644
+ },
645
+ children: [
646
+ editing && richEnabled ? (
647
+ // Rich-text editor (web): a contenteditable surface holding HTML.
648
+ /* @__PURE__ */ jsxs(Box, { flex: 1, position: "relative", children: [
649
+ !isFilled && /* @__PURE__ */ jsx5(
650
+ Text,
651
+ {
652
+ "aria-hidden": true,
653
+ color: inputColors.placeholder,
654
+ fontSize,
655
+ fontWeight: String(fontWeight),
656
+ style: {
657
+ position: "absolute",
658
+ top: 0,
659
+ left: 0,
660
+ fontFamily,
661
+ lineHeight: `${lineHeight}px`,
662
+ pointerEvents: "none"
663
+ },
664
+ children: placeholder
665
+ }
666
+ ),
667
+ /* @__PURE__ */ jsx5(
668
+ "div",
669
+ {
670
+ ref: editableRef,
671
+ id: inputId,
672
+ contentEditable: !disabled,
673
+ suppressContentEditableWarning: true,
674
+ role: "textbox",
675
+ "aria-multiline": "true",
676
+ "aria-label": ariaLabel,
677
+ "aria-labelledby": ariaLabelledBy,
678
+ "aria-invalid": isError || void 0,
679
+ "aria-describedby": errorMessage ? errorId : void 0,
680
+ "data-testid": "input-edit__field",
681
+ onInput: handleRichInput,
682
+ onFocus: handleFocus,
683
+ onBlur: handleBlur,
684
+ onKeyDown: handleKeyDown,
685
+ onPaste: handlePaste,
686
+ style: {
687
+ outline: "none",
688
+ minHeight: `${lineHeight}px`,
689
+ color: textColor,
690
+ fontFamily,
691
+ fontWeight,
692
+ fontSize: `${fontSize}px`,
693
+ lineHeight: `${lineHeight}px`,
694
+ whiteSpace: "pre-wrap",
695
+ wordBreak: "break-word"
696
+ }
697
+ }
698
+ )
699
+ ] })
700
+ ) : editing ? /* @__PURE__ */ jsx5(Box, { flex: 1, children: /* @__PURE__ */ jsx5(
701
+ TextAreaPrimitive,
702
+ {
703
+ ref: inputRef,
704
+ id: inputId,
705
+ value: plainText,
706
+ placeholder,
707
+ onChangeText: handleChangeText,
708
+ onFocus: handleFocus,
709
+ onBlur: handleBlur,
710
+ onKeyDown: handleKeyDown,
711
+ disabled,
712
+ rows: rows ?? 1,
713
+ color: textColor,
714
+ fontSize,
715
+ fontFamily,
716
+ placeholderTextColor: inputColors.placeholder,
717
+ "aria-invalid": isError || void 0,
718
+ "aria-describedby": errorMessage ? errorId : void 0,
719
+ "aria-label": ariaLabel,
720
+ "aria-labelledby": ariaLabelledBy,
721
+ "data-testid": "input-edit__field",
722
+ style: {
723
+ display: "block",
724
+ overflow: "hidden",
725
+ fontWeight,
726
+ lineHeight: `${lineHeight}px`
727
+ },
728
+ ...rest
729
+ }
730
+ ) }) : richText && isWeb && isFilled ? (
731
+ // Rich display (read-only): render the stored HTML.
732
+ /* @__PURE__ */ jsx5(
733
+ "div",
734
+ {
735
+ "data-testid": "input-edit__text",
736
+ dangerouslySetInnerHTML: { __html: currentValue },
737
+ style: {
738
+ flex: 1,
739
+ color: textColor,
740
+ fontFamily,
741
+ fontWeight,
742
+ fontSize: `${fontSize}px`,
743
+ lineHeight: `${lineHeight}px`,
744
+ whiteSpace: "pre-wrap",
745
+ wordBreak: "break-word"
746
+ }
747
+ }
748
+ )
749
+ ) : /* @__PURE__ */ jsx5(
750
+ Text,
751
+ {
752
+ color: displayColor,
753
+ fontSize,
754
+ fontWeight: String(fontWeight),
755
+ "data-testid": "input-edit__text",
756
+ style: {
757
+ flex: 1,
758
+ fontFamily,
759
+ lineHeight: `${lineHeight}px`,
760
+ wordBreak: "break-word",
761
+ // Preserve author line breaks in multi-line values.
762
+ whiteSpace: "pre-wrap"
763
+ },
764
+ children: isFilled ? plainText : placeholder
765
+ }
766
+ ),
767
+ icon && !editing && !isError && !disabled && /* @__PURE__ */ jsx5(Box, { alignItems: "center", justifyContent: "center", "aria-hidden": true, children: /* @__PURE__ */ jsx5(Icon, { size: FIELD.iconSize, color: textColor, children: editIcon ?? /* @__PURE__ */ jsx5(Edit, {}) }) })
768
+ ]
769
+ }
770
+ ) }),
771
+ active && editControl && /* @__PURE__ */ jsxs(
772
+ Box,
773
+ {
774
+ position: "absolute",
775
+ flexDirection: "row",
776
+ alignItems: "center",
777
+ backgroundColor: controlBg,
778
+ "data-testid": "input-edit__edit-control",
779
+ style: {
780
+ top: controlsTop,
781
+ right: -FIELD.framePadding,
782
+ borderRadius: 4,
783
+ overflow: "hidden",
784
+ ...isWeb ? {
785
+ boxShadow: POPOVER_SHADOW,
786
+ backdropFilter: "blur(12px)",
787
+ WebkitBackdropFilter: "blur(12px)"
788
+ } : {}
789
+ },
790
+ children: [
791
+ /* @__PURE__ */ jsx5(
792
+ Box,
793
+ {
794
+ as: "button",
795
+ type: "button",
796
+ alignItems: "center",
797
+ justifyContent: "center",
798
+ backgroundColor: controlBg,
799
+ borderWidth: 0,
800
+ padding: 4,
801
+ cursor: "pointer",
802
+ onPress: handleConfirm,
803
+ "aria-label": "Confirm",
804
+ "data-testid": "input-edit__confirm",
805
+ hoverStyle: { backgroundColor: controlBgHover },
806
+ ...preventBlur,
807
+ children: /* @__PURE__ */ jsx5(Icon, { size: CONTROL_ICON_SIZE, color: controlIconColor, children: /* @__PURE__ */ jsx5(Check, {}) })
808
+ }
809
+ ),
810
+ /* @__PURE__ */ jsx5(
811
+ Box,
812
+ {
813
+ backgroundColor: theme.colors.border.inverse,
814
+ style: { width: 1, alignSelf: "stretch" }
815
+ }
816
+ ),
817
+ /* @__PURE__ */ jsx5(
818
+ Box,
819
+ {
820
+ as: "button",
821
+ type: "button",
822
+ alignItems: "center",
823
+ justifyContent: "center",
824
+ backgroundColor: controlBg,
825
+ borderWidth: 0,
826
+ padding: 4,
827
+ cursor: "pointer",
828
+ onPress: handleCancel,
829
+ "aria-label": "Cancel",
830
+ "data-testid": "input-edit__cancel",
831
+ hoverStyle: { backgroundColor: controlBgHover },
832
+ ...preventBlur,
833
+ children: /* @__PURE__ */ jsx5(Icon, { size: CONTROL_ICON_SIZE, color: controlIconColor, children: /* @__PURE__ */ jsx5(Remove, {}) })
834
+ }
835
+ )
836
+ ]
837
+ }
838
+ ),
839
+ active && tools && /* @__PURE__ */ jsx5(
840
+ Box,
841
+ {
842
+ position: "absolute",
843
+ flexDirection: "row",
844
+ alignItems: "center",
845
+ gap: 4,
846
+ padding: 4,
847
+ backgroundColor: theme.colors.background.primary,
848
+ "data-testid": "input-edit__tools",
849
+ style: {
850
+ top: controlsTop,
851
+ left: -FIELD.framePadding,
852
+ borderRadius: 4,
853
+ ...isWeb ? {
854
+ boxShadow: POPOVER_SHADOW,
855
+ backdropFilter: "blur(12px)",
856
+ WebkitBackdropFilter: "blur(12px)"
857
+ } : {}
858
+ },
859
+ ...preventBlur,
860
+ children: tools
861
+ }
862
+ ),
863
+ isError && errorMessage && /* @__PURE__ */ jsxs(
864
+ Box,
865
+ {
866
+ position: "relative",
867
+ style: {
868
+ marginTop: FIELD.framePadding + 4,
869
+ width: "max-content",
870
+ maxWidth: "100%",
871
+ ...isWeb ? {
872
+ filter: POPOVER_DROP_SHADOW,
873
+ WebkitFilter: POPOVER_DROP_SHADOW
874
+ } : {}
875
+ },
876
+ children: [
877
+ /* @__PURE__ */ jsx5(
878
+ Box,
879
+ {
880
+ position: "absolute",
881
+ backgroundColor: theme.colors.background.primary,
882
+ style: {
883
+ width: 8,
884
+ height: 8,
885
+ top: -3,
886
+ left: 16,
887
+ transform: "rotate(45deg)"
888
+ }
889
+ }
890
+ ),
891
+ /* @__PURE__ */ jsx5(
892
+ Box,
893
+ {
894
+ backgroundColor: theme.colors.background.primary,
895
+ paddingVertical: 8,
896
+ paddingHorizontal: 12,
897
+ style: {
898
+ borderRadius: 4,
899
+ ...isWeb ? {
900
+ backdropFilter: "blur(12px)",
901
+ WebkitBackdropFilter: "blur(12px)"
902
+ } : {}
903
+ },
904
+ children: /* @__PURE__ */ jsx5(
905
+ Text,
906
+ {
907
+ id: errorId,
908
+ role: "status",
909
+ "aria-live": "polite",
910
+ color: theme.colors.content.alert.primary,
911
+ fontSize: FIELD.fontSize - 2,
912
+ style: { lineHeight: `${FIELD.lineHeight}px` },
913
+ children: errorMessage
914
+ }
915
+ )
916
+ }
917
+ )
918
+ ]
919
+ }
920
+ )
921
+ ]
922
+ }
923
+ );
924
+ }
925
+ );
926
+ InputEdit.displayName = "InputEdit";
927
+ export {
928
+ InputEdit
929
+ };
930
+ //# sourceMappingURL=index.mjs.map