@open-mercato/ui 0.6.6-develop.6542.1.fee3e68aa6 → 0.6.6-develop.6551.1.1511bfa890
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/AGENTS.md +1 -1
- package/dist/ai/ChatPaneTabs.js +2 -2
- package/dist/ai/ChatPaneTabs.js.map +2 -2
- package/dist/backend/AppShell.js +2 -2
- package/dist/backend/AppShell.js.map +1 -1
- package/dist/backend/detail/AttachmentMetadataDialog.js +9 -17
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
- package/dist/backend/filters/AdvancedFilterBuilder.js +1 -1
- package/dist/backend/filters/AdvancedFilterBuilder.js.map +1 -1
- package/dist/primitives/drawer.js +6 -3
- package/dist/primitives/drawer.js.map +2 -2
- package/dist/primitives/tabs.js +2 -1
- package/dist/primitives/tabs.js.map +2 -2
- package/package.json +3 -3
- package/src/ai/ChatPaneTabs.tsx +2 -2
- package/src/backend/AppShell.tsx +2 -2
- package/src/backend/__tests__/DataTable.extensions.test.tsx +26 -10
- package/src/backend/detail/AttachmentMetadataDialog.tsx +16 -23
- package/src/backend/filters/AdvancedFilterBuilder.tsx +1 -1
- package/src/primitives/__tests__/drawer.test.tsx +2 -1
- package/src/primitives/drawer.tsx +4 -3
- package/src/primitives/tabs.tsx +6 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/tabs.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from './button'\n\n/**\n * Tab navigation primitive \u2014 backward compatible with the original\n * pill-style API. Phase B.5 rewrite adds the Figma underline variant\n * (`Tab Menu Horizontal [1.1]` in `553:734`), the vertical orientation\n * (`Tab Menu Vertical [1.1]`), and per-tab `leading` icon + `count`\n * badge slots.\n *\n * Backward compatibility (6 import sites):\n * integrations/[id]/page.tsx\n * scheduler JobLogsModal.tsx\n * checkout LinkTemplateForm.tsx\n * search VectorSearchSection.tsx + FulltextSearchSection.tsx\n * ai-assistant AiPlaygroundPageClient.tsx\n *\n * All six call `<Tabs>` + `<TabsList>` + `<TabsTrigger>` +\n * `<TabsContent>` with the default pill look. The default `variant`\n * stays `pill` so they keep rendering verbatim.\n */\n\ntype TabsContextValue = {\n value: string\n onValueChange: (value: string) => void\n variant: 'pill' | 'underline'\n orientation: 'horizontal' | 'vertical'\n}\n\nexport const TabsContext = createContext<TabsContextValue | undefined>(undefined)\n\nexport function useTabsContext() {\n const context = useContext(TabsContext)\n if (!context) {\n throw new Error('Tabs components must be used within a Tabs provider')\n }\n return context\n}\n\nexport type TabsVariant = 'pill' | 'underline'\nexport type TabsOrientation = 'horizontal' | 'vertical'\n\nexport type TabsProps = {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n /** Visual style. `pill` is the original look (bg-muted container,\n * bg-background selected pill). `underline` matches Figma `Tab Menu\n * Horizontal [1.1]` \u2014 flat strip with a thin border-bottom and an\n * accent-indigo underline on the active tab. */\n variant?: TabsVariant\n /** Tab strip orientation. `horizontal` lays the strip in a row;\n * `vertical` lays it in a column. The default `pill` variant\n * supports both; the underline variant supports only `horizontal`\n * (per Figma \u2014 vertical underline is not a documented pattern). */\n orientation?: TabsOrientation\n children: React.ReactNode\n className?: string\n}\n\nexport function Tabs({\n value: controlledValue,\n defaultValue,\n onValueChange,\n variant = 'pill',\n orientation = 'horizontal',\n children,\n className,\n}: TabsProps) {\n const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue ?? '')\n const isControlled = controlledValue !== undefined\n const value = isControlled ? controlledValue : uncontrolledValue\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n if (!isControlled) {\n setUncontrolledValue(newValue)\n }\n onValueChange?.(newValue)\n },\n [isControlled, onValueChange],\n )\n\n const contextValue = React.useMemo<TabsContextValue>(\n () => ({ value, onValueChange: handleValueChange, variant, orientation }),\n [value, handleValueChange, variant, orientation],\n )\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n data-slot=\"tabs\"\n data-variant={variant}\n data-orientation={orientation}\n className={cn(orientation === 'vertical' ? 'flex gap-4' : '', className)}\n >\n {children}\n </div>\n </TabsContext.Provider>\n )\n}\n\nexport type TabsListProps = {\n children: React.ReactNode\n className?: string\n}\n\nexport function TabsList({ children, className }: TabsListProps) {\n const { variant, orientation } = useTabsContext()\n\n const baseClasses =\n variant === 'underline'\n ? // Underline strip \u2014 flat, full-width border-bottom for the rail,\n // selected trigger gets its own border-b-2 accent below.\n orientation === 'vertical'\n ? 'inline-flex flex-col items-stretch gap-1 border-r border-input pr-3'\n : 'inline-flex h-10 items-center justify-start gap-4 border-b border-input text-muted-foreground'\n : // Pill strip (original) \u2014 rounded container, bg-muted shell.\n orientation === 'vertical'\n ? 'inline-flex flex-col items-stretch gap-1 rounded-lg bg-muted p-1 text-muted-foreground'\n : 'inline-flex h-9 items-center justify-start gap-1 rounded-lg bg-muted p-1 text-muted-foreground'\n\n return (\n <div\n data-slot=\"tabs-list\"\n className={cn(baseClasses, className)}\n role=\"tablist\"\n aria-orientation={orientation}\n >\n {children}\n </div>\n )\n}\n\nexport type TabsTriggerProps = {\n value: string\n children: React.ReactNode\n className?: string\n disabled?: boolean\n /** Optional leading icon slot \u2014 typically a lucide-react icon at\n * `size-4`. Per Figma `Tab Menu Horizontal Items [1.1]`. */\n leading?: React.ReactNode\n /** Optional trailing count badge \u2014 typically a numeric count or\n * \"New\". Rendered as a muted pill that highlights to indigo when\n * the tab is selected. */\n count?: React.ReactNode\n}\n\nexport function TabsTrigger({\n value,\n children,\n className,\n disabled,\n leading,\n count,\n}: TabsTriggerProps) {\n const { value: selectedValue, onValueChange, variant, orientation } = useTabsContext()\n const isSelected = selectedValue === value\n\n if (variant === 'underline') {\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isSelected}\n disabled={disabled}\n onClick={() => onValueChange(value)}\n data-slot=\"tabs-trigger\"\n data-state={isSelected ? 'active' : 'inactive'}\n data-variant=\"underline\"\n className={cn(\n // Underline trigger \u2014 flat, bottom-border accent when active.\n // Negative margin-bottom -1px so the active accent sits on\n // top of the rail's border-bottom (rather than below it).\n orientation === 'vertical'\n ? 'group relative inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground outline-none transition-colors'\n : 'group relative -mb-px inline-flex items-center gap-2 border-b-2 border-transparent px-1 py-2 text-sm font-medium text-muted-foreground outline-none transition-colors',\n 'hover:text-foreground focus-visible:shadow-focus',\n 'disabled:pointer-events-none disabled:opacity-50',\n isSelected\n ? orientation === 'vertical'\n ? 'bg-muted/40 text-foreground'\n : 'border-accent-indigo font-semibold text-foreground'\n : '',\n className,\n )}\n >\n {leading ? (\n <span\n data-slot=\"tabs-trigger-leading\"\n aria-hidden=\"true\"\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n isSelected ? 'text-accent-indigo' : 'text-muted-foreground',\n )}\n >\n {leading}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{children}</span>\n {count !== undefined ? (\n <span\n data-slot=\"tabs-trigger-count\"\n className={cn(\n 'inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-xs font-medium',\n isSelected\n ? 'bg-accent-indigo/10 text-accent-indigo'\n : 'bg-muted text-muted-foreground',\n )}\n >\n {count}\n </span>\n ) : null}\n </button>\n )\n }\n\n // Pill variant (original look) \u2014 `<Button variant=\"ghost\" size=\"sm\">`\n // with state classes. Backward compatible.\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n role=\"tab\"\n aria-selected={isSelected}\n disabled={disabled}\n onClick={() => onValueChange(value)}\n data-slot=\"tabs-trigger\"\n data-state={isSelected ? 'active' : 'inactive'}\n data-variant=\"pill\"\n className={cn(\n 'gap-2',\n isSelected\n ? 'bg-background text-foreground shadow'\n : 'hover:bg-background/80 hover:text-foreground',\n className,\n )}\n >\n {leading ? (\n <span\n data-slot=\"tabs-trigger-leading\"\n aria-hidden=\"true\"\n className=\"inline-flex shrink-0 items-center justify-center\"\n >\n {leading}\n </span>\n ) : null}\n <span>{children}</span>\n {count !== undefined ? (\n <span\n data-slot=\"tabs-trigger-count\"\n className={cn(\n 'inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-xs font-medium',\n isSelected\n ? 'bg-accent-indigo/10 text-accent-indigo'\n : 'bg-background/60 text-muted-foreground',\n )}\n >\n {count}\n </span>\n ) : null}\n </Button>\n )\n}\n\nexport type TabsContentProps = {\n value: string\n children: React.ReactNode\n className?: string\n}\n\nexport function TabsContent({ value, children, className }: TabsContentProps) {\n const { value: selectedValue, orientation } = useTabsContext()\n\n if (selectedValue !== value) {\n return null\n }\n\n return (\n <div\n role=\"tabpanel\"\n data-slot=\"tabs-content\"\n className={cn(\n orientation === 'vertical' ? 'flex-1 min-w-0' : 'mt-2',\n 'ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n >\n {children}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AA+FM,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from './button'\n\n/**\n * Tab navigation primitive \u2014 backward compatible with the original\n * pill-style API. Phase B.5 rewrite adds the Figma underline variant\n * (`Tab Menu Horizontal [1.1]` in `553:734`), the vertical orientation\n * (`Tab Menu Vertical [1.1]`), and per-tab `leading` icon + `count`\n * badge slots.\n *\n * Backward compatibility (6 import sites):\n * integrations/[id]/page.tsx\n * scheduler JobLogsModal.tsx\n * checkout LinkTemplateForm.tsx\n * search VectorSearchSection.tsx + FulltextSearchSection.tsx\n * ai-assistant AiPlaygroundPageClient.tsx\n *\n * All six call `<Tabs>` + `<TabsList>` + `<TabsTrigger>` +\n * `<TabsContent>` with the default pill look. The default `variant`\n * stays `pill` so they keep rendering verbatim.\n */\n\ntype TabsContextValue = {\n value: string\n onValueChange: (value: string) => void\n variant: 'pill' | 'underline'\n orientation: 'horizontal' | 'vertical'\n}\n\nexport const TabsContext = createContext<TabsContextValue | undefined>(undefined)\n\nexport function useTabsContext() {\n const context = useContext(TabsContext)\n if (!context) {\n throw new Error('Tabs components must be used within a Tabs provider')\n }\n return context\n}\n\nexport type TabsVariant = 'pill' | 'underline'\nexport type TabsOrientation = 'horizontal' | 'vertical'\n\nexport type TabsProps = {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n /** Visual style. `pill` is the original look (bg-muted container,\n * bg-background selected pill). `underline` matches Figma `Tab Menu\n * Horizontal [1.1]` \u2014 flat strip with a thin border-bottom and an\n * accent-indigo underline on the active tab. */\n variant?: TabsVariant\n /** Tab strip orientation. `horizontal` lays the strip in a row;\n * `vertical` lays it in a column. The default `pill` variant\n * supports both; the underline variant supports only `horizontal`\n * (per Figma \u2014 vertical underline is not a documented pattern). */\n orientation?: TabsOrientation\n children: React.ReactNode\n className?: string\n}\n\nexport function Tabs({\n value: controlledValue,\n defaultValue,\n onValueChange,\n variant = 'pill',\n orientation = 'horizontal',\n children,\n className,\n}: TabsProps) {\n const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue ?? '')\n const isControlled = controlledValue !== undefined\n const value = isControlled ? controlledValue : uncontrolledValue\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n if (!isControlled) {\n setUncontrolledValue(newValue)\n }\n onValueChange?.(newValue)\n },\n [isControlled, onValueChange],\n )\n\n const contextValue = React.useMemo<TabsContextValue>(\n () => ({ value, onValueChange: handleValueChange, variant, orientation }),\n [value, handleValueChange, variant, orientation],\n )\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n data-slot=\"tabs\"\n data-variant={variant}\n data-orientation={orientation}\n className={cn(orientation === 'vertical' ? 'flex gap-4' : '', className)}\n >\n {children}\n </div>\n </TabsContext.Provider>\n )\n}\n\nexport type TabsListProps = {\n children: React.ReactNode\n className?: string\n /** Accessible name for the `role=\"tablist\"` element. Set it when the\n * surrounding context does not already label the strip (e.g. a bare\n * detail-page section switcher). */\n 'aria-label'?: string\n}\n\nexport function TabsList({ children, className, 'aria-label': ariaLabel }: TabsListProps) {\n const { variant, orientation } = useTabsContext()\n\n const baseClasses =\n variant === 'underline'\n ? // Underline strip \u2014 flat, full-width border-bottom for the rail,\n // selected trigger gets its own border-b-2 accent below.\n orientation === 'vertical'\n ? 'inline-flex flex-col items-stretch gap-1 border-r border-input pr-3'\n : 'inline-flex h-10 items-center justify-start gap-4 border-b border-input text-muted-foreground'\n : // Pill strip (original) \u2014 rounded container, bg-muted shell.\n orientation === 'vertical'\n ? 'inline-flex flex-col items-stretch gap-1 rounded-lg bg-muted p-1 text-muted-foreground'\n : 'inline-flex h-9 items-center justify-start gap-1 rounded-lg bg-muted p-1 text-muted-foreground'\n\n return (\n <div\n data-slot=\"tabs-list\"\n className={cn(baseClasses, className)}\n role=\"tablist\"\n aria-label={ariaLabel}\n aria-orientation={orientation}\n >\n {children}\n </div>\n )\n}\n\nexport type TabsTriggerProps = {\n value: string\n children: React.ReactNode\n className?: string\n disabled?: boolean\n /** Optional leading icon slot \u2014 typically a lucide-react icon at\n * `size-4`. Per Figma `Tab Menu Horizontal Items [1.1]`. */\n leading?: React.ReactNode\n /** Optional trailing count badge \u2014 typically a numeric count or\n * \"New\". Rendered as a muted pill that highlights to indigo when\n * the tab is selected. */\n count?: React.ReactNode\n}\n\nexport function TabsTrigger({\n value,\n children,\n className,\n disabled,\n leading,\n count,\n}: TabsTriggerProps) {\n const { value: selectedValue, onValueChange, variant, orientation } = useTabsContext()\n const isSelected = selectedValue === value\n\n if (variant === 'underline') {\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isSelected}\n disabled={disabled}\n onClick={() => onValueChange(value)}\n data-slot=\"tabs-trigger\"\n data-state={isSelected ? 'active' : 'inactive'}\n data-variant=\"underline\"\n className={cn(\n // Underline trigger \u2014 flat, bottom-border accent when active.\n // Negative margin-bottom -1px so the active accent sits on\n // top of the rail's border-bottom (rather than below it).\n orientation === 'vertical'\n ? 'group relative inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground outline-none transition-colors'\n : 'group relative -mb-px inline-flex items-center gap-2 border-b-2 border-transparent px-1 py-2 text-sm font-medium text-muted-foreground outline-none transition-colors',\n 'hover:text-foreground focus-visible:shadow-focus',\n 'disabled:pointer-events-none disabled:opacity-50',\n isSelected\n ? orientation === 'vertical'\n ? 'bg-muted/40 text-foreground'\n : 'border-accent-indigo font-semibold text-foreground'\n : '',\n className,\n )}\n >\n {leading ? (\n <span\n data-slot=\"tabs-trigger-leading\"\n aria-hidden=\"true\"\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n isSelected ? 'text-accent-indigo' : 'text-muted-foreground',\n )}\n >\n {leading}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{children}</span>\n {count !== undefined ? (\n <span\n data-slot=\"tabs-trigger-count\"\n className={cn(\n 'inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-xs font-medium',\n isSelected\n ? 'bg-accent-indigo/10 text-accent-indigo'\n : 'bg-muted text-muted-foreground',\n )}\n >\n {count}\n </span>\n ) : null}\n </button>\n )\n }\n\n // Pill variant (original look) \u2014 `<Button variant=\"ghost\" size=\"sm\">`\n // with state classes. Backward compatible.\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n role=\"tab\"\n aria-selected={isSelected}\n disabled={disabled}\n onClick={() => onValueChange(value)}\n data-slot=\"tabs-trigger\"\n data-state={isSelected ? 'active' : 'inactive'}\n data-variant=\"pill\"\n className={cn(\n 'gap-2',\n isSelected\n ? 'bg-background text-foreground shadow'\n : 'hover:bg-background/80 hover:text-foreground',\n className,\n )}\n >\n {leading ? (\n <span\n data-slot=\"tabs-trigger-leading\"\n aria-hidden=\"true\"\n className=\"inline-flex shrink-0 items-center justify-center\"\n >\n {leading}\n </span>\n ) : null}\n <span>{children}</span>\n {count !== undefined ? (\n <span\n data-slot=\"tabs-trigger-count\"\n className={cn(\n 'inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-xs font-medium',\n isSelected\n ? 'bg-accent-indigo/10 text-accent-indigo'\n : 'bg-background/60 text-muted-foreground',\n )}\n >\n {count}\n </span>\n ) : null}\n </Button>\n )\n}\n\nexport type TabsContentProps = {\n value: string\n children: React.ReactNode\n className?: string\n}\n\nexport function TabsContent({ value, children, className }: TabsContentProps) {\n const { value: selectedValue, orientation } = useTabsContext()\n\n if (selectedValue !== value) {\n return null\n }\n\n return (\n <div\n role=\"tabpanel\"\n data-slot=\"tabs-content\"\n className={cn(\n orientation === 'vertical' ? 'flex-1 min-w-0' : 'mt-2',\n 'ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n >\n {children}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA+FM,cA4EA,YA5EA;AA7FN,YAAY,WAAW;AACvB,SAAS,eAAe,kBAAkB;AAE1C,SAAS,UAAU;AACnB,SAAS,cAAc;AA4BhB,MAAM,cAAc,cAA4C,MAAS;AAEzE,SAAS,iBAAiB;AAC/B,QAAM,UAAU,WAAW,WAAW;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAuBO,SAAS,KAAK;AAAA,EACnB,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAAc;AACZ,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,gBAAgB,EAAE;AACnF,QAAM,eAAe,oBAAoB;AACzC,QAAM,QAAQ,eAAe,kBAAkB;AAE/C,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,aAAqB;AACpB,UAAI,CAAC,cAAc;AACjB,6BAAqB,QAAQ;AAAA,MAC/B;AACA,sBAAgB,QAAQ;AAAA,IAC1B;AAAA,IACA,CAAC,cAAc,aAAa;AAAA,EAC9B;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,EAAE,OAAO,eAAe,mBAAmB,SAAS,YAAY;AAAA,IACvE,CAAC,OAAO,mBAAmB,SAAS,WAAW;AAAA,EACjD;AAEA,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAO,cAC3B;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,oBAAkB;AAAA,MAClB,WAAW,GAAG,gBAAgB,aAAa,eAAe,IAAI,SAAS;AAAA,MAEtE;AAAA;AAAA,EACH,GACF;AAEJ;AAWO,SAAS,SAAS,EAAE,UAAU,WAAW,cAAc,UAAU,GAAkB;AACxF,QAAM,EAAE,SAAS,YAAY,IAAI,eAAe;AAEhD,QAAM,cACJ,YAAY;AAAA;AAAA;AAAA,IAGR,gBAAgB,aACZ,wEACA;AAAA;AAAA;AAAA,IAEJ,gBAAgB,aACd,2FACA;AAAA;AAER,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,aAAa,SAAS;AAAA,MACpC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,oBAAkB;AAAA,MAEjB;AAAA;AAAA,EACH;AAEJ;AAgBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,OAAO,eAAe,eAAe,SAAS,YAAY,IAAI,eAAe;AACrF,QAAM,aAAa,kBAAkB;AAErC,MAAI,YAAY,aAAa;AAC3B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAe;AAAA,QACf;AAAA,QACA,SAAS,MAAM,cAAc,KAAK;AAAA,QAClC,aAAU;AAAA,QACV,cAAY,aAAa,WAAW;AAAA,QACpC,gBAAa;AAAA,QACb,WAAW;AAAA;AAAA;AAAA;AAAA,UAIT,gBAAgB,aACZ,gJACA;AAAA,UACJ;AAAA,UACA;AAAA,UACA,aACI,gBAAgB,aACd,gCACA,uDACF;AAAA,UACJ;AAAA,QACF;AAAA,QAEC;AAAA,oBACC;AAAA,YAAC;AAAA;AAAA,cACC,aAAU;AAAA,cACV,eAAY;AAAA,cACZ,WAAW;AAAA,gBACT;AAAA,gBACA,aAAa,uBAAuB;AAAA,cACtC;AAAA,cAEC;AAAA;AAAA,UACH,IACE;AAAA,UACJ,oBAAC,UAAK,WAAU,oBAAoB,UAAS;AAAA,UAC5C,UAAU,SACT;AAAA,YAAC;AAAA;AAAA,cACC,aAAU;AAAA,cACV,WAAW;AAAA,gBACT;AAAA,gBACA,aACI,2CACA;AAAA,cACN;AAAA,cAEC;AAAA;AAAA,UACH,IACE;AAAA;AAAA;AAAA,IACN;AAAA,EAEJ;AAIA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,MAAK;AAAA,MACL,iBAAe;AAAA,MACf;AAAA,MACA,SAAS,MAAM,cAAc,KAAK;AAAA,MAClC,aAAU;AAAA,MACV,cAAY,aAAa,WAAW;AAAA,MACpC,gBAAa;AAAA,MACb,WAAW;AAAA,QACT;AAAA,QACA,aACI,yCACA;AAAA,QACJ;AAAA,MACF;AAAA,MAEC;AAAA,kBACC;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,eAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH,IACE;AAAA,QACJ,oBAAC,UAAM,UAAS;AAAA,QACf,UAAU,SACT;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,aACI,2CACA;AAAA,YACN;AAAA,YAEC;AAAA;AAAA,QACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;AAQO,SAAS,YAAY,EAAE,OAAO,UAAU,UAAU,GAAqB;AAC5E,QAAM,EAAE,OAAO,eAAe,YAAY,IAAI,eAAe;AAE7D,MAAI,kBAAkB,OAAO;AAC3B,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,WAAW;AAAA,QACT,gBAAgB,aAAa,mBAAmB;AAAA,QAChD;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6551.1.1511bfa890",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -155,13 +155,13 @@
|
|
|
155
155
|
"remark-gfm": "^4.0.1"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
158
|
+
"@open-mercato/shared": "0.6.6-develop.6551.1.1511bfa890",
|
|
159
159
|
"react": ">=18.0.0",
|
|
160
160
|
"react-dom": ">=18.0.0",
|
|
161
161
|
"react-is": ">=18.0.0"
|
|
162
162
|
},
|
|
163
163
|
"devDependencies": {
|
|
164
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
164
|
+
"@open-mercato/shared": "0.6.6-develop.6551.1.1511bfa890",
|
|
165
165
|
"@testing-library/dom": "^10.4.1",
|
|
166
166
|
"@testing-library/jest-dom": "^6.9.1",
|
|
167
167
|
"@testing-library/react": "^16.3.1",
|
package/src/ai/ChatPaneTabs.tsx
CHANGED
|
@@ -97,7 +97,7 @@ export function ChatPaneTabs({ agentId, className }: ChatPaneTabsProps) {
|
|
|
97
97
|
className={cn(
|
|
98
98
|
'group flex max-w-[12rem] shrink-0 items-center gap-1 rounded-t-md border-b-2 px-2 py-1',
|
|
99
99
|
isActive
|
|
100
|
-
? 'border-
|
|
100
|
+
? 'border-accent-indigo bg-background text-foreground'
|
|
101
101
|
: 'border-transparent text-muted-foreground hover:text-foreground',
|
|
102
102
|
)}
|
|
103
103
|
>
|
|
@@ -126,7 +126,7 @@ export function ChatPaneTabs({ agentId, className }: ChatPaneTabsProps) {
|
|
|
126
126
|
onClick={() => sessions.setActiveSession(session.id)}
|
|
127
127
|
onDoubleClick={() => startRename(session)}
|
|
128
128
|
title={label}
|
|
129
|
-
className="truncate text-xs font-medium"
|
|
129
|
+
className="truncate rounded-sm text-xs font-medium outline-none focus-visible:shadow-focus"
|
|
130
130
|
>
|
|
131
131
|
{label}
|
|
132
132
|
</button>
|
package/src/backend/AppShell.tsx
CHANGED
|
@@ -1494,13 +1494,13 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
1494
1494
|
aria-selected={isActive}
|
|
1495
1495
|
aria-controls="mobile-drawer-tabpanel"
|
|
1496
1496
|
onClick={() => setMobileDrawerView(tab.id === 'main' ? 'main' : 'auto')}
|
|
1497
|
-
className="relative inline-flex items-center pb-2 text-sm font-medium leading-5 tracking-tight transition-colors
|
|
1497
|
+
className="relative inline-flex items-center pb-2 text-sm font-medium leading-5 tracking-tight transition-colors outline-none focus-visible:shadow-focus data-[active=true]:text-foreground data-[active=false]:text-muted-foreground hover:text-foreground"
|
|
1498
1498
|
data-active={isActive}
|
|
1499
1499
|
>
|
|
1500
1500
|
<span>{tab.label}</span>
|
|
1501
1501
|
{isActive ? (
|
|
1502
1502
|
<span
|
|
1503
|
-
className="absolute -bottom-px left-0 right-0 h-0.5 bg-
|
|
1503
|
+
className="absolute -bottom-px left-0 right-0 h-0.5 bg-accent-indigo"
|
|
1504
1504
|
aria-hidden="true"
|
|
1505
1505
|
/>
|
|
1506
1506
|
) : null}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as React from 'react'
|
|
5
5
|
import { renderToString } from 'react-dom/server'
|
|
6
|
-
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
6
|
+
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
7
7
|
import type { ColumnDef } from '@tanstack/react-table'
|
|
8
8
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
9
9
|
import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
|
|
@@ -21,6 +21,16 @@ const flashMock = jest.fn()
|
|
|
21
21
|
jest.mock('../injection/useInjectionDataWidgets', () => ({
|
|
22
22
|
useInjectionDataWidgets: (spotId: string) => useInjectionDataWidgetsMock(spotId),
|
|
23
23
|
}))
|
|
24
|
+
// InjectionSpot/useInjectionSpotEvents (rendered whenever injectionSpotId is
|
|
25
|
+
// set) load spot widgets through the real async registry loader; unmocked it
|
|
26
|
+
// resolves at an arbitrary point of the test run, racing waitFor() flushes
|
|
27
|
+
// and firing setState outside act().
|
|
28
|
+
jest.mock('@open-mercato/shared/modules/widgets/injection-loader', () => ({
|
|
29
|
+
getInjectionRegistryVersion: () => 0,
|
|
30
|
+
subscribeToInjectionRegistryChanges: () => () => {},
|
|
31
|
+
loadInjectionWidgetsForSpot: jest.fn(async () => []),
|
|
32
|
+
loadInjectionDataWidgetsForSpot: jest.fn(async () => []),
|
|
33
|
+
}))
|
|
24
34
|
jest.mock('../FlashMessages', () => ({
|
|
25
35
|
flash: (...args: unknown[]) => flashMock(...args),
|
|
26
36
|
}))
|
|
@@ -297,19 +307,25 @@ describe('DataTable extensions', () => {
|
|
|
297
307
|
try {
|
|
298
308
|
fireEvent.click(screen.getByRole('button', { name: 'Delete all filtered' }))
|
|
299
309
|
|
|
310
|
+
// onExecute being CALLED is not enough — job-1 is registered as tracked
|
|
311
|
+
// in the click handler's continuation after `await onExecute()`. The
|
|
312
|
+
// "started" flash is the observable proof that continuation ran; without
|
|
313
|
+
// it the completion event below can race the registration and be ignored.
|
|
300
314
|
await waitFor(() => expect(onExecute).toHaveBeenCalledTimes(1))
|
|
301
|
-
await
|
|
315
|
+
await waitFor(() => expect(flashMock).toHaveBeenCalledWith(expect.any(String), 'success'))
|
|
302
316
|
refreshButtonMock.mockClear()
|
|
303
317
|
mockRouterRefresh.mockClear()
|
|
304
318
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
319
|
+
act(() => {
|
|
320
|
+
window.dispatchEvent(new CustomEvent(APP_EVENT_DOM_NAME, {
|
|
321
|
+
detail: {
|
|
322
|
+
id: 'progress.job.completed',
|
|
323
|
+
payload: { jobId: 'job-1' },
|
|
324
|
+
timestamp: Date.now(),
|
|
325
|
+
organizationId: 'org-1',
|
|
326
|
+
},
|
|
327
|
+
}))
|
|
328
|
+
})
|
|
313
329
|
|
|
314
330
|
await waitFor(() => expect(refreshButtonMock).toHaveBeenCalledTimes(1))
|
|
315
331
|
expect(mockRouterRefresh).not.toHaveBeenCalled()
|
|
@@ -6,6 +6,7 @@ import { Button } from '../../primitives/button'
|
|
|
6
6
|
import { IconButton } from '../../primitives/icon-button'
|
|
7
7
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
|
|
8
8
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
9
|
+
import { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'
|
|
9
10
|
import { TagsInput } from '@open-mercato/ui/backend/inputs/TagsInput'
|
|
10
11
|
import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
|
|
11
12
|
import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
|
|
@@ -576,29 +577,21 @@ export function AttachmentMetadataDialog({ open, onOpenChange, item, availableTa
|
|
|
576
577
|
</div>
|
|
577
578
|
{isImage ? (
|
|
578
579
|
<div className="rounded border">
|
|
579
|
-
<
|
|
580
|
-
{
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
)}
|
|
595
|
-
>
|
|
596
|
-
{tab === 'preview'
|
|
597
|
-
? t('attachments.library.metadata.preview', 'Preview')
|
|
598
|
-
: t('attachments.library.metadata.resizeTool.title', 'Generate resized URL')}
|
|
599
|
-
</Button>
|
|
600
|
-
))}
|
|
601
|
-
</div>
|
|
580
|
+
<Tabs
|
|
581
|
+
value={imageTab}
|
|
582
|
+
onValueChange={(value) => setImageTab(value as 'preview' | 'resize')}
|
|
583
|
+
variant="underline"
|
|
584
|
+
>
|
|
585
|
+
<TabsList className="w-full flex-wrap px-3">
|
|
586
|
+
{(['preview', 'resize'] as const).map((tab) => (
|
|
587
|
+
<TabsTrigger key={tab} value={tab}>
|
|
588
|
+
{tab === 'preview'
|
|
589
|
+
? t('attachments.library.metadata.preview', 'Preview')
|
|
590
|
+
: t('attachments.library.metadata.resizeTool.title', 'Generate resized URL')}
|
|
591
|
+
</TabsTrigger>
|
|
592
|
+
))}
|
|
593
|
+
</TabsList>
|
|
594
|
+
</Tabs>
|
|
602
595
|
<div className="space-y-3 p-3">
|
|
603
596
|
{imageTab === 'preview' ? (
|
|
604
597
|
previewUrl ? (
|
|
@@ -332,7 +332,7 @@ function AndOrToggle({
|
|
|
332
332
|
const andLabel = t('ui.advancedFilter.combinator.and', 'And')
|
|
333
333
|
const orLabel = t('ui.advancedFilter.combinator.or', 'Or')
|
|
334
334
|
const baseBtn = 'h-8 px-3 text-sm font-medium outline-none focus-visible:shadow-focus disabled:cursor-not-allowed'
|
|
335
|
-
const selBtn = 'bg-brand-violet text-
|
|
335
|
+
const selBtn = 'bg-brand-violet text-brand-violet-foreground hover:bg-brand-violet/90'
|
|
336
336
|
const unselBtn = 'bg-background text-foreground hover:bg-accent'
|
|
337
337
|
return (
|
|
338
338
|
<div
|
|
@@ -86,7 +86,8 @@ describe('Drawer', () => {
|
|
|
86
86
|
const content = document.querySelector('[data-slot="drawer-content"]') as HTMLElement
|
|
87
87
|
expect(content.className).toContain('right-0')
|
|
88
88
|
expect(content.className).toContain('inset-y-0')
|
|
89
|
-
|
|
89
|
+
// Figma Drawer [1.1] width contract: 400px
|
|
90
|
+
expect(content.className).toContain('max-w-[400px]')
|
|
90
91
|
// Per Figma: inner edge rounded, no panel border on the seam.
|
|
91
92
|
expect(content.className).toContain('rounded-l-xl')
|
|
92
93
|
expect(content.className).not.toMatch(/\bborder-l\b/)
|
|
@@ -94,10 +94,11 @@ const drawerContentVariants = cva(
|
|
|
94
94
|
variants: {
|
|
95
95
|
side: {
|
|
96
96
|
right:
|
|
97
|
-
|
|
97
|
+
// Figma Drawer [1.1] width: 400px (not the Tailwind max-w-md 448px)
|
|
98
|
+
'inset-y-0 right-0 h-full w-full max-w-[400px] rounded-l-xl ' +
|
|
98
99
|
'data-[state=open]:slide-in-from-right data-[state=closed]:slide-out-to-right',
|
|
99
100
|
left:
|
|
100
|
-
'inset-y-0 left-0 h-full w-full max-w-
|
|
101
|
+
'inset-y-0 left-0 h-full w-full max-w-[400px] rounded-r-xl ' +
|
|
101
102
|
'data-[state=open]:slide-in-from-left data-[state=closed]:slide-out-to-left',
|
|
102
103
|
top:
|
|
103
104
|
'inset-x-0 top-0 max-h-[80vh] w-full rounded-b-xl ' +
|
|
@@ -303,7 +304,7 @@ const DrawerTitle = React.forwardRef<
|
|
|
303
304
|
<DialogPrimitive.Title
|
|
304
305
|
ref={ref}
|
|
305
306
|
data-slot="drawer-title"
|
|
306
|
-
className={cn('text-
|
|
307
|
+
className={cn('text-lg font-semibold leading-tight text-foreground', className)}
|
|
307
308
|
{...props}
|
|
308
309
|
/>
|
|
309
310
|
))
|
package/src/primitives/tabs.tsx
CHANGED
|
@@ -108,9 +108,13 @@ export function Tabs({
|
|
|
108
108
|
export type TabsListProps = {
|
|
109
109
|
children: React.ReactNode
|
|
110
110
|
className?: string
|
|
111
|
+
/** Accessible name for the `role="tablist"` element. Set it when the
|
|
112
|
+
* surrounding context does not already label the strip (e.g. a bare
|
|
113
|
+
* detail-page section switcher). */
|
|
114
|
+
'aria-label'?: string
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
export function TabsList({ children, className }: TabsListProps) {
|
|
117
|
+
export function TabsList({ children, className, 'aria-label': ariaLabel }: TabsListProps) {
|
|
114
118
|
const { variant, orientation } = useTabsContext()
|
|
115
119
|
|
|
116
120
|
const baseClasses =
|
|
@@ -130,6 +134,7 @@ export function TabsList({ children, className }: TabsListProps) {
|
|
|
130
134
|
data-slot="tabs-list"
|
|
131
135
|
className={cn(baseClasses, className)}
|
|
132
136
|
role="tablist"
|
|
137
|
+
aria-label={ariaLabel}
|
|
133
138
|
aria-orientation={orientation}
|
|
134
139
|
>
|
|
135
140
|
{children}
|