@open-mercato/ui 0.6.1-develop.3278.1.a4d26475e4 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +23 -14
- package/dist/backend/AppShell.js +8 -32
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/CrudForm.js +18 -0
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/FilterBar.js +11 -14
- package/dist/backend/FilterBar.js.map +2 -2
- package/dist/backend/columns/ColumnChooserPanel.js +4 -6
- package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +11 -7
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/EventPatternInput.js +5 -2
- package/dist/backend/inputs/EventPatternInput.js.map +2 -2
- package/dist/backend/inputs/EventSelect.js +10 -4
- package/dist/backend/inputs/EventSelect.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +51 -47
- package/dist/backend/inputs/LookupSelect.js.map +2 -2
- package/dist/backend/inputs/PhoneNumberField.js +196 -45
- package/dist/backend/inputs/PhoneNumberField.js.map +2 -2
- package/dist/backend/inputs/SwitchableMarkdownInput.js.map +2 -2
- package/dist/backend/messages/message-compose-form-groups.js +2 -2
- package/dist/backend/messages/message-compose-form-groups.js.map +2 -2
- package/dist/backend/utils/serverErrors.js +6 -2
- package/dist/backend/utils/serverErrors.js.map +2 -2
- package/dist/portal/utils/nav.js +17 -0
- package/dist/portal/utils/nav.js.map +2 -2
- package/dist/primitives/amount-input.js +128 -0
- package/dist/primitives/amount-input.js.map +7 -0
- package/dist/primitives/button-input.js +47 -0
- package/dist/primitives/button-input.js.map +7 -0
- package/dist/primitives/card-input.js +133 -0
- package/dist/primitives/card-input.js.map +7 -0
- package/dist/primitives/email-input.js +29 -0
- package/dist/primitives/email-input.js.map +7 -0
- package/dist/primitives/password-input.js +64 -0
- package/dist/primitives/password-input.js.map +7 -0
- package/dist/primitives/search-input.js +57 -0
- package/dist/primitives/search-input.js.map +7 -0
- package/dist/primitives/select.js +43 -5
- package/dist/primitives/select.js.map +2 -2
- package/dist/primitives/website-input.js +56 -0
- package/dist/primitives/website-input.js.map +7 -0
- package/package.json +3 -3
- package/src/backend/AppShell.tsx +7 -30
- package/src/backend/CrudForm.tsx +17 -0
- package/src/backend/FilterBar.tsx +5 -6
- package/src/backend/__tests__/serverErrors.test.ts +79 -1
- package/src/backend/columns/ColumnChooserPanel.tsx +4 -5
- package/src/backend/inputs/ComboboxInput.tsx +32 -26
- package/src/backend/inputs/EventPatternInput.tsx +11 -2
- package/src/backend/inputs/EventSelect.tsx +13 -4
- package/src/backend/inputs/LookupSelect.tsx +75 -53
- package/src/backend/inputs/PhoneNumberField.tsx +237 -47
- package/src/backend/inputs/SwitchableMarkdownInput.tsx +30 -0
- package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +4 -0
- package/src/backend/inputs/__tests__/PhoneNumberField.test.tsx +4 -0
- package/src/backend/messages/message-compose-form-groups.tsx +2 -2
- package/src/backend/utils/serverErrors.ts +15 -8
- package/src/portal/utils/__tests__/nav.test.ts +80 -0
- package/src/portal/utils/nav.ts +23 -1
- package/src/primitives/__tests__/password-input.test.tsx +93 -0
- package/src/primitives/__tests__/search-input.test.tsx +95 -0
- package/src/primitives/__tests__/zindex-overlay.test.tsx +4 -0
- package/src/primitives/amount-input.tsx +184 -0
- package/src/primitives/button-input.tsx +68 -0
- package/src/primitives/card-input.tsx +193 -0
- package/src/primitives/email-input.tsx +36 -0
- package/src/primitives/password-input.tsx +100 -0
- package/src/primitives/search-input.tsx +87 -0
- package/src/primitives/select.tsx +98 -11
- package/src/primitives/website-input.tsx +72 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/amount-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'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectItemLeading,\n SelectTrigger,\n} from './select'\n\nexport type AmountCurrency = {\n /** ISO 4217 code (e.g. `'EUR'`, `'USD'`, `'PLN'`). */\n code: string\n /** Symbol shown as the leading prefix inside the amount input (e.g. `'\u20AC'`). */\n symbol: string\n /** Human-readable label for the picker dropdown. */\n label: string\n /** Optional emoji flag (or any leading visual) for the picker row. */\n flag?: string\n}\n\n/**\n * Static currency list \u2014 extend as needed. Order is alphabetical by code so the picker reads\n * predictably; the first entry is the default fallback when `value.currency` is empty or\n * unrecognised.\n */\nexport const AMOUNT_CURRENCIES: AmountCurrency[] = [\n { code: 'EUR', symbol: '\u20AC', label: 'Euro', flag: '\uD83C\uDDEA\uD83C\uDDFA' },\n { code: 'USD', symbol: '$', label: 'US Dollar', flag: '\uD83C\uDDFA\uD83C\uDDF8' },\n { code: 'GBP', symbol: '\u00A3', label: 'Pound Sterling', flag: '\uD83C\uDDEC\uD83C\uDDE7' },\n { code: 'PLN', symbol: 'z\u0142', label: 'Polish Z\u0142oty', flag: '\uD83C\uDDF5\uD83C\uDDF1' },\n { code: 'CHF', symbol: 'CHF', label: 'Swiss Franc', flag: '\uD83C\uDDE8\uD83C\uDDED' },\n { code: 'SEK', symbol: 'kr', label: 'Swedish Krona', flag: '\uD83C\uDDF8\uD83C\uDDEA' },\n { code: 'CZK', symbol: 'K\u010D', label: 'Czech Koruna', flag: '\uD83C\uDDE8\uD83C\uDDFF' },\n { code: 'JPY', symbol: '\u00A5', label: 'Japanese Yen', flag: '\uD83C\uDDEF\uD83C\uDDF5' },\n { code: 'AUD', symbol: 'A$', label: 'Australian Dollar', flag: '\uD83C\uDDE6\uD83C\uDDFA' },\n { code: 'CAD', symbol: 'C$', label: 'Canadian Dollar', flag: '\uD83C\uDDE8\uD83C\uDDE6' },\n]\n\nconst DEFAULT_CURRENCY = AMOUNT_CURRENCIES[0]\n\nexport type AmountValue = {\n /** Raw numeric amount as a string (preserves leading zeros, partial decimals like `'12.'`). */\n amount: string\n /** ISO 4217 currency code from the picker. */\n currency: string\n}\n\nexport type AmountInputProps = Omit<\n React.ComponentPropsWithoutRef<'input'>,\n 'size' | 'type' | 'value' | 'onChange'\n> &\n VariantProps<typeof inputWrapperVariants> & {\n value: AmountValue\n onChange: (next: AmountValue) => void\n /** Override the static currency list (e.g. limit to specific markets). */\n currencies?: AmountCurrency[]\n /** Hide the currency picker entirely \u2014 single-currency surfaces (e.g. settings page). */\n showCurrency?: boolean\n /** Optional className on the wrapper. */\n className?: string\n /** Optional className on the inner `<input>`. */\n inputClassName?: string\n }\n\nfunction findCurrency(code: string, list: AmountCurrency[]): AmountCurrency {\n return list.find((c) => c.code === code) ?? list[0] ?? DEFAULT_CURRENCY\n}\n\n/**\n * Amount input matching Figma `Text Input [1.1]` (node `266:5251`) **Amount** variant \u2014 leading\n * currency symbol inside the input, then a vertical divider, then a `Select`-driven currency\n * picker (flag + code + chevron). Numeric `inputMode=\"decimal\"`.\n *\n * Value shape is `{ amount: string, currency: string }` \u2014 the amount is stored as a string to\n * preserve raw user input (leading zeros, in-progress decimals). Parse to `Number` at the\n * API/persistence boundary.\n */\nexport const AmountInput = React.forwardRef<HTMLInputElement, AmountInputProps>(\n (\n {\n className,\n inputClassName,\n size,\n value,\n onChange,\n currencies: currenciesProp,\n showCurrency = true,\n placeholder,\n disabled,\n ...props\n },\n ref,\n ) => {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.amountInput.placeholder', '0.00')\n const currencies = currenciesProp ?? AMOUNT_CURRENCIES\n const current = findCurrency(value.currency, currencies)\n\n const handleAmountChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange({ amount: event.target.value, currency: current.code })\n },\n [current.code, onChange],\n )\n\n const handleCurrencyChange = React.useCallback(\n (nextCode: string) => {\n onChange({ amount: value.amount, currency: nextCode })\n },\n [onChange, value.amount],\n )\n\n return (\n <div\n className={cn(inputWrapperVariants({ size }), 'px-0 overflow-hidden', className)}\n data-slot=\"amount-input-wrapper\"\n >\n <div className=\"flex flex-1 min-w-0 items-center gap-2 pl-3\">\n <span\n className=\"text-sm text-muted-foreground select-none shrink-0 tabular-nums\"\n aria-hidden=\"true\"\n >\n {current.symbol}\n </span>\n <input\n ref={ref}\n type=\"text\"\n inputMode=\"decimal\"\n value={value.amount}\n onChange={handleAmountChange}\n placeholder={resolvedPlaceholder}\n disabled={disabled}\n className={cn(\n inputElementVariants({ size }),\n 'pr-3 tabular-nums',\n inputClassName,\n )}\n {...props}\n />\n </div>\n {showCurrency ? (\n <>\n <div aria-hidden=\"true\" className=\"w-px self-stretch bg-input\" />\n <Select value={current.code} onValueChange={handleCurrencyChange} disabled={disabled}>\n <SelectTrigger\n aria-label={t('ui.inputs.amountInput.currencyLabel', 'Currency')}\n className={cn(\n 'h-auto w-auto shrink-0 gap-1.5 rounded-none border-0 bg-transparent px-2.5 py-2 shadow-none',\n 'hover:bg-muted/40 focus:bg-muted/40 focus-visible:shadow-none focus-visible:border-0',\n 'disabled:bg-transparent disabled:hover:bg-transparent',\n )}\n >\n {current.flag ? (\n <span className=\"text-base leading-none\" aria-hidden=\"true\">{current.flag}</span>\n ) : null}\n <span className=\"text-sm text-foreground tabular-nums\">{current.code}</span>\n </SelectTrigger>\n <SelectContent align=\"end\">\n {currencies.map((c) => (\n <SelectItem key={c.code} value={c.code}>\n {c.flag ? (\n <SelectItemLeading>\n <span className=\"text-base leading-none\">{c.flag}</span>\n </SelectItemLeading>\n ) : null}\n <span className=\"flex-1 truncate\">{c.label}</span>\n <span className=\"ml-2 text-xs text-muted-foreground tabular-nums\">{c.code}</span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n ) : null}\n </div>\n )\n },\n)\nAmountInput.displayName = 'AmountInput'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
5
|
+
import { inputWrapperVariants, inputElementVariants } from "./input.js";
|
|
6
|
+
const ButtonInput = React.forwardRef(
|
|
7
|
+
({ className, inputClassName, size, leftIcon, trailingAction, disabled, ...props }, ref) => {
|
|
8
|
+
return /* @__PURE__ */ jsxs(
|
|
9
|
+
"div",
|
|
10
|
+
{
|
|
11
|
+
className: cn(inputWrapperVariants({ size }), "px-0 overflow-hidden", className),
|
|
12
|
+
"data-slot": "button-input-wrapper",
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-w-0 items-center gap-2 pl-3 pr-2", children: [
|
|
15
|
+
leftIcon ? /* @__PURE__ */ jsx(
|
|
16
|
+
"span",
|
|
17
|
+
{
|
|
18
|
+
className: "flex shrink-0 items-center text-muted-foreground [&_svg]:size-4",
|
|
19
|
+
"aria-hidden": "true",
|
|
20
|
+
children: leftIcon
|
|
21
|
+
}
|
|
22
|
+
) : null,
|
|
23
|
+
/* @__PURE__ */ jsx(
|
|
24
|
+
"input",
|
|
25
|
+
{
|
|
26
|
+
ref,
|
|
27
|
+
disabled,
|
|
28
|
+
className: cn(
|
|
29
|
+
inputElementVariants({ size }),
|
|
30
|
+
inputClassName
|
|
31
|
+
),
|
|
32
|
+
...props
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "w-px self-stretch bg-input" }),
|
|
37
|
+
/* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-stretch", children: trailingAction })
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
ButtonInput.displayName = "ButtonInput";
|
|
44
|
+
export {
|
|
45
|
+
ButtonInput
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=button-input.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/button-input.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { inputWrapperVariants, inputElementVariants } from './input'\n\nexport type ButtonInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, 'size'> &\n VariantProps<typeof inputWrapperVariants> & {\n /** Optional leading icon (decorative `aria-hidden` wrapper). */\n leftIcon?: React.ReactNode\n /**\n * Required interactive trailing element rendered inside the wrapper, after the vertical\n * divider. Typically an `<IconButton>` (copy URL, send, refresh). The slot stays focusable\n * and screen-reader accessible \u2014 do NOT pass a decorative icon here.\n */\n trailingAction: React.ReactNode\n /** Optional className on the wrapper. */\n className?: string\n /** Optional className on the inner `<input>`. */\n inputClassName?: string\n }\n\n/**\n * Button input matching Figma `Text Input [1.1]` (node `266:5251`) **Button** variant \u2014 a text\n * input with an interactive trailing button slot, separated by a vertical divider. Built on the\n * shared `inputWrapperVariants` / `inputElementVariants` CVA so the visual contract matches the\n * foundation `Input` primitive.\n *\n * Common pairings: share-link + copy button, subscribe-email + send button, API key + regenerate\n * button. The trailing slot is rendered AS-IS (no auto-wrapping), so consumers control button\n * type, size, variant, and `aria-label` directly via the passed element.\n */\nexport const ButtonInput = React.forwardRef<HTMLInputElement, ButtonInputProps>(\n ({ className, inputClassName, size, leftIcon, trailingAction, disabled, ...props }, ref) => {\n return (\n <div\n className={cn(inputWrapperVariants({ size }), 'px-0 overflow-hidden', className)}\n data-slot=\"button-input-wrapper\"\n >\n <div className=\"flex flex-1 min-w-0 items-center gap-2 pl-3 pr-2\">\n {leftIcon ? (\n <span\n className=\"flex shrink-0 items-center text-muted-foreground [&_svg]:size-4\"\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n ) : null}\n <input\n ref={ref}\n disabled={disabled}\n className={cn(\n inputElementVariants({ size }),\n inputClassName,\n )}\n {...props}\n />\n </div>\n <div aria-hidden=\"true\" className=\"w-px self-stretch bg-input\" />\n <div className=\"flex shrink-0 items-stretch\">\n {trailingAction}\n </div>\n </div>\n )\n },\n)\nButtonInput.displayName = 'ButtonInput'\n"],
|
|
5
|
+
"mappings": ";AAwCQ,SAEI,KAFJ;AAtCR,YAAY,WAAW;AAEvB,SAAS,UAAU;AACnB,SAAS,sBAAsB,4BAA4B;AA4BpD,MAAM,cAAc,MAAM;AAAA,EAC/B,CAAC,EAAE,WAAW,gBAAgB,MAAM,UAAU,gBAAgB,UAAU,GAAG,MAAM,GAAG,QAAQ;AAC1F,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,wBAAwB,SAAS;AAAA,QAC/E,aAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,oDACZ;AAAA,uBACC;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,eAAY;AAAA,gBAEX;AAAA;AAAA,YACH,IACE;AAAA,YACJ;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA,WAAW;AAAA,kBACT,qBAAqB,EAAE,KAAK,CAAC;AAAA,kBAC7B;AAAA,gBACF;AAAA,gBACC,GAAG;AAAA;AAAA,YACN;AAAA,aACF;AAAA,UACA,oBAAC,SAAI,eAAY,QAAO,WAAU,8BAA6B;AAAA,UAC/D,oBAAC,SAAI,WAAU,+BACZ,0BACH;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AACA,YAAY,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { CreditCard } 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 CARD_BRANDS = [
|
|
9
|
+
{ id: "amex", label: "AMEX", regex: /^3[47]/, format: [4, 6, 5], maxLength: 15, bg: "#006FCF" },
|
|
10
|
+
{ id: "visa", label: "VISA", regex: /^4/, format: [4, 4, 4, 4], maxLength: 16, bg: "#1A1F71" },
|
|
11
|
+
{ id: "mastercard", label: "MC", regex: /^(5[1-5]|2[2-7])/, format: [4, 4, 4, 4], maxLength: 16, bg: "#EB001B" },
|
|
12
|
+
{ id: "discover", label: "DISC", regex: /^(6011|64[4-9]|65|622)/, format: [4, 4, 4, 4], maxLength: 16, bg: "#FF6000" },
|
|
13
|
+
{ id: "diners", label: "DC", regex: /^(36|30[0-5]|309)/, format: [4, 6, 4], maxLength: 14, bg: "#0079BE" },
|
|
14
|
+
{ id: "jcb", label: "JCB", regex: /^35(2[89]|[3-8])/, format: [4, 4, 4, 4], maxLength: 16, bg: "#0E4C96" },
|
|
15
|
+
{ id: "unionpay", label: "UP", regex: /^62/, format: [4, 4, 4, 4], maxLength: 16, bg: "#E21836" }
|
|
16
|
+
];
|
|
17
|
+
const DEFAULT_FORMAT = [4, 4, 4, 4];
|
|
18
|
+
const DEFAULT_MAX_LENGTH = 16;
|
|
19
|
+
function detectBrand(digits, brands) {
|
|
20
|
+
if (!digits) return null;
|
|
21
|
+
for (const brand of brands) {
|
|
22
|
+
if (brand.regex.test(digits)) return brand;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function formatDigits(digits, format) {
|
|
27
|
+
if (!digits) return "";
|
|
28
|
+
const groups = [];
|
|
29
|
+
let cursor = 0;
|
|
30
|
+
for (const groupSize of format) {
|
|
31
|
+
if (cursor >= digits.length) break;
|
|
32
|
+
groups.push(digits.slice(cursor, cursor + groupSize));
|
|
33
|
+
cursor += groupSize;
|
|
34
|
+
}
|
|
35
|
+
if (cursor < digits.length) groups.push(digits.slice(cursor));
|
|
36
|
+
return groups.join(" ");
|
|
37
|
+
}
|
|
38
|
+
function sanitize(input, maxLength) {
|
|
39
|
+
return input.replace(/\D+/g, "").slice(0, maxLength);
|
|
40
|
+
}
|
|
41
|
+
const CardInput = React.forwardRef(
|
|
42
|
+
({
|
|
43
|
+
className,
|
|
44
|
+
inputClassName,
|
|
45
|
+
size,
|
|
46
|
+
value,
|
|
47
|
+
onChange,
|
|
48
|
+
onBrandChange,
|
|
49
|
+
brands: brandsProp,
|
|
50
|
+
showLeadingIcon = true,
|
|
51
|
+
showBrandBadge = true,
|
|
52
|
+
placeholder,
|
|
53
|
+
disabled,
|
|
54
|
+
...props
|
|
55
|
+
}, ref) => {
|
|
56
|
+
const t = useT();
|
|
57
|
+
const brands = brandsProp ?? CARD_BRANDS;
|
|
58
|
+
const sanitizedValue = sanitize(value ?? "", DEFAULT_MAX_LENGTH);
|
|
59
|
+
const detected = detectBrand(sanitizedValue, brands);
|
|
60
|
+
const format = detected?.format ?? DEFAULT_FORMAT;
|
|
61
|
+
const maxLength = detected?.maxLength ?? DEFAULT_MAX_LENGTH;
|
|
62
|
+
const truncated = sanitizedValue.slice(0, maxLength);
|
|
63
|
+
const displayed = formatDigits(truncated, format);
|
|
64
|
+
const resolvedPlaceholder = placeholder ?? t("ui.inputs.cardInput.placeholder", "0000 0000 0000 0000");
|
|
65
|
+
const lastBrandRef = React.useRef(null);
|
|
66
|
+
React.useEffect(() => {
|
|
67
|
+
const next = detected?.id ?? null;
|
|
68
|
+
if (next !== lastBrandRef.current) {
|
|
69
|
+
lastBrandRef.current = next;
|
|
70
|
+
onBrandChange?.(detected);
|
|
71
|
+
}
|
|
72
|
+
}, [detected, onBrandChange]);
|
|
73
|
+
const handleChange = React.useCallback(
|
|
74
|
+
(event) => {
|
|
75
|
+
const nextBrand = detectBrand(sanitize(event.target.value, DEFAULT_MAX_LENGTH), brands);
|
|
76
|
+
const nextMax = nextBrand?.maxLength ?? DEFAULT_MAX_LENGTH;
|
|
77
|
+
const digits = sanitize(event.target.value, nextMax);
|
|
78
|
+
onChange(digits);
|
|
79
|
+
},
|
|
80
|
+
[brands, onChange]
|
|
81
|
+
);
|
|
82
|
+
return /* @__PURE__ */ jsxs(
|
|
83
|
+
"div",
|
|
84
|
+
{
|
|
85
|
+
className: cn(inputWrapperVariants({ size }), className),
|
|
86
|
+
"data-slot": "card-input-wrapper",
|
|
87
|
+
"data-card-brand": detected?.id ?? "unknown",
|
|
88
|
+
children: [
|
|
89
|
+
showLeadingIcon ? /* @__PURE__ */ jsx(CreditCard, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }) : null,
|
|
90
|
+
/* @__PURE__ */ jsx(
|
|
91
|
+
"input",
|
|
92
|
+
{
|
|
93
|
+
ref,
|
|
94
|
+
type: "text",
|
|
95
|
+
inputMode: "numeric",
|
|
96
|
+
autoComplete: "cc-number",
|
|
97
|
+
value: displayed,
|
|
98
|
+
onChange: handleChange,
|
|
99
|
+
placeholder: resolvedPlaceholder,
|
|
100
|
+
disabled,
|
|
101
|
+
className: cn(
|
|
102
|
+
inputElementVariants({ size }),
|
|
103
|
+
"tracking-[0.02em] tabular-nums",
|
|
104
|
+
inputClassName
|
|
105
|
+
),
|
|
106
|
+
...props
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
showBrandBadge && detected ? /* @__PURE__ */ jsx(
|
|
110
|
+
"span",
|
|
111
|
+
{
|
|
112
|
+
className: "flex shrink-0 items-center justify-center rounded-sm px-1.5 text-overline font-semibold uppercase tracking-wider tabular-nums leading-none select-none",
|
|
113
|
+
style: {
|
|
114
|
+
backgroundColor: detected.bg,
|
|
115
|
+
color: detected.fg ?? "#ffffff",
|
|
116
|
+
height: "24px",
|
|
117
|
+
minWidth: "32px"
|
|
118
|
+
},
|
|
119
|
+
"aria-hidden": "true",
|
|
120
|
+
children: detected.label
|
|
121
|
+
}
|
|
122
|
+
) : null
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
CardInput.displayName = "CardInput";
|
|
129
|
+
export {
|
|
130
|
+
CARD_BRANDS,
|
|
131
|
+
CardInput
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=card-input.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/card-input.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { CreditCard } 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 CardBrand = {\n /** Stable identifier (e.g. `'visa'`, `'mastercard'`). Used as the `data-card-brand` attribute. */\n id: string\n /** Short label rendered on the trailing badge (e.g. `'VISA'`, `'MC'`). */\n label: string\n /** Regex pattern matched against the digits-only card number. */\n regex: RegExp\n /** Grouping for display (e.g. `[4, 4, 4, 4]` for Visa, `[4, 6, 5]` for Amex). */\n format: number[]\n /** Maximum number of digits allowed (Visa/MC/Disc 16, Amex 15, Diners 14). */\n maxLength: number\n /** Background color for the brand badge. */\n bg: string\n /** Foreground (text) color for the brand badge label. Defaults to white. */\n fg?: string\n}\n\n/**\n * Default card brand list \u2014 covers the most common networks. Order matters: the first matching\n * regex wins, so brands with broader prefixes (e.g. UnionPay `^62`) MUST come after narrower ones\n * (e.g. Discover `^622`). Override with the `brands` prop for region-specific surfaces.\n */\nexport const CARD_BRANDS: CardBrand[] = [\n { id: 'amex', label: 'AMEX', regex: /^3[47]/, format: [4, 6, 5], maxLength: 15, bg: '#006FCF' },\n { id: 'visa', label: 'VISA', regex: /^4/, format: [4, 4, 4, 4], maxLength: 16, bg: '#1A1F71' },\n { id: 'mastercard', label: 'MC', regex: /^(5[1-5]|2[2-7])/, format: [4, 4, 4, 4], maxLength: 16, bg: '#EB001B' },\n { id: 'discover', label: 'DISC', regex: /^(6011|64[4-9]|65|622)/, format: [4, 4, 4, 4], maxLength: 16, bg: '#FF6000' },\n { id: 'diners', label: 'DC', regex: /^(36|30[0-5]|309)/, format: [4, 6, 4], maxLength: 14, bg: '#0079BE' },\n { id: 'jcb', label: 'JCB', regex: /^35(2[89]|[3-8])/, format: [4, 4, 4, 4], maxLength: 16, bg: '#0E4C96' },\n { id: 'unionpay', label: 'UP', regex: /^62/, format: [4, 4, 4, 4], maxLength: 16, bg: '#E21836' },\n]\n\nconst DEFAULT_FORMAT: number[] = [4, 4, 4, 4]\nconst DEFAULT_MAX_LENGTH = 16\n\nfunction detectBrand(digits: string, brands: CardBrand[]): CardBrand | null {\n if (!digits) return null\n for (const brand of brands) {\n if (brand.regex.test(digits)) return brand\n }\n return null\n}\n\nfunction formatDigits(digits: string, format: number[]): string {\n if (!digits) return ''\n const groups: string[] = []\n let cursor = 0\n for (const groupSize of format) {\n if (cursor >= digits.length) break\n groups.push(digits.slice(cursor, cursor + groupSize))\n cursor += groupSize\n }\n if (cursor < digits.length) groups.push(digits.slice(cursor))\n return groups.join(' ')\n}\n\nfunction sanitize(input: string, maxLength: number): string {\n return input.replace(/\\D+/g, '').slice(0, maxLength)\n}\n\nexport type CardInputProps = Omit<\n React.ComponentPropsWithoutRef<'input'>,\n 'size' | 'type' | 'value' | 'onChange'\n> &\n VariantProps<typeof inputWrapperVariants> & {\n /** Controlled value \u2014 digits-only (no spaces). Pass formatted strings if you must; non-digits are stripped. */\n value: string\n /** Called with the new digits-only string. The component handles formatting on display. */\n onChange: (digits: string) => void\n /** Called when the detected brand changes. `null` when nothing matches yet. */\n onBrandChange?: (brand: CardBrand | null) => void\n /** Override the card brand list (e.g. region-specific). */\n brands?: CardBrand[]\n /** Render the leading bank-card icon. Defaults to `true`. */\n showLeadingIcon?: boolean\n /** Render the trailing brand badge. Defaults to `true`. */\n showBrandBadge?: boolean\n /** Optional className on the wrapper. */\n className?: string\n /** Optional className on the inner `<input>`. */\n inputClassName?: string\n }\n\n/**\n * Card-number input matching Figma `Text Input [1.1]` (node `266:5251`) **Card** variant \u2014 leading\n * `CreditCard` icon, the formatted card-number input, and a trailing brand badge that auto-detects\n * the issuer (Visa, Mastercard, Amex, Discover, Diners, JCB, UnionPay) from the typed digits.\n *\n * Built on the shared `inputWrapperVariants` / `inputElementVariants` CVA. Brand detection is\n * regex-based \u2014 no external library dependency. The `value` prop is digits-only; the component\n * formats per brand on display (`[4,4,4,4]` default, `[4,6,5]` Amex, `[4,6,4]` Diners) and emits\n * digits-only via `onChange`.\n */\nexport const CardInput = React.forwardRef<HTMLInputElement, CardInputProps>(\n (\n {\n className,\n inputClassName,\n size,\n value,\n onChange,\n onBrandChange,\n brands: brandsProp,\n showLeadingIcon = true,\n showBrandBadge = true,\n placeholder,\n disabled,\n ...props\n },\n ref,\n ) => {\n const t = useT()\n const brands = brandsProp ?? CARD_BRANDS\n const sanitizedValue = sanitize(value ?? '', DEFAULT_MAX_LENGTH)\n const detected = detectBrand(sanitizedValue, brands)\n const format = detected?.format ?? DEFAULT_FORMAT\n const maxLength = detected?.maxLength ?? DEFAULT_MAX_LENGTH\n const truncated = sanitizedValue.slice(0, maxLength)\n const displayed = formatDigits(truncated, format)\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.cardInput.placeholder', '0000 0000 0000 0000')\n\n const lastBrandRef = React.useRef<string | null>(null)\n React.useEffect(() => {\n const next = detected?.id ?? null\n if (next !== lastBrandRef.current) {\n lastBrandRef.current = next\n onBrandChange?.(detected)\n }\n }, [detected, onBrandChange])\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n const nextBrand = detectBrand(sanitize(event.target.value, DEFAULT_MAX_LENGTH), brands)\n const nextMax = nextBrand?.maxLength ?? DEFAULT_MAX_LENGTH\n const digits = sanitize(event.target.value, nextMax)\n onChange(digits)\n },\n [brands, onChange],\n )\n\n return (\n <div\n className={cn(inputWrapperVariants({ size }), className)}\n data-slot=\"card-input-wrapper\"\n data-card-brand={detected?.id ?? 'unknown'}\n >\n {showLeadingIcon ? (\n <CreditCard className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n <input\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"cc-number\"\n value={displayed}\n onChange={handleChange}\n placeholder={resolvedPlaceholder}\n disabled={disabled}\n className={cn(\n inputElementVariants({ size }),\n 'tracking-[0.02em] tabular-nums',\n inputClassName,\n )}\n {...props}\n />\n {showBrandBadge && detected ? (\n <span\n className=\"flex shrink-0 items-center justify-center rounded-sm px-1.5 text-overline font-semibold uppercase tracking-wider tabular-nums leading-none select-none\"\n style={{\n backgroundColor: detected.bg,\n color: detected.fg ?? '#ffffff',\n height: '24px',\n minWidth: '32px',\n }}\n aria-hidden=\"true\"\n >\n {detected.label}\n </span>\n ) : null}\n </div>\n )\n },\n)\nCardInput.displayName = 'CardInput'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Mail } from "lucide-react";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
|
+
import { Input } from "./input.js";
|
|
7
|
+
const EmailInput = React.forwardRef(
|
|
8
|
+
({ showIcon = true, placeholder, ...props }, ref) => {
|
|
9
|
+
const t = useT();
|
|
10
|
+
const resolvedPlaceholder = placeholder ?? t("ui.inputs.emailInput.placeholder", "name@example.com");
|
|
11
|
+
return /* @__PURE__ */ jsx(
|
|
12
|
+
Input,
|
|
13
|
+
{
|
|
14
|
+
ref,
|
|
15
|
+
type: "email",
|
|
16
|
+
inputMode: "email",
|
|
17
|
+
autoComplete: "email",
|
|
18
|
+
placeholder: resolvedPlaceholder,
|
|
19
|
+
leftIcon: showIcon ? /* @__PURE__ */ jsx(Mail, { "aria-hidden": "true" }) : void 0,
|
|
20
|
+
...props
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
EmailInput.displayName = "EmailInput";
|
|
26
|
+
export {
|
|
27
|
+
EmailInput
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=email-input.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/primitives/email-input.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Mail } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Input, type InputProps } from './input'\n\nexport type EmailInputProps = Omit<InputProps, 'leftIcon' | 'type'> & {\n /** Show the leading Mail icon. Defaults to `true` per Figma `Text Input [1.1]` Email variant. */\n showIcon?: boolean\n}\n\n/**\n * Email input matching Figma `Text Input [1.1]` (node `266:5251`) **Email** variant \u2014 a thin\n * `Input` wrapper that hardcodes `type=\"email\"`, `inputMode=\"email\"`, `autoComplete=\"email\"`,\n * and renders a leading `Mail` icon by default. Placeholder defaults to an auto-translated\n * `ui.inputs.emailInput.placeholder`.\n */\nexport const EmailInput = React.forwardRef<HTMLInputElement, EmailInputProps>(\n ({ showIcon = true, placeholder, ...props }, ref) => {\n const t = useT()\n const resolvedPlaceholder = placeholder ?? t('ui.inputs.emailInput.placeholder', 'name@example.com')\n return (\n <Input\n ref={ref}\n type=\"email\"\n inputMode=\"email\"\n autoComplete=\"email\"\n placeholder={resolvedPlaceholder}\n leftIcon={showIcon ? <Mail aria-hidden=\"true\" /> : undefined}\n {...props}\n />\n )\n },\n)\nEmailInput.displayName = 'EmailInput'\n"],
|
|
5
|
+
"mappings": ";AA6B6B;AA3B7B,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,SAAS,aAA8B;AAahC,MAAM,aAAa,MAAM;AAAA,EAC9B,CAAC,EAAE,WAAW,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ;AACnD,UAAM,IAAI,KAAK;AACf,UAAM,sBAAsB,eAAe,EAAE,oCAAoC,kBAAkB;AACnG,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAU;AAAA,QACV,cAAa;AAAA,QACb,aAAa;AAAA,QACb,UAAU,WAAW,oBAAC,QAAK,eAAY,QAAO,IAAK;AAAA,QAClD,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,WAAW,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -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-
|
|
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(
|
|
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-
|
|
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(
|
|
112
|
-
/* @__PURE__ */ jsx(
|
|
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": ";AAgCE,SAQI,KARJ;AA9BF,YAAY,WAAW;AACvB,YAAY,qBAAqB;AACjC,SAAS,OAAO,aAAa,iBAAiB;AAC9C,SAAS,WAA8B;AACvC,SAAS,UAAU;AAEnB,MAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAKA,MAAM,gBAAgB,MAAM,WAG1B,CAAC,EAAE,WAAW,MAAM,UAAU,GAAG,MAAM,GAAG,QAC1C;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,sBAAsB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,IACxD,aAAU;AAAA,IACT,GAAG;AAAA,IAEH;AAAA;AAAA,MACD,oBAAC,gBAAgB,MAAhB,EAAqB,SAAO,MAC3B,8BAAC,eAAY,WAAU,yBAAwB,eAAY,QAAO,GACpE;AAAA;AAAA;AACF,CACD;AACD,cAAc,cAAc,gBAAgB,QAAQ;
|
|
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
|
}
|