@clubmed/trident-ui 2.0.0-beta.64 → 2.0.0-beta.65

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.
Files changed (61) hide show
  1. package/chunks/Backdrop.js +1 -1
  2. package/chunks/Backdrop.js.map +1 -1
  3. package/chunks/DateField.js +2 -2
  4. package/chunks/DateField.js.map +1 -1
  5. package/package.json +1 -1
  6. package/styles/ui/controls.css +3 -6
  7. package/styles/ui/range.css +5 -0
  8. package/ui/AdvancedToast.js +2 -2
  9. package/ui/AdvancedToast.js.map +1 -1
  10. package/ui/Avatar.js +1 -1
  11. package/ui/Avatar.js.map +1 -1
  12. package/ui/BasicToast.js +2 -2
  13. package/ui/BasicToast.js.map +1 -1
  14. package/ui/Breadcrumb.js +1 -0
  15. package/ui/Breadcrumb.js.map +1 -1
  16. package/ui/ChatButton.js +1 -1
  17. package/ui/ChatButton.js.map +1 -1
  18. package/ui/ChatInput.js +1 -1
  19. package/ui/ChatInput.js.map +1 -1
  20. package/ui/ChatWindow.js +2 -2
  21. package/ui/ChatWindow.js.map +1 -1
  22. package/ui/ChatWindowCsatPrompt.js +1 -1
  23. package/ui/ChatWindowCsatPrompt.js.map +1 -1
  24. package/ui/ChoiceExpander.js +1 -1
  25. package/ui/ChoiceExpander.js.map +1 -1
  26. package/ui/ExpandableText.js +1 -1
  27. package/ui/ExpandableText.js.map +1 -1
  28. package/ui/HamburgerIcon.js +1 -1
  29. package/ui/HamburgerIcon.js.map +1 -1
  30. package/ui/Link.js +1 -1
  31. package/ui/Link.js.map +1 -1
  32. package/ui/PageNavigation.js +1 -1
  33. package/ui/PageNavigation.js.map +1 -1
  34. package/ui/Pagination.js +2 -2
  35. package/ui/Pagination.js.map +1 -1
  36. package/ui/SidebarLayout.js +4 -4
  37. package/ui/SidebarLayout.js.map +1 -1
  38. package/ui/Toggle.js +1 -1
  39. package/ui/Toggle.js.map +1 -1
  40. package/ui/buttons/Button.type.js +1 -1
  41. package/ui/buttons/Button.type.js.map +1 -1
  42. package/ui/forms/CheckboxSelect.js +1 -1
  43. package/ui/forms/CheckboxSelect.js.map +1 -1
  44. package/ui/forms/FloatingLabelTextField.js +2 -2
  45. package/ui/forms/FloatingLabelTextField.js.map +1 -1
  46. package/ui/forms/NumberField.js +1 -1
  47. package/ui/forms/NumberField.js.map +1 -1
  48. package/ui/forms/PhoneFieldFullInput.js +1 -1
  49. package/ui/forms/PhoneFieldFullInput.js.map +1 -1
  50. package/ui/forms/PhoneFieldSplitInput.js +2 -2
  51. package/ui/forms/PhoneFieldSplitInput.js.map +1 -1
  52. package/ui/forms/Select.js +1 -1
  53. package/ui/forms/Select.js.map +1 -1
  54. package/ui/forms/TextField.js +3 -3
  55. package/ui/forms/TextField.js.map +1 -1
  56. package/ui/forms/checkboxes/Checkbox.js +1 -1
  57. package/ui/forms/checkboxes/Checkbox.js.map +1 -1
  58. package/ui/forms/password/Password.js +2 -2
  59. package/ui/forms/password/Password.js.map +1 -1
  60. package/ui/journey-card/JourneyDetails.js +2 -2
  61. package/ui/journey-card/JourneyDetails.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingLabelTextField.js","names":[],"sources":["../../../lib/ui/forms/FloatingLabelTextField.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState, useRef } from 'react';\nimport clsx from 'clsx';\nimport { useValue, type UseValueProps } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControlError } from './FormControlError';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport { type FormControlProps } from './FormControl';\n\nexport interface FloatingLabelTextFieldProps<Value = string> extends FormControlProps<Value> {\n icon?: IconicNames;\n iconType?: IconicTypes;\n formatter?: UseValueProps<Value>['formatter'];\n hasDropdown?: boolean;\n clear?: string;\n iconFirst?: boolean;\n}\n\nexport const FloatingLabelTextField = <Value = string,>(\n props: FloatingLabelTextFieldProps<Value>,\n) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n labelClassName,\n value: initialValue,\n validationStatus = 'default',\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n description,\n icon,\n iconType,\n iconFirst = true,\n hasDropdown = false,\n clear = '',\n className,\n dataTestId,\n dataName,\n 'data-testid': dataTestIdProp,\n onChange,\n formatter,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<Value>({ initialValue, onChange, formatter });\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const [focused, setFocused] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isFloating = focused || (value !== '' && value !== null && value !== undefined);\n const showError = internalStatus === 'error' && errorMessage;\n const shouldDisplayClearButton =\n !disabled && value !== '' && value !== null && value !== undefined && !!clear;\n\n const nbIconsStart = Number(!!icon && iconFirst);\n const nbIconsEnd =\n Number(!!icon && !iconFirst) +\n Number(shouldDisplayClearButton || hasDropdown) +\n Number(internalStatus === 'error' || internalStatus === 'success');\n\n return (\n <div\n className={clsx('flex flex-col gap-4', className)}\n data-name={dataName ?? 'FloatingLabelTextField'}\n data-testid={dataTestIdProp ?? dataTestId}\n >\n <div className=\"relative\">\n <input\n {...rest}\n ref={inputRef}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n value={value as string}\n onChange={(e) => setValue(e.target.value as Value, e.nativeEvent)}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n style={\n {\n '--nbIconsStart': nbIconsStart,\n '--nbIconsEnd': nbIconsEnd,\n } as React.CSSProperties\n }\n className={clsx(\n 'text-b3 rounded-pill w-full border h-60 font-normal outline-none',\n 'ps-[calc(20px+var(--nbIconsStart)*32px)] pe-[calc(20px+var(--nbIconsEnd)*32px)]',\n {\n 'pt-[26px] pb-[8px]': isFloating && label,\n 'py-12': !(isFloating && label),\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={label ? undefined : name}\n aria-invalid={internalStatus === 'error'}\n aria-describedby={showError ? `${id}-error` : undefined}\n />\n\n {label && (\n <label\n htmlFor={id}\n style={\n {\n left: `calc(20px + ${nbIconsStart} * 32px)`,\n } as React.CSSProperties\n }\n className={clsx(\n 'pointer-events-none absolute right-0 transition-all duration-150 select-none pe-[calc(20px+var(--nbIconsEnd)*32px)]',\n {\n 'top-[10px] text-b6 font-semibold': isFloating,\n 'top-1/2 -translate-y-1/2 text-b3 font-normal': !isFloating,\n 'text-red': internalStatus === 'error' && isFloating,\n 'text-middleGrey': internalStatus !== 'error' && isFloating,\n 'text-black': !isFloating && internalStatus !== 'disabled',\n 'text-grey': internalStatus === 'disabled',\n },\n labelClassName,\n )}\n >\n {label}\n {required && !hideRequiredStar && (\n <span className=\"text-red ms-4\" aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && iconFirst && <Icon name={icon} width=\"24px\" />}\n\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n {(hasDropdown || shouldDisplayClearButton) &&\n (shouldDisplayClearButton ? (\n <button\n className=\"pointer-events-auto\"\n onClick={(e) => setValue('' as Value, e.nativeEvent)}\n aria-label={clear}\n type=\"reset\"\n >\n <Icon name=\"CrossDefault\" className=\"text-black\" width=\"24px\" />\n </button>\n ) : (\n <Icon name=\"ArrowDefaultDown\" className=\"text-black\" width=\"24px\" />\n ))}\n {!iconFirst && icon && <Icon name={icon} width=\"24px\" />}\n </span>\n </div>\n </div>\n\n {description && (\n <span className=\"text-b4 text-darkGrey font-normal px-20\">{description}</span>\n )}\n\n {showError && <FormControlError id={`${id}-error`}>{errorMessage}</FormControlError>}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"FloatingLabelTextField.js","names":[],"sources":["../../../lib/ui/forms/FloatingLabelTextField.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState, useRef } from 'react';\nimport clsx from 'clsx';\nimport { useValue, type UseValueProps } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControlError } from './FormControlError';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport { type FormControlProps } from './FormControl';\n\nexport interface FloatingLabelTextFieldProps<Value = string> extends FormControlProps<Value> {\n icon?: IconicNames;\n iconType?: IconicTypes;\n formatter?: UseValueProps<Value>['formatter'];\n hasDropdown?: boolean;\n clear?: string;\n iconFirst?: boolean;\n}\n\nexport const FloatingLabelTextField = <Value = string,>(\n props: FloatingLabelTextFieldProps<Value>,\n) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n labelClassName,\n value: initialValue,\n validationStatus = 'default',\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n description,\n icon,\n iconType,\n iconFirst = true,\n hasDropdown = false,\n clear = '',\n className,\n dataTestId,\n dataName,\n 'data-testid': dataTestIdProp,\n onChange,\n formatter,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<Value>({ initialValue, onChange, formatter });\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const [focused, setFocused] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isFloating = focused || (value !== '' && value !== null && value !== undefined);\n const showError = internalStatus === 'error' && errorMessage;\n const shouldDisplayClearButton =\n !disabled && value !== '' && value !== null && value !== undefined && !!clear;\n\n const nbIconsStart = Number(!!icon && iconFirst);\n const nbIconsEnd =\n Number(!!icon && !iconFirst) +\n Number(shouldDisplayClearButton || hasDropdown) +\n Number(internalStatus === 'error' || internalStatus === 'success');\n\n return (\n <div\n className={clsx('flex flex-col gap-4', className)}\n data-name={dataName ?? 'FloatingLabelTextField'}\n data-testid={dataTestIdProp ?? dataTestId}\n >\n <div className=\"relative\">\n <input\n {...rest}\n ref={inputRef}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n value={value as string}\n onChange={(e) => setValue(e.target.value as Value, e.nativeEvent)}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n style={\n {\n '--nbIconsStart': nbIconsStart,\n '--nbIconsEnd': nbIconsEnd,\n } as React.CSSProperties\n }\n className={clsx(\n 'text-b3 rounded-pill w-full border h-60 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n 'ps-[calc(20px+var(--nbIconsStart)*32px)] pe-[calc(20px+var(--nbIconsEnd)*32px)]',\n {\n 'pt-[26px] pb-[8px]': isFloating && label,\n 'py-12': !(isFloating && label),\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={label ? undefined : name}\n aria-invalid={internalStatus === 'error'}\n aria-describedby={showError ? `${id}-error` : undefined}\n />\n\n {label && (\n <label\n htmlFor={id}\n style={\n {\n left: `calc(20px + ${nbIconsStart} * 32px)`,\n } as React.CSSProperties\n }\n className={clsx(\n 'pointer-events-none absolute right-0 transition-all duration-150 select-none pe-[calc(20px+var(--nbIconsEnd)*32px)]',\n {\n 'top-[10px] text-b6 font-semibold': isFloating,\n 'top-1/2 -translate-y-1/2 text-b3 font-normal': !isFloating,\n 'text-red': internalStatus === 'error' && isFloating,\n 'text-middleGrey': internalStatus !== 'error' && isFloating,\n 'text-black': !isFloating && internalStatus !== 'disabled',\n 'text-grey': internalStatus === 'disabled',\n },\n labelClassName,\n )}\n >\n {label}\n {required && !hideRequiredStar && (\n <span className=\"text-red ms-4\" aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && iconFirst && <Icon name={icon} width=\"24px\" />}\n\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n {(hasDropdown || shouldDisplayClearButton) &&\n (shouldDisplayClearButton ? (\n <button\n className=\"pointer-events-auto outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n onClick={(e) => setValue('' as Value, e.nativeEvent)}\n aria-label={clear}\n type=\"reset\"\n >\n <Icon name=\"CrossDefault\" className=\"text-black\" width=\"24px\" />\n </button>\n ) : (\n <Icon name=\"ArrowDefaultDown\" className=\"text-black\" width=\"24px\" />\n ))}\n {!iconFirst && icon && <Icon name={icon} width=\"24px\" />}\n </span>\n </div>\n </div>\n\n {description && (\n <span className=\"text-b4 text-darkGrey font-normal px-20\">{description}</span>\n )}\n\n {showError && <FormControlError id={`${id}-error`}>{errorMessage}</FormControlError>}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -67,7 +67,7 @@ var l = (r) => {
67
67
  }),
68
68
  /* @__PURE__ */ a("input", {
69
69
  ...T,
70
- className: e("text-b2 text-center font-bold outline-none", { "bg-pearl text-grey": i }),
70
+ className: e("text-b2 text-center font-bold outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", { "bg-pearl text-grey": i }),
71
71
  style: s,
72
72
  type: "number",
73
73
  title: w.input,
@@ -1 +1 @@
1
- {"version":3,"file":"NumberField.js","names":[],"sources":["../../../lib/ui/forms/NumberField.tsx"],"sourcesContent":["import clsx from 'clsx';\n\nimport { type ChangeEvent, type MouseEvent, useId } from 'react';\n\nimport { useValue } from '../hooks/useValue';\nimport { Button } from '../buttons/Button';\nimport { FormControl, type FormControlProps } from './FormControl';\n\ninterface NumberFieldProps extends FormControlProps<number> {\n min?: number;\n max?: number;\n labels: {\n increase: string;\n decrease: string;\n input: string;\n };\n}\n\nconst INPUT_STYLE = { width: '28px', height: '28px' };\n\nexport function useNumberField(props: NumberFieldProps) {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n value: initialValue = 0,\n onChange,\n min = 0,\n max = 10,\n disabled = false,\n dataTestId = 'NumberField',\n inline = true,\n labels,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<number, string | number | boolean>({\n defaultValue: initialValue,\n formatter: Number,\n onChange,\n });\n\n const handleChange = (e: ChangeEvent<HTMLInputElement>) => {\n const newValue = Number(e.target.value);\n\n if (newValue >= min && newValue <= max) {\n setValue(Number(newValue), e.nativeEvent);\n }\n };\n\n const increase = (event?: Event) => {\n if (value < max) {\n setValue(value + 1, event);\n }\n };\n\n const decrease = (event?: Event) => {\n if (value > min) {\n setValue(value - 1, event);\n }\n };\n\n return {\n ...rest,\n id,\n min,\n max,\n name,\n value,\n dataTestId,\n disabled,\n inline,\n labels,\n handleChange,\n increase,\n decrease,\n };\n}\n\nexport const NumberField = (props: NumberFieldProps) => {\n const {\n disabled,\n min,\n max,\n label,\n description,\n id,\n dataTestId,\n value,\n handleChange,\n increase,\n decrease,\n errorMessage,\n validationStatus,\n hideRequiredStar,\n required,\n inline,\n labels,\n ...rest\n } = useNumberField(props);\n\n return (\n <FormControl\n id={id}\n label={label}\n description={description}\n className={clsx('flex items-center', props.className)}\n dataName=\"NumberField\"\n dataTestId={dataTestId}\n errorMessage={errorMessage}\n validationStatus={validationStatus}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n layout=\"vertical\"\n inline={inline}\n >\n <div className=\"flex items-center gap-x-8\">\n <Button\n onClick={(e: MouseEvent<HTMLButtonElement>) => decrease(e.nativeEvent)}\n variant=\"circle\"\n disabled={value <= min || disabled}\n icon=\"MinusDefault\"\n aria-label={labels.decrease}\n data-testid={`${dataTestId}-decrease`}\n />\n <input\n {...rest}\n className={clsx('text-b2 text-center font-bold outline-none', {\n 'bg-pearl text-grey': disabled,\n })}\n style={INPUT_STYLE}\n type=\"number\"\n title={labels.input}\n id={id}\n onChange={handleChange}\n disabled={disabled}\n required={required}\n value={value}\n min={min}\n max={max}\n />\n <Button\n onClick={(e: MouseEvent<HTMLButtonElement>) => increase(e.nativeEvent)}\n variant=\"circle\"\n aria-label={labels.increase}\n disabled={value >= max || disabled}\n icon=\"PlusDefault\"\n data-testid={`${dataTestId}-increase`}\n />\n </div>\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;AAkBA,IAAM,IAAc;CAAE,OAAO;CAAQ,QAAQ;CAAQ;AAErD,SAAgB,EAAe,GAAyB;CACtD,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,OAAO,IAAe,GACtB,aACA,SAAM,GACN,SAAM,IACN,cAAW,IACX,gBAAa,eACb,YAAS,IACT,WACA,GAAG,MACD,GAEE,EAAE,UAAO,gBAAa,EAA4C;EACtE,cAAc;EACd,WAAW;EACX;EACD,CAAC,EAEI,KAAgB,MAAqC;EACzD,IAAM,IAAW,OAAO,EAAE,OAAO,MAAM;AAEvC,EAAI,KAAY,KAAO,KAAY,KACjC,EAAS,OAAO,EAAS,EAAE,EAAE,YAAY;IAIvC,KAAY,MAAkB;AAClC,EAAI,IAAQ,KACV,EAAS,IAAQ,GAAG,EAAM;IAIxB,KAAY,MAAkB;AAClC,EAAI,IAAQ,KACV,EAAS,IAAQ,GAAG,EAAM;;AAI9B,QAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,IAAa,KAAe,MAA4B;CACtD,IAAM,EACJ,aACA,QACA,QACA,UACA,gBACA,OACA,eACA,UACA,iBACA,aACA,aACA,iBACA,qBACA,qBACA,aACA,WACA,WACA,GAAG,MACD,EAAe,EAAM;AAEzB,QACE,kBAAC,GAAD;EACM;EACG;EACM;EACb,WAAW,EAAK,qBAAqB,EAAM,UAAU;EACrD,UAAS;EACG;EACE;EACI;EACR;EACA;EACQ;EAClB,QAAO;EACC;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,UAAU,MAAqC,EAAS,EAAE,YAAY;KACtE,SAAQ;KACR,UAAU,KAAS,KAAO;KAC1B,MAAK;KACL,cAAY,EAAO;KACnB,eAAa,GAAG,EAAW;KAC3B,CAAA;IACF,kBAAC,SAAD;KACE,GAAI;KACJ,WAAW,EAAK,8CAA8C,EAC5D,sBAAsB,GACvB,CAAC;KACF,OAAO;KACP,MAAK;KACL,OAAO,EAAO;KACV;KACJ,UAAU;KACA;KACA;KACH;KACF;KACA;KACL,CAAA;IACF,kBAAC,GAAD;KACE,UAAU,MAAqC,EAAS,EAAE,YAAY;KACtE,SAAQ;KACR,cAAY,EAAO;KACnB,UAAU,KAAS,KAAO;KAC1B,MAAK;KACL,eAAa,GAAG,EAAW;KAC3B,CAAA;IACE;;EACM,CAAA"}
1
+ {"version":3,"file":"NumberField.js","names":[],"sources":["../../../lib/ui/forms/NumberField.tsx"],"sourcesContent":["import clsx from 'clsx';\n\nimport { type ChangeEvent, type MouseEvent, useId } from 'react';\n\nimport { useValue } from '../hooks/useValue';\nimport { Button } from '../buttons/Button';\nimport { FormControl, type FormControlProps } from './FormControl';\n\ninterface NumberFieldProps extends FormControlProps<number> {\n min?: number;\n max?: number;\n labels: {\n increase: string;\n decrease: string;\n input: string;\n };\n}\n\nconst INPUT_STYLE = { width: '28px', height: '28px' };\n\nexport function useNumberField(props: NumberFieldProps) {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n value: initialValue = 0,\n onChange,\n min = 0,\n max = 10,\n disabled = false,\n dataTestId = 'NumberField',\n inline = true,\n labels,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<number, string | number | boolean>({\n defaultValue: initialValue,\n formatter: Number,\n onChange,\n });\n\n const handleChange = (e: ChangeEvent<HTMLInputElement>) => {\n const newValue = Number(e.target.value);\n\n if (newValue >= min && newValue <= max) {\n setValue(Number(newValue), e.nativeEvent);\n }\n };\n\n const increase = (event?: Event) => {\n if (value < max) {\n setValue(value + 1, event);\n }\n };\n\n const decrease = (event?: Event) => {\n if (value > min) {\n setValue(value - 1, event);\n }\n };\n\n return {\n ...rest,\n id,\n min,\n max,\n name,\n value,\n dataTestId,\n disabled,\n inline,\n labels,\n handleChange,\n increase,\n decrease,\n };\n}\n\nexport const NumberField = (props: NumberFieldProps) => {\n const {\n disabled,\n min,\n max,\n label,\n description,\n id,\n dataTestId,\n value,\n handleChange,\n increase,\n decrease,\n errorMessage,\n validationStatus,\n hideRequiredStar,\n required,\n inline,\n labels,\n ...rest\n } = useNumberField(props);\n\n return (\n <FormControl\n id={id}\n label={label}\n description={description}\n className={clsx('flex items-center', props.className)}\n dataName=\"NumberField\"\n dataTestId={dataTestId}\n errorMessage={errorMessage}\n validationStatus={validationStatus}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n layout=\"vertical\"\n inline={inline}\n >\n <div className=\"flex items-center gap-x-8\">\n <Button\n onClick={(e: MouseEvent<HTMLButtonElement>) => decrease(e.nativeEvent)}\n variant=\"circle\"\n disabled={value <= min || disabled}\n icon=\"MinusDefault\"\n aria-label={labels.decrease}\n data-testid={`${dataTestId}-decrease`}\n />\n <input\n {...rest}\n className={clsx(\n 'text-b2 text-center font-bold outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'bg-pearl text-grey': disabled,\n },\n )}\n style={INPUT_STYLE}\n type=\"number\"\n title={labels.input}\n id={id}\n onChange={handleChange}\n disabled={disabled}\n required={required}\n value={value}\n min={min}\n max={max}\n />\n <Button\n onClick={(e: MouseEvent<HTMLButtonElement>) => increase(e.nativeEvent)}\n variant=\"circle\"\n aria-label={labels.increase}\n disabled={value >= max || disabled}\n icon=\"PlusDefault\"\n data-testid={`${dataTestId}-increase`}\n />\n </div>\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;AAkBA,IAAM,IAAc;CAAE,OAAO;CAAQ,QAAQ;CAAQ;AAErD,SAAgB,EAAe,GAAyB;CACtD,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,OAAO,IAAe,GACtB,aACA,SAAM,GACN,SAAM,IACN,cAAW,IACX,gBAAa,eACb,YAAS,IACT,WACA,GAAG,MACD,GAEE,EAAE,UAAO,gBAAa,EAA4C;EACtE,cAAc;EACd,WAAW;EACX;EACD,CAAC,EAEI,KAAgB,MAAqC;EACzD,IAAM,IAAW,OAAO,EAAE,OAAO,MAAM;AAEvC,EAAI,KAAY,KAAO,KAAY,KACjC,EAAS,OAAO,EAAS,EAAE,EAAE,YAAY;IAIvC,KAAY,MAAkB;AAClC,EAAI,IAAQ,KACV,EAAS,IAAQ,GAAG,EAAM;IAIxB,KAAY,MAAkB;AAClC,EAAI,IAAQ,KACV,EAAS,IAAQ,GAAG,EAAM;;AAI9B,QAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,IAAa,KAAe,MAA4B;CACtD,IAAM,EACJ,aACA,QACA,QACA,UACA,gBACA,OACA,eACA,UACA,iBACA,aACA,aACA,iBACA,qBACA,qBACA,aACA,WACA,WACA,GAAG,MACD,EAAe,EAAM;AAEzB,QACE,kBAAC,GAAD;EACM;EACG;EACM;EACb,WAAW,EAAK,qBAAqB,EAAM,UAAU;EACrD,UAAS;EACG;EACE;EACI;EACR;EACA;EACQ;EAClB,QAAO;EACC;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,UAAU,MAAqC,EAAS,EAAE,YAAY;KACtE,SAAQ;KACR,UAAU,KAAS,KAAO;KAC1B,MAAK;KACL,cAAY,EAAO;KACnB,eAAa,GAAG,EAAW;KAC3B,CAAA;IACF,kBAAC,SAAD;KACE,GAAI;KACJ,WAAW,EACT,wHACA,EACE,sBAAsB,GACvB,CACF;KACD,OAAO;KACP,MAAK;KACL,OAAO,EAAO;KACV;KACJ,UAAU;KACA;KACA;KACH;KACF;KACA;KACL,CAAA;IACF,kBAAC,GAAD;KACE,UAAU,MAAqC,EAAS,EAAE,YAAY;KACtE,SAAQ;KACR,cAAY,EAAO;KACnB,UAAU,KAAS,KAAO;KAC1B,MAAK;KACL,eAAa,GAAG,EAAW;KAC3B,CAAA;IACE;;EACM,CAAA"}
@@ -70,7 +70,7 @@ var g = (e) => {
70
70
  onChange: E,
71
71
  onKeyDown: T,
72
72
  placeholder: o,
73
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none", {
73
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", {
74
74
  "border-middleGrey focus:border-black active:border-black": S === "default",
75
75
  "ps-[52px]": s,
76
76
  "pe-[52px]": S === "error" || S === "success",
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneFieldFullInput.js","names":[],"sources":["../../../lib/ui/forms/PhoneFieldFullInput.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type InputHTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n calculateCursorPosition,\n extractDigits,\n formatPhoneDigits,\n formatWithPattern,\n getLiteralPrefixInfo,\n stripLiteralPrefix,\n} from '../helpers/phone';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport type { FormControlProps } from './FormControl';\nimport type { PhoneValue } from './PhoneField';\n\nexport interface PhoneFieldFullProps {\n icon?: IconicNames;\n}\n\nconst usePhoneField = (args: {\n externalValue?: PhoneValue;\n pattern: string;\n onChange?: (event: Event, value: PhoneValue) => void;\n}) => {\n const { externalValue, pattern, onChange } = args;\n const inputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [value, setValue] = useState('');\n\n useEffect(() => {\n if (cursorPositionRef.current !== null && inputRef.current) {\n inputRef.current.setSelectionRange(cursorPositionRef.current, cursorPositionRef.current);\n cursorPositionRef.current = null;\n }\n }, [value]);\n\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n setValue(externalValue.full || '');\n }\n }, [externalValue]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') return;\n const cursorPosition = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n if (cursorPosition !== selectionEnd) return;\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n const targetChar = e.key === 'Backspace' ? value[cursorPosition - 1] : value[cursorPosition];\n if (!targetChar || /\\d/.test(targetChar)) return;\n\n e.preventDefault();\n const allDigits = stripLiteralPrefix(extractDigits(value), literalPrefixDigits);\n const digitsBeforeCursor = stripLiteralPrefix(\n extractDigits(value.substring(0, cursorPosition)),\n literalPrefixDigits,\n ).length;\n\n let newDigits: string;\n let targetCursorDigits: number;\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n if (digitsBeforeCursor >= allDigits.length) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n const formatted = formatWithPattern(newDigits, pattern);\n cursorPositionRef.current = calculateCursorPosition(\n targetCursorDigits,\n formatted,\n literalPrefixDigits,\n );\n setValue(formatted);\n onChange?.(e.nativeEvent, { full: formatted, raw: extractDigits(formatted) });\n },\n [pattern, value, onChange],\n );\n\n const onInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const formattedValue = formatPhoneDigits(\n e.target.value,\n pattern,\n e.target.selectionStart || 0,\n );\n if (!formattedValue) return;\n cursorPositionRef.current = formattedValue.nextCursor;\n setValue(formattedValue.formatted);\n\n onChange?.(e.nativeEvent, {\n full: formattedValue.formatted,\n raw: extractDigits(formattedValue.formatted),\n });\n },\n [pattern, onChange],\n );\n\n return { inputRef, value, onKeyDown, onInputChange };\n};\n\nexport interface PhoneFieldFullInputProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {\n value?: PhoneValue;\n pattern?: string;\n icon?: PhoneFieldFullProps['icon'];\n iconType?: IconicTypes;\n validationStatus?: FormControlProps<PhoneValue>['validationStatus'];\n onChange?: (event: Event, value: PhoneValue) => void;\n}\n\nexport const PhoneFieldFullInput = ({\n id,\n name,\n value: externalValue,\n pattern = '## ## ## ## ##',\n placeholder = '',\n icon,\n iconType,\n disabled = false,\n required = false,\n validationStatus = 'default',\n onChange,\n ...inputProps\n}: PhoneFieldFullInputProps) => {\n const internalId = useId();\n const resolvedId = id ?? internalId;\n const resolvedName = name ?? resolvedId;\n\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const { inputRef, value, onKeyDown, onInputChange } = usePhoneField({\n externalValue,\n pattern,\n onChange,\n });\n\n return (\n <div className=\"relative\">\n <input\n {...(inputProps as any)}\n ref={inputRef}\n id={resolvedId}\n name={resolvedName}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={value}\n onChange={onInputChange}\n onKeyDown={onKeyDown}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'ps-[52px]': icon,\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={resolvedName}\n />\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && <Icon name={icon} width=\"24px\" />}\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PhoneFieldFullInput.js","names":[],"sources":["../../../lib/ui/forms/PhoneFieldFullInput.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type InputHTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n calculateCursorPosition,\n extractDigits,\n formatPhoneDigits,\n formatWithPattern,\n getLiteralPrefixInfo,\n stripLiteralPrefix,\n} from '../helpers/phone';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport type { FormControlProps } from './FormControl';\nimport type { PhoneValue } from './PhoneField';\n\nexport interface PhoneFieldFullProps {\n icon?: IconicNames;\n}\n\nconst usePhoneField = (args: {\n externalValue?: PhoneValue;\n pattern: string;\n onChange?: (event: Event, value: PhoneValue) => void;\n}) => {\n const { externalValue, pattern, onChange } = args;\n const inputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [value, setValue] = useState('');\n\n useEffect(() => {\n if (cursorPositionRef.current !== null && inputRef.current) {\n inputRef.current.setSelectionRange(cursorPositionRef.current, cursorPositionRef.current);\n cursorPositionRef.current = null;\n }\n }, [value]);\n\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n setValue(externalValue.full || '');\n }\n }, [externalValue]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') return;\n const cursorPosition = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n if (cursorPosition !== selectionEnd) return;\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n const targetChar = e.key === 'Backspace' ? value[cursorPosition - 1] : value[cursorPosition];\n if (!targetChar || /\\d/.test(targetChar)) return;\n\n e.preventDefault();\n const allDigits = stripLiteralPrefix(extractDigits(value), literalPrefixDigits);\n const digitsBeforeCursor = stripLiteralPrefix(\n extractDigits(value.substring(0, cursorPosition)),\n literalPrefixDigits,\n ).length;\n\n let newDigits: string;\n let targetCursorDigits: number;\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n if (digitsBeforeCursor >= allDigits.length) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n const formatted = formatWithPattern(newDigits, pattern);\n cursorPositionRef.current = calculateCursorPosition(\n targetCursorDigits,\n formatted,\n literalPrefixDigits,\n );\n setValue(formatted);\n onChange?.(e.nativeEvent, { full: formatted, raw: extractDigits(formatted) });\n },\n [pattern, value, onChange],\n );\n\n const onInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const formattedValue = formatPhoneDigits(\n e.target.value,\n pattern,\n e.target.selectionStart || 0,\n );\n if (!formattedValue) return;\n cursorPositionRef.current = formattedValue.nextCursor;\n setValue(formattedValue.formatted);\n\n onChange?.(e.nativeEvent, {\n full: formattedValue.formatted,\n raw: extractDigits(formattedValue.formatted),\n });\n },\n [pattern, onChange],\n );\n\n return { inputRef, value, onKeyDown, onInputChange };\n};\n\nexport interface PhoneFieldFullInputProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {\n value?: PhoneValue;\n pattern?: string;\n icon?: PhoneFieldFullProps['icon'];\n iconType?: IconicTypes;\n validationStatus?: FormControlProps<PhoneValue>['validationStatus'];\n onChange?: (event: Event, value: PhoneValue) => void;\n}\n\nexport const PhoneFieldFullInput = ({\n id,\n name,\n value: externalValue,\n pattern = '## ## ## ## ##',\n placeholder = '',\n icon,\n iconType,\n disabled = false,\n required = false,\n validationStatus = 'default',\n onChange,\n ...inputProps\n}: PhoneFieldFullInputProps) => {\n const internalId = useId();\n const resolvedId = id ?? internalId;\n const resolvedName = name ?? resolvedId;\n\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const { inputRef, value, onKeyDown, onInputChange } = usePhoneField({\n externalValue,\n pattern,\n onChange,\n });\n\n return (\n <div className=\"relative\">\n <input\n {...(inputProps as any)}\n ref={inputRef}\n id={resolvedId}\n name={resolvedName}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={value}\n onChange={onInputChange}\n onKeyDown={onKeyDown}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'ps-[52px]': icon,\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={resolvedName}\n />\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && <Icon name={icon} width=\"24px\" />}\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -100,7 +100,7 @@ var _ = (e) => {
100
100
  disabled: f,
101
101
  value: E,
102
102
  onChange: (e) => A(e.nativeEvent, e.target.value),
103
- className: "opacity-0 absolute inset-0 w-full h-full cursor-pointer",
103
+ className: "opacity-0 absolute inset-0 w-full h-full cursor-pointer outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
104
104
  id: `${S}-prefix`,
105
105
  "aria-label": `${C}-prefix`,
106
106
  children: o.map((e) => /* @__PURE__ */ h("option", {
@@ -120,7 +120,7 @@ var _ = (e) => {
120
120
  onChange: k,
121
121
  onKeyDown: O,
122
122
  placeholder: l,
123
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none", {
123
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", {
124
124
  "border-middleGrey focus:border-black active:border-black": w === "default",
125
125
  "pe-[52px]": w === "error" || w === "success",
126
126
  "bg-white text-black": w !== "disabled",
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneFieldSplitInput.js","names":[],"sources":["../../../lib/ui/forms/PhoneFieldSplitInput.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { Icon, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type InputHTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n calculateCursorPosition,\n DEFAULT_PHONE_PREFIXES,\n extractDigits,\n formatPhoneDigits,\n formatWithPattern,\n getLiteralPrefixInfo,\n type PhonePrefix,\n stripLiteralPrefix,\n} from '../helpers/phone';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport type { FormControlProps } from './FormControl';\nimport type { PhoneValue } from './PhoneField';\n\ntype PhoneFieldChangeHandler = (event: Event, value: PhoneValue) => void;\n\nexport interface PhoneFieldSplitProps {\n prefixes?: PhonePrefix[];\n defaultPrefix?: string;\n}\n\nconst usePhoneField = (args: {\n externalValue?: PhoneValue;\n pattern: string;\n defaultPrefix: string;\n onChange?: PhoneFieldChangeHandler;\n}) => {\n const { externalValue, pattern, defaultPrefix, onChange } = args;\n const numberInputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [prefix, setPrefix] = useState(defaultPrefix);\n const [number, setNumber] = useState('');\n\n const emitChange = useCallback(\n (event: Event, nextPrefix: string, nextNumber: string) => {\n if (nextPrefix === prefix && nextNumber === number) {\n return;\n }\n\n const full = `${nextPrefix} ${nextNumber}`.trim();\n\n onChange?.(event, {\n full,\n prefix: nextPrefix,\n number: nextNumber,\n raw: extractDigits(full),\n });\n },\n [number, onChange, prefix],\n );\n\n useEffect(() => {\n if (cursorPositionRef.current !== null && numberInputRef.current) {\n numberInputRef.current.setSelectionRange(\n cursorPositionRef.current,\n cursorPositionRef.current,\n );\n cursorPositionRef.current = null;\n }\n }, [number]);\n\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n setPrefix(externalValue.prefix || defaultPrefix);\n setNumber(externalValue.number || '');\n }\n }, [externalValue, defaultPrefix]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') {\n return;\n }\n\n const cursorPosition = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n\n const targetChar =\n e.key === 'Backspace' ? number[cursorPosition - 1] : number[cursorPosition];\n\n if (!targetChar || /\\d/.test(targetChar)) {\n return;\n }\n\n e.preventDefault();\n\n const allDigits = stripLiteralPrefix(extractDigits(number), literalPrefixDigits);\n const digitsBeforeCursor = stripLiteralPrefix(\n extractDigits(number.substring(0, cursorPosition)),\n literalPrefixDigits,\n ).length;\n\n let newDigits: string;\n let targetCursorDigits: number;\n\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) {\n return;\n }\n\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n if (digitsBeforeCursor >= allDigits.length) {\n return;\n }\n\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n const formatted = formatWithPattern(newDigits, pattern);\n cursorPositionRef.current = calculateCursorPosition(\n targetCursorDigits,\n formatted,\n literalPrefixDigits,\n );\n\n setNumber(formatted);\n\n emitChange(e.nativeEvent, prefix, formatted);\n },\n [emitChange, pattern, number, prefix],\n );\n\n const onInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const formattedValue = formatPhoneDigits(\n e.target.value,\n pattern,\n e.target.selectionStart || 0,\n );\n if (!formattedValue) return;\n\n cursorPositionRef.current = formattedValue.nextCursor;\n setNumber(formattedValue.formatted);\n\n emitChange(e.nativeEvent, prefix, formattedValue.formatted);\n },\n [emitChange, pattern, prefix],\n );\n\n const onPrefixChange = useCallback(\n (event: Event, newPrefix: string) => {\n setPrefix(newPrefix);\n\n emitChange(event, newPrefix, number);\n },\n [emitChange, number],\n );\n\n return { numberInputRef, prefix, number, onKeyDown, onInputChange, onPrefixChange };\n};\n\nexport interface PhoneFieldSplitInputProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {\n value?: PhoneValue;\n pattern?: string;\n prefixes?: PhoneFieldSplitProps['prefixes'];\n defaultPrefix?: PhoneFieldSplitProps['defaultPrefix'];\n iconType?: IconicTypes;\n validationStatus?: FormControlProps<PhoneValue>['validationStatus'];\n onChange?: (event: Event, value: PhoneValue) => void;\n}\n\nexport const PhoneFieldSplitInput = ({\n id,\n name,\n value: externalValue,\n pattern = '## ## ## ## ##',\n prefixes = DEFAULT_PHONE_PREFIXES,\n defaultPrefix = prefixes[0]?.code || '+1',\n placeholder = '',\n iconType,\n disabled = false,\n required = false,\n validationStatus = 'default',\n onChange,\n ...rest\n}: PhoneFieldSplitInputProps) => {\n const internalId = useId();\n const resolvedId = id ?? internalId;\n const resolvedName = name ?? resolvedId;\n\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const { numberInputRef, prefix, number, onKeyDown, onInputChange, onPrefixChange } =\n usePhoneField({\n externalValue,\n pattern,\n defaultPrefix,\n onChange,\n });\n\n return (\n <div className=\"flex gap-8\">\n <div\n className={clsx('relative rounded-pill z-0 flex-shrink-0', {\n 'bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n })}\n >\n {/* Visible fake select showing the short label */}\n <div\n aria-hidden=\"true\"\n className={clsx(\n 'text-b3 rounded-pill w-full border px-20 py-12 font-semibold pointer-events-none flex items-center justify-between gap-8',\n {\n 'border-middleGrey': internalStatus === 'default',\n 'text-black bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey text-black': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n >\n <span>{(prefixes as PhonePrefix[]).find((p) => p.code === prefix)?.label ?? prefix}</span>\n <Icon name=\"ArrowDefaultDown\" type=\"svg\" width=\"24px\" color=\"black\" />\n </div>\n\n {/* Real select — invisible but interactive, uses fullLabel for dropdown options */}\n <select\n disabled={disabled}\n value={prefix}\n onChange={(e) => onPrefixChange(e.nativeEvent, e.target.value)}\n className=\"opacity-0 absolute inset-0 w-full h-full cursor-pointer\"\n id={`${resolvedId}-prefix`}\n aria-label={`${resolvedName}-prefix`}\n >\n {(prefixes as PhonePrefix[]).map((p) => (\n <option key={p.code} value={p.code}>\n {p.fullLabel ?? p.label ?? p.code}\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"relative flex-1\">\n <input\n {...(rest as Record<string, unknown>)}\n ref={numberInputRef}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={number}\n onChange={onInputChange}\n onKeyDown={onKeyDown}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={`${resolvedName}-number`}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n <span className=\"flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PhoneFieldSplitInput.js","names":[],"sources":["../../../lib/ui/forms/PhoneFieldSplitInput.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { Icon, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type InputHTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n calculateCursorPosition,\n DEFAULT_PHONE_PREFIXES,\n extractDigits,\n formatPhoneDigits,\n formatWithPattern,\n getLiteralPrefixInfo,\n type PhonePrefix,\n stripLiteralPrefix,\n} from '../helpers/phone';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport type { FormControlProps } from './FormControl';\nimport type { PhoneValue } from './PhoneField';\n\ntype PhoneFieldChangeHandler = (event: Event, value: PhoneValue) => void;\n\nexport interface PhoneFieldSplitProps {\n prefixes?: PhonePrefix[];\n defaultPrefix?: string;\n}\n\nconst usePhoneField = (args: {\n externalValue?: PhoneValue;\n pattern: string;\n defaultPrefix: string;\n onChange?: PhoneFieldChangeHandler;\n}) => {\n const { externalValue, pattern, defaultPrefix, onChange } = args;\n const numberInputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [prefix, setPrefix] = useState(defaultPrefix);\n const [number, setNumber] = useState('');\n\n const emitChange = useCallback(\n (event: Event, nextPrefix: string, nextNumber: string) => {\n if (nextPrefix === prefix && nextNumber === number) {\n return;\n }\n\n const full = `${nextPrefix} ${nextNumber}`.trim();\n\n onChange?.(event, {\n full,\n prefix: nextPrefix,\n number: nextNumber,\n raw: extractDigits(full),\n });\n },\n [number, onChange, prefix],\n );\n\n useEffect(() => {\n if (cursorPositionRef.current !== null && numberInputRef.current) {\n numberInputRef.current.setSelectionRange(\n cursorPositionRef.current,\n cursorPositionRef.current,\n );\n cursorPositionRef.current = null;\n }\n }, [number]);\n\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n setPrefix(externalValue.prefix || defaultPrefix);\n setNumber(externalValue.number || '');\n }\n }, [externalValue, defaultPrefix]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') {\n return;\n }\n\n const cursorPosition = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n\n const targetChar =\n e.key === 'Backspace' ? number[cursorPosition - 1] : number[cursorPosition];\n\n if (!targetChar || /\\d/.test(targetChar)) {\n return;\n }\n\n e.preventDefault();\n\n const allDigits = stripLiteralPrefix(extractDigits(number), literalPrefixDigits);\n const digitsBeforeCursor = stripLiteralPrefix(\n extractDigits(number.substring(0, cursorPosition)),\n literalPrefixDigits,\n ).length;\n\n let newDigits: string;\n let targetCursorDigits: number;\n\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) {\n return;\n }\n\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n if (digitsBeforeCursor >= allDigits.length) {\n return;\n }\n\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n const formatted = formatWithPattern(newDigits, pattern);\n cursorPositionRef.current = calculateCursorPosition(\n targetCursorDigits,\n formatted,\n literalPrefixDigits,\n );\n\n setNumber(formatted);\n\n emitChange(e.nativeEvent, prefix, formatted);\n },\n [emitChange, pattern, number, prefix],\n );\n\n const onInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const formattedValue = formatPhoneDigits(\n e.target.value,\n pattern,\n e.target.selectionStart || 0,\n );\n if (!formattedValue) return;\n\n cursorPositionRef.current = formattedValue.nextCursor;\n setNumber(formattedValue.formatted);\n\n emitChange(e.nativeEvent, prefix, formattedValue.formatted);\n },\n [emitChange, pattern, prefix],\n );\n\n const onPrefixChange = useCallback(\n (event: Event, newPrefix: string) => {\n setPrefix(newPrefix);\n\n emitChange(event, newPrefix, number);\n },\n [emitChange, number],\n );\n\n return { numberInputRef, prefix, number, onKeyDown, onInputChange, onPrefixChange };\n};\n\nexport interface PhoneFieldSplitInputProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {\n value?: PhoneValue;\n pattern?: string;\n prefixes?: PhoneFieldSplitProps['prefixes'];\n defaultPrefix?: PhoneFieldSplitProps['defaultPrefix'];\n iconType?: IconicTypes;\n validationStatus?: FormControlProps<PhoneValue>['validationStatus'];\n onChange?: (event: Event, value: PhoneValue) => void;\n}\n\nexport const PhoneFieldSplitInput = ({\n id,\n name,\n value: externalValue,\n pattern = '## ## ## ## ##',\n prefixes = DEFAULT_PHONE_PREFIXES,\n defaultPrefix = prefixes[0]?.code || '+1',\n placeholder = '',\n iconType,\n disabled = false,\n required = false,\n validationStatus = 'default',\n onChange,\n ...rest\n}: PhoneFieldSplitInputProps) => {\n const internalId = useId();\n const resolvedId = id ?? internalId;\n const resolvedName = name ?? resolvedId;\n\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const { numberInputRef, prefix, number, onKeyDown, onInputChange, onPrefixChange } =\n usePhoneField({\n externalValue,\n pattern,\n defaultPrefix,\n onChange,\n });\n\n return (\n <div className=\"flex gap-8\">\n <div\n className={clsx('relative rounded-pill z-0 flex-shrink-0', {\n 'bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n })}\n >\n {/* Visible fake select showing the short label */}\n <div\n aria-hidden=\"true\"\n className={clsx(\n 'text-b3 rounded-pill w-full border px-20 py-12 font-semibold pointer-events-none flex items-center justify-between gap-8',\n {\n 'border-middleGrey': internalStatus === 'default',\n 'text-black bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey text-black': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n >\n <span>{(prefixes as PhonePrefix[]).find((p) => p.code === prefix)?.label ?? prefix}</span>\n <Icon name=\"ArrowDefaultDown\" type=\"svg\" width=\"24px\" color=\"black\" />\n </div>\n\n {/* Real select — invisible but interactive, uses fullLabel for dropdown options */}\n <select\n disabled={disabled}\n value={prefix}\n onChange={(e) => onPrefixChange(e.nativeEvent, e.target.value)}\n className=\"opacity-0 absolute inset-0 w-full h-full cursor-pointer outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n id={`${resolvedId}-prefix`}\n aria-label={`${resolvedName}-prefix`}\n >\n {(prefixes as PhonePrefix[]).map((p) => (\n <option key={p.code} value={p.code}>\n {p.fullLabel ?? p.label ?? p.code}\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"relative flex-1\">\n <input\n {...(rest as Record<string, unknown>)}\n ref={numberInputRef}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={number}\n onChange={onInputChange}\n onKeyDown={onKeyDown}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={`${resolvedName}-number`}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n <span className=\"flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -41,7 +41,7 @@ var c = (c) => {
41
41
  onChange: (e) => {
42
42
  D(e.target.value, e.nativeEvent);
43
43
  },
44
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none appearance-none bg-transparent", {
44
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 appearance-none bg-transparent", {
45
45
  "border-middleGrey focus:border-black active:border-black pe-[52px]": O === "default",
46
46
  "pe-[84px]": O === "error" || O === "success",
47
47
  "text-black": O !== "disabled",
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","names":[],"sources":["../../../lib/ui/forms/Select.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useValue } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\nimport { Icon, type IconicTypes } from '@clubmed/trident-icons';\nimport { type SelectHTMLAttributes, useId } from 'react';\n\nexport interface SelectProps<Value>\n extends FormControlProps<Value, SelectHTMLAttributes<HTMLSelectElement>> {\n description?: string;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n}\n\nexport const Select = <Value = string,>(props: SelectProps<Value>) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: initialValue,\n description,\n validationStatus = 'default',\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n className,\n dataTestId = id,\n onChange,\n children,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<Value>({ initialValue, onChange });\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n return (\n <FormControl\n id={id}\n className={className}\n label={label}\n description={description}\n dataName=\"TextField\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={validationStatus}\n errorMessage={errorMessage}\n >\n <div\n className={clsx('relative rounded-pill z-0', {\n 'bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n })}\n >\n <select\n {...rest}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n value={value as any}\n onChange={(e) => {\n setValue(e.target.value as Value, e.nativeEvent);\n }}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none appearance-none bg-transparent',\n {\n 'border-middleGrey focus:border-black active:border-black pe-[52px]':\n internalStatus === 'default',\n 'pe-[84px]': internalStatus === 'error' || internalStatus === 'success',\n 'text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={name}\n >\n {children}\n </select>\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12 -z-1',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n\n <button className=\"pointer-events-auto inline-flex\">\n <Icon name=\"ArrowDefaultDown\" type=\"svg\" width=\"24px\" color=\"black\" />\n </button>\n </span>\n </div>\n </div>\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;;AAeA,IAAa,KAA2B,MAA8B;CACpE,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,UACA,OAAO,GACP,gBACA,sBAAmB,WACnB,aACA,iBACA,cAAW,IACX,cAAW,IACX,qBACA,cACA,gBAAa,GACb,aACA,aACA,GAAG,MACD,GAEE,EAAE,UAAO,gBAAa,EAAgB;EAAE;EAAc;EAAU,CAAC,EACjE,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC;AAEF,QACE,kBAAC,GAAD;EACM;EACO;EACJ;EACM;EACb,UAAS;EACG;EACF;EACA;EACQ;EACA;EACJ;YAEd,kBAAC,OAAD;GACE,WAAW,EAAK,6BAA6B;IAC3C,YAAY,MAAmB;IAC/B,8BAA8B,MAAmB;IAClD,CAAC;aAJJ,CAME,kBAAC,UAAD;IACE,GAAI;IACA;IACE;IACI;IACA;IACH;IACP,WAAW,MAAM;AACf,OAAS,EAAE,OAAO,OAAgB,EAAE,YAAY;;IAElD,WAAW,EACT,0HACA;KACE,sEACE,MAAmB;KACrB,aAAa,MAAmB,WAAW,MAAmB;KAC9D,cAAc,MAAmB;KACjC,8BAA8B,MAAmB;KACjD,cAAc,MAAmB;KACjC,gBAAgB,MAAmB;KACpC,CACF;IACD,cAAY;IAEX;IACM,CAAA,EAET,kBAAC,OAAD;IACE,WAAW,EACT,2FACA;KACE,aAAa,MAAmB;KAChC,YAAY,MAAmB;KAC/B,cAAc,MAAmB;KAClC,CACF;cAED,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,MAAmB,WAClB,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;MAG1D,MAAmB,aAClB,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;MAG3D,kBAAC,UAAD;OAAQ,WAAU;iBAChB,kBAAC,GAAD;QAAM,MAAK;QAAmB,MAAK;QAAM,OAAM;QAAO,OAAM;QAAU,CAAA;OAC/D,CAAA;MACJ;;IACH,CAAA,CACF;;EACM,CAAA"}
1
+ {"version":3,"file":"Select.js","names":[],"sources":["../../../lib/ui/forms/Select.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useValue } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\nimport { Icon, type IconicTypes } from '@clubmed/trident-icons';\nimport { type SelectHTMLAttributes, useId } from 'react';\n\nexport interface SelectProps<Value>\n extends FormControlProps<Value, SelectHTMLAttributes<HTMLSelectElement>> {\n description?: string;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n}\n\nexport const Select = <Value = string,>(props: SelectProps<Value>) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: initialValue,\n description,\n validationStatus = 'default',\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n className,\n dataTestId = id,\n onChange,\n children,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<Value>({ initialValue, onChange });\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n return (\n <FormControl\n id={id}\n className={className}\n label={label}\n description={description}\n dataName=\"TextField\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={validationStatus}\n errorMessage={errorMessage}\n >\n <div\n className={clsx('relative rounded-pill z-0', {\n 'bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n })}\n >\n <select\n {...rest}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n value={value as any}\n onChange={(e) => {\n setValue(e.target.value as Value, e.nativeEvent);\n }}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 appearance-none bg-transparent',\n {\n 'border-middleGrey focus:border-black active:border-black pe-[52px]':\n internalStatus === 'default',\n 'pe-[84px]': internalStatus === 'error' || internalStatus === 'success',\n 'text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={name}\n >\n {children}\n </select>\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12 -z-1',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n\n <button className=\"pointer-events-auto inline-flex\">\n <Icon name=\"ArrowDefaultDown\" type=\"svg\" width=\"24px\" color=\"black\" />\n </button>\n </span>\n </div>\n </div>\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;;AAeA,IAAa,KAA2B,MAA8B;CACpE,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,UACA,OAAO,GACP,gBACA,sBAAmB,WACnB,aACA,iBACA,cAAW,IACX,cAAW,IACX,qBACA,cACA,gBAAa,GACb,aACA,aACA,GAAG,MACD,GAEE,EAAE,UAAO,gBAAa,EAAgB;EAAE;EAAc;EAAU,CAAC,EACjE,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC;AAEF,QACE,kBAAC,GAAD;EACM;EACO;EACJ;EACM;EACb,UAAS;EACG;EACF;EACA;EACQ;EACA;EACJ;YAEd,kBAAC,OAAD;GACE,WAAW,EAAK,6BAA6B;IAC3C,YAAY,MAAmB;IAC/B,8BAA8B,MAAmB;IAClD,CAAC;aAJJ,CAME,kBAAC,UAAD;IACE,GAAI;IACA;IACE;IACI;IACA;IACH;IACP,WAAW,MAAM;AACf,OAAS,EAAE,OAAO,OAAgB,EAAE,YAAY;;IAElD,WAAW,EACT,oMACA;KACE,sEACE,MAAmB;KACrB,aAAa,MAAmB,WAAW,MAAmB;KAC9D,cAAc,MAAmB;KACjC,8BAA8B,MAAmB;KACjD,cAAc,MAAmB;KACjC,gBAAgB,MAAmB;KACpC,CACF;IACD,cAAY;IAEX;IACM,CAAA,EAET,kBAAC,OAAD;IACE,WAAW,EACT,2FACA;KACE,aAAa,MAAmB;KAChC,YAAY,MAAmB;KAC/B,cAAc,MAAmB;KAClC,CACF;cAED,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,MAAmB,WAClB,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;MAG1D,MAAmB,aAClB,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;MAG3D,kBAAC,UAAD;OAAQ,WAAU;iBAChB,kBAAC,GAAD;QAAM,MAAK;QAAmB,MAAK;QAAM,OAAM;QAAO,OAAM;QAAU,CAAA;OAC/D,CAAA;MACJ;;IACH,CAAA,CACF;;EACM,CAAA"}
@@ -41,7 +41,7 @@ var c = (c) => {
41
41
  "--nbIconsStart": Number(!!g && E),
42
42
  "--nbIconsEnd": Number(!!g && !E) + Number(w && !N || N && M !== "error") + Number(M === "error" || M === "success")
43
43
  },
44
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none ps-[calc(20px+var(--nbIconsStart)*32px)] pe-[calc(20px+var(--nbIconsEnd)*32px)]", {
44
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 ps-[calc(20px+var(--nbIconsStart)*32px)] pe-[calc(20px+var(--nbIconsEnd)*32px)]", {
45
45
  "border-middleGrey focus:border-black active:border-black": M === "default",
46
46
  "bg-white text-black": M !== "disabled",
47
47
  "bg-pearl border-middleGrey": M === "disabled",
@@ -62,7 +62,7 @@ var c = (c) => {
62
62
  className: "ms-auto flex gap-x-8",
63
63
  children: [
64
64
  M === "error" && (N ? /* @__PURE__ */ o("button", {
65
- className: "pointer-events-auto cursor-pointer flex",
65
+ className: "pointer-events-auto cursor-pointer flex outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
66
66
  onClick: (e) => j("", e.nativeEvent),
67
67
  "aria-label": T,
68
68
  type: "reset",
@@ -82,7 +82,7 @@ var c = (c) => {
82
82
  type: _
83
83
  }),
84
84
  (w || N && M !== "error") && (N ? /* @__PURE__ */ o("button", {
85
- className: "pointer-events-auto cursor-pointer flex",
85
+ className: "pointer-events-auto cursor-pointer flex outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
86
86
  onClick: (e) => j("", e.nativeEvent),
87
87
  "aria-label": T,
88
88
  type: "reset",
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.js","names":[],"sources":["../../../lib/ui/forms/TextField.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useValue, type UseValueProps } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport { useId } from 'react';\n\nexport interface TextFieldProps<Value> extends FormControlProps<Value> {\n description?: string;\n icon?: IconicNames;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n formatter?: UseValueProps<Value>['formatter'];\n hasDropdown?: boolean;\n clear?: string;\n iconFirst?: boolean;\n}\n\nexport const TextField = <Value = string,>(props: TextFieldProps<Value>) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: initialValue,\n description,\n validationStatus = 'default',\n icon,\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n className,\n dataTestId = 'TextField',\n hasDropdown = false,\n clear = '',\n iconFirst = true,\n onChange,\n formatter,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<Value>({ initialValue, onChange, formatter });\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n const shouldDisplayClearButton =\n !disabled && value !== '' && value !== null && value !== undefined && !!clear;\n\n return (\n <FormControl\n id={id}\n label={label}\n className={className}\n description={description}\n dataName=\"TextField\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={validationStatus}\n errorMessage={errorMessage}\n >\n <div className=\"relative\">\n <input\n {...rest}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n value={value as any}\n onChange={(e) => setValue(e.target.value as Value, e.nativeEvent)}\n style={\n {\n '--nbIconsStart': Number(!!icon && iconFirst),\n '--nbIconsEnd':\n Number(!!icon && !iconFirst) +\n Number(\n (hasDropdown && !shouldDisplayClearButton) ||\n (shouldDisplayClearButton && internalStatus !== 'error'),\n ) +\n Number(internalStatus === 'error' || internalStatus === 'success'),\n } as React.CSSProperties\n }\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none ps-[calc(20px+var(--nbIconsStart)*32px)] pe-[calc(20px+var(--nbIconsEnd)*32px)]',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={name}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && iconFirst && <Icon name={icon} width=\"24px\" />}\n\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' &&\n (shouldDisplayClearButton ? (\n <button\n className=\"pointer-events-auto cursor-pointer flex\"\n onClick={(e) => setValue('' as Value, e.nativeEvent)}\n aria-label={clear}\n type=\"reset\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n </button>\n ) : (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n ))}\n\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n {(hasDropdown || (shouldDisplayClearButton && internalStatus !== 'error')) &&\n (shouldDisplayClearButton ? (\n <button\n className=\"pointer-events-auto cursor-pointer flex\"\n onClick={(e) => setValue('' as Value, e.nativeEvent)}\n aria-label={clear}\n type=\"reset\"\n >\n <Icon name=\"CrossDefault\" className=\"text-black\" width=\"24px\" />\n </button>\n ) : (\n <Icon name=\"ArrowDefaultDown\" className=\"text-black\" width=\"24px\" />\n ))}\n {!iconFirst && icon && <Icon name={icon} width=\"24px\" />}\n </span>\n </div>\n </div>\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;;AAmBA,IAAa,KAA8B,MAAiC;CAC1E,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,UACA,OAAO,GACP,gBACA,sBAAmB,WACnB,SACA,aACA,iBACA,cAAW,IACX,cAAW,IACX,qBACA,cACA,gBAAa,aACb,iBAAc,IACd,WAAQ,IACR,eAAY,IACZ,aACA,cACA,GAAG,MACD,GAEE,EAAE,UAAO,gBAAa,EAAgB;EAAE;EAAc;EAAU;EAAW,CAAC,EAC5E,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC,EAEI,IACJ,CAAC,KAAY,MAAU,MAAM,KAAU,QAA+B,CAAC,CAAC;AAE1E,QACE,kBAAC,GAAD;EACM;EACG;EACI;EACE;EACb,UAAS;EACG;EACF;EACA;EACQ;EACA;EACJ;YAEd,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,SAAD;IACE,GAAI;IACA;IACE;IACI;IACA;IACH;IACP,WAAW,MAAM,EAAS,EAAE,OAAO,OAAgB,EAAE,YAAY;IACjE,OACE;KACE,kBAAkB,OAAO,CAAC,CAAC,KAAQ,EAAU;KAC7C,gBACE,OAAO,CAAC,CAAC,KAAQ,CAAC,EAAU,GAC5B,OACG,KAAe,CAAC,KACd,KAA4B,MAAmB,QACnD,GACD,OAAO,MAAmB,WAAW,MAAmB,UAAU;KACrE;IAEH,WAAW,EACT,2KACA;KACE,4DACE,MAAmB;KACrB,uBAAuB,MAAmB;KAC1C,8BAA8B,MAAmB;KACjD,cAAc,MAAmB;KACjC,gBAAgB,MAAmB;KACpC,CACF;IACD,cAAY;IACZ,CAAA,EAEF,kBAAC,OAAD;IACE,WAAW,EACT,sFACA;KACE,aAAa,MAAmB;KAChC,YAAY,MAAmB;KAC/B,cAAc,MAAmB;KAClC,CACF;cARH,CAUG,KAAQ,KAAa,kBAAC,GAAD;KAAM,MAAM;KAAM,OAAM;KAAS,CAAA,EAEvD,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,MAAmB,YACjB,IACC,kBAAC,UAAD;OACE,WAAU;OACV,UAAU,MAAM,EAAS,IAAa,EAAE,YAAY;OACpD,cAAY;OACZ,MAAK;iBAEL,kBAAC,GAAD;QAAM,MAAK;QAAe,OAAM;QAAO,MAAM;QAAY,CAAA;OAClD,CAAA,GAET,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;MAG5D,MAAmB,aAClB,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;OAEzD,KAAgB,KAA4B,MAAmB,aAC9D,IACC,kBAAC,UAAD;OACE,WAAU;OACV,UAAU,MAAM,EAAS,IAAa,EAAE,YAAY;OACpD,cAAY;OACZ,MAAK;iBAEL,kBAAC,GAAD;QAAM,MAAK;QAAe,WAAU;QAAa,OAAM;QAAS,CAAA;OACzD,CAAA,GAET,kBAAC,GAAD;OAAM,MAAK;OAAmB,WAAU;OAAa,OAAM;OAAS,CAAA;MAEvE,CAAC,KAAa,KAAQ,kBAAC,GAAD;OAAM,MAAM;OAAM,OAAM;OAAS,CAAA;MACnD;OACH;MACF;;EACM,CAAA"}
1
+ {"version":3,"file":"TextField.js","names":[],"sources":["../../../lib/ui/forms/TextField.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useValue, type UseValueProps } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport { useId } from 'react';\n\nexport interface TextFieldProps<Value> extends FormControlProps<Value> {\n description?: string;\n icon?: IconicNames;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n formatter?: UseValueProps<Value>['formatter'];\n hasDropdown?: boolean;\n clear?: string;\n iconFirst?: boolean;\n}\n\nexport const TextField = <Value = string,>(props: TextFieldProps<Value>) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: initialValue,\n description,\n validationStatus = 'default',\n icon,\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n className,\n dataTestId = 'TextField',\n hasDropdown = false,\n clear = '',\n iconFirst = true,\n onChange,\n formatter,\n ...rest\n } = props;\n\n const { value, setValue } = useValue<Value>({ initialValue, onChange, formatter });\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n const shouldDisplayClearButton =\n !disabled && value !== '' && value !== null && value !== undefined && !!clear;\n\n return (\n <FormControl\n id={id}\n label={label}\n className={className}\n description={description}\n dataName=\"TextField\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={validationStatus}\n errorMessage={errorMessage}\n >\n <div className=\"relative\">\n <input\n {...rest}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n value={value as any}\n onChange={(e) => setValue(e.target.value as Value, e.nativeEvent)}\n style={\n {\n '--nbIconsStart': Number(!!icon && iconFirst),\n '--nbIconsEnd':\n Number(!!icon && !iconFirst) +\n Number(\n (hasDropdown && !shouldDisplayClearButton) ||\n (shouldDisplayClearButton && internalStatus !== 'error'),\n ) +\n Number(internalStatus === 'error' || internalStatus === 'success'),\n } as React.CSSProperties\n }\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 ps-[calc(20px+var(--nbIconsStart)*32px)] pe-[calc(20px+var(--nbIconsEnd)*32px)]',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={name}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && iconFirst && <Icon name={icon} width=\"24px\" />}\n\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' &&\n (shouldDisplayClearButton ? (\n <button\n className=\"pointer-events-auto cursor-pointer flex outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n onClick={(e) => setValue('' as Value, e.nativeEvent)}\n aria-label={clear}\n type=\"reset\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n </button>\n ) : (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n ))}\n\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n {(hasDropdown || (shouldDisplayClearButton && internalStatus !== 'error')) &&\n (shouldDisplayClearButton ? (\n <button\n className=\"pointer-events-auto cursor-pointer flex outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n onClick={(e) => setValue('' as Value, e.nativeEvent)}\n aria-label={clear}\n type=\"reset\"\n >\n <Icon name=\"CrossDefault\" className=\"text-black\" width=\"24px\" />\n </button>\n ) : (\n <Icon name=\"ArrowDefaultDown\" className=\"text-black\" width=\"24px\" />\n ))}\n {!iconFirst && icon && <Icon name={icon} width=\"24px\" />}\n </span>\n </div>\n </div>\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;;AAmBA,IAAa,KAA8B,MAAiC;CAC1E,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,UACA,OAAO,GACP,gBACA,sBAAmB,WACnB,SACA,aACA,iBACA,cAAW,IACX,cAAW,IACX,qBACA,cACA,gBAAa,aACb,iBAAc,IACd,WAAQ,IACR,eAAY,IACZ,aACA,cACA,GAAG,MACD,GAEE,EAAE,UAAO,gBAAa,EAAgB;EAAE;EAAc;EAAU;EAAW,CAAC,EAC5E,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC,EAEI,IACJ,CAAC,KAAY,MAAU,MAAM,KAAU,QAA+B,CAAC,CAAC;AAE1E,QACE,kBAAC,GAAD;EACM;EACG;EACI;EACE;EACb,UAAS;EACG;EACF;EACA;EACQ;EACA;EACJ;YAEd,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,SAAD;IACE,GAAI;IACA;IACE;IACI;IACA;IACH;IACP,WAAW,MAAM,EAAS,EAAE,OAAO,OAAgB,EAAE,YAAY;IACjE,OACE;KACE,kBAAkB,OAAO,CAAC,CAAC,KAAQ,EAAU;KAC7C,gBACE,OAAO,CAAC,CAAC,KAAQ,CAAC,EAAU,GAC5B,OACG,KAAe,CAAC,KACd,KAA4B,MAAmB,QACnD,GACD,OAAO,MAAmB,WAAW,MAAmB,UAAU;KACrE;IAEH,WAAW,EACT,qPACA;KACE,4DACE,MAAmB;KACrB,uBAAuB,MAAmB;KAC1C,8BAA8B,MAAmB;KACjD,cAAc,MAAmB;KACjC,gBAAgB,MAAmB;KACpC,CACF;IACD,cAAY;IACZ,CAAA,EAEF,kBAAC,OAAD;IACE,WAAW,EACT,sFACA;KACE,aAAa,MAAmB;KAChC,YAAY,MAAmB;KAC/B,cAAc,MAAmB;KAClC,CACF;cARH,CAUG,KAAQ,KAAa,kBAAC,GAAD;KAAM,MAAM;KAAM,OAAM;KAAS,CAAA,EAEvD,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,MAAmB,YACjB,IACC,kBAAC,UAAD;OACE,WAAU;OACV,UAAU,MAAM,EAAS,IAAa,EAAE,YAAY;OACpD,cAAY;OACZ,MAAK;iBAEL,kBAAC,GAAD;QAAM,MAAK;QAAe,OAAM;QAAO,MAAM;QAAY,CAAA;OAClD,CAAA,GAET,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;MAG5D,MAAmB,aAClB,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAO,MAAM;OAAY,CAAA;OAEzD,KAAgB,KAA4B,MAAmB,aAC9D,IACC,kBAAC,UAAD;OACE,WAAU;OACV,UAAU,MAAM,EAAS,IAAa,EAAE,YAAY;OACpD,cAAY;OACZ,MAAK;iBAEL,kBAAC,GAAD;QAAM,MAAK;QAAe,WAAU;QAAa,OAAM;QAAS,CAAA;OACzD,CAAA,GAET,kBAAC,GAAD;OAAM,MAAK;OAAmB,WAAU;OAAa,OAAM;OAAS,CAAA;MAEvE,CAAC,KAAa,KAAQ,kBAAC,GAAD;OAAM,MAAM;OAAM,OAAM;OAAS,CAAA;MACnD;OACH;MACF;;EACM,CAAA"}
@@ -14,7 +14,7 @@ function l(l) {
14
14
  "!border-red !text-red": E === "error",
15
15
  "bg-pearl": D
16
16
  }), A = /* @__PURE__ */ c("span", {
17
- className: e("rounded-8 inline-flex shrink-0 items-center justify-center self-start border transition-colors hover:!border-current/60 focus-visible:!border-current/60", t(m), n(m), h, k, p),
17
+ className: e("rounded-8 inline-flex shrink-0 items-center justify-center self-start border transition-colors hover:!border-current/60 has-[:focus-visible]:ring-4 has-[:focus-visible]:ring-black has-[:focus-visible]:ring-offset-2", t(m), n(m), h, k, p),
18
18
  "data-name": "Checkbox",
19
19
  children: [/* @__PURE__ */ s("input", {
20
20
  ...T,
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.js","names":[],"sources":["../../../../lib/ui/forms/checkboxes/Checkbox.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useId, type ComponentPropsWithoutRef, type Ref } from 'react';\nimport { Icon } from '@clubmed/trident-icons';\nimport type { Colors } from '../../types/Colors';\nimport { getBorderColor, getTextColor } from '../../helpers/colors/colors';\nimport { FormControlError } from '@/ui/forms/FormControlError';\nimport { useInternalStatus, type ValidationStatus } from '@/ui/hooks/useInternalStatus';\n\nexport interface CheckboxProps\n extends Omit<ComponentPropsWithoutRef<'input'>, 'size' | 'onChange' | 'value'> {\n color?: Colors;\n size?: string;\n value?: string | number | boolean;\n validationStatus?: ValidationStatus;\n errorMessage?: string;\n ['data-testid']?: string;\n ref?: Ref<HTMLInputElement>;\n onChange?: (event: Event, value: boolean) => void;\n}\n\nexport function Checkbox(props: CheckboxProps) {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n className,\n color = 'black',\n size = 'size-24',\n disabled = false,\n tabIndex = 0,\n validationStatus = 'default',\n errorMessage,\n children,\n ref,\n onChange,\n value,\n 'data-testid': dataTestId,\n ...rest\n } = props;\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n const isDisabled = internalStatus === 'disabled';\n\n const shouldDisplayErrorMessage = internalStatus === 'error' && errorMessage;\n\n // Apply validation styling to checkbox via className\n const checkboxClassName = clsx({\n '!border-red !text-red': internalStatus === 'error',\n 'bg-pearl': isDisabled,\n });\n\n const checkbox = (\n <span\n className={clsx(\n 'rounded-8 inline-flex shrink-0 items-center justify-center self-start border transition-colors hover:!border-current/60 focus-visible:!border-current/60',\n getBorderColor(color),\n getTextColor(color),\n size,\n checkboxClassName,\n className,\n )}\n data-name=\"Checkbox\"\n >\n <input\n {...rest}\n disabled={disabled}\n id={id}\n name={name}\n className=\"peer sr-only\"\n ref={ref}\n tabIndex={tabIndex}\n type=\"checkbox\"\n value={typeof value === 'boolean' ? String(value) : value}\n onChange={(e) => {\n onChange?.(e.nativeEvent, e.target.checked);\n }}\n />\n <Icon\n className=\"scale-1/2 transition-[transform,opacity] peer-checked:scale-auto opacity-0 peer-checked:opacity-100\"\n name=\"CheckDefault\"\n width=\"100%\"\n />\n </span>\n );\n\n if (!children && !errorMessage) {\n return checkbox;\n }\n\n return (\n <div\n className={clsx('flex flex-col space-y-2', className)}\n data-testid={dataTestId || `checkbox-container-${id}`}\n >\n <label\n className={clsx('relative flex items-center gap-8', {\n 'text-grey': disabled,\n })}\n >\n {checkbox}\n {children}\n </label>\n {shouldDisplayErrorMessage && <FormControlError>{errorMessage}</FormControlError>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAoBA,SAAgB,EAAS,GAAsB;CAC7C,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,cACA,WAAQ,SACR,UAAO,WACP,cAAW,IACX,cAAW,GACX,sBAAmB,WACnB,iBACA,aACA,QACA,aACA,UACA,eAAe,GACf,GAAG,MACD,GAEE,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC,EACI,IAAa,MAAmB,YAEhC,IAA4B,MAAmB,WAAW,GAG1D,IAAoB,EAAK;EAC7B,yBAAyB,MAAmB;EAC5C,YAAY;EACb,CAAC,EAEI,IACJ,kBAAC,QAAD;EACE,WAAW,EACT,4JACA,EAAe,EAAM,EACrB,EAAa,EAAM,EACnB,GACA,GACA,EACD;EACD,aAAU;YATZ,CAWE,kBAAC,SAAD;GACE,GAAI;GACM;GACN;GACE;GACN,WAAU;GACL;GACK;GACV,MAAK;GACL,OAAO,OAAO,KAAU,YAAY,OAAO,EAAM,GAAG;GACpD,WAAW,MAAM;AACf,QAAW,EAAE,aAAa,EAAE,OAAO,QAAQ;;GAE7C,CAAA,EACF,kBAAC,GAAD;GACE,WAAU;GACV,MAAK;GACL,OAAM;GACN,CAAA,CACG;;AAOT,QAJI,CAAC,KAAY,CAAC,IACT,IAIP,kBAAC,OAAD;EACE,WAAW,EAAK,2BAA2B,EAAU;EACrD,eAAa,KAAc,sBAAsB;YAFnD,CAIE,kBAAC,SAAD;GACE,WAAW,EAAK,oCAAoC,EAClD,aAAa,GACd,CAAC;aAHJ,CAKG,GACA,EACK;MACP,KAA6B,kBAAC,GAAD,EAAA,UAAmB,GAAgC,CAAA,CAC7E"}
1
+ {"version":3,"file":"Checkbox.js","names":[],"sources":["../../../../lib/ui/forms/checkboxes/Checkbox.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useId, type ComponentPropsWithoutRef, type Ref } from 'react';\nimport { Icon } from '@clubmed/trident-icons';\nimport type { Colors } from '../../types/Colors';\nimport { getBorderColor, getTextColor } from '../../helpers/colors/colors';\nimport { FormControlError } from '@/ui/forms/FormControlError';\nimport { useInternalStatus, type ValidationStatus } from '@/ui/hooks/useInternalStatus';\n\nexport interface CheckboxProps\n extends Omit<ComponentPropsWithoutRef<'input'>, 'size' | 'onChange' | 'value'> {\n color?: Colors;\n size?: string;\n value?: string | number | boolean;\n validationStatus?: ValidationStatus;\n errorMessage?: string;\n ['data-testid']?: string;\n ref?: Ref<HTMLInputElement>;\n onChange?: (event: Event, value: boolean) => void;\n}\n\nexport function Checkbox(props: CheckboxProps) {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n className,\n color = 'black',\n size = 'size-24',\n disabled = false,\n tabIndex = 0,\n validationStatus = 'default',\n errorMessage,\n children,\n ref,\n onChange,\n value,\n 'data-testid': dataTestId,\n ...rest\n } = props;\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n const isDisabled = internalStatus === 'disabled';\n\n const shouldDisplayErrorMessage = internalStatus === 'error' && errorMessage;\n\n // Apply validation styling to checkbox via className\n const checkboxClassName = clsx({\n '!border-red !text-red': internalStatus === 'error',\n 'bg-pearl': isDisabled,\n });\n\n const checkbox = (\n <span\n className={clsx(\n 'rounded-8 inline-flex shrink-0 items-center justify-center self-start border transition-colors hover:!border-current/60 has-[:focus-visible]:ring-4 has-[:focus-visible]:ring-black has-[:focus-visible]:ring-offset-2',\n getBorderColor(color),\n getTextColor(color),\n size,\n checkboxClassName,\n className,\n )}\n data-name=\"Checkbox\"\n >\n <input\n {...rest}\n disabled={disabled}\n id={id}\n name={name}\n className=\"peer sr-only\"\n ref={ref}\n tabIndex={tabIndex}\n type=\"checkbox\"\n value={typeof value === 'boolean' ? String(value) : value}\n onChange={(e) => {\n onChange?.(e.nativeEvent, e.target.checked);\n }}\n />\n <Icon\n className=\"scale-1/2 transition-[transform,opacity] peer-checked:scale-auto opacity-0 peer-checked:opacity-100\"\n name=\"CheckDefault\"\n width=\"100%\"\n />\n </span>\n );\n\n if (!children && !errorMessage) {\n return checkbox;\n }\n\n return (\n <div\n className={clsx('flex flex-col space-y-2', className)}\n data-testid={dataTestId || `checkbox-container-${id}`}\n >\n <label\n className={clsx('relative flex items-center gap-8', {\n 'text-grey': disabled,\n })}\n >\n {checkbox}\n {children}\n </label>\n {shouldDisplayErrorMessage && <FormControlError>{errorMessage}</FormControlError>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAoBA,SAAgB,EAAS,GAAsB;CAC7C,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,cACA,WAAQ,SACR,UAAO,WACP,cAAW,IACX,cAAW,GACX,sBAAmB,WACnB,iBACA,aACA,QACA,aACA,UACA,eAAe,GACf,GAAG,MACD,GAEE,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC,EACI,IAAa,MAAmB,YAEhC,IAA4B,MAAmB,WAAW,GAG1D,IAAoB,EAAK;EAC7B,yBAAyB,MAAmB;EAC5C,YAAY;EACb,CAAC,EAEI,IACJ,kBAAC,QAAD;EACE,WAAW,EACT,0NACA,EAAe,EAAM,EACrB,EAAa,EAAM,EACnB,GACA,GACA,EACD;EACD,aAAU;YATZ,CAWE,kBAAC,SAAD;GACE,GAAI;GACM;GACN;GACE;GACN,WAAU;GACL;GACK;GACV,MAAK;GACL,OAAO,OAAO,KAAU,YAAY,OAAO,EAAM,GAAG;GACpD,WAAW,MAAM;AACf,QAAW,EAAE,aAAa,EAAE,OAAO,QAAQ;;GAE7C,CAAA,EACF,kBAAC,GAAD;GACE,WAAU;GACV,MAAK;GACL,OAAM;GACN,CAAA,CACG;;AAOT,QAJI,CAAC,KAAY,CAAC,IACT,IAIP,kBAAC,OAAD;EACE,WAAW,EAAK,2BAA2B,EAAU;EACrD,eAAa,KAAc,sBAAsB;YAFnD,CAIE,kBAAC,SAAD;GACE,WAAW,EAAK,oCAAoC,EAClD,aAAa,GACd,CAAC;aAHJ,CAKG,GACA,EACK;MACP,KAA6B,kBAAC,GAAD,EAAA,UAAmB,GAAgC,CAAA,CAC7E"}
@@ -41,7 +41,7 @@ function d(d) {
41
41
  disabled: _,
42
42
  required: v,
43
43
  onChange: (e) => A(e.target.value, e.nativeEvent),
44
- className: e("text-b3 rounded-pill w-full border overflow-hidden ps-20 py-[11px] font-normal outline-none", {
44
+ className: e("text-b3 rounded-pill w-full border overflow-hidden ps-20 py-[11px] font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", {
45
45
  "border-middleGrey focus:border-black active:border-black pe-[52px]": (C || b === "default") && O !== "disabled",
46
46
  "bg-white text-black": O !== "disabled",
47
47
  "bg-pearl border-middleGrey": O === "disabled",
@@ -67,7 +67,7 @@ function d(d) {
67
67
  }),
68
68
  !_ && /* @__PURE__ */ l("button", {
69
69
  type: "button",
70
- className: "pointer-events-auto size-24 inline-flex",
70
+ className: "pointer-events-auto size-24 inline-flex outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
71
71
  onClick: N,
72
72
  children: /* @__PURE__ */ l(c, {
73
73
  name: j === "password" ? "Invisible" : "Visible",
@@ -1 +1 @@
1
- {"version":3,"file":"Password.js","names":[],"sources":["../../../../lib/ui/forms/password/Password.tsx"],"sourcesContent":["import { useInternalStatus } from '../../hooks/useInternalStatus';\nimport clsx from 'clsx';\n\nimport { type HTMLInputTypeAttribute, useEffect, useId, useState } from 'react';\n\nimport { useValue } from '../../hooks/useValue';\nimport { FormControl, type FormControlProps } from '../FormControl';\nimport { Icon } from '@clubmed/trident-icons';\nimport { ValidationMessage } from './ValidationMessage';\n\nexport interface PasswordProps<Value> extends FormControlProps<Value> {\n validationMessages?: ValidationMessage[];\n withoutFieldValidation?: boolean;\n}\n\nexport function Password<Value = string>(props: PasswordProps<Value>) {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: initialValue,\n disabled = false,\n required = false,\n hideRequiredStar,\n validationStatus = 'default',\n errorMessage,\n validationMessages = [],\n withoutFieldValidation = false,\n className,\n onChange,\n dataTestId = 'Password',\n ...rest\n } = props;\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n const { value, setValue } = useValue<Value>({\n initialValue,\n onChange,\n });\n const [type, setType] = useState<HTMLInputTypeAttribute>('password');\n\n useEffect(() => {\n if (disabled) {\n setType('password');\n } else {\n setType(type);\n }\n }, [type, disabled]);\n\n const toggleType = () => setType(type === 'password' ? 'text' : 'password');\n\n return (\n <FormControl\n className={className}\n id={id}\n label={label}\n dataName=\"Password\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={withoutFieldValidation ? 'default' : validationStatus}\n errorMessage={errorMessage}\n >\n <div className=\"relative\">\n <input\n {...rest}\n id={id}\n name={name}\n type={type}\n value={value as any}\n disabled={disabled}\n required={required}\n onChange={(e) => setValue(e.target.value as Value, e.nativeEvent)}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden ps-20 py-[11px] font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black pe-[52px]':\n (withoutFieldValidation || validationStatus === 'default') &&\n internalStatus !== 'disabled',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'pe-[84px]':\n !withoutFieldValidation &&\n (validationStatus === 'error' || validationStatus === 'success'),\n 'border-red': !withoutFieldValidation && validationStatus === 'error',\n 'border-green': !withoutFieldValidation && validationStatus === 'success',\n },\n )}\n aria-label={name}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n )}\n >\n <span className=\"ms-auto flex gap-x-8\">\n {!withoutFieldValidation && validationStatus === 'error' && (\n <Icon name=\"CrossDefault\" color=\"red\" width=\"24px\" />\n )}\n\n {!withoutFieldValidation && validationStatus === 'success' && (\n <Icon name=\"CheckDefault\" color=\"green\" width=\"24px\" />\n )}\n\n {!disabled && (\n <button\n type=\"button\"\n className=\"pointer-events-auto size-24 inline-flex\"\n onClick={toggleType}\n >\n <Icon\n name={type === 'password' ? 'Invisible' : 'Visible'}\n width=\"24px\"\n type=\"svg\"\n />\n </button>\n )}\n </span>\n </div>\n </div>\n\n {validationMessages.length > 0 && (\n <div className=\"flex flex-col gap-[10px] mt-8\">\n {validationMessages.map(({ label, status }, index) => (\n <ValidationMessage label={label} status={status} key={index} />\n ))}\n </div>\n )}\n </FormControl>\n );\n}\n"],"mappings":";;;;;;;;;AAeA,SAAgB,EAAyB,GAA6B;CACpE,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,UACA,OAAO,GACP,cAAW,IACX,cAAW,IACX,qBACA,sBAAmB,WACnB,iBACA,wBAAqB,EAAE,EACvB,4BAAyB,IACzB,cACA,aACA,gBAAa,YACb,GAAG,MACD,GAEE,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC,EAEI,EAAE,UAAO,gBAAa,EAAgB;EAC1C;EACA;EACD,CAAC,EACI,CAAC,GAAM,KAAW,EAAiC,WAAW;AAEpE,SAAgB;AACd,EACE,EADE,IACM,aAEA,EAAK;IAEd,CAAC,GAAM,EAAS,CAAC;CAEpB,IAAM,UAAmB,EAAQ,MAAS,aAAa,SAAS,WAAW;AAE3E,QACE,kBAAC,GAAD;EACa;EACP;EACG;EACP,UAAS;EACG;EACF;EACA;EACQ;EAClB,kBAAkB,IAAyB,YAAY;EACzC;YAVhB,CAYE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,SAAD;IACE,GAAI;IACA;IACE;IACA;IACC;IACG;IACA;IACV,WAAW,MAAM,EAAS,EAAE,OAAO,OAAgB,EAAE,YAAY;IACjE,WAAW,EACT,+FACA;KACE,uEACG,KAA0B,MAAqB,cAChD,MAAmB;KACrB,uBAAuB,MAAmB;KAC1C,8BAA8B,MAAmB;KACjD,aACE,CAAC,MACA,MAAqB,WAAW,MAAqB;KACxD,cAAc,CAAC,KAA0B,MAAqB;KAC9D,gBAAgB,CAAC,KAA0B,MAAqB;KACjE,CACF;IACD,cAAY;IACZ,CAAA,EAEF,kBAAC,OAAD;IACE,WAAW,EACT,qFACD;cAED,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,CAAC,KAA0B,MAAqB,WAC/C,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAM,OAAM;OAAS,CAAA;MAGtD,CAAC,KAA0B,MAAqB,aAC/C,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAQ,OAAM;OAAS,CAAA;MAGxD,CAAC,KACA,kBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,SAAS;iBAET,kBAAC,GAAD;QACE,MAAM,MAAS,aAAa,cAAc;QAC1C,OAAM;QACN,MAAK;QACL,CAAA;OACK,CAAA;MAEN;;IACH,CAAA,CACF;MAEL,EAAmB,SAAS,KAC3B,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAmB,KAAK,EAAE,UAAO,aAAU,MAC1C,kBAAC,GAAD;IAA0B;IAAe;IAAsB,EAAT,EAAS,CAC/D;GACE,CAAA,CAEI"}
1
+ {"version":3,"file":"Password.js","names":[],"sources":["../../../../lib/ui/forms/password/Password.tsx"],"sourcesContent":["import { useInternalStatus } from '../../hooks/useInternalStatus';\nimport clsx from 'clsx';\n\nimport { type HTMLInputTypeAttribute, useEffect, useId, useState } from 'react';\n\nimport { useValue } from '../../hooks/useValue';\nimport { FormControl, type FormControlProps } from '../FormControl';\nimport { Icon } from '@clubmed/trident-icons';\nimport { ValidationMessage } from './ValidationMessage';\n\nexport interface PasswordProps<Value> extends FormControlProps<Value> {\n validationMessages?: ValidationMessage[];\n withoutFieldValidation?: boolean;\n}\n\nexport function Password<Value = string>(props: PasswordProps<Value>) {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: initialValue,\n disabled = false,\n required = false,\n hideRequiredStar,\n validationStatus = 'default',\n errorMessage,\n validationMessages = [],\n withoutFieldValidation = false,\n className,\n onChange,\n dataTestId = 'Password',\n ...rest\n } = props;\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n const { value, setValue } = useValue<Value>({\n initialValue,\n onChange,\n });\n const [type, setType] = useState<HTMLInputTypeAttribute>('password');\n\n useEffect(() => {\n if (disabled) {\n setType('password');\n } else {\n setType(type);\n }\n }, [type, disabled]);\n\n const toggleType = () => setType(type === 'password' ? 'text' : 'password');\n\n return (\n <FormControl\n className={className}\n id={id}\n label={label}\n dataName=\"Password\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={withoutFieldValidation ? 'default' : validationStatus}\n errorMessage={errorMessage}\n >\n <div className=\"relative\">\n <input\n {...rest}\n id={id}\n name={name}\n type={type}\n value={value as any}\n disabled={disabled}\n required={required}\n onChange={(e) => setValue(e.target.value as Value, e.nativeEvent)}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden ps-20 py-[11px] font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'border-middleGrey focus:border-black active:border-black pe-[52px]':\n (withoutFieldValidation || validationStatus === 'default') &&\n internalStatus !== 'disabled',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'pe-[84px]':\n !withoutFieldValidation &&\n (validationStatus === 'error' || validationStatus === 'success'),\n 'border-red': !withoutFieldValidation && validationStatus === 'error',\n 'border-green': !withoutFieldValidation && validationStatus === 'success',\n },\n )}\n aria-label={name}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n )}\n >\n <span className=\"ms-auto flex gap-x-8\">\n {!withoutFieldValidation && validationStatus === 'error' && (\n <Icon name=\"CrossDefault\" color=\"red\" width=\"24px\" />\n )}\n\n {!withoutFieldValidation && validationStatus === 'success' && (\n <Icon name=\"CheckDefault\" color=\"green\" width=\"24px\" />\n )}\n\n {!disabled && (\n <button\n type=\"button\"\n className=\"pointer-events-auto size-24 inline-flex outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n onClick={toggleType}\n >\n <Icon\n name={type === 'password' ? 'Invisible' : 'Visible'}\n width=\"24px\"\n type=\"svg\"\n />\n </button>\n )}\n </span>\n </div>\n </div>\n\n {validationMessages.length > 0 && (\n <div className=\"flex flex-col gap-[10px] mt-8\">\n {validationMessages.map(({ label, status }, index) => (\n <ValidationMessage label={label} status={status} key={index} />\n ))}\n </div>\n )}\n </FormControl>\n );\n}\n"],"mappings":";;;;;;;;;AAeA,SAAgB,EAAyB,GAA6B;CACpE,IAAM,IAAa,GAAO,EAEpB,EACJ,QAAK,GACL,UAAO,GACP,UACA,OAAO,GACP,cAAW,IACX,cAAW,IACX,qBACA,sBAAmB,WACnB,iBACA,wBAAqB,EAAE,EACvB,4BAAyB,IACzB,cACA,aACA,gBAAa,YACb,GAAG,MACD,GAEE,IAAiB,EAAkB;EACvC,YAAY;EACZ;EACD,CAAC,EAEI,EAAE,UAAO,gBAAa,EAAgB;EAC1C;EACA;EACD,CAAC,EACI,CAAC,GAAM,KAAW,EAAiC,WAAW;AAEpE,SAAgB;AACd,EACE,EADE,IACM,aAEA,EAAK;IAEd,CAAC,GAAM,EAAS,CAAC;CAEpB,IAAM,UAAmB,EAAQ,MAAS,aAAa,SAAS,WAAW;AAE3E,QACE,kBAAC,GAAD;EACa;EACP;EACG;EACP,UAAS;EACG;EACF;EACA;EACQ;EAClB,kBAAkB,IAAyB,YAAY;EACzC;YAVhB,CAYE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,SAAD;IACE,GAAI;IACA;IACE;IACA;IACC;IACG;IACA;IACV,WAAW,MAAM,EAAS,EAAE,OAAO,OAAgB,EAAE,YAAY;IACjE,WAAW,EACT,yKACA;KACE,uEACG,KAA0B,MAAqB,cAChD,MAAmB;KACrB,uBAAuB,MAAmB;KAC1C,8BAA8B,MAAmB;KACjD,aACE,CAAC,MACA,MAAqB,WAAW,MAAqB;KACxD,cAAc,CAAC,KAA0B,MAAqB;KAC9D,gBAAgB,CAAC,KAA0B,MAAqB;KACjE,CACF;IACD,cAAY;IACZ,CAAA,EAEF,kBAAC,OAAD;IACE,WAAW,EACT,qFACD;cAED,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,CAAC,KAA0B,MAAqB,WAC/C,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAM,OAAM;OAAS,CAAA;MAGtD,CAAC,KAA0B,MAAqB,aAC/C,kBAAC,GAAD;OAAM,MAAK;OAAe,OAAM;OAAQ,OAAM;OAAS,CAAA;MAGxD,CAAC,KACA,kBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,SAAS;iBAET,kBAAC,GAAD;QACE,MAAM,MAAS,aAAa,cAAc;QAC1C,OAAM;QACN,MAAK;QACL,CAAA;OACK,CAAA;MAEN;;IACH,CAAA,CACF;MAEL,EAAmB,SAAS,KAC3B,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAmB,KAAK,EAAE,UAAO,aAAU,MAC1C,kBAAC,GAAD;IAA0B;IAAe;IAAsB,EAAT,EAAS,CAC/D;GACE,CAAA,CAEI"}
@@ -122,7 +122,7 @@ function l({ itinerary: l, direction: u, directionType: d, departureDate: f, dep
122
122
  type: "button",
123
123
  onClick: () => I((e) => !e),
124
124
  "aria-expanded": F,
125
- className: "flex w-full cursor-pointer flex-row items-end justify-between border-none bg-transparent p-0",
125
+ className: "flex w-full cursor-pointer flex-row items-end justify-between border-none bg-transparent p-0 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
126
126
  children: [/* @__PURE__ */ c("div", {
127
127
  className: "flex flex-col text-left",
128
128
  children: [/* @__PURE__ */ s("span", {
@@ -166,7 +166,7 @@ function l({ itinerary: l, direction: u, directionType: d, departureDate: f, dep
166
166
  children: E
167
167
  }), D && /* @__PURE__ */ s("a", {
168
168
  href: D,
169
- className: "font-sans text-b4 underline",
169
+ className: "font-sans text-b4 underline outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill",
170
170
  children: O
171
171
  })]
172
172
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"JourneyDetails.js","names":[],"sources":["../../../lib/ui/journey-card/JourneyDetails.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport type { ComponentPropsWithoutRef } from 'react';\nimport { useState } from 'react';\nimport { Icon } from '@clubmed/trident-icons';\nimport { Tag } from '../Tag';\nimport { ElasticHeight } from '../ElasticHeight';\nimport { FlightTimeline } from './FlightTimeline';\nimport { ItineraryDetails } from './ItineraryDetails';\nimport type { ItineraryDetailsProps } from './ItineraryDetails';\n\nexport interface AirportInfo {\n name: string;\n terminal?: string;\n}\n\nexport interface JourneyDetailsProps extends ComponentPropsWithoutRef<'div'> {\n /** Human-readable leg label, e.g. \"Outbound\" or \"Inbound\" */\n direction: string;\n /** Controls which plane icon is rendered; falls back to `direction` when omitted */\n directionType?: 'Inbound' | 'Outbound';\n departureDate: string;\n departureTime: string;\n arrivalDate: string;\n arrivalTime: string;\n /** Total flight duration, e.g. \"9h30mn\" */\n duration: string;\n departureAirport: AirportInfo;\n arrivalAirport: AirportInfo;\n /** Connection label shown on the timeline, e.g. \"Direct\" or \"1 Stop over\" */\n stopLabel: string;\n /** Number of stopover dots on the timeline */\n nbStopovers?: number;\n airlineLogoSrc: string;\n airlineLogoAlt: string;\n /** Primary operator name */\n operatedBy: string;\n /** Secondary operator line, e.g. \"Operated by Hop\" */\n companyName?: string;\n /** Cabin / luggage summary text */\n characteristics: string;\n /** Carbon footprint text */\n carbonEmission: string;\n carbonEmissionLearnMoreUrl?: string;\n carbonEmissionLearnMoreLabel: string;\n /** How many calendar days after departure the arrival falls */\n plusDays?: number;\n /** Label appended to plusDays value, e.g. \"day\" */\n plusDaysLabel: string;\n /** Local time qualifier shown near arrival time, e.g. \"(local time)\" */\n localTimeLabel: string;\n /** Optional expanded itinerary with per-segment flight cards */\n itinerary?: ItineraryDetailsProps;\n /** When true, the component is displayed in a vertical layout for side-by-side JourneyCards */\n vertical?: boolean;\n}\n\nexport function JourneyDetails({\n itinerary,\n direction,\n directionType,\n departureDate,\n departureTime,\n arrivalDate,\n arrivalTime,\n duration,\n departureAirport,\n arrivalAirport,\n stopLabel,\n nbStopovers,\n airlineLogoSrc,\n airlineLogoAlt,\n operatedBy,\n companyName,\n characteristics,\n carbonEmission,\n carbonEmissionLearnMoreUrl,\n carbonEmissionLearnMoreLabel,\n plusDays,\n plusDaysLabel,\n localTimeLabel,\n className,\n vertical = false,\n ...props\n}: JourneyDetailsProps) {\n const [isOpen, setIsOpen] = useState(false);\n const isOutbound = (directionType ?? direction) === 'Outbound';\n\n return (\n <div\n data-name=\"JourneyDetails\"\n {...props}\n className={clsx('flex h-fit w-full flex-col gap-8', className)}\n >\n {/* Row 1 — direction label + airline logo */}\n <div className=\"flex h-48 w-full flex-row items-center justify-between\">\n <div className=\"flex h-fit flex-row items-center gap-12\">\n <Icon\n name={isOutbound ? 'PlaneOutbound' : 'PlaneInBound'}\n width=\"24px\"\n className=\"text-black\"\n />\n <span className=\"font-sans text-b3 font-semibold\">{direction}</span>\n </div>\n <img\n src={airlineLogoSrc}\n alt={airlineLogoAlt}\n width={100}\n height={28}\n className=\"object-contain\"\n />\n </div>\n\n {/* Row 2 — schedule */}\n <div className=\"flex h-fit w-full flex-col gap-4\">\n <div className=\"flex w-full flex-row items-end justify-between\">\n <div className=\"flex flex-1 flex-col\">\n <span className=\"font-sans text-b4\">{departureDate}</span>\n <span className=\"font-sans text-b1 font-semibold\">{departureTime}</span>\n </div>\n\n <div className=\"flex flex-1 justify-center\">\n <span className=\"text-center font-sans text-b4\">{duration}</span>\n </div>\n\n <div className=\"flex flex-1 flex-col items-end\">\n <div\n className={clsx(\n 'flex',\n !vertical && 'flex-row items-center justify-end gap-4',\n vertical && 'flex-col items-end justify-start gap-0',\n )}\n >\n <span className=\"font-sans text-b4\">{arrivalDate}</span>\n {plusDays && plusDays > 0 && (\n <Tag\n label={`+${plusDays} ${plusDaysLabel}`}\n theme=\"label\"\n backgroundColor=\"black\"\n color=\"white\"\n className=\"rounded-full\"\n />\n )}\n </div>\n <span className=\"text-right font-sans text-b4\">{localTimeLabel}</span>\n <span className=\"text-right font-sans text-b1 font-semibold\">{arrivalTime}</span>\n </div>\n </div>\n\n <FlightTimeline nbStopovers={nbStopovers} />\n\n <div className=\"flex w-full flex-row items-start justify-between\">\n <div className=\"flex flex-1 flex-col\">\n <span className=\"font-sans text-b4 font-semibold\">{departureAirport.name}</span>\n {departureAirport.terminal && (\n <span className=\"font-sans text-b4\">{departureAirport.terminal}</span>\n )}\n </div>\n <span className=\"flex-1 text-center font-sans text-b4\">{stopLabel}</span>\n <div className=\"flex flex-1 flex-col items-end\">\n <span className=\"font-sans text-b4 font-semibold\">{arrivalAirport.name}</span>\n {arrivalAirport.terminal && (\n <span className=\"text-right font-sans text-b4\">{arrivalAirport.terminal}</span>\n )}\n </div>\n </div>\n </div>\n\n {/* Row 3 — operator toggle */}\n <button\n type=\"button\"\n onClick={() => setIsOpen((v) => !v)}\n aria-expanded={isOpen}\n className=\"flex w-full cursor-pointer flex-row items-end justify-between border-none bg-transparent p-0\"\n >\n <div className=\"flex flex-col text-left\">\n <span className=\"font-sans text-b4 font-semibold\">{operatedBy}</span>\n {companyName && <span className=\"font-sans text-b4\">{companyName}</span>}\n </div>\n <Icon\n name=\"ArrowOutlinedUp\"\n width=\"24px\"\n className={clsx('text-black transition-transform duration-200', {\n 'rotate-180': !isOpen,\n })}\n />\n </button>\n\n {/* Rows 4 & 5 — accordion */}\n <ElasticHeight isExpanded={isOpen} innerClassName=\"flex flex-col gap-8\">\n <div className=\"flex h-fit w-full flex-row items-start gap-8\">\n <Icon name=\"Luggage\" width=\"24px\" className=\"shrink-0 text-black\" />\n <span className=\"font-sans text-b4\">{characteristics}</span>\n </div>\n <div className=\"flex h-fit w-full flex-row items-start gap-8\">\n <Icon name=\"HappyToCare\" width=\"24px\" className=\"shrink-0 text-black\" />\n <div className=\"flex flex-col\">\n <span className=\"font-sans text-b4\">{carbonEmission}</span>\n {carbonEmissionLearnMoreUrl && (\n <a href={carbonEmissionLearnMoreUrl} className=\"font-sans text-b4 underline\">\n {carbonEmissionLearnMoreLabel}\n </a>\n )}\n </div>\n </div>\n {itinerary && <ItineraryDetails {...itinerary} />}\n </ElasticHeight>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"JourneyDetails.js","names":[],"sources":["../../../lib/ui/journey-card/JourneyDetails.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport type { ComponentPropsWithoutRef } from 'react';\nimport { useState } from 'react';\nimport { Icon } from '@clubmed/trident-icons';\nimport { Tag } from '../Tag';\nimport { ElasticHeight } from '../ElasticHeight';\nimport { FlightTimeline } from './FlightTimeline';\nimport { ItineraryDetails } from './ItineraryDetails';\nimport type { ItineraryDetailsProps } from './ItineraryDetails';\n\nexport interface AirportInfo {\n name: string;\n terminal?: string;\n}\n\nexport interface JourneyDetailsProps extends ComponentPropsWithoutRef<'div'> {\n /** Human-readable leg label, e.g. \"Outbound\" or \"Inbound\" */\n direction: string;\n /** Controls which plane icon is rendered; falls back to `direction` when omitted */\n directionType?: 'Inbound' | 'Outbound';\n departureDate: string;\n departureTime: string;\n arrivalDate: string;\n arrivalTime: string;\n /** Total flight duration, e.g. \"9h30mn\" */\n duration: string;\n departureAirport: AirportInfo;\n arrivalAirport: AirportInfo;\n /** Connection label shown on the timeline, e.g. \"Direct\" or \"1 Stop over\" */\n stopLabel: string;\n /** Number of stopover dots on the timeline */\n nbStopovers?: number;\n airlineLogoSrc: string;\n airlineLogoAlt: string;\n /** Primary operator name */\n operatedBy: string;\n /** Secondary operator line, e.g. \"Operated by Hop\" */\n companyName?: string;\n /** Cabin / luggage summary text */\n characteristics: string;\n /** Carbon footprint text */\n carbonEmission: string;\n carbonEmissionLearnMoreUrl?: string;\n carbonEmissionLearnMoreLabel: string;\n /** How many calendar days after departure the arrival falls */\n plusDays?: number;\n /** Label appended to plusDays value, e.g. \"day\" */\n plusDaysLabel: string;\n /** Local time qualifier shown near arrival time, e.g. \"(local time)\" */\n localTimeLabel: string;\n /** Optional expanded itinerary with per-segment flight cards */\n itinerary?: ItineraryDetailsProps;\n /** When true, the component is displayed in a vertical layout for side-by-side JourneyCards */\n vertical?: boolean;\n}\n\nexport function JourneyDetails({\n itinerary,\n direction,\n directionType,\n departureDate,\n departureTime,\n arrivalDate,\n arrivalTime,\n duration,\n departureAirport,\n arrivalAirport,\n stopLabel,\n nbStopovers,\n airlineLogoSrc,\n airlineLogoAlt,\n operatedBy,\n companyName,\n characteristics,\n carbonEmission,\n carbonEmissionLearnMoreUrl,\n carbonEmissionLearnMoreLabel,\n plusDays,\n plusDaysLabel,\n localTimeLabel,\n className,\n vertical = false,\n ...props\n}: JourneyDetailsProps) {\n const [isOpen, setIsOpen] = useState(false);\n const isOutbound = (directionType ?? direction) === 'Outbound';\n\n return (\n <div\n data-name=\"JourneyDetails\"\n {...props}\n className={clsx('flex h-fit w-full flex-col gap-8', className)}\n >\n {/* Row 1 — direction label + airline logo */}\n <div className=\"flex h-48 w-full flex-row items-center justify-between\">\n <div className=\"flex h-fit flex-row items-center gap-12\">\n <Icon\n name={isOutbound ? 'PlaneOutbound' : 'PlaneInBound'}\n width=\"24px\"\n className=\"text-black\"\n />\n <span className=\"font-sans text-b3 font-semibold\">{direction}</span>\n </div>\n <img\n src={airlineLogoSrc}\n alt={airlineLogoAlt}\n width={100}\n height={28}\n className=\"object-contain\"\n />\n </div>\n\n {/* Row 2 — schedule */}\n <div className=\"flex h-fit w-full flex-col gap-4\">\n <div className=\"flex w-full flex-row items-end justify-between\">\n <div className=\"flex flex-1 flex-col\">\n <span className=\"font-sans text-b4\">{departureDate}</span>\n <span className=\"font-sans text-b1 font-semibold\">{departureTime}</span>\n </div>\n\n <div className=\"flex flex-1 justify-center\">\n <span className=\"text-center font-sans text-b4\">{duration}</span>\n </div>\n\n <div className=\"flex flex-1 flex-col items-end\">\n <div\n className={clsx(\n 'flex',\n !vertical && 'flex-row items-center justify-end gap-4',\n vertical && 'flex-col items-end justify-start gap-0',\n )}\n >\n <span className=\"font-sans text-b4\">{arrivalDate}</span>\n {plusDays && plusDays > 0 && (\n <Tag\n label={`+${plusDays} ${plusDaysLabel}`}\n theme=\"label\"\n backgroundColor=\"black\"\n color=\"white\"\n className=\"rounded-full\"\n />\n )}\n </div>\n <span className=\"text-right font-sans text-b4\">{localTimeLabel}</span>\n <span className=\"text-right font-sans text-b1 font-semibold\">{arrivalTime}</span>\n </div>\n </div>\n\n <FlightTimeline nbStopovers={nbStopovers} />\n\n <div className=\"flex w-full flex-row items-start justify-between\">\n <div className=\"flex flex-1 flex-col\">\n <span className=\"font-sans text-b4 font-semibold\">{departureAirport.name}</span>\n {departureAirport.terminal && (\n <span className=\"font-sans text-b4\">{departureAirport.terminal}</span>\n )}\n </div>\n <span className=\"flex-1 text-center font-sans text-b4\">{stopLabel}</span>\n <div className=\"flex flex-1 flex-col items-end\">\n <span className=\"font-sans text-b4 font-semibold\">{arrivalAirport.name}</span>\n {arrivalAirport.terminal && (\n <span className=\"text-right font-sans text-b4\">{arrivalAirport.terminal}</span>\n )}\n </div>\n </div>\n </div>\n\n {/* Row 3 — operator toggle */}\n <button\n type=\"button\"\n onClick={() => setIsOpen((v) => !v)}\n aria-expanded={isOpen}\n className=\"flex w-full cursor-pointer flex-row items-end justify-between border-none bg-transparent p-0 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n >\n <div className=\"flex flex-col text-left\">\n <span className=\"font-sans text-b4 font-semibold\">{operatedBy}</span>\n {companyName && <span className=\"font-sans text-b4\">{companyName}</span>}\n </div>\n <Icon\n name=\"ArrowOutlinedUp\"\n width=\"24px\"\n className={clsx('text-black transition-transform duration-200', {\n 'rotate-180': !isOpen,\n })}\n />\n </button>\n\n {/* Rows 4 & 5 — accordion */}\n <ElasticHeight isExpanded={isOpen} innerClassName=\"flex flex-col gap-8\">\n <div className=\"flex h-fit w-full flex-row items-start gap-8\">\n <Icon name=\"Luggage\" width=\"24px\" className=\"shrink-0 text-black\" />\n <span className=\"font-sans text-b4\">{characteristics}</span>\n </div>\n <div className=\"flex h-fit w-full flex-row items-start gap-8\">\n <Icon name=\"HappyToCare\" width=\"24px\" className=\"shrink-0 text-black\" />\n <div className=\"flex flex-col\">\n <span className=\"font-sans text-b4\">{carbonEmission}</span>\n {carbonEmissionLearnMoreUrl && (\n <a\n href={carbonEmissionLearnMoreUrl}\n className=\"font-sans text-b4 underline outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill\"\n >\n {carbonEmissionLearnMoreLabel}\n </a>\n )}\n </div>\n </div>\n {itinerary && <ItineraryDetails {...itinerary} />}\n </ElasticHeight>\n </div>\n );\n}\n"],"mappings":"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"}