@giro-ds/react 12.1.1 → 12.2.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/dist/index.d.ts CHANGED
@@ -1109,6 +1109,8 @@ interface VirtualKeyboardProps extends Omit<BaseProps, 'disabled'> {
1109
1109
  onChange?: (value: string) => void;
1110
1110
  /** Callback executado quando uma tecla é pressionada: (key) => void */
1111
1111
  onKeyPress?: (key: string) => void;
1112
+ /** Callback executado quando a tecla Enter é pressionada */
1113
+ onEnterPress?: () => void;
1112
1114
  /**
1113
1115
  * Callback executado sempre que o `type` do teclado muda.
1114
1116
  * Útil para que o consumidor saiba o tipo atual e possa reagir à mudança
@@ -1137,6 +1139,13 @@ interface VirtualKeyboardProps extends Omit<BaseProps, 'disabled'> {
1137
1139
  * @default true
1138
1140
  */
1139
1141
  showTypeSwitchKey?: boolean;
1142
+ /**
1143
+ * Quando `true`, exibe o numpad numérico no formato de 4 colunas (estilo iOS nativo),
1144
+ * com as teclas `-`, `↵` `_`, `⌫` e `→|` (enter/submit).
1145
+ * Só tem efeito quando `type="numeric"` e `variant="native"`.
1146
+ * @default false
1147
+ */
1148
+ numpadWithEnter?: boolean;
1140
1149
  /** Texto de ajuda do TextField exibido no modo `fixed` */
1141
1150
  helperText?: string;
1142
1151
  /** Sinaliza erro no TextField exibido no modo `fixed` */
package/dist/index.esm.js CHANGED
@@ -3,7 +3,7 @@ import clsx from 'clsx';
3
3
  import { Avatar as Avatar$1, Tooltip as Tooltip$1, Checkbox as Checkbox$1, Popover as Popover$1, Label, AlertDialog, DropdownMenu, Dialog as Dialog$1, RadioGroup, Select as Select$1, Toggle as Toggle$1, ToggleGroup as ToggleGroup$1, Toast as Toast$1, Switch as Switch$1, unstable_OneTimePasswordField } from 'radix-ui';
4
4
  import * as React from 'react';
5
5
  import React__default, { useId, useMemo, createContext, useState, useContext, useRef, useCallback, useEffect, useLayoutEffect, createElement, forwardRef, memo, useReducer } from 'react';
6
- import { SpinnerIos16Regular, ChevronUp16Regular, ChevronLeft16Regular, ChevronRight16Regular, ChevronDown16Regular, Dismiss24Regular, Emoji20Regular, ChevronDown20Regular, KeyboardShiftUppercase20Regular, KeyboardShift20Filled, KeyboardShift20Regular, Backspace20Regular, Info12Regular, Dismiss16Regular, Calendar16Regular, DocumentAdd24Regular, Warning24Regular, Document24Regular, FilterRegular, Search16Regular, ChevronDownRegular, ChevronRight16Filled, Subtract16Regular, Add16Regular, SearchInfo20Regular, ArrowSortUp16Regular, ArrowSortDown16Regular, ArrowSort16Regular, Info20Filled, CheckmarkCircle20Filled, Warning20Filled, Dismiss16Filled } from '@fluentui/react-icons';
6
+ import { SpinnerIos16Regular, ChevronUp16Regular, ChevronLeft16Regular, ChevronRight16Regular, ChevronDown16Regular, Dismiss24Regular, ArrowImport20Regular, Emoji20Regular, ChevronDown20Regular, KeyboardShiftUppercase20Regular, KeyboardShift20Filled, KeyboardShift20Regular, Backspace20Regular, Info12Regular, Dismiss16Regular, Calendar16Regular, DocumentAdd24Regular, Warning24Regular, Document24Regular, FilterRegular, Search16Regular, ChevronDownRegular, ChevronRight16Filled, Subtract16Regular, Add16Regular, SearchInfo20Regular, ArrowSortUp16Regular, ArrowSortDown16Regular, ArrowSort16Regular, Info20Filled, CheckmarkCircle20Filled, Warning20Filled, Dismiss16Filled } from '@fluentui/react-icons';
7
7
  import { DayPicker } from 'react-day-picker';
8
8
  import { format, addDays, addMonths, addWeeks, addYears, differenceInCalendarDays, differenceInCalendarMonths, eachMonthOfInterval, eachYearOfInterval, endOfISOWeek, endOfMonth, endOfWeek, endOfYear, getISOWeek, getMonth, getYear, getWeek, isAfter, isBefore, isDate, isSameDay, isSameMonth, isSameYear, max, min, setMonth, setYear, startOfDay, startOfISOWeek, startOfMonth, startOfWeek, startOfYear } from 'date-fns';
9
9
  import { createPortal } from 'react-dom';
@@ -2909,6 +2909,7 @@ const ICON_KEY_MAP = {
2909
2909
  capslock: KeyboardShiftUppercase20Regular,
2910
2910
  downkeyboard: ChevronDown20Regular,
2911
2911
  emoji: Emoji20Regular,
2912
+ enterNumpad: ArrowImport20Regular,
2912
2913
  };
2913
2914
  const SHARED_DISPLAY = {
2914
2915
  '{capslock}': iconSlot('capslock'),
@@ -2934,9 +2935,11 @@ const LAYOUT_DISPLAY = {
2934
2935
  const EMOTICON_KEY = '{emoticon}';
2935
2936
  const DOWN_KEYBOARD_KEY = '{downkeyboard}';
2936
2937
  const ENTER_KEY = '{enter}';
2937
- const removeKeyFromLayout = (layout, key) => Object.fromEntries(Object.entries(layout).map(([layoutName, rows]) => [
2938
+ const removeKeyFromLayout = (layout, key, skipLayouts = []) => Object.fromEntries(Object.entries(layout).map(([layoutName, rows]) => [
2938
2939
  layoutName,
2939
- rows.map((row) => row.split(key).join(' ').replace(/\s+/g, ' ').trim()),
2940
+ skipLayouts.includes(layoutName)
2941
+ ? rows
2942
+ : rows.map((row) => row.split(key).join(' ').replace(/\s+/g, ' ').trim()),
2940
2943
  ]));
2941
2944
  const replaceKeyWithBlank = (layout, key) => Object.fromEntries(Object.entries(layout).map(([layoutName, rows]) => [
2942
2945
  layoutName,
@@ -2949,7 +2952,8 @@ const QWERTY_LOWERCASE = [
2949
2952
  '{shift} z x c v b n m {bksp}',
2950
2953
  '{numbers} {alt} {space} . {enter}',
2951
2954
  ];
2952
- const NUMPAD = ['1 2 3', '4 5 6', '7 8 9', '{bksp} 0 {abc}'];
2955
+ const NUMPAD = ['1 2 3', '4 5 6', '7 8 9', '{abc} 0 {bksp}'];
2956
+ const NUMPAD_WITH_ENTER = ['1 2 3 -', '4 5 6 _', '7 8 9 {bksp}', '{abc} 0 . {enter}'];
2953
2957
  const SHARED_LAYOUTS = {
2954
2958
  shift: [
2955
2959
  '1 2 3 4 5 6 7 8 9 0',
@@ -2999,11 +3003,17 @@ const NATIVE_LAYOUTS = {
2999
3003
  const NATIVE_LAYOUT_KEYS = new Set([
3000
3004
  'default', 'numeric',
3001
3005
  ]);
3002
- const getNativeLayout = (type, Emoji = true, showDownKeyboardButton = true, isFixed = false, showEnterKey = true, showTypeSwitchKey = true) => {
3006
+ const getNativeLayout = (type, Emoji = true, showDownKeyboardButton = true, isFixed = false, showEnterKey = true, showTypeSwitchKey = true, numpadWithEnter = false) => {
3003
3007
  const layout = NATIVE_LAYOUTS[type] ?? NATIVE_LAYOUTS.default ?? null;
3004
3008
  if (!layout)
3005
3009
  return null;
3006
- let computedLayout = layout;
3010
+ let computedLayout = numpadWithEnter
3011
+ ? {
3012
+ ...layout,
3013
+ ...(type === 'numeric' ? { default: NUMPAD_WITH_ENTER } : {}),
3014
+ numbers: NUMPAD_WITH_ENTER,
3015
+ }
3016
+ : layout;
3007
3017
  if (!Emoji) {
3008
3018
  computedLayout = removeKeyFromLayout(computedLayout, EMOTICON_KEY);
3009
3019
  }
@@ -3011,7 +3021,9 @@ const getNativeLayout = (type, Emoji = true, showDownKeyboardButton = true, isFi
3011
3021
  computedLayout = removeKeyFromLayout(computedLayout, DOWN_KEYBOARD_KEY);
3012
3022
  }
3013
3023
  if (!showEnterKey) {
3014
- computedLayout = removeKeyFromLayout(computedLayout, ENTER_KEY);
3024
+ // Numpad layouts always preserve {enter}; only remove from QWERTY layouts
3025
+ const numpadLayouts = type === 'numeric' ? ['default', 'numbers'] : ['numbers'];
3026
+ computedLayout = removeKeyFromLayout(computedLayout, ENTER_KEY, numpadLayouts);
3015
3027
  }
3016
3028
  if (!showTypeSwitchKey) {
3017
3029
  computedLayout = removeKeyFromLayout(computedLayout, '{numbers}');
@@ -3082,13 +3094,16 @@ const ACCENT_OPTIONS = {
3082
3094
  * <VirtualKeyboard targetRef={inputRef} value={value} onChange={setValue} />
3083
3095
  * ```
3084
3096
  */
3085
- const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onChange, onKeyPress, onTypeChange, maxLength, Emoji = false, className, id, placeholder = 'Digite aqui...', helperText, error, errorMessage, targetRef, textFieldScale = 1, showEnterKey = true, showTypeSwitchKey = true, }) => {
3097
+ const VirtualKeyboard = ({ variant = 'native', type = 'default', value, onChange, onKeyPress, onEnterPress, onTypeChange, maxLength, Emoji = false, className, id, placeholder = 'Digite aqui...', helperText, error, errorMessage, targetRef, textFieldScale = 1, showEnterKey = true, showTypeSwitchKey = true, numpadWithEnter = false, }) => {
3098
+ const [internalValue, setInternalValue] = useState(value ?? '');
3086
3099
  const [layoutName, setLayoutName] = useState('default');
3087
3100
  const [capsLockOn, setCapsLockOn] = useState(false);
3088
3101
  const [accentMenu, setAccentMenu] = useState(null);
3089
3102
  const [accentMenuOffsetX, setAccentMenuOffsetX] = useState(0);
3090
- const [activeLayout, setActiveLayout] = useState(getNativeLayout(type, Emoji, variant === 'native', variant === 'fixed', showEnterKey, showTypeSwitchKey));
3103
+ const [activeLayout, setActiveLayout] = useState(getNativeLayout(type, Emoji, variant === 'native', variant === 'fixed', showEnterKey, showTypeSwitchKey, numpadWithEnter));
3091
3104
  const visualType = NATIVE_LAYOUT_KEYS.has(type) ? type : 'default';
3105
+ const effectiveValue = onChange !== undefined ? (value ?? '') : internalValue;
3106
+ const isNumpadLayout = (type === 'numeric' && layoutName === 'default') || layoutName === 'numbers';
3092
3107
  const [isOpen, setIsOpen] = useState(variant !== 'native');
3093
3108
  const hideTimeoutRef = useRef(null);
3094
3109
  const scrollTimeoutRef = useRef(null);
@@ -3101,15 +3116,15 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3101
3116
  const keyboardInstanceRef = useRef(null);
3102
3117
  const accentMenuRef = useRef(null);
3103
3118
  const accentButtonRef = useRef(null);
3104
- const valueRef = useRef(value);
3119
+ const valueRef = useRef(value ?? '');
3105
3120
  const [iconSlots, setIconSlots] = useState([]);
3106
3121
  const [keyPreview, setKeyPreview] = useState(null);
3107
3122
  const [keyPreviewOffsetX, setKeyPreviewOffsetX] = useState(0);
3108
3123
  const keyPreviewRef = useRef(null);
3109
3124
  const [containerWidth, setContainerWidth] = useState(Infinity);
3110
3125
  useEffect(() => {
3111
- valueRef.current = value;
3112
- }, [value]);
3126
+ valueRef.current = effectiveValue;
3127
+ }, [effectiveValue]);
3113
3128
  const clearLongPressTimeout = useCallback(() => {
3114
3129
  if (longPressTimeoutRef.current !== null) {
3115
3130
  clearTimeout(longPressTimeoutRef.current);
@@ -3124,8 +3139,8 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3124
3139
  keyboardInstanceRef.current?.setInput(nextValue);
3125
3140
  }, []);
3126
3141
  useEffect(() => {
3127
- syncKeyboardInput(value);
3128
- }, [value, syncKeyboardInput]);
3142
+ syncKeyboardInput(effectiveValue);
3143
+ }, [effectiveValue, syncKeyboardInput]);
3129
3144
  useEffect(() => {
3130
3145
  onTypeChange?.(type);
3131
3146
  }, [type, onTypeChange]);
@@ -3203,8 +3218,8 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3203
3218
  useEffect(() => {
3204
3219
  setLayoutName('default');
3205
3220
  setCapsLockOn(false);
3206
- setActiveLayout(getNativeLayout(type, Emoji, variant === 'native', variant === 'fixed', showEnterKey, showTypeSwitchKey));
3207
- }, [type, Emoji, variant, showEnterKey, showTypeSwitchKey]);
3221
+ setActiveLayout(getNativeLayout(type, Emoji, variant === 'native', variant === 'fixed', showEnterKey, showTypeSwitchKey, numpadWithEnter));
3222
+ }, [type, Emoji, variant, showEnterKey, showTypeSwitchKey, numpadWithEnter]);
3208
3223
  useEffect(() => {
3209
3224
  if (!Emoji && layoutName === 'emoticon') {
3210
3225
  setLayoutName(type === 'numeric' ? 'abc' : 'default');
@@ -3284,7 +3299,10 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3284
3299
  if (newSlots.length === 0)
3285
3300
  return;
3286
3301
  newSlots.forEach((s) => s.setAttribute('data-icon-root', 'true'));
3287
- setIconSlots((prev) => [...prev, ...newSlots]);
3302
+ setIconSlots((prev) => [
3303
+ ...prev.filter((slot) => container.contains(slot)),
3304
+ ...newSlots,
3305
+ ]);
3288
3306
  };
3289
3307
  registerNewSlots();
3290
3308
  const observer = new MutationObserver(() => {
@@ -3295,7 +3313,7 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3295
3313
  return () => {
3296
3314
  observer.disconnect();
3297
3315
  };
3298
- }, []);
3316
+ }, [variant]);
3299
3317
  useEffect(() => {
3300
3318
  closeAccentMenu();
3301
3319
  setAccentMenuOffsetX(0);
@@ -3377,6 +3395,8 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3377
3395
  const currentValue = valueRef.current;
3378
3396
  if (maxLength === undefined || currentValue.length < maxLength) {
3379
3397
  const nextValue = `${currentValue}${heldKey}`;
3398
+ if (onChange === undefined)
3399
+ setInternalValue(nextValue);
3380
3400
  onChange?.(nextValue);
3381
3401
  onKeyPress?.(heldKey);
3382
3402
  syncKeyboardInput(nextValue);
@@ -3410,6 +3430,8 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3410
3430
  return;
3411
3431
  }
3412
3432
  const nextValue = `${currentValue}${accentedChar}`;
3433
+ if (onChange === undefined)
3434
+ setInternalValue(nextValue);
3413
3435
  onChange?.(nextValue);
3414
3436
  onKeyPress?.(accentedChar);
3415
3437
  closeAccentMenu();
@@ -3500,12 +3522,16 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3500
3522
  if (isHeldAccentKey) {
3501
3523
  return;
3502
3524
  }
3525
+ if (button === '{enter}') {
3526
+ onEnterPress?.();
3527
+ }
3503
3528
  onKeyPress?.(button);
3504
3529
  }, [
3505
3530
  type,
3506
3531
  layoutName,
3507
3532
  capsLockOn,
3508
3533
  onKeyPress,
3534
+ onEnterPress,
3509
3535
  onTypeChange,
3510
3536
  Emoji,
3511
3537
  variant,
@@ -3532,8 +3558,10 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3532
3558
  }
3533
3559
  if (maxLength !== undefined && input.length > maxLength)
3534
3560
  return;
3561
+ if (onChange === undefined)
3562
+ setInternalValue(input);
3535
3563
  onChange?.(input);
3536
- }, [maxLength, onChange, syncKeyboardInput]);
3564
+ }, [maxLength, onChange, setInternalValue, syncKeyboardInput]);
3537
3565
  const portalSizes = useMemo(() => {
3538
3566
  if (containerWidth <= 360)
3539
3567
  return { preview: 42, option: 44, fontSize: 'var(--font-size-16)' };
@@ -3549,15 +3577,16 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3549
3577
  const baseDisplay = LAYOUT_DISPLAY[visualType];
3550
3578
  if (!baseDisplay)
3551
3579
  return baseDisplay;
3552
- if (variant !== 'fixed') {
3553
- return baseDisplay;
3580
+ const display = { ...baseDisplay };
3581
+ if (numpadWithEnter && isNumpadLayout) {
3582
+ display['{enter}'] = '<span data-icon-key="enterNumpad"></span>';
3554
3583
  }
3555
- return {
3556
- ...baseDisplay,
3557
- '{bksp}': (baseDisplay['{bksp}'] ?? '').replace(/\s*Apagar$/, ''),
3558
- };
3559
- }, [visualType, variant]);
3560
- const keyboardEl = activeLayout ? (jsxs("div", { ref: keyboardWrapperRef, className: clsx(styles$k.keyboardWrapper, ((type === 'numeric' && layoutName === 'default') || layoutName === 'numbers' || type === 'default') && styles$k.keyboardNumpadActive), onPointerDownCapture: handleLongPressStart, onPointerUpCapture: handleLongPressEnd, onPointerLeave: handleLongPressEnd, onPointerCancelCapture: handleLongPressEnd, children: [jsx(Keyboard, { keyboardRef: (instance) => {
3584
+ if (variant === 'fixed') {
3585
+ display['{bksp}'] = (display['{bksp}'] ?? '').replace(/\s*Apagar$/, '');
3586
+ }
3587
+ return display;
3588
+ }, [visualType, variant, numpadWithEnter, isNumpadLayout]);
3589
+ const keyboardEl = activeLayout ? (jsxs("div", { ref: keyboardWrapperRef, className: clsx(styles$k.keyboardWrapper, isNumpadLayout && styles$k.keyboardNumpadActive), onPointerDownCapture: handleLongPressStart, onPointerUpCapture: handleLongPressEnd, onPointerLeave: handleLongPressEnd, onPointerCancelCapture: handleLongPressEnd, children: [jsx(Keyboard, { keyboardRef: (instance) => {
3561
3590
  keyboardInstanceRef.current = instance;
3562
3591
  if (instance) {
3563
3592
  instance.setInput(valueRef.current);
@@ -3595,9 +3624,9 @@ const VirtualKeyboard = ({ variant = 'native', type = 'default', value = '', onC
3595
3624
  event.stopPropagation();
3596
3625
  }, onClick: () => handleAccentSelect(option), children: option }, `${accentMenu.sourceKey}-${option}`))) }, rowIndex))) }), document.body))] })) : null;
3597
3626
  if (variant === 'native' && typeof document !== 'undefined') {
3598
- return createPortal(jsx("div", { id: id, className: clsx(styles$k.overlay, { [styles$k.overlayOpen]: isOpen }, styles$k[`layout--${visualType}`], className), children: keyboardEl }), document.body);
3627
+ return createPortal(jsx("div", { id: id, className: clsx(styles$k.overlay, { [styles$k.overlayOpen]: isOpen }, styles$k[isNumpadLayout ? 'layout--numeric' : 'layout--default'], className), children: keyboardEl }), document.body);
3599
3628
  }
3600
- return (jsxs("div", { id: id, className: clsx(styles$k.container, styles$k[`mode--${variant}`], styles$k[`layout--${visualType}`], className), children: [variant === 'fixed' && (jsx("div", { className: styles$k.textFieldContainer, children: jsx(TextField$1, { placeholder: placeholder, value: value, onChange: onChange, helperText: helperText, error: error, errorMessage: errorMessage, readOnly: true, disableAutoComplete: true, maxLength: maxLength, attachedToVirtualKeyboard: true, className: styles$k.textFieldWrapper, scale: textFieldScale }) })), keyboardEl] }));
3629
+ return (jsxs("div", { id: id, className: clsx(styles$k.container, styles$k[`mode--${variant}`], styles$k[isNumpadLayout ? 'layout--numeric' : 'layout--default'], className), children: [variant === 'fixed' && (jsx("div", { className: styles$k.textFieldContainer, children: jsx(TextField$1, { placeholder: placeholder, value: effectiveValue, onChange: onChange, helperText: helperText, error: error, errorMessage: errorMessage, readOnly: true, disableAutoComplete: true, maxLength: maxLength, attachedToVirtualKeyboard: true, className: styles$k.textFieldWrapper, scale: textFieldScale }) })), keyboardEl] }));
3601
3630
  };
3602
3631
 
3603
3632
  var styles$j = {"container":"TextField-module__container___WJHFR","inputWrapper":"TextField-module__inputWrapper___INjfd","inputContainer":"TextField-module__inputContainer___3EsFJ","icon":"TextField-module__icon___p1vwi","clearButton":"TextField-module__clearButton___kgOot","inputWithIcon":"TextField-module__inputWithIcon___shqkL","helperText":"TextField-module__helperText___d5QGC","error":"TextField-module__error___IGQzp","disabled":"TextField-module__disabled___roIKP","attachedToVirtualKeyboard":"TextField-module__attachedToVirtualKeyboard___fuO2O","errorWithMessage":"TextField-module__errorWithMessage___m-5AA","clearButtonIcon":"TextField-module__clearButtonIcon___Z2iyc"};