@scripso-homepad/ui 0.4.18 → 0.4.20

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/index.cjs CHANGED
@@ -460,7 +460,8 @@ var styles = reactNative.StyleSheet.create({
460
460
  justifyContent: "center"
461
461
  },
462
462
  withIcon: {
463
- justifyContent: "space-between"
463
+ justifyContent: "space-between",
464
+ gap: 24
464
465
  },
465
466
  disabled: {
466
467
  opacity: 0.6
@@ -474,8 +475,7 @@ var styles = reactNative.StyleSheet.create({
474
475
  },
475
476
  labelWithIcon: {
476
477
  flex: 1,
477
- flexShrink: 1,
478
- marginRight: 8
478
+ flexShrink: 1
479
479
  },
480
480
  iconContainer: {
481
481
  alignItems: "center",
@@ -532,12 +532,14 @@ function EyeOffIcon({
532
532
  }
533
533
  ) });
534
534
  }
535
- var INPUT_HEIGHT = 52;
535
+ var INPUT_HEIGHT = 56;
536
536
  var INPUT_ICON_SIZE = 20;
537
537
  var INPUT_ICON_GAP = 10;
538
538
  var INPUT_OUTLINE_WIDTH = 2;
539
- var INPUT_TEXT_LINE_HEIGHT = 19;
539
+ var INPUT_TEXT_LINE_HEIGHT = 20;
540
540
  var INPUT_WEB_VERTICAL_PADDING = 14;
541
+ var INPUT_VERTICAL_PADDING = 14;
542
+ var INPUT_HORIZONTAL_PADDING = 16;
541
543
  function resolveInputVisualState({
542
544
  focused = false,
543
545
  error = false,
@@ -556,8 +558,7 @@ function createOutlineStyle(ringColor) {
556
558
  padding: 0,
557
559
  backgroundColor: colors.transparent,
558
560
  width: "100%",
559
- alignSelf: "stretch",
560
- ...reactNative.Platform.OS !== "web" ? { overflow: "hidden" } : null
561
+ alignSelf: "stretch"
561
562
  };
562
563
  }
563
564
  var defaultOutline = {
@@ -567,13 +568,11 @@ var defaultOutline = {
567
568
  padding: 0,
568
569
  backgroundColor: colors.transparent,
569
570
  width: "100%",
570
- alignSelf: "stretch",
571
- ...reactNative.Platform.OS !== "web" ? { overflow: "hidden" } : null
571
+ alignSelf: "stretch"
572
572
  };
573
573
  function getInputFieldStyles(state) {
574
574
  const containerBase = {
575
- borderRadius: radii.lg,
576
- ...reactNative.Platform.OS !== "web" ? { overflow: "hidden" } : null
575
+ borderRadius: radii.lg
577
576
  };
578
577
  switch (state) {
579
578
  case "disabled":
@@ -636,12 +635,15 @@ var inputFieldMetrics = reactNative.StyleSheet.create({
636
635
  alignItems: "center",
637
636
  height: INPUT_HEIGHT,
638
637
  borderRadius: radii.lg,
639
- padding: 16,
638
+ paddingHorizontal: INPUT_HORIZONTAL_PADDING,
639
+ paddingVertical: INPUT_VERTICAL_PADDING,
640
640
  gap: INPUT_ICON_GAP,
641
+ // Do not clip glyph ascenders (Armenian / tall fonts).
642
+ overflow: "visible",
641
643
  ...reactNative.Platform.OS === "web" ? {
642
644
  boxSizing: "border-box",
643
645
  paddingVertical: INPUT_WEB_VERTICAL_PADDING,
644
- paddingHorizontal: 16
646
+ paddingHorizontal: INPUT_HORIZONTAL_PADDING
645
647
  } : null
646
648
  },
647
649
  input: {
@@ -656,8 +658,7 @@ var inputFieldMetrics = reactNative.StyleSheet.create({
656
658
  margin: 0,
657
659
  borderWidth: 0,
658
660
  backgroundColor: colors.transparent,
659
- ...reactNative.Platform.OS === "android" ? { includeFontPadding: false } : null,
660
- ...reactNative.Platform.OS === "ios" ? { lineHeight: INPUT_TEXT_LINE_HEIGHT, paddingVertical: 2 } : null,
661
+ // Leave lineHeight unset so platform font metrics keep placeholder tops visible.
661
662
  ...reactNative.Platform.OS === "web" ? {
662
663
  alignSelf: "center",
663
664
  outlineStyle: "none",
@@ -669,6 +670,18 @@ var inputFieldMetrics = reactNative.StyleSheet.create({
669
670
  /** Single-line: avoid Android lineHeight jump between placeholder and typed text. */
670
671
  inputSingleLine: {
671
672
  ...reactNative.Platform.OS === "android" ? { textAlignVertical: "center" } : null
673
+ },
674
+ /**
675
+ * Multiline fields should not flex-fill the container — on iOS a tall TextInput
676
+ * vertically centers the placeholder.
677
+ */
678
+ inputMultiline: {
679
+ flexGrow: 0,
680
+ flexShrink: 1,
681
+ flexBasis: "auto",
682
+ width: "100%",
683
+ alignSelf: "stretch",
684
+ textAlignVertical: "top"
672
685
  }
673
686
  });
674
687
  function Label({
@@ -717,6 +730,25 @@ function renderInputIcon(icon, color) {
717
730
  }
718
731
  return icon;
719
732
  }
733
+ var INTEGER_KEYBOARD_TYPES = /* @__PURE__ */ new Set([
734
+ "numeric",
735
+ "number-pad",
736
+ "phone-pad"
737
+ ]);
738
+ function sanitizeNumericInput(value, keyboardType) {
739
+ if (keyboardType === "decimal-pad") {
740
+ const normalized = value.replace(/,/g, ".").replace(/[^\d.]/g, "");
741
+ const separatorIndex = normalized.indexOf(".");
742
+ if (separatorIndex === -1) {
743
+ return normalized;
744
+ }
745
+ return normalized.slice(0, separatorIndex + 1) + normalized.slice(separatorIndex + 1).replace(/\./g, "");
746
+ }
747
+ if (keyboardType && INTEGER_KEYBOARD_TYPES.has(keyboardType)) {
748
+ return value.replace(/\D/g, "");
749
+ }
750
+ return value;
751
+ }
720
752
  function Input({
721
753
  label,
722
754
  leftIcon,
@@ -739,7 +771,10 @@ function Input({
739
771
  showPasswordToggle,
740
772
  onFocus,
741
773
  onBlur,
774
+ onChangeText,
775
+ keyboardType,
742
776
  multiline,
777
+ textAlign = "left",
743
778
  textAlignVertical,
744
779
  ...props
745
780
  }) {
@@ -773,6 +808,9 @@ function Input({
773
808
  setFocused(false);
774
809
  onBlur?.(event);
775
810
  }
811
+ function handleChangeText(value) {
812
+ onChangeText?.(sanitizeNumericInput(value, keyboardType));
813
+ }
776
814
  const helperMessage = error ?? hint;
777
815
  function togglePasswordVisibility() {
778
816
  if (!isDisabled) {
@@ -817,20 +855,22 @@ function Input({
817
855
  ref: inputRef,
818
856
  style: [
819
857
  inputFieldMetrics.input,
820
- !multiline ? inputFieldMetrics.inputSingleLine : null,
858
+ multiline ? inputFieldMetrics.inputMultiline : inputFieldMetrics.inputSingleLine,
821
859
  fieldStyles.text,
822
860
  style
823
861
  ],
824
862
  placeholderTextColor: fieldStyles.placeholder,
825
863
  editable,
864
+ keyboardType,
826
865
  multiline,
827
866
  secureTextEntry: effectiveSecureTextEntry,
828
- textAlign: "left",
867
+ textAlign,
829
868
  textAlignVertical: resolvedTextAlignVertical,
830
869
  onFocus: handleFocus,
831
870
  onBlur: handleBlur,
832
871
  accessibilityState: { disabled: isDisabled },
833
- ...props
872
+ ...props,
873
+ onChangeText: handleChangeText
834
874
  }
835
875
  ),
836
876
  trailingIcon ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.iconSlot, children: trailingIcon }) : null
@@ -1386,13 +1426,6 @@ function Select({
1386
1426
  inputRange: [0, 1],
1387
1427
  outputRange: ["0deg", "180deg"]
1388
1428
  });
1389
- const hasError = Boolean(error) || Boolean(invalid);
1390
- const visualState = resolveInputVisualState({
1391
- focused: open,
1392
- error: hasError,
1393
- disabled
1394
- });
1395
- const fieldStyles = getSelectFieldStyles(visualState);
1396
1429
  const selectedValues = react.useMemo(
1397
1430
  () => normalizeSelectedValues(multiple, value),
1398
1431
  [multiple, value]
@@ -1406,6 +1439,14 @@ function Select({
1406
1439
  () => options.filter((option) => !option.disabled),
1407
1440
  [options]
1408
1441
  );
1442
+ const isEffectivelyDisabled = disabled || selectableOptions.length === 0;
1443
+ const hasError = Boolean(error) || Boolean(invalid);
1444
+ const visualState = resolveInputVisualState({
1445
+ focused: open,
1446
+ error: hasError,
1447
+ disabled: isEffectivelyDisabled
1448
+ });
1449
+ const fieldStyles = getSelectFieldStyles(visualState);
1409
1450
  const selectableIndexByValue = react.useMemo(
1410
1451
  () => new Map(
1411
1452
  selectableOptions.map((option, index) => [option.value, index])
@@ -1433,7 +1474,7 @@ function Select({
1433
1474
  setHighlightedIndex(-1);
1434
1475
  }
1435
1476
  function openMenu() {
1436
- if (disabled || selectableOptions.length === 0) {
1477
+ if (isEffectivelyDisabled) {
1437
1478
  return;
1438
1479
  }
1439
1480
  triggerRef.current?.measureInWindow((x, y, width, height) => {
@@ -1452,7 +1493,6 @@ function Select({
1452
1493
  return;
1453
1494
  }
1454
1495
  if (selectedValues.includes(nextValue)) {
1455
- onValueChange?.("");
1456
1496
  closeMenu();
1457
1497
  return;
1458
1498
  }
@@ -1482,14 +1522,14 @@ function Select({
1482
1522
  });
1483
1523
  const dropdownTop = anchor ? resolveSelectDropdownTop(anchor, dropdownHeight) : 0;
1484
1524
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles4.wrapper, containerStyle], children: [
1485
- label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled, style: styles4.label, className: labelClassName, children: label }) : null,
1525
+ label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled: isEffectivelyDisabled, style: styles4.label, className: labelClassName, children: label }) : null,
1486
1526
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: fieldStyles.outline, children: /* @__PURE__ */ jsxRuntime.jsxs(
1487
1527
  reactNative.Pressable,
1488
1528
  {
1489
1529
  ref: triggerRef,
1490
1530
  accessibilityRole: "button",
1491
- accessibilityState: { disabled, expanded: open },
1492
- disabled,
1531
+ accessibilityState: { disabled: isEffectivelyDisabled, expanded: open },
1532
+ disabled: isEffectivelyDisabled,
1493
1533
  onPress: open ? closeMenu : openMenu,
1494
1534
  style: [selectFieldMetrics.container, fieldStyles.container, fieldStyle],
1495
1535
  children: [
@@ -1523,7 +1563,7 @@ function Select({
1523
1563
  ChevronDownIcon,
1524
1564
  {
1525
1565
  size: SELECT_CHEVRON_SIZE,
1526
- color: disabled ? colors.stormGray200 : SELECT_CHEVRON_COLOR
1566
+ color: isEffectivelyDisabled ? colors.stormGray200 : SELECT_CHEVRON_COLOR
1527
1567
  }
1528
1568
  )
1529
1569
  }
@@ -1532,35 +1572,46 @@ function Select({
1532
1572
  }
1533
1573
  ) }),
1534
1574
  helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles4.error : styles4.hint, children: helperMessage }) : null,
1535
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: closeMenu, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles4.overlay, onPress: closeMenu, children: anchor ? /* @__PURE__ */ jsxRuntime.jsx(
1536
- reactNative.View,
1537
- {
1538
- ref: menuRef,
1539
- style: [
1540
- selectDropdownMetrics.menu,
1541
- styles4.dropdown,
1542
- {
1543
- top: dropdownTop,
1544
- left: anchor.x,
1545
- width: anchor.width,
1546
- height: dropdownHeight
1547
- }
1548
- ],
1549
- children: needsScroll ? /* @__PURE__ */ jsxRuntime.jsx(
1550
- reactNative.ScrollView,
1551
- {
1552
- ref: scrollRef,
1553
- style: styles4.dropdownScroll,
1554
- contentContainerStyle: styles4.dropdownContent,
1555
- keyboardShouldPersistTaps: "handled",
1556
- showsVerticalScrollIndicator: true,
1557
- bounces: false,
1558
- nestedScrollEnabled: true,
1559
- children: optionList
1560
- }
1561
- ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.dropdownContent, children: optionList })
1562
- }
1563
- ) : null }) })
1575
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: closeMenu, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.overlay, children: [
1576
+ /* @__PURE__ */ jsxRuntime.jsx(
1577
+ reactNative.Pressable,
1578
+ {
1579
+ style: styles4.backdrop,
1580
+ onPress: closeMenu,
1581
+ accessibilityRole: "button",
1582
+ accessibilityLabel: "Close"
1583
+ }
1584
+ ),
1585
+ anchor ? /* @__PURE__ */ jsxRuntime.jsx(
1586
+ reactNative.View,
1587
+ {
1588
+ ref: menuRef,
1589
+ style: [
1590
+ selectDropdownMetrics.menu,
1591
+ styles4.dropdown,
1592
+ {
1593
+ top: dropdownTop,
1594
+ left: anchor.x,
1595
+ width: anchor.width,
1596
+ height: dropdownHeight
1597
+ }
1598
+ ],
1599
+ children: needsScroll ? /* @__PURE__ */ jsxRuntime.jsx(
1600
+ reactNative.ScrollView,
1601
+ {
1602
+ ref: scrollRef,
1603
+ style: styles4.dropdownScroll,
1604
+ contentContainerStyle: styles4.dropdownContent,
1605
+ keyboardShouldPersistTaps: "handled",
1606
+ showsVerticalScrollIndicator: true,
1607
+ bounces: false,
1608
+ nestedScrollEnabled: true,
1609
+ children: optionList
1610
+ }
1611
+ ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.dropdownContent, children: optionList })
1612
+ }
1613
+ ) : null
1614
+ ] }) })
1564
1615
  ] });
1565
1616
  }
1566
1617
  var styles4 = reactNative.StyleSheet.create({
@@ -1584,11 +1635,15 @@ var styles4 = reactNative.StyleSheet.create({
1584
1635
  justifyContent: "center"
1585
1636
  },
1586
1637
  overlay: {
1587
- flex: 1,
1638
+ flex: 1
1639
+ },
1640
+ backdrop: {
1641
+ ...reactNative.StyleSheet.absoluteFillObject,
1588
1642
  backgroundColor: colors.transparent
1589
1643
  },
1590
1644
  dropdown: {
1591
- position: "absolute"
1645
+ position: "absolute",
1646
+ zIndex: 1
1592
1647
  },
1593
1648
  dropdownScroll: {
1594
1649
  flex: 1
@@ -1949,6 +2004,9 @@ var styles5 = reactNative.StyleSheet.create({
1949
2004
  color: colors.slateBlue
1950
2005
  }
1951
2006
  });
2007
+ function sanitizePhoneDigits(value) {
2008
+ return value.replace(/\D/g, "");
2009
+ }
1952
2010
  function PhoneInput({
1953
2011
  label,
1954
2012
  countryCode,
@@ -1966,6 +2024,7 @@ function PhoneInput({
1966
2024
  editable = true,
1967
2025
  onFocus,
1968
2026
  onBlur,
2027
+ onChangeText,
1969
2028
  ...props
1970
2029
  }) {
1971
2030
  const wrapperRef = react.useRef(null);
@@ -1990,6 +2049,9 @@ function PhoneInput({
1990
2049
  setFocused(false);
1991
2050
  onBlur?.(event);
1992
2051
  }
2052
+ function handleChangeText(value) {
2053
+ onChangeText?.(sanitizePhoneDigits(value));
2054
+ }
1993
2055
  const helperMessage = error ?? hint;
1994
2056
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles6.wrapper, containerStyle], children: [
1995
2057
  label ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2034,7 +2096,8 @@ function PhoneInput({
2034
2096
  onFocus: handleFocus,
2035
2097
  onBlur: handleBlur,
2036
2098
  accessibilityState: { disabled: isDisabled },
2037
- ...props
2099
+ ...props,
2100
+ onChangeText: handleChangeText
2038
2101
  }
2039
2102
  )
2040
2103
  }