@open-mercato/ui 0.6.1-develop.3276.1.1fcd145078 → 0.6.1-develop.3287.1.450f4ffb56

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 (131) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +23 -14
  3. package/dist/backend/AppShell.js +8 -32
  4. package/dist/backend/AppShell.js.map +2 -2
  5. package/dist/backend/CrudForm.js +18 -0
  6. package/dist/backend/CrudForm.js.map +2 -2
  7. package/dist/backend/DataTable.js +149 -53
  8. package/dist/backend/DataTable.js.map +2 -2
  9. package/dist/backend/FilterBar.js +11 -14
  10. package/dist/backend/FilterBar.js.map +2 -2
  11. package/dist/backend/columns/ColumnChooserPanel.js +4 -6
  12. package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
  13. package/dist/backend/filters/ActiveFilterChips.js +142 -0
  14. package/dist/backend/filters/ActiveFilterChips.js.map +7 -0
  15. package/dist/backend/filters/AdvancedFilterBuilder.js +689 -163
  16. package/dist/backend/filters/AdvancedFilterBuilder.js.map +2 -2
  17. package/dist/backend/filters/AdvancedFilterPanel.js +354 -0
  18. package/dist/backend/filters/AdvancedFilterPanel.js.map +7 -0
  19. package/dist/backend/filters/FilterEmptyState.js +41 -0
  20. package/dist/backend/filters/FilterEmptyState.js.map +7 -0
  21. package/dist/backend/filters/FilterFieldPicker.js +148 -0
  22. package/dist/backend/filters/FilterFieldPicker.js.map +7 -0
  23. package/dist/backend/filters/FilteredEmptyResults.js +26 -0
  24. package/dist/backend/filters/FilteredEmptyResults.js.map +7 -0
  25. package/dist/backend/filters/QuickFilters.js +41 -0
  26. package/dist/backend/filters/QuickFilters.js.map +7 -0
  27. package/dist/backend/filters/filterValidation.js +47 -0
  28. package/dist/backend/filters/filterValidation.js.map +7 -0
  29. package/dist/backend/filters/treeReducer.js +159 -0
  30. package/dist/backend/filters/treeReducer.js.map +7 -0
  31. package/dist/backend/hooks/useAdvancedFilter.js +82 -1
  32. package/dist/backend/hooks/useAdvancedFilter.js.map +2 -2
  33. package/dist/backend/inputs/ComboboxInput.js +11 -7
  34. package/dist/backend/inputs/ComboboxInput.js.map +2 -2
  35. package/dist/backend/inputs/EventPatternInput.js +5 -2
  36. package/dist/backend/inputs/EventPatternInput.js.map +2 -2
  37. package/dist/backend/inputs/EventSelect.js +10 -4
  38. package/dist/backend/inputs/EventSelect.js.map +2 -2
  39. package/dist/backend/inputs/LookupSelect.js +51 -47
  40. package/dist/backend/inputs/LookupSelect.js.map +2 -2
  41. package/dist/backend/inputs/PhoneNumberField.js +196 -45
  42. package/dist/backend/inputs/PhoneNumberField.js.map +2 -2
  43. package/dist/backend/inputs/SwitchableMarkdownInput.js.map +2 -2
  44. package/dist/backend/messages/message-compose-form-groups.js +2 -2
  45. package/dist/backend/messages/message-compose-form-groups.js.map +2 -2
  46. package/dist/backend/utils/api.js +18 -1
  47. package/dist/backend/utils/api.js.map +2 -2
  48. package/dist/backend/utils/serverErrors.js +6 -2
  49. package/dist/backend/utils/serverErrors.js.map +2 -2
  50. package/dist/backend/utils/useAutoDiscoveredFields.js +1 -0
  51. package/dist/backend/utils/useAutoDiscoveredFields.js.map +2 -2
  52. package/dist/backend/utils/useCurrentUserId.js +31 -0
  53. package/dist/backend/utils/useCurrentUserId.js.map +7 -0
  54. package/dist/portal/utils/nav.js +17 -0
  55. package/dist/portal/utils/nav.js.map +2 -2
  56. package/dist/primitives/amount-input.js +128 -0
  57. package/dist/primitives/amount-input.js.map +7 -0
  58. package/dist/primitives/button-input.js +47 -0
  59. package/dist/primitives/button-input.js.map +7 -0
  60. package/dist/primitives/card-input.js +133 -0
  61. package/dist/primitives/card-input.js.map +7 -0
  62. package/dist/primitives/dialog.js +7 -5
  63. package/dist/primitives/dialog.js.map +2 -2
  64. package/dist/primitives/email-input.js +29 -0
  65. package/dist/primitives/email-input.js.map +7 -0
  66. package/dist/primitives/password-input.js +64 -0
  67. package/dist/primitives/password-input.js.map +7 -0
  68. package/dist/primitives/search-input.js +57 -0
  69. package/dist/primitives/search-input.js.map +7 -0
  70. package/dist/primitives/select.js +43 -5
  71. package/dist/primitives/select.js.map +2 -2
  72. package/dist/primitives/tag.js +4 -2
  73. package/dist/primitives/tag.js.map +2 -2
  74. package/dist/primitives/website-input.js +56 -0
  75. package/dist/primitives/website-input.js.map +7 -0
  76. package/package.json +3 -3
  77. package/src/backend/AppShell.tsx +7 -30
  78. package/src/backend/CrudForm.tsx +17 -0
  79. package/src/backend/DataTable.tsx +307 -63
  80. package/src/backend/FilterBar.tsx +5 -6
  81. package/src/backend/__tests__/DataTable.extensions.test.tsx +170 -0
  82. package/src/backend/__tests__/serverErrors.test.ts +79 -1
  83. package/src/backend/columns/ColumnChooserPanel.tsx +4 -5
  84. package/src/backend/filters/ActiveFilterChips.tsx +179 -0
  85. package/src/backend/filters/AdvancedFilterBuilder.tsx +840 -237
  86. package/src/backend/filters/AdvancedFilterPanel.tsx +456 -0
  87. package/src/backend/filters/FilterEmptyState.tsx +51 -0
  88. package/src/backend/filters/FilterFieldPicker.tsx +139 -0
  89. package/src/backend/filters/FilteredEmptyResults.tsx +40 -0
  90. package/src/backend/filters/QuickFilters.tsx +62 -0
  91. package/src/backend/filters/__tests__/ActiveFilterChips.test.tsx +75 -0
  92. package/src/backend/filters/__tests__/AdvancedFilterBuilder.test.tsx +210 -0
  93. package/src/backend/filters/__tests__/AdvancedFilterPanel.test.tsx +212 -0
  94. package/src/backend/filters/__tests__/FilterEmptyState.test.tsx +31 -0
  95. package/src/backend/filters/__tests__/FilterFieldPicker.test.tsx +61 -0
  96. package/src/backend/filters/__tests__/FilteredEmptyResults.test.tsx +35 -0
  97. package/src/backend/filters/__tests__/QuickFilters.test.tsx +34 -0
  98. package/src/backend/filters/__tests__/filterValidation.test.ts +69 -0
  99. package/src/backend/filters/__tests__/treeReducer.test.ts +189 -0
  100. package/src/backend/filters/filterValidation.ts +61 -0
  101. package/src/backend/filters/treeReducer.ts +201 -0
  102. package/src/backend/hooks/__tests__/useAdvancedFilter.test.tsx +213 -0
  103. package/src/backend/hooks/useAdvancedFilter.ts +130 -0
  104. package/src/backend/inputs/ComboboxInput.tsx +32 -26
  105. package/src/backend/inputs/EventPatternInput.tsx +11 -2
  106. package/src/backend/inputs/EventSelect.tsx +13 -4
  107. package/src/backend/inputs/LookupSelect.tsx +75 -53
  108. package/src/backend/inputs/PhoneNumberField.tsx +237 -47
  109. package/src/backend/inputs/SwitchableMarkdownInput.tsx +30 -0
  110. package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +4 -0
  111. package/src/backend/inputs/__tests__/PhoneNumberField.test.tsx +4 -0
  112. package/src/backend/messages/message-compose-form-groups.tsx +2 -2
  113. package/src/backend/utils/api.ts +27 -2
  114. package/src/backend/utils/serverErrors.ts +15 -8
  115. package/src/backend/utils/useAutoDiscoveredFields.ts +2 -0
  116. package/src/backend/utils/useCurrentUserId.ts +40 -0
  117. package/src/portal/utils/__tests__/nav.test.ts +80 -0
  118. package/src/portal/utils/nav.ts +23 -1
  119. package/src/primitives/__tests__/password-input.test.tsx +93 -0
  120. package/src/primitives/__tests__/search-input.test.tsx +95 -0
  121. package/src/primitives/__tests__/zindex-overlay.test.tsx +26 -0
  122. package/src/primitives/amount-input.tsx +184 -0
  123. package/src/primitives/button-input.tsx +68 -0
  124. package/src/primitives/card-input.tsx +193 -0
  125. package/src/primitives/dialog.tsx +18 -7
  126. package/src/primitives/email-input.tsx +36 -0
  127. package/src/primitives/password-input.tsx +100 -0
  128. package/src/primitives/search-input.tsx +87 -0
  129. package/src/primitives/select.tsx +98 -11
  130. package/src/primitives/tag.tsx +3 -1
  131. package/src/primitives/website-input.tsx +72 -0
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Eye, EyeOff, Lock } from "lucide-react";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ import { cn } from "@open-mercato/shared/lib/utils";
7
+ import { inputWrapperVariants, inputElementVariants } from "./input.js";
8
+ const PasswordInput = React.forwardRef(
9
+ ({
10
+ className,
11
+ inputClassName,
12
+ size,
13
+ showLockIcon = true,
14
+ revealable = true,
15
+ revealed: revealedProp,
16
+ onRevealedChange,
17
+ showLabel,
18
+ hideLabel,
19
+ disabled,
20
+ ...props
21
+ }, ref) => {
22
+ const t = useT();
23
+ const resolvedShowLabel = showLabel ?? t("ui.inputs.passwordInput.show", "Show password");
24
+ const resolvedHideLabel = hideLabel ?? t("ui.inputs.passwordInput.hide", "Hide password");
25
+ const [internalRevealed, setInternalRevealed] = React.useState(false);
26
+ const isControlled = revealedProp !== void 0;
27
+ const revealed = isControlled ? revealedProp : internalRevealed;
28
+ const toggleRevealed = React.useCallback(() => {
29
+ const next = !revealed;
30
+ if (!isControlled) setInternalRevealed(next);
31
+ onRevealedChange?.(next);
32
+ }, [revealed, isControlled, onRevealedChange]);
33
+ return /* @__PURE__ */ jsxs("div", { className: cn(inputWrapperVariants({ size }), className), "data-slot": "password-input-wrapper", children: [
34
+ showLockIcon ? /* @__PURE__ */ jsx(Lock, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }) : null,
35
+ /* @__PURE__ */ jsx(
36
+ "input",
37
+ {
38
+ ref,
39
+ type: revealed ? "text" : "password",
40
+ disabled,
41
+ className: cn(inputElementVariants({ size }), inputClassName),
42
+ ...props
43
+ }
44
+ ),
45
+ revealable ? /* @__PURE__ */ jsx(
46
+ "button",
47
+ {
48
+ type: "button",
49
+ onClick: toggleRevealed,
50
+ "aria-label": revealed ? resolvedHideLabel : resolvedShowLabel,
51
+ "aria-pressed": revealed,
52
+ disabled,
53
+ className: "flex shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50",
54
+ children: revealed ? /* @__PURE__ */ jsx(EyeOff, { className: "size-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Eye, { className: "size-4", "aria-hidden": "true" })
55
+ }
56
+ ) : null
57
+ ] });
58
+ }
59
+ );
60
+ PasswordInput.displayName = "PasswordInput";
61
+ export {
62
+ PasswordInput
63
+ };
64
+ //# sourceMappingURL=password-input.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/password-input.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Eye, EyeOff, Lock } from 'lucide-react'\nimport type { VariantProps } from 'class-variance-authority'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { inputWrapperVariants, inputElementVariants } from './input'\n\nexport type PasswordInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, 'size' | 'type'> &\n VariantProps<typeof inputWrapperVariants> & {\n /** Render the leading lock icon per Figma `Text Input [1.1]` Password variant. Defaults to `true`. */\n showLockIcon?: boolean\n /** Allow the user to toggle reveal (eye / eye-off). Defaults to `true`. */\n revealable?: boolean\n /** Optional controlled reveal state (otherwise managed internally). */\n revealed?: boolean\n /** Called when the reveal state changes. */\n onRevealedChange?: (next: boolean) => void\n /** Optional className on the wrapper. */\n className?: string\n /** Optional className on the inner `<input>`. */\n inputClassName?: string\n /** Translated aria-label for the reveal button when password is hidden. */\n showLabel?: string\n /** Translated aria-label for the reveal button when password is shown. */\n hideLabel?: string\n }\n\n/**\n * Password input matching Figma `Text Input [1.1]` (node `266:5251`) **Password** variant \u2014 a\n * trailing `Eye` / `EyeOff` toggle that switches the inner `<input>`'s `type` between `\"password\"`\n * (default) and `\"text\"`. The toggle is a proper `<button>` (focusable, screen-reader labelled).\n *\n * Internally managed reveal state when `revealed` / `onRevealedChange` are not passed. Otherwise\n * the consumer owns the toggle state (useful for \"show password\" master toggle on login screens).\n */\nexport const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(\n (\n {\n className,\n inputClassName,\n size,\n showLockIcon = true,\n revealable = true,\n revealed: revealedProp,\n onRevealedChange,\n showLabel,\n hideLabel,\n disabled,\n ...props\n },\n ref,\n ) => {\n const t = useT()\n const resolvedShowLabel = showLabel ?? t('ui.inputs.passwordInput.show', 'Show password')\n const resolvedHideLabel = hideLabel ?? t('ui.inputs.passwordInput.hide', 'Hide password')\n const [internalRevealed, setInternalRevealed] = React.useState(false)\n const isControlled = revealedProp !== undefined\n const revealed = isControlled ? revealedProp! : internalRevealed\n\n const toggleRevealed = React.useCallback(() => {\n const next = !revealed\n if (!isControlled) setInternalRevealed(next)\n onRevealedChange?.(next)\n }, [revealed, isControlled, onRevealedChange])\n\n return (\n <div className={cn(inputWrapperVariants({ size }), className)} data-slot=\"password-input-wrapper\">\n {showLockIcon ? (\n <Lock className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n <input\n ref={ref}\n type={revealed ? 'text' : 'password'}\n disabled={disabled}\n className={cn(inputElementVariants({ size }), inputClassName)}\n {...props}\n />\n {revealable ? (\n <button\n type=\"button\"\n onClick={toggleRevealed}\n aria-label={revealed ? resolvedHideLabel : resolvedShowLabel}\n aria-pressed={revealed}\n disabled={disabled}\n className=\"flex shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {revealed ? (\n <EyeOff className=\"size-4\" aria-hidden=\"true\" />\n ) : (\n <Eye className=\"size-4\" aria-hidden=\"true\" />\n )}\n </button>\n ) : null}\n </div>\n )\n },\n)\nPasswordInput.displayName = 'PasswordInput'\n"],
5
+ "mappings": ";AAoEM,SAEI,KAFJ;AAlEN,YAAY,WAAW;AACvB,SAAS,KAAK,QAAQ,YAAY;AAElC,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,sBAAsB,4BAA4B;AA8BpD,MAAM,gBAAgB,MAAM;AAAA,EACjC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,aAAa;AAAA,IACb,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,IAAI,KAAK;AACf,UAAM,oBAAoB,aAAa,EAAE,gCAAgC,eAAe;AACxF,UAAM,oBAAoB,aAAa,EAAE,gCAAgC,eAAe;AACxF,UAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,KAAK;AACpE,UAAM,eAAe,iBAAiB;AACtC,UAAM,WAAW,eAAe,eAAgB;AAEhD,UAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,YAAM,OAAO,CAAC;AACd,UAAI,CAAC,aAAc,qBAAoB,IAAI;AAC3C,yBAAmB,IAAI;AAAA,IACzB,GAAG,CAAC,UAAU,cAAc,gBAAgB,CAAC;AAE7C,WACE,qBAAC,SAAI,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,SAAS,GAAG,aAAU,0BACtE;AAAA,qBACC,oBAAC,QAAK,WAAU,yCAAwC,eAAY,QAAO,IACzE;AAAA,MACJ;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,MAAM,WAAW,SAAS;AAAA,UAC1B;AAAA,UACA,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,cAAc;AAAA,UAC3D,GAAG;AAAA;AAAA,MACN;AAAA,MACC,aACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY,WAAW,oBAAoB;AAAA,UAC3C,gBAAc;AAAA,UACd;AAAA,UACA,WAAU;AAAA,UAET,qBACC,oBAAC,UAAO,WAAU,UAAS,eAAY,QAAO,IAE9C,oBAAC,OAAI,WAAU,UAAS,eAAY,QAAO;AAAA;AAAA,MAE/C,IACE;AAAA,OACN;AAAA,EAEJ;AACF;AACA,cAAc,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Search, X } from "lucide-react";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ import { cn } from "@open-mercato/shared/lib/utils";
7
+ import { inputWrapperVariants, inputElementVariants } from "./input.js";
8
+ const SearchInput = React.forwardRef(
9
+ ({ className, inputClassName, size, value, onChange, onClear, clearable = true, clearLabel, placeholder, disabled, ...props }, ref) => {
10
+ const t = useT();
11
+ const resolvedPlaceholder = placeholder ?? t("ui.inputs.searchInput.placeholder", "Search\u2026");
12
+ const resolvedClearLabel = clearLabel ?? t("ui.inputs.searchInput.clear", "Clear search");
13
+ const showClear = clearable && value.length > 0 && !disabled;
14
+ const handleClear = React.useCallback(() => {
15
+ if (onClear) onClear();
16
+ else onChange("");
17
+ }, [onChange, onClear]);
18
+ return /* @__PURE__ */ jsxs("div", { className: cn(inputWrapperVariants({ size }), className), "data-slot": "search-input-wrapper", children: [
19
+ /* @__PURE__ */ jsx(Search, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
20
+ /* @__PURE__ */ jsx(
21
+ "input",
22
+ {
23
+ ref,
24
+ type: "search",
25
+ inputMode: "search",
26
+ autoComplete: "off",
27
+ placeholder: resolvedPlaceholder,
28
+ value,
29
+ onChange: (event) => onChange(event.target.value),
30
+ disabled,
31
+ className: cn(
32
+ inputElementVariants({ size }),
33
+ // Disable the native search clear button — we render our own.
34
+ "[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none",
35
+ inputClassName
36
+ ),
37
+ ...props
38
+ }
39
+ ),
40
+ showClear ? /* @__PURE__ */ jsx(
41
+ "button",
42
+ {
43
+ type: "button",
44
+ onClick: handleClear,
45
+ "aria-label": resolvedClearLabel,
46
+ className: "flex shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
47
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
48
+ }
49
+ ) : null
50
+ ] });
51
+ }
52
+ );
53
+ SearchInput.displayName = "SearchInput";
54
+ export {
55
+ SearchInput
56
+ };
57
+ //# sourceMappingURL=search-input.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/search-input.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Search, X } from 'lucide-react'\nimport type { VariantProps } from 'class-variance-authority'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { inputWrapperVariants, inputElementVariants } from './input'\n\nexport type SearchInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, 'size' | 'type' | 'onChange'> &\n VariantProps<typeof inputWrapperVariants> & {\n /** Controlled value. */\n value: string\n /** Called on every keystroke with the new value. */\n onChange: (next: string) => void\n /**\n * Called when the user presses the trailing \u00D7 button.\n * Defaults to `onChange('')` \u2014 pass an explicit handler to also reset adjacent state\n * (e.g. cancel an in-flight request, reset paging).\n */\n onClear?: () => void\n /** Show the trailing \u00D7 button when the value is non-empty. Defaults to `true`. */\n clearable?: boolean\n /** Optional className on the wrapper. */\n className?: string\n /** Optional className on the inner `<input>`. */\n inputClassName?: string\n /** Translated aria-label for the clear button. Defaults to `t('ui.inputs.searchInput.clear', 'Clear search')`. */\n clearLabel?: string\n }\n\n/**\n * Search input matching Figma `Text Input [1.1]` (node `266:5251`) **Search** variant \u2014 a leading\n * `Search` icon, the text input, and an optional trailing `X` button to clear the field. Built on\n * the shared `inputWrapperVariants` / `inputElementVariants` CVA so the visual contract stays in\n * sync with the foundation `Input` primitive.\n *\n * The clear button is rendered as a proper `<button>` (not inside an `aria-hidden` decorative\n * span), so it remains keyboard-focusable and screen-reader-accessible.\n */\nexport const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(\n ({ className, inputClassName, size, value, onChange, onClear, clearable = true, clearLabel, placeholder, disabled, ...props }, ref) => {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.searchInput.placeholder', 'Search\u2026')\n const resolvedClearLabel = clearLabel ?? t('ui.inputs.searchInput.clear', 'Clear search')\n const showClear = clearable && value.length > 0 && !disabled\n\n const handleClear = React.useCallback(() => {\n if (onClear) onClear()\n else onChange('')\n }, [onChange, onClear])\n\n return (\n <div className={cn(inputWrapperVariants({ size }), className)} data-slot=\"search-input-wrapper\">\n <Search className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <input\n ref={ref}\n type=\"search\"\n inputMode=\"search\"\n autoComplete=\"off\"\n placeholder={resolvedPlaceholder}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n disabled={disabled}\n className={cn(\n inputElementVariants({ size }),\n // Disable the native search clear button \u2014 we render our own.\n '[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none',\n inputClassName,\n )}\n {...props}\n />\n {showClear ? (\n <button\n type=\"button\"\n onClick={handleClear}\n aria-label={resolvedClearLabel}\n className=\"flex shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </div>\n )\n },\n)\nSearchInput.displayName = 'SearchInput'\n"],
5
+ "mappings": ";AAqDM,SACE,KADF;AAnDN,YAAY,WAAW;AACvB,SAAS,QAAQ,SAAS;AAE1B,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,sBAAsB,4BAA4B;AAiCpD,MAAM,cAAc,MAAM;AAAA,EAC/B,CAAC,EAAE,WAAW,gBAAgB,MAAM,OAAO,UAAU,SAAS,YAAY,MAAM,YAAY,aAAa,UAAU,GAAG,MAAM,GAAG,QAAQ;AACrI,UAAM,IAAI,KAAK;AACf,UAAM,sBAAsB,eAAe,EAAE,qCAAqC,cAAS;AAC3F,UAAM,qBAAqB,cAAc,EAAE,+BAA+B,cAAc;AACxF,UAAM,YAAY,aAAa,MAAM,SAAS,KAAK,CAAC;AAEpD,UAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,UAAI,QAAS,SAAQ;AAAA,UAChB,UAAS,EAAE;AAAA,IAClB,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,WACE,qBAAC,SAAI,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,SAAS,GAAG,aAAU,wBACvE;AAAA,0BAAC,UAAO,WAAU,yCAAwC,eAAY,QAAO;AAAA,MAC7E;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,MAAK;AAAA,UACL,WAAU;AAAA,UACV,cAAa;AAAA,UACb,aAAa;AAAA,UACb;AAAA,UACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,UAChD;AAAA,UACA,WAAW;AAAA,YACT,qBAAqB,EAAE,KAAK,CAAC;AAAA;AAAA,YAE7B;AAAA,YACA;AAAA,UACF;AAAA,UACC,GAAG;AAAA;AAAA,MACN;AAAA,MACC,YACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY;AAAA,UACZ,WAAU;AAAA,UAEV,8BAAC,KAAE,WAAU,UAAS,eAAY,QAAO;AAAA;AAAA,MAC3C,IACE;AAAA,OACN;AAAA,EAEJ;AACF;AACA,YAAY,cAAc;",
6
+ "names": []
7
+ }
@@ -35,6 +35,22 @@ const SelectTrigger = React.forwardRef(({ className, size, children, ...props },
35
35
  }
36
36
  ));
