@xsolla/xui-tag 0.222.0 → 0.223.1

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/web/index.mjs CHANGED
@@ -363,10 +363,141 @@ var Icon = ({
363
363
  return /* @__PURE__ */ jsx3(StyledIcon, { "data-testid": dataTestId || testID, ...props, children });
364
364
  };
365
365
 
366
+ // ../../foundation/primitives-web/src/Input.tsx
367
+ import { forwardRef } from "react";
368
+ import styled4 from "styled-components";
369
+ import { jsx as jsx4 } from "react/jsx-runtime";
370
+ var FilteredInput = createFilteredElement("input");
371
+ var StyledInput = styled4(FilteredInput)`
372
+ background: transparent;
373
+ border: none;
374
+ outline: none;
375
+ width: 100%;
376
+ height: 100%;
377
+ padding: 0;
378
+ margin: 0;
379
+ color: ${(props) => props.color || "inherit"};
380
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
381
+ font-family: ${(props) => props.fontFamily || '"Aktiv Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'};
382
+ text-align: inherit;
383
+
384
+ &::placeholder {
385
+ color: ${(props) => props.placeholderTextColor || "rgba(255, 255, 255, 0.5)"};
386
+ }
387
+
388
+ &:disabled {
389
+ cursor: not-allowed;
390
+ }
391
+
392
+ /*
393
+ * Hide the native number spin buttons. type="number" is rendered with the
394
+ * toolkit's own chevron stepper (see Input "Type number" in Figma), so the
395
+ * OS chrome must not show alongside it. Keyboard ArrowUp / ArrowDown are
396
+ * unaffected — this only removes the painted control.
397
+ */
398
+ &[type="number"] {
399
+ -moz-appearance: textfield;
400
+ appearance: textfield;
401
+ }
402
+
403
+ &[type="number"]::-webkit-outer-spin-button,
404
+ &[type="number"]::-webkit-inner-spin-button {
405
+ -webkit-appearance: none;
406
+ appearance: none;
407
+ margin: 0;
408
+ }
409
+
410
+ /* Override browser autofill background */
411
+ &:-webkit-autofill,
412
+ &:-webkit-autofill:hover,
413
+ &:-webkit-autofill:focus,
414
+ &:-webkit-autofill:active {
415
+ -webkit-box-shadow: 0 0 0 1000px transparent inset !important;
416
+ -webkit-background-clip: text !important;
417
+ -webkit-text-fill-color: ${(props) => props.color || "inherit"} !important;
418
+ }
419
+ `;
420
+ var InputPrimitive = forwardRef(
421
+ ({
422
+ value,
423
+ placeholder,
424
+ onChange,
425
+ onChangeText,
426
+ onFocus,
427
+ onBlur,
428
+ onKeyDown,
429
+ disabled,
430
+ secureTextEntry,
431
+ style,
432
+ color,
433
+ fontSize,
434
+ fontFamily,
435
+ placeholderTextColor,
436
+ maxLength,
437
+ name,
438
+ type,
439
+ inputMode,
440
+ autoComplete,
441
+ id,
442
+ "aria-invalid": ariaInvalid,
443
+ "aria-describedby": ariaDescribedBy,
444
+ "aria-labelledby": ariaLabelledBy,
445
+ "aria-label": ariaLabel,
446
+ "aria-disabled": ariaDisabled,
447
+ "data-testid": dataTestId,
448
+ testID,
449
+ ...rest
450
+ }, ref) => {
451
+ const handleChange = (e) => {
452
+ if (onChange) {
453
+ onChange(e);
454
+ }
455
+ if (onChangeText) {
456
+ onChangeText(e.target.value);
457
+ }
458
+ };
459
+ return /* @__PURE__ */ jsx4(
460
+ StyledInput,
461
+ {
462
+ ref,
463
+ id,
464
+ value,
465
+ name,
466
+ placeholder,
467
+ onChange: handleChange,
468
+ onFocus,
469
+ onBlur,
470
+ onKeyDown,
471
+ disabled,
472
+ type: secureTextEntry ? "password" : type || "text",
473
+ inputMode,
474
+ autoComplete,
475
+ style,
476
+ color,
477
+ fontSize,
478
+ fontFamily,
479
+ placeholderTextColor,
480
+ maxLength,
481
+ "aria-invalid": ariaInvalid,
482
+ "aria-describedby": ariaDescribedBy,
483
+ "aria-labelledby": ariaLabelledBy,
484
+ "aria-label": ariaLabel,
485
+ "aria-disabled": ariaDisabled,
486
+ "data-testid": dataTestId || testID,
487
+ ...rest
488
+ }
489
+ );
490
+ }
491
+ );
492
+ InputPrimitive.displayName = "InputPrimitive";
493
+
494
+ // ../../foundation/primitives-web/src/index.tsx
495
+ var isWeb = true;
496
+
366
497
  // src/Tag.tsx
367
498
  import { useResolvedTheme } from "@xsolla/xui-core";
368
499
  import { X } from "@xsolla/xui-icons";
369
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
500
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
370
501
  var Tag = ({
371
502
  size = "md",
372
503
  tone = "primary",
@@ -375,6 +506,7 @@ var Tag = ({
375
506
  iconLeft,
376
507
  iconRight,
377
508
  onRemove,
509
+ removeLabel,
378
510
  testID,
379
511
  themeMode,
380
512
  themeProductContext,
@@ -437,6 +569,7 @@ var Tag = ({
437
569
  };
438
570
  const { bg, text, border } = resolveColors();
439
571
  const isIconOnly = !children && (!!iconLeft || !!iconRight);
572
+ const resolvedRemoveLabel = removeLabel ?? (typeof children === "string" || typeof children === "number" ? `Remove ${children}` : "Remove");
440
573
  return /* @__PURE__ */ jsxs(
441
574
  Box,
442
575
  {
@@ -460,8 +593,8 @@ var Tag = ({
460
593
  ...style
461
594
  },
462
595
  children: [
463
- iconLeft && /* @__PURE__ */ jsx4(Icon, { size: sizeStyles.iconSize, color: text, children: iconLeft }),
464
- children && /* @__PURE__ */ jsx4(
596
+ iconLeft && /* @__PURE__ */ jsx5(Icon, { size: sizeStyles.iconSize, color: text, children: iconLeft }),
597
+ children && /* @__PURE__ */ jsx5(
465
598
  Text,
466
599
  {
467
600
  color: text,
@@ -476,13 +609,245 @@ var Tag = ({
476
609
  children
477
610
  }
478
611
  ),
479
- iconRight && /* @__PURE__ */ jsx4(Icon, { size: sizeStyles.iconSize, color: text, children: iconRight }),
480
- onRemove && /* @__PURE__ */ jsx4(Box, { onPress: onRemove, children: /* @__PURE__ */ jsx4(X, { size: sizeStyles.iconSize, color: text }) })
612
+ iconRight && /* @__PURE__ */ jsx5(Icon, { size: sizeStyles.iconSize, color: text, children: iconRight }),
613
+ onRemove && // A real <button>: focusable, activates on Enter / Space, and
614
+ // announced with its accessible name by screen readers.
615
+ /* @__PURE__ */ jsx5(
616
+ Box,
617
+ {
618
+ as: "button",
619
+ type: "button",
620
+ onPress: onRemove,
621
+ "aria-label": resolvedRemoveLabel,
622
+ alignItems: "center",
623
+ justifyContent: "center",
624
+ paddingHorizontal: 0,
625
+ paddingVertical: 0,
626
+ backgroundColor: "transparent",
627
+ borderWidth: 0,
628
+ cursor: "pointer",
629
+ "data-testid": "tag__remove-button",
630
+ children: /* @__PURE__ */ jsx5(X, { size: sizeStyles.iconSize, color: text })
631
+ }
632
+ )
481
633
  ]
482
634
  }
483
635
  );
484
636
  };
637
+
638
+ // src/TagInput.tsx
639
+ import {
640
+ useState,
641
+ useRef,
642
+ forwardRef as forwardRef2,
643
+ useImperativeHandle
644
+ } from "react";
645
+ import {
646
+ useResolvedTheme as useResolvedTheme2,
647
+ useId
648
+ } from "@xsolla/xui-core";
649
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
650
+ var chipSizeConfig = {
651
+ xl: "lg",
652
+ lg: "md",
653
+ md: "sm",
654
+ sm: "xs",
655
+ xs: "xs"
656
+ };
657
+ var TagInput = forwardRef2(
658
+ ({
659
+ value,
660
+ onChange,
661
+ placeholder,
662
+ size = "md",
663
+ disabled = false,
664
+ error,
665
+ errorMessage,
666
+ label,
667
+ "aria-label": ariaLabel,
668
+ id: providedId,
669
+ name,
670
+ onFocus,
671
+ onBlur,
672
+ testID,
673
+ themeMode,
674
+ themeProductContext
675
+ }, ref) => {
676
+ const { theme } = useResolvedTheme2({ themeMode, themeProductContext });
677
+ const [draft, setDraft] = useState("");
678
+ const [isFocus, setIsFocus] = useState(false);
679
+ const inputRef = useRef(null);
680
+ const rawId = useId();
681
+ const safeId = rawId.replace(/:/g, "");
682
+ const inputId = providedId || `tag-input-${safeId}`;
683
+ const labelId = `${inputId}-label`;
684
+ const errorId = `${inputId}-error`;
685
+ useImperativeHandle(ref, () => inputRef.current, []);
686
+ const isError = !!(errorMessage || error);
687
+ const sizeStyles = theme.sizing.input(size);
688
+ const inputColors = theme.colors.control.input;
689
+ const borderRadius = theme.shape.input[size].borderRadius;
690
+ const chipSize = chipSizeConfig[size];
691
+ const chipHeight = theme.sizing.tag(chipSize).height;
692
+ const paddingVertical = Math.max(
693
+ (sizeStyles.height - chipHeight) / 2 - 1,
694
+ 0
695
+ );
696
+ const commitDraft = () => {
697
+ if (draft.length === 0) return;
698
+ onChange([...value, draft]);
699
+ setDraft("");
700
+ };
701
+ const removeAt = (index2) => {
702
+ if (disabled) return;
703
+ onChange(value.filter((_, i) => i !== index2));
704
+ inputRef.current?.focus();
705
+ };
706
+ const handleKeyDown = (e) => {
707
+ const nativeEvent = e.nativeEvent;
708
+ const isComposing = nativeEvent?.isComposing === true || nativeEvent?.keyCode === 229;
709
+ if (e.key === "Enter") {
710
+ e.preventDefault();
711
+ if (!isComposing) commitDraft();
712
+ return;
713
+ }
714
+ if (e.key === "Backspace" && draft.length === 0 && value.length > 0) {
715
+ e.preventDefault();
716
+ onChange(value.slice(0, -1));
717
+ }
718
+ };
719
+ const handleChange = (e) => {
720
+ setDraft(e.target.value);
721
+ };
722
+ const handleFocus = () => {
723
+ if (!disabled) setIsFocus(true);
724
+ onFocus?.();
725
+ };
726
+ const handleBlur = () => {
727
+ setIsFocus(false);
728
+ commitDraft();
729
+ onBlur?.();
730
+ };
731
+ let backgroundColor = inputColors.bg;
732
+ let borderColor = inputColors.border;
733
+ let outlineColor;
734
+ if (disabled) {
735
+ backgroundColor = inputColors.bgDisable;
736
+ borderColor = inputColors.borderDisable;
737
+ } else if (isError) {
738
+ outlineColor = theme.colors.border.alert;
739
+ if (isFocus) backgroundColor = theme.colors.control.focus.bg;
740
+ } else if (isFocus) {
741
+ backgroundColor = theme.colors.control.focus.bg;
742
+ outlineColor = theme.colors.border.brand;
743
+ }
744
+ const textColor = disabled ? inputColors.textDisable : inputColors.text;
745
+ return /* @__PURE__ */ jsxs2(
746
+ Box,
747
+ {
748
+ flexDirection: "column",
749
+ gap: sizeStyles.fieldGap,
750
+ width: "100%",
751
+ testID,
752
+ children: [
753
+ label && /* @__PURE__ */ jsx6(Box, { as: "label", id: labelId, htmlFor: inputId, children: /* @__PURE__ */ jsx6(
754
+ Text,
755
+ {
756
+ color: theme.colors.content.secondary,
757
+ fontSize: sizeStyles.fontSize - 2,
758
+ fontWeight: "500",
759
+ children: label
760
+ }
761
+ ) }),
762
+ /* @__PURE__ */ jsxs2(
763
+ Box,
764
+ {
765
+ backgroundColor,
766
+ borderColor,
767
+ borderWidth: 1,
768
+ minHeight: sizeStyles.height,
769
+ paddingVertical,
770
+ paddingHorizontal: sizeStyles.paddingHorizontal,
771
+ flexDirection: "row",
772
+ flexWrap: "wrap",
773
+ alignItems: "center",
774
+ gap: 4,
775
+ cursor: disabled ? "not-allowed" : "text",
776
+ onPress: () => {
777
+ if (!disabled) inputRef.current?.focus();
778
+ },
779
+ "data-testid": "tag-input__container",
780
+ style: {
781
+ borderRadius,
782
+ ...outlineColor && isWeb ? { outline: `1px solid ${outlineColor}`, outlineOffset: "-1px" } : outlineColor && !isWeb ? { borderColor: outlineColor } : {}
783
+ },
784
+ hoverStyle: !disabled && !isFocus && !isError ? {
785
+ backgroundColor: inputColors.bgHover,
786
+ borderColor: inputColors.borderHover
787
+ } : void 0,
788
+ children: [
789
+ value.map((item, index2) => /* @__PURE__ */ jsx6(
790
+ Tag,
791
+ {
792
+ size: chipSize,
793
+ type: "outlined",
794
+ onRemove: disabled ? void 0 : () => removeAt(index2),
795
+ removeLabel: `Remove ${item}`,
796
+ testID: "tag-input__chip",
797
+ themeMode,
798
+ themeProductContext,
799
+ children: item
800
+ },
801
+ `${index2}-${item}`
802
+ )),
803
+ /* @__PURE__ */ jsx6(Box, { flex: 1, minWidth: 80, height: chipHeight, justifyContent: "center", children: /* @__PURE__ */ jsx6(
804
+ InputPrimitive,
805
+ {
806
+ ref: inputRef,
807
+ id: inputId,
808
+ name,
809
+ value: draft,
810
+ placeholder: value.length === 0 ? placeholder : void 0,
811
+ onChange: handleChange,
812
+ onKeyDown: handleKeyDown,
813
+ onFocus: handleFocus,
814
+ onBlur: handleBlur,
815
+ disabled,
816
+ type: "text",
817
+ color: textColor,
818
+ fontSize: sizeStyles.fontSize,
819
+ fontFamily: theme.fonts.body,
820
+ placeholderTextColor: inputColors.placeholder,
821
+ "aria-invalid": isError || void 0,
822
+ "aria-describedby": errorMessage ? errorId : void 0,
823
+ "aria-labelledby": label ? labelId : void 0,
824
+ "aria-label": !label ? ariaLabel : void 0,
825
+ "aria-disabled": disabled || void 0,
826
+ "data-testid": "tag-input__field"
827
+ }
828
+ ) })
829
+ ]
830
+ }
831
+ ),
832
+ errorMessage && /* @__PURE__ */ jsx6(
833
+ Text,
834
+ {
835
+ id: errorId,
836
+ role: "alert",
837
+ color: theme.colors.content.alert.primary,
838
+ fontSize: sizeStyles.fontSize - 2,
839
+ style: { lineHeight: sizeStyles.lineHeight + "px" },
840
+ children: errorMessage
841
+ }
842
+ )
843
+ ]
844
+ }
845
+ );
846
+ }
847
+ );
848
+ TagInput.displayName = "TagInput";
485
849
  export {
486
- Tag
850
+ Tag,
851
+ TagInput
487
852
  };
488
853
  //# sourceMappingURL=index.mjs.map