37
37
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
38
+ const SelectTriggerLeading = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
39
+ "span",
40
+ {
41
+ ref,
42
+ "data-slot": "select-trigger-leading",
43
+ "aria-hidden": "true",
44
+ className: cn(
45
+ "flex shrink-0 items-center justify-center",
46
+ "[&>svg]:size-4 [&>svg]:shrink-0 [&>img]:size-5 [&>img]:shrink-0",
47
+ className
48
+ ),
49
+ ...props,
50
+ children
51
+ }
52
+ ));
53
+ SelectTriggerLeading.displayName = "SelectTriggerLeading";
38
54
  const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
39
55
  SelectPrimitive.ScrollUpButton,
40
56
  {
@@ -74,7 +90,7 @@ const SelectContent = React.forwardRef(({ className, children, position = "poppe
74
90
  SelectPrimitive.Viewport,
75
91
  {
76
92
  className: cn(
77
- "p-1 max-h-[var(--radix-select-content-available-height)] overflow-y-auto",
93
+ "p-2 max-h-[var(--radix-select-content-available-height)] overflow-y-auto",
78
94
  position === "popper" && "w-full min-w-[var(--radix-select-trigger-width)]"
79
95
  ),
80
96
  children
@@ -89,7 +105,11 @@ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
89
105
  SelectPrimitive.Label,
90
106
  {
91
107
  ref,
92
- className: cn("px-2 py-1.5 text-overline uppercase text-muted-foreground", className),
108
+ className: cn(
109
+ "px-2 pt-2 pb-1 text-overline font-semibold uppercase tracking-wider text-muted-foreground/80",
110
+ "select-none",
111
+ className
112
+ ),
93
113
  ...props
94
114
  }
95
115
  ));
@@ -99,7 +119,7 @@ const SelectItem = React.forwardRef(({ className, children, ...props }, ref) =>
99
119
  {
100
120
  ref,
101
121
  className: cn(
102
- "relative flex w-full cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none transition-colors",
122
+ "relative flex w-full cursor-default select-none items-center gap-2 rounded-md p-2 text-sm outline-none transition-colors",
103
123
  "focus:bg-muted focus:text-foreground",
104
124
  "data-[state=checked]:bg-muted/70 data-[state=checked]:text-foreground",
105
125
  "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
@@ -108,12 +128,28 @@ const SelectItem = React.forwardRef(({ className, children, ...props }, ref) =>
108
128
  ),
109
129
  ...props,
110
130
  children: [
111
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }) }),
112
- /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "size-4 text-foreground", "aria-hidden": "true" }) }) })
131
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "flex min-w-0 flex-1 items-center gap-2 truncate", children }) }),
132
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { className: "ml-auto flex shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx(Check, { className: "size-4 text-foreground", "aria-hidden": "true" }) })
113
133
  ]
114
134
  }
115
135
  ));
116
136
  SelectItem.displayName = SelectPrimitive.Item.displayName;
137
+ const SelectItemLeading = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
138
+ "span",
139
+ {
140
+ ref,
141
+ "data-slot": "select-item-leading",
142
+ "aria-hidden": "true",
143
+ className: cn(
144
+ "flex shrink-0 items-center justify-center",
145
+ "[&>svg]:size-4 [&>svg]:shrink-0 [&>img]:size-5 [&>img]:shrink-0",
146
+ className
147
+ ),
148
+ ...props,
149
+ children
150
+ }
151
+ ));
152
+ SelectItemLeading.displayName = "SelectItemLeading";
117
153
  const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
118
154
  SelectPrimitive.Separator,
119
155
  {
@@ -145,11 +181,13 @@ export {
145
181
  SelectContent,
146
182
  SelectGroup,
147
183
  SelectItem,
184
+ SelectItemLeading,
148
185
  SelectLabel,
149
186
  SelectScrollDownButton,
150
187
  SelectScrollUpButton,
151
188
  SelectSeparator,
152
189
  SelectTrigger,
190
+ SelectTriggerLeading,
153
191
  SelectValue,
154
192
  selectTriggerVariants
155
193
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/primitives/select.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport * as SelectPrimitive from '@radix-ui/react-select'\nimport { Check, ChevronDown, ChevronUp } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst selectTriggerVariants = cva(\n 'inline-flex w-full items-center justify-between gap-2 rounded-md border border-input bg-background shadow-xs transition-colors outline-none placeholder:text-muted-foreground hover:bg-muted/40 focus:outline-none focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground disabled:cursor-not-allowed disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:[&_svg]:opacity-60 aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0',\n {\n variants: {\n size: {\n xs: 'h-7 px-2 text-xs',\n sm: 'h-8 px-2.5 text-xs',\n default: 'h-9 px-3 text-sm',\n lg: 'h-10 px-3 text-sm',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n)\n\nexport type SelectTriggerProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> &\n VariantProps<typeof selectTriggerVariants>\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, size, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(selectTriggerVariants({ size }), className)}\n data-slot=\"select-trigger\"\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"text-muted-foreground\" aria-hidden=\"true\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn('flex cursor-default items-center justify-center py-1 text-muted-foreground', className)}\n {...props}\n >\n <ChevronUp className=\"size-4\" aria-hidden=\"true\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn('flex cursor-default items-center justify-center py-1 text-muted-foreground', className)}\n {...props}\n >\n <ChevronDown className=\"size-4\" aria-hidden=\"true\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = 'popper', sideOffset = 4, ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n className={cn(\n 'relative z-popover min-w-[8rem] overflow-hidden rounded-md border border-input bg-popover text-popover-foreground shadow-md outline-none',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n position === 'popper' && 'w-full min-w-[var(--radix-select-trigger-width)]',\n className\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-1 max-h-[var(--radix-select-content-available-height)] overflow-y-auto',\n position === 'popper' && 'w-full min-w-[var(--radix-select-trigger-width)]'\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn('px-2 py-1.5 text-overline uppercase text-muted-foreground', className)}\n {...props}\n />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none transition-colors',\n 'focus:bg-muted focus:text-foreground',\n 'data-[state=checked]:bg-muted/70 data-[state=checked]:text-foreground',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n '[&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0',\n className\n )}\n {...props}\n >\n <span className=\"flex-1 truncate\">\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </span>\n <span className=\"absolute right-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"size-4 text-foreground\" aria-hidden=\"true\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 my-1 h-px bg-border', className)}\n {...props}\n />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\n/**\n * Wraps Radix `Select.Root` to absorb the controlled/uncontrolled transition\n * many call sites trigger by passing `value={x || undefined}`. React fires\n * \"Select is changing from uncontrolled to controlled\" the moment value flips\n * from undefined to a defined string, and Radix's internal state ends up in\n * an inconsistent shape (dropdown flashes, selections no-op). Coercing\n * `undefined` \u2192 `''` keeps Radix in stable controlled mode for the lifetime\n * of the component while preserving \"no selection\" semantics \u2014 Radix simply\n * matches no SelectItem and `SelectValue` falls back to the placeholder.\n */\nconst Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>\n>(({ value, defaultValue, onValueChange, ...props }, _ref) => {\n const isControlled = value !== undefined || onValueChange !== undefined\n if (!isControlled) {\n return <SelectPrimitive.Root defaultValue={defaultValue} {...props} />\n }\n return (\n <SelectPrimitive.Root\n value={value ?? ''}\n onValueChange={onValueChange}\n {...props}\n />\n )\n}) as unknown as typeof SelectPrimitive.Root\n;(Select as React.ComponentType).displayName = 'Select'\nconst SelectGroup = SelectPrimitive.Group\nconst SelectValue = SelectPrimitive.Value\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n selectTriggerVariants,\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport * as SelectPrimitive from '@radix-ui/react-select'\nimport { Check, ChevronDown, ChevronUp } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst selectTriggerVariants = cva(\n 'inline-flex w-full items-center justify-between gap-2 rounded-md border border-input bg-background shadow-xs transition-colors outline-none placeholder:text-muted-foreground hover:bg-muted/40 focus:outline-none focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground disabled:cursor-not-allowed disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:[&_svg]:opacity-60 aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0',\n {\n variants: {\n size: {\n xs: 'h-7 px-2 text-xs',\n sm: 'h-8 px-2.5 text-xs',\n default: 'h-9 px-3 text-sm',\n lg: 'h-10 px-3 text-sm',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n)\n\nexport type SelectTriggerProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> &\n VariantProps<typeof selectTriggerVariants>\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, size, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(selectTriggerVariants({ size }), className)}\n data-slot=\"select-trigger\"\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"text-muted-foreground\" aria-hidden=\"true\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\n/**\n * Leading visual slot inside a `SelectTrigger` \u2014 render flags, avatars,\n * provider logos, brand marks, or company icons before the `<SelectValue>`.\n *\n * Mirrors the Type variants in the Figma DS (Basic / Country / Avatar /\n * Provider / Brand / Company \u2014 Figma `Select [1.1]`, node `270:1085`).\n *\n * Render inside `SelectTrigger`, BEFORE the `<SelectValue>`:\n *\n * ```tsx\n * <SelectTrigger>\n * <SelectTriggerLeading><Avatar size=\"sm\" label={user.name} /></SelectTriggerLeading>\n * <SelectValue placeholder=\"Select user\" />\n * </SelectTrigger>\n * ```\n *\n * Sizing follows the trigger: icons render `size-4`, images render `size-5`\n * by default \u2014 pass your own classes on the inner element when you need a\n * different visual size (e.g. country flags `h-3 w-4`).\n */\nconst SelectTriggerLeading = React.forwardRef<\n HTMLSpanElement,\n React.HTMLAttributes<HTMLSpanElement>\n>(({ className, children, ...props }, ref) => (\n <span\n ref={ref}\n data-slot=\"select-trigger-leading\"\n aria-hidden=\"true\"\n className={cn(\n 'flex shrink-0 items-center justify-center',\n '[&>svg]:size-4 [&>svg]:shrink-0 [&>img]:size-5 [&>img]:shrink-0',\n className\n )}\n {...props}\n >\n {children}\n </span>\n))\nSelectTriggerLeading.displayName = 'SelectTriggerLeading'\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn('flex cursor-default items-center justify-center py-1 text-muted-foreground', className)}\n {...props}\n >\n <ChevronUp className=\"size-4\" aria-hidden=\"true\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn('flex cursor-default items-center justify-center py-1 text-muted-foreground', className)}\n {...props}\n >\n <ChevronDown className=\"size-4\" aria-hidden=\"true\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = 'popper', sideOffset = 4, ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n className={cn(\n 'relative z-popover min-w-[8rem] overflow-hidden rounded-md border border-input bg-popover text-popover-foreground shadow-md outline-none',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n position === 'popper' && 'w-full min-w-[var(--radix-select-trigger-width)]',\n className\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-2 max-h-[var(--radix-select-content-available-height)] overflow-y-auto',\n position === 'popper' && 'w-full min-w-[var(--radix-select-trigger-width)]'\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\n 'px-2 pt-2 pb-1 text-overline font-semibold uppercase tracking-wider text-muted-foreground/80',\n 'select-none',\n className\n )}\n {...props}\n />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center gap-2 rounded-md p-2 text-sm outline-none transition-colors',\n 'focus:bg-muted focus:text-foreground',\n 'data-[state=checked]:bg-muted/70 data-[state=checked]:text-foreground',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n '[&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0',\n className\n )}\n {...props}\n >\n <SelectPrimitive.ItemText asChild>\n <span className=\"flex min-w-0 flex-1 items-center gap-2 truncate\">\n {children}\n </span>\n </SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator className=\"ml-auto flex shrink-0 items-center justify-center\">\n <Check className=\"size-4 text-foreground\" aria-hidden=\"true\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\n/**\n * Leading visual slot inside a `SelectItem` \u2014 mirror of `SelectTriggerLeading`\n * for dropdown rows. Compose with the DS `Avatar`, country flag images, or\n * `lucide-react` icons.\n *\n * Render inside the `<SelectItem>` children, before the row label:\n *\n * ```tsx\n * <SelectItem value=\"us\">\n * <SelectItemLeading><span className=\"h-3 w-4 rounded-sm overflow-hidden\">\uD83C\uDDFA\uD83C\uDDF8</span></SelectItemLeading>\n * United States\n * </SelectItem>\n * ```\n *\n * NOTE: `SelectPrimitive.ItemText` only reads the text node siblings for the\n * trigger preview \u2014 wrap the leading slot OUTSIDE `ItemText` (which this\n * primitive does by default, since `SelectItem`'s `children` are placed inside\n * `<SelectPrimitive.ItemText>` only via the inner `<span className=\"flex-1\n * truncate\">`). Place the `SelectItemLeading` alongside the row text and the\n * label will read correctly in the trigger preview.\n */\nconst SelectItemLeading = React.forwardRef<\n HTMLSpanElement,\n React.HTMLAttributes<HTMLSpanElement>\n>(({ className, children, ...props }, ref) => (\n <span\n ref={ref}\n data-slot=\"select-item-leading\"\n aria-hidden=\"true\"\n className={cn(\n 'flex shrink-0 items-center justify-center',\n '[&>svg]:size-4 [&>svg]:shrink-0 [&>img]:size-5 [&>img]:shrink-0',\n className\n )}\n {...props}\n >\n {children}\n </span>\n))\nSelectItemLeading.displayName = 'SelectItemLeading'\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 my-1 h-px bg-border', className)}\n {...props}\n />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\n/**\n * Wraps Radix `Select.Root` to absorb the controlled/uncontrolled transition\n * many call sites trigger by passing `value={x || undefined}`. React fires\n * \"Select is changing from uncontrolled to controlled\" the moment value flips\n * from undefined to a defined string, and Radix's internal state ends up in\n * an inconsistent shape (dropdown flashes, selections no-op). Coercing\n * `undefined` \u2192 `''` keeps Radix in stable controlled mode for the lifetime\n * of the component while preserving \"no selection\" semantics \u2014 Radix simply\n * matches no SelectItem and `SelectValue` falls back to the placeholder.\n */\nconst Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>\n>(({ value, defaultValue, onValueChange, ...props }, _ref) => {\n const isControlled = value !== undefined || onValueChange !== undefined\n if (!isControlled) {\n return <SelectPrimitive.Root defaultValue={defaultValue} {...props} />\n }\n return (\n <SelectPrimitive.Root\n value={value ?? ''}\n onValueChange={onValueChange}\n {...props}\n />\n )\n}) as unknown as typeof SelectPrimitive.Root\n;(Select as React.ComponentType).displayName = 'Select'\nconst SelectGroup = SelectPrimitive.Group\nconst SelectValue = SelectPrimitive.Value\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectTriggerLeading,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectItemLeading,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n selectTriggerVariants,\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,7 +13,8 @@ const tagVariants = cva(
13
13
  error: "border-status-error-border bg-status-error-bg text-status-error-text",
14
14
  info: "border-status-info-border bg-status-info-bg text-status-info-text",
15
15
  neutral: "border-status-neutral-border bg-status-neutral-bg text-status-neutral-text",
16
- brand: "border-brand-violet/30 bg-brand-violet/10 text-brand-violet"
16
+ brand: "border-brand-violet/30 bg-brand-violet/10 text-brand-violet",
17
+ pink: "border-status-pink-border bg-status-pink-bg text-status-pink-text"
17
18
  },
18
19
  shape: {
19
20
  pill: "rounded-full px-2.5 py-0.5",
@@ -30,7 +31,8 @@ const dotColorMap = {
30
31
  error: "bg-status-error-icon",
31
32
  info: "bg-status-info-icon",
32
33
  neutral: "bg-status-neutral-icon",
33
- brand: "bg-brand-violet"
34
+ brand: "bg-brand-violet",
35
+ pink: "bg-status-pink-icon"
34
36
  };
35
37
  function Tag({
36
38
  className,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/primitives/tag.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { X } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst tagVariants = cva(\n 'inline-flex items-center gap-1.5 border text-xs font-medium',\n {\n variants: {\n variant: {\n default: 'border-border bg-background text-muted-foreground',\n success: 'border-status-success-border bg-status-success-bg text-status-success-text',\n warning: 'border-status-warning-border bg-status-warning-bg text-status-warning-text',\n error: 'border-status-error-border bg-status-error-bg text-status-error-text',\n info: 'border-status-info-border bg-status-info-bg text-status-info-text',\n neutral: 'border-status-neutral-border bg-status-neutral-bg text-status-neutral-text',\n brand: 'border-brand-violet/30 bg-brand-violet/10 text-brand-violet',\n },\n shape: {\n pill: 'rounded-full px-2.5 py-0.5',\n square: 'rounded-md px-2 py-1',\n },\n },\n defaultVariants: { variant: 'default', shape: 'pill' },\n }\n)\n\nconst dotColorMap: Record<TagVariant, string> = {\n default: 'bg-muted-foreground',\n success: 'bg-status-success-icon',\n warning: 'bg-status-warning-icon',\n error: 'bg-status-error-icon',\n info: 'bg-status-info-icon',\n neutral: 'bg-status-neutral-icon',\n brand: 'bg-brand-violet',\n}\n\nexport type TagVariant = 'default' | 'success' | 'warning' | 'error' | 'info' | 'neutral' | 'brand'\n\nexport type TagShape = 'pill' | 'square'\n\nexport type TagProps = React.HTMLAttributes<HTMLSpanElement> &\n Pick<VariantProps<typeof tagVariants>, 'shape'> & {\n variant?: TagVariant\n dot?: boolean\n /**\n * Render an inline close (\"\u00D7\") button after the label. When provided, the button\n * fires `onRemove` on click and stops propagation so a click on the chip body is\n * not triggered. Matches Figma \"Dismiss Icon=On\" Tag variants.\n */\n onRemove?: () => void\n /** Accessible label for the close button. Defaults to `Remove`. */\n removeAriaLabel?: string\n /** Visually mute the chip and disable its close button. */\n disabled?: boolean\n }\n\nexport function Tag({\n className,\n variant = 'default',\n shape = 'pill',\n dot = false,\n onRemove,\n removeAriaLabel = 'Remove',\n disabled = false,\n children,\n ...props\n}: TagProps) {\n return (\n <span\n className={cn(\n tagVariants({ variant, shape }),\n disabled && 'opacity-60',\n className,\n )}\n aria-disabled={disabled || undefined}\n data-slot=\"tag\"\n data-shape={shape}\n {...props}\n >\n {dot && (\n <span\n className={cn('inline-block size-1.5 rounded-full shrink-0', dotColorMap[variant])}\n aria-hidden=\"true\"\n />\n )}\n <span className=\"min-w-0\">{children}</span>\n {onRemove ? (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation()\n onRemove()\n }}\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring/40 disabled:pointer-events-none disabled:opacity-60',\n shape === 'pill' ? 'size-3' : 'size-4',\n )}\n aria-label={removeAriaLabel}\n data-slot=\"tag-remove\"\n >\n <X\n className={shape === 'pill' ? 'size-2.5' : 'size-3.5'}\n aria-hidden=\"true\"\n />\n </button>\n ) : null}\n </span>\n )\n}\n\n/**\n * Helper type: modules define their own tag label \u2192 variant mapping.\n *\n * @example\n * const leadTagMap: TagMap<'customer' | 'hot' | 'inactive'> = {\n * customer: 'success',\n * hot: 'error',\n * inactive: 'neutral',\n * }\n *\n * <Tag variant={leadTagMap[tag.type]} dot>{tag.label}</Tag>\n */\nexport type TagMap<T extends string = string> = Record<T, TagVariant>\n"],
5
- "mappings": "AAqEI,SAYI,KAZJ;AApEJ,SAAS,SAAS;AAClB,SAAS,WAA8B;AACvC,SAAS,UAAU;AAEnB,MAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAS;AAAA,QACT,MAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAS;AAAA,MACX;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,SAAS,WAAW,OAAO,OAAO;AAAA,EACvD;AACF;AAEA,MAAM,cAA0C;AAAA,EAC9C,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAS;AAAA,EACT,MAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAS;AACX;AAsBO,SAAS,IAAI;AAAA,EAClB;AAAA,EACA,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN;AAAA,EACA,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX;AAAA,EACA,GAAG;AACL,GAAa;AACX,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT,YAAY,EAAE,SAAS,MAAM,CAAC;AAAA,QAC9B,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,MACA,iBAAe,YAAY;AAAA,MAC3B,aAAU;AAAA,MACV,cAAY;AAAA,MACX,GAAG;AAAA,MAEH;AAAA,eACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,GAAG,+CAA+C,YAAY,OAAO,CAAC;AAAA,YACjF,eAAY;AAAA;AAAA,QACd;AAAA,QAEF,oBAAC,UAAK,WAAU,WAAW,UAAS;AAAA,QACnC,WACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL;AAAA,YACA,SAAS,CAAC,UAAU;AAClB,oBAAM,gBAAgB;AACtB,uBAAS;AAAA,YACX;AAAA,YACA,WAAW;AAAA,cACT;AAAA,cACA,UAAU,SAAS,WAAW;AAAA,YAChC;AAAA,YACA,cAAY;AAAA,YACZ,aAAU;AAAA,YAEV;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,UAAU,SAAS,aAAa;AAAA,gBAC3C,eAAY;AAAA;AAAA,YACd;AAAA;AAAA,QACF,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { X } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst tagVariants = cva(\n 'inline-flex items-center gap-1.5 border text-xs font-medium',\n {\n variants: {\n variant: {\n default: 'border-border bg-background text-muted-foreground',\n success: 'border-status-success-border bg-status-success-bg text-status-success-text',\n warning: 'border-status-warning-border bg-status-warning-bg text-status-warning-text',\n error: 'border-status-error-border bg-status-error-bg text-status-error-text',\n info: 'border-status-info-border bg-status-info-bg text-status-info-text',\n neutral: 'border-status-neutral-border bg-status-neutral-bg text-status-neutral-text',\n brand: 'border-brand-violet/30 bg-brand-violet/10 text-brand-violet',\n pink: 'border-status-pink-border bg-status-pink-bg text-status-pink-text',\n },\n shape: {\n pill: 'rounded-full px-2.5 py-0.5',\n square: 'rounded-md px-2 py-1',\n },\n },\n defaultVariants: { variant: 'default', shape: 'pill' },\n }\n)\n\nconst dotColorMap: Record<TagVariant, string> = {\n default: 'bg-muted-foreground',\n success: 'bg-status-success-icon',\n warning: 'bg-status-warning-icon',\n error: 'bg-status-error-icon',\n info: 'bg-status-info-icon',\n neutral: 'bg-status-neutral-icon',\n brand: 'bg-brand-violet',\n pink: 'bg-status-pink-icon',\n}\n\nexport type TagVariant = 'default' | 'success' | 'warning' | 'error' | 'info' | 'neutral' | 'brand' | 'pink'\n\nexport type TagShape = 'pill' | 'square'\n\nexport type TagProps = React.HTMLAttributes<HTMLSpanElement> &\n Pick<VariantProps<typeof tagVariants>, 'shape'> & {\n variant?: TagVariant\n dot?: boolean\n /**\n * Render an inline close (\"\u00D7\") button after the label. When provided, the button\n * fires `onRemove` on click and stops propagation so a click on the chip body is\n * not triggered. Matches Figma \"Dismiss Icon=On\" Tag variants.\n */\n onRemove?: () => void\n /** Accessible label for the close button. Defaults to `Remove`. */\n removeAriaLabel?: string\n /** Visually mute the chip and disable its close button. */\n disabled?: boolean\n }\n\nexport function Tag({\n className,\n variant = 'default',\n shape = 'pill',\n dot = false,\n onRemove,\n removeAriaLabel = 'Remove',\n disabled = false,\n children,\n ...props\n}: TagProps) {\n return (\n <span\n className={cn(\n tagVariants({ variant, shape }),\n disabled && 'opacity-60',\n className,\n )}\n aria-disabled={disabled || undefined}\n data-slot=\"tag\"\n data-shape={shape}\n {...props}\n >\n {dot && (\n <span\n className={cn('inline-block size-1.5 rounded-full shrink-0', dotColorMap[variant])}\n aria-hidden=\"true\"\n />\n )}\n <span className=\"min-w-0\">{children}</span>\n {onRemove ? (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation()\n onRemove()\n }}\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring/40 disabled:pointer-events-none disabled:opacity-60',\n shape === 'pill' ? 'size-3' : 'size-4',\n )}\n aria-label={removeAriaLabel}\n data-slot=\"tag-remove\"\n >\n <X\n className={shape === 'pill' ? 'size-2.5' : 'size-3.5'}\n aria-hidden=\"true\"\n />\n </button>\n ) : null}\n </span>\n )\n}\n\n/**\n * Helper type: modules define their own tag label \u2192 variant mapping.\n *\n * @example\n * const leadTagMap: TagMap<'customer' | 'hot' | 'inactive'> = {\n * customer: 'success',\n * hot: 'error',\n * inactive: 'neutral',\n * }\n *\n * <Tag variant={leadTagMap[tag.type]} dot>{tag.label}</Tag>\n */\nexport type TagMap<T extends string = string> = Record<T, TagVariant>\n"],
5
+ "mappings": "AAuEI,SAYI,KAZJ;AAtEJ,SAAS,SAAS;AAClB,SAAS,WAA8B;AACvC,SAAS,UAAU;AAEnB,MAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAS;AAAA,QACT,MAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAS;AAAA,QACT,MAAS;AAAA,MACX;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,SAAS,WAAW,OAAO,OAAO;AAAA,EACvD;AACF;AAEA,MAAM,cAA0C;AAAA,EAC9C,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAS;AAAA,EACT,MAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAS;AAAA,EACT,MAAS;AACX;AAsBO,SAAS,IAAI;AAAA,EAClB;AAAA,EACA,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN;AAAA,EACA,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX;AAAA,EACA,GAAG;AACL,GAAa;AACX,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT,YAAY,EAAE,SAAS,MAAM,CAAC;AAAA,QAC9B,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,MACA,iBAAe,YAAY;AAAA,MAC3B,aAAU;AAAA,MACV,cAAY;AAAA,MACX,GAAG;AAAA,MAEH;AAAA,eACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,GAAG,+CAA+C,YAAY,OAAO,CAAC;AAAA,YACjF,eAAY;AAAA;AAAA,QACd;AAAA,QAEF,oBAAC,UAAK,WAAU,WAAW,UAAS;AAAA,QACnC,WACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL;AAAA,YACA,SAAS,CAAC,UAAU;AAClB,oBAAM,gBAAgB;AACtB,uBAAS;AAAA,YACX;AAAA,YACA,WAAW;AAAA,cACT;AAAA,cACA,UAAU,SAAS,WAAW;AAAA,YAChC;AAAA,YACA,cAAY;AAAA,YACZ,aAAU;AAAA,YAEV;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,UAAU,SAAS,aAAa;AAAA,gBAC3C,eAAY;AAAA;AAAA,YACd;AAAA;AAAA,QACF,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { cn } from "@open-mercato/shared/lib/utils";
6
+ import { inputWrapperVariants, inputElementVariants } from "./input.js";
7
+ const WebsiteInput = React.forwardRef(
8
+ ({ className, inputClassName, size, prefix = "https://", showPrefix = true, placeholder, disabled, ...props }, ref) => {
9
+ const t = useT();
10
+ const resolvedPlaceholder = placeholder ?? t("ui.inputs.websiteInput.placeholder", "www.example.com");
11
+ return /* @__PURE__ */ jsxs(
12
+ "div",
13
+ {
14
+ className: cn(
15
+ inputWrapperVariants({ size }),
16
+ // Override default px so prefix box can sit flush left.
17
+ "px-0 overflow-hidden",
18
+ className
19
+ ),
20
+ "data-slot": "website-input-wrapper",
21
+ children: [
22
+ showPrefix ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
23
+ "span",
24
+ {
25
+ className: "flex shrink-0 items-center self-stretch border-r border-input px-3 text-sm text-muted-foreground select-none",
26
+ "aria-hidden": "true",
27
+ children: prefix
28
+ }
29
+ ) }) : null,
30
+ /* @__PURE__ */ jsx(
31
+ "input",
32
+ {
33
+ ref,
34
+ type: "url",
35
+ inputMode: "url",
36
+ autoComplete: "url",
37
+ placeholder: resolvedPlaceholder,
38
+ disabled,
39
+ className: cn(
40
+ inputElementVariants({ size }),
41
+ "px-3",
42
+ inputClassName
43
+ ),
44
+ ...props
45
+ }
46
+ )
47
+ ]
48
+ }
49
+ );
50
+ }
51
+ );
52
+ WebsiteInput.displayName = "WebsiteInput";
53
+ export {
54
+ WebsiteInput
55
+ };
56
+ //# sourceMappingURL=website-input.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/website-input.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { VariantProps } from 'class-variance-authority'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { inputWrapperVariants, inputElementVariants } from './input'\n\nexport type WebsiteInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, 'size' | 'type'> &\n VariantProps<typeof inputWrapperVariants> & {\n /** Protocol shown in the left prefix box. Defaults to `'https://'` per Figma. */\n prefix?: string\n /** Hide the prefix box entirely (consumer wants a bare URL input). */\n showPrefix?: boolean\n /** Optional className on the wrapper. */\n className?: string\n /** Optional className on the inner `<input>`. */\n inputClassName?: string\n }\n\n/**\n * Website input matching Figma `Text Input [1.1]` (node `266:5251`) **Website** variant \u2014 a left\n * prefix box with the protocol text (default `'https://'`), a vertical divider, then the URL text\n * input. Built on the shared `inputWrapperVariants` / `inputElementVariants` CVA so the visual\n * contract matches the foundation `Input` primitive.\n *\n * The prefix is **display-only** \u2014 the inner `<input>` value contains ONLY the host/path portion\n * (e.g. `'www.example.com/path'`). Compose the full URL at the consumer boundary if needed.\n */\nexport const WebsiteInput = React.forwardRef<HTMLInputElement, WebsiteInputProps>(\n ({ className, inputClassName, size, prefix = 'https://', showPrefix = true, placeholder, disabled, ...props }, ref) => {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.websiteInput.placeholder', 'www.example.com')\n return (\n <div\n className={cn(\n inputWrapperVariants({ size }),\n // Override default px so prefix box can sit flush left.\n 'px-0 overflow-hidden',\n className,\n )}\n data-slot=\"website-input-wrapper\"\n >\n {showPrefix ? (\n <>\n <span\n className=\"flex shrink-0 items-center self-stretch border-r border-input px-3 text-sm text-muted-foreground select-none\"\n aria-hidden=\"true\"\n >\n {prefix}\n </span>\n </>\n ) : null}\n <input\n ref={ref}\n type=\"url\"\n inputMode=\"url\"\n autoComplete=\"url\"\n placeholder={resolvedPlaceholder}\n disabled={disabled}\n className={cn(\n inputElementVariants({ size }),\n 'px-3',\n inputClassName,\n )}\n {...props}\n />\n </div>\n )\n },\n)\nWebsiteInput.displayName = 'WebsiteInput'\n"],
5
+ "mappings": ";AAkCM,SAUI,UACE,KAXN;AAhCN,YAAY,WAAW;AAEvB,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,sBAAsB,4BAA4B;AAuBpD,MAAM,eAAe,MAAM;AAAA,EAChC,CAAC,EAAE,WAAW,gBAAgB,MAAM,SAAS,YAAY,aAAa,MAAM,aAAa,UAAU,GAAG,MAAM,GAAG,QAAQ;AACrH,UAAM,IAAI,KAAK;AACf,UAAM,sBAAsB,eAAe,EAAE,sCAAsC,iBAAiB;AACpG,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT,qBAAqB,EAAE,KAAK,CAAC;AAAA;AAAA,UAE7B;AAAA,UACA;AAAA,QACF;AAAA,QACA,aAAU;AAAA,QAET;AAAA,uBACC,gCACE;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX;AAAA;AAAA,UACH,GACF,IACE;AAAA,UACJ;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,MAAK;AAAA,cACL,WAAU;AAAA,cACV,cAAa;AAAA,cACb,aAAa;AAAA,cACb;AAAA,cACA,WAAW;AAAA,gBACT,qBAAqB,EAAE,KAAK,CAAC;AAAA,gBAC7B;AAAA,gBACA;AAAA,cACF;AAAA,cACC,GAAG;AAAA;AAAA,UACN;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AACA,aAAa,cAAc;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.6.1-develop.3276.1.1fcd145078",
3
+ "version": "0.6.1-develop.3287.1.450f4ffb56",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -141,12 +141,12 @@
141
141
  "remark-gfm": "^4.0.1"
142
142
  },
143
143
  "peerDependencies": {
144
- "@open-mercato/shared": "0.6.1-develop.3276.1.1fcd145078",
144
+ "@open-mercato/shared": "0.6.1-develop.3287.1.450f4ffb56",
145
145
  "react": ">=18.0.0",
146
146
  "react-dom": ">=18.0.0"
147
147
  },
148
148
  "devDependencies": {
149
- "@open-mercato/shared": "0.6.1-develop.3276.1.1fcd145078",
149
+ "@open-mercato/shared": "0.6.1-develop.3287.1.450f4ffb56",
150
150
  "@testing-library/dom": "^10.4.1",
151
151
  "@testing-library/jest-dom": "^6.9.1",
152
152
  "@testing-library/react": "^16.3.1",
@@ -7,6 +7,7 @@ import { ChevronDown, ChevronLeft, Search, X } from 'lucide-react'
7
7
  import { Button } from '../primitives/button'
8
8
  import { IconButton } from '../primitives/icon-button'
9
9
  import { Input } from '../primitives/input'
10
+ import { SearchInput } from '../primitives/search-input'
10
11
  import { Checkbox } from '../primitives/checkbox'
11
12
  import { Separator } from '../primitives/separator'
12
13
  import { FlashMessages } from './FlashMessages'
@@ -674,24 +675,12 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
674
675
  </div>
675
676
  )}
676
677
  {!compact && !hideSearch && (
677
- <Input
678
- type="text"
678
+ <SearchInput
679
679
  value={navQuery}
680
- onChange={(e) => setNavQuery(e.target.value)}
680
+ onChange={setNavQuery}
681
681
  placeholder={t('appShell.searchNavPlaceholder', 'Search...')}
682
682
  aria-label={t('appShell.searchNavAria', 'Search navigation')}
683
- leftIcon={<Search aria-hidden />}
684
- rightIcon={navQueryActive ? (
685
- <IconButton
686
- type="button"
687
- variant="ghost"
688
- size="xs"
689
- onClick={() => setNavQuery('')}
690
- aria-label={t('appShell.searchNavClear', 'Clear search')}
691
- >
692
- <X className="size-3.5" aria-hidden />
693
- </IconButton>
694
- ) : undefined}
683
+ clearLabel={t('appShell.searchNavClear', 'Clear search')}
695
684
  className="mb-2"
696
685
  />
697
686
  )}
@@ -891,24 +880,12 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
891
880
  />
892
881
  ) : null}
893
882
  {!compact && (
894
- <Input
895
- type="text"
883
+ <SearchInput
896
884
  value={navQuery}
897
- onChange={(e) => setNavQuery(e.target.value)}
885
+ onChange={setNavQuery}
898
886
  placeholder={t('appShell.searchNavPlaceholder', 'Search...')}
899
887
  aria-label={t('appShell.searchNavAria', 'Search navigation')}
900
- leftIcon={<Search aria-hidden />}
901
- rightIcon={navQueryActive ? (
902
- <IconButton
903
- type="button"
904
- variant="ghost"
905
- size="xs"
906
- onClick={() => setNavQuery('')}
907
- aria-label={t('appShell.searchNavClear', 'Clear search')}
908
- >
909
- <X className="size-3.5" aria-hidden />
910
- </IconButton>
911
- ) : undefined}
888
+ clearLabel={t('appShell.searchNavClear', 'Clear search')}
912
889
  className="mb-2"
913
890
  />
914
891
  )}
@@ -19,6 +19,7 @@ import { CSS } from '@dnd-kit/utilities'
19
19
  import { DataLoader } from '../primitives/DataLoader'
20
20
  import { Checkbox } from '../primitives/checkbox'
21
21
  import { Input } from '../primitives/input'
22
+ import { PasswordInput } from '../primitives/password-input'
22
23
  import { Textarea } from '../primitives/textarea'
23
24
  import {
24
25
  Select,
@@ -3401,6 +3402,22 @@ function TextInput({
3401
3402
  commitIfChanged()
3402
3403
  }, [commitIfChanged])
3403
3404
 
3405
+ if (inputType === 'password') {
3406
+ return (
3407
+ <PasswordInput
3408
+ placeholder={placeholder}
3409
+ value={local}
3410
+ onChange={handleChange}
3411
+ onKeyDown={handleKeyDown}
3412
+ onFocus={handleFocus}
3413
+ onBlur={handleBlur}
3414
+ spellCheck={false}
3415
+ autoFocus={autoFocus}
3416
+ data-crud-focus-target=""
3417
+ disabled={disabled}
3418
+ />
3419
+ )
3420
+ }
3404
3421
  return (
3405
3422
  <>
3406
3423
  <Input