@open-mercato/ui 0.6.8-develop.7099.1.7ba2771d06 → 0.7.0
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/dist/ai/records/ActivityCard.js +3 -4
- package/dist/ai/records/ActivityCard.js.map +2 -2
- package/dist/ai/records/DealCard.js +3 -4
- package/dist/ai/records/DealCard.js.map +2 -2
- package/dist/ai/records/ProductCard.js +2 -3
- package/dist/ai/records/ProductCard.js.map +2 -2
- package/dist/backend/AppShell.js +106 -129
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/DataTable.js +9 -22
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/detail/NotesSection.js +20 -26
- package/dist/backend/detail/NotesSection.js.map +2 -2
- package/dist/backend/icons/lucideRegistry.generated.js +0 -4
- package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
- package/dist/backend/injection/useRegisteredComponent.js +6 -24
- package/dist/backend/injection/useRegisteredComponent.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +4 -8
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +9 -9
- package/dist/backend/inputs/LookupSelect.js.map +2 -2
- package/dist/backend/sidebar/SidebarCustomizationEditor.js +30 -36
- package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
- package/dist/backend/utils/crud.js.map +2 -2
- package/dist/backend/utils/useAutoDiscoveredFields.js +8 -4
- package/dist/backend/utils/useAutoDiscoveredFields.js.map +2 -2
- package/dist/primitives/alert.js +1 -1
- package/dist/primitives/alert.js.map +2 -2
- package/dist/primitives/icon-button.js +1 -20
- package/dist/primitives/icon-button.js.map +2 -2
- package/dist/primitives/pagination.js +9 -14
- package/dist/primitives/pagination.js.map +2 -2
- package/dist/primitives/rich-editor.js +7 -14
- package/dist/primitives/rich-editor.js.map +2 -2
- package/dist/utils/format.js +5 -5
- package/dist/utils/format.js.map +2 -2
- package/package.json +4 -5
- package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +0 -2
- package/src/ai/records/ActivityCard.tsx +3 -4
- package/src/ai/records/DealCard.tsx +3 -4
- package/src/ai/records/ProductCard.tsx +2 -3
- package/src/backend/AppShell.tsx +58 -89
- package/src/backend/DataTable.tsx +12 -46
- package/src/backend/__tests__/AppShell.test.tsx +1 -162
- package/src/backend/__tests__/DataTable.render.test.tsx +0 -37
- package/src/backend/__tests__/NotesSection.test.tsx +0 -89
- package/src/backend/detail/NotesSection.tsx +67 -92
- package/src/backend/icons/lucideRegistry.generated.tsx +0 -4
- package/src/backend/injection/useRegisteredComponent.tsx +8 -49
- package/src/backend/inputs/ComboboxInput.tsx +5 -21
- package/src/backend/inputs/LookupSelect.tsx +14 -10
- package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +0 -142
- package/src/backend/inputs/__tests__/LookupSelect.test.tsx +40 -63
- package/src/backend/sidebar/SidebarCustomizationEditor.tsx +11 -14
- package/src/backend/utils/__tests__/useAutoDiscoveredFields.groupLabels.test.tsx +59 -0
- package/src/backend/utils/crud.ts +0 -2
- package/src/backend/utils/useAutoDiscoveredFields.ts +10 -4
- package/src/primitives/__tests__/alert.test.tsx +1 -41
- package/src/primitives/__tests__/icon-button.test.tsx +0 -70
- package/src/primitives/__tests__/notification.test.tsx +1 -1
- package/src/primitives/__tests__/pagination.test.tsx +0 -54
- package/src/primitives/__tests__/rich-editor.test.tsx +0 -43
- package/src/primitives/alert.tsx +2 -5
- package/src/primitives/icon-button.tsx +1 -20
- package/src/primitives/pagination.tsx +9 -40
- package/src/primitives/rich-editor.tsx +4 -9
- package/src/utils/__tests__/format.test.ts +14 -27
- package/src/utils/format.ts +5 -10
- package/src/backend/__tests__/DataTable.cappedPagination.test.tsx +0 -122
- package/src/backend/__tests__/DataTable.headerWrap.test.tsx +0 -111
- package/src/backend/__tests__/useRegisteredComponent.identity.test.tsx +0 -305
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { useOptionalT } from "@open-mercato/shared/lib/i18n/context";
|
|
3
4
|
import {
|
|
4
5
|
mapCustomFieldKindToFilterType,
|
|
5
6
|
normalizeCustomFieldFilterOptions,
|
|
@@ -23,6 +24,9 @@ function useAutoDiscoveredFields({
|
|
|
23
24
|
columns,
|
|
24
25
|
customFieldDefs
|
|
25
26
|
}) {
|
|
27
|
+
const t = useOptionalT();
|
|
28
|
+
const defaultGroupLabel = t?.("ui.columnChooser.defaultGroup", "Columns") ?? "Columns";
|
|
29
|
+
const customFieldsGroupLabel = t?.("ui.columnChooser.customFieldsGroup", "Custom Fields") ?? "Custom Fields";
|
|
26
30
|
return React.useMemo(() => {
|
|
27
31
|
const filterFields = [];
|
|
28
32
|
const chooserFields = [];
|
|
@@ -55,7 +59,7 @@ function useAutoDiscoveredFields({
|
|
|
55
59
|
chooserFields.push({
|
|
56
60
|
key: accessorKey,
|
|
57
61
|
label,
|
|
58
|
-
group: meta?.columnChooserGroup ??
|
|
62
|
+
group: meta?.columnChooserGroup ?? defaultGroupLabel,
|
|
59
63
|
alwaysVisible: meta?.alwaysVisible ?? i === 0,
|
|
60
64
|
defaultVisible: true
|
|
61
65
|
});
|
|
@@ -71,7 +75,7 @@ function useAutoDiscoveredFields({
|
|
|
71
75
|
key: filterKey,
|
|
72
76
|
label: def.label || def.key,
|
|
73
77
|
type,
|
|
74
|
-
group:
|
|
78
|
+
group: customFieldsGroupLabel
|
|
75
79
|
};
|
|
76
80
|
if (type === "select" && Array.isArray(def.options) && def.options.length) {
|
|
77
81
|
field.options = normalizeCustomFieldFilterOptions(def.options);
|
|
@@ -83,13 +87,13 @@ function useAutoDiscoveredFields({
|
|
|
83
87
|
chooserFields.push({
|
|
84
88
|
key: filterKey,
|
|
85
89
|
label: def.label || def.key,
|
|
86
|
-
group: def.group?.title ??
|
|
90
|
+
group: def.group?.title ?? customFieldsGroupLabel,
|
|
87
91
|
defaultVisible: false
|
|
88
92
|
});
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
95
|
return { advancedFilterFields: filterFields, columnChooserFields: chooserFields };
|
|
92
|
-
}, [columns, customFieldDefs]);
|
|
96
|
+
}, [columns, customFieldDefs, defaultGroupLabel, customFieldsGroupLabel]);
|
|
93
97
|
}
|
|
94
98
|
export {
|
|
95
99
|
useAutoDiscoveredFields
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/utils/useAutoDiscoveredFields.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { RowData } from '@tanstack/react-table'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterFieldDef as AdvancedFilterFieldDef, FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { ColumnChooserField } from '../columns/ColumnChooserPanel'\nimport type { CustomFieldDefDto } from './customFieldDefs'\nimport {\n mapCustomFieldKindToFilterType,\n normalizeCustomFieldFilterOptions,\n supportsCustomFieldColumn,\n} from './customFieldColumns'\n\ntype ColumnMeta = {\n filterKey?: string\n filterType?: FilterFieldType\n filterOptions?: FilterOption[]\n filterLoadOptions?: (query?: string) => Promise<FilterOption[]>\n filterable?: boolean\n filterGroup?: string\n filterIconName?: string\n columnChooserGroup?: string\n alwaysVisible?: boolean\n hidden?: boolean\n truncate?: boolean\n maxWidth?: string\n priority?: number\n tooltipContent?: (row: unknown) => string | undefined\n}\n\nfunction resolveHeaderLabel<T extends RowData>(column: ColumnDef<T, unknown>): string {\n const header = (column as any).header\n if (typeof header === 'string') return header\n const accessorKey = (column as any).accessorKey as string | undefined\n if (!accessorKey) return ''\n return accessorKey\n .replace(/^cf_/, '')\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c: string) => c.toUpperCase())\n}\n\nfunction inferFilterType(accessorKey: string, meta?: ColumnMeta): FilterFieldType {\n if (meta?.filterType) return meta.filterType\n if (accessorKey.endsWith('_at') || accessorKey === 'createdAt' || accessorKey === 'updatedAt' || accessorKey === 'expectedCloseAt' || accessorKey.endsWith('At')) return 'date'\n if (accessorKey === 'probability' || accessorKey === 'valueAmount' || accessorKey === 'value_amount') return 'number'\n if (meta?.filterOptions) return 'select'\n return 'text'\n}\n\nexport type UseAutoDiscoveredFieldsInput<T extends RowData = RowData> = {\n columns: ColumnDef<T, unknown>[]\n customFieldDefs: CustomFieldDefDto[]\n}\n\nexport type UseAutoDiscoveredFieldsResult = {\n advancedFilterFields: AdvancedFilterFieldDef[]\n columnChooserFields: ColumnChooserField[]\n}\n\nexport function useAutoDiscoveredFields<T extends RowData = RowData>({\n columns,\n customFieldDefs,\n}: UseAutoDiscoveredFieldsInput<T>): UseAutoDiscoveredFieldsResult {\n return React.useMemo(() => {\n const filterFields: AdvancedFilterFieldDef[] = []\n const chooserFields: ColumnChooserField[] = []\n const seenFilterKeys = new Set<string>()\n const seenChooserKeys = new Set<string>()\n\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n const accessorKey = (col as any).accessorKey as string | undefined\n if (!accessorKey) continue\n\n const meta = (col as any).meta as ColumnMeta | undefined\n const label = resolveHeaderLabel(col)\n if (!label) continue\n const filterKey = meta?.filterKey ?? accessorKey\n\n // Advanced filter field\n if (meta?.filterable !== false && filterKey && !seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = inferFilterType(filterKey, meta)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label,\n type,\n group: meta?.filterGroup ?? meta?.columnChooserGroup,\n }\n if (meta?.filterIconName) field.iconName = meta.filterIconName\n if (meta?.filterOptions) field.options = meta.filterOptions\n if (meta?.filterLoadOptions) field.loadOptions = meta.filterLoadOptions\n filterFields.push(field)\n }\n\n // Column chooser field\n if (!seenChooserKeys.has(accessorKey)) {\n seenChooserKeys.add(accessorKey)\n chooserFields.push({\n key: accessorKey,\n label,\n group: meta?.columnChooserGroup ??
|
|
5
|
-
"mappings": ";AACA,YAAY,WAAW;
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { RowData } from '@tanstack/react-table'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useOptionalT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterFieldDef as AdvancedFilterFieldDef, FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { ColumnChooserField } from '../columns/ColumnChooserPanel'\nimport type { CustomFieldDefDto } from './customFieldDefs'\nimport {\n mapCustomFieldKindToFilterType,\n normalizeCustomFieldFilterOptions,\n supportsCustomFieldColumn,\n} from './customFieldColumns'\n\ntype ColumnMeta = {\n filterKey?: string\n filterType?: FilterFieldType\n filterOptions?: FilterOption[]\n filterLoadOptions?: (query?: string) => Promise<FilterOption[]>\n filterable?: boolean\n filterGroup?: string\n filterIconName?: string\n columnChooserGroup?: string\n alwaysVisible?: boolean\n hidden?: boolean\n truncate?: boolean\n maxWidth?: string\n priority?: number\n tooltipContent?: (row: unknown) => string | undefined\n}\n\nfunction resolveHeaderLabel<T extends RowData>(column: ColumnDef<T, unknown>): string {\n const header = (column as any).header\n if (typeof header === 'string') return header\n const accessorKey = (column as any).accessorKey as string | undefined\n if (!accessorKey) return ''\n return accessorKey\n .replace(/^cf_/, '')\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c: string) => c.toUpperCase())\n}\n\nfunction inferFilterType(accessorKey: string, meta?: ColumnMeta): FilterFieldType {\n if (meta?.filterType) return meta.filterType\n if (accessorKey.endsWith('_at') || accessorKey === 'createdAt' || accessorKey === 'updatedAt' || accessorKey === 'expectedCloseAt' || accessorKey.endsWith('At')) return 'date'\n if (accessorKey === 'probability' || accessorKey === 'valueAmount' || accessorKey === 'value_amount') return 'number'\n if (meta?.filterOptions) return 'select'\n return 'text'\n}\n\nexport type UseAutoDiscoveredFieldsInput<T extends RowData = RowData> = {\n columns: ColumnDef<T, unknown>[]\n customFieldDefs: CustomFieldDefDto[]\n}\n\nexport type UseAutoDiscoveredFieldsResult = {\n advancedFilterFields: AdvancedFilterFieldDef[]\n columnChooserFields: ColumnChooserField[]\n}\n\nexport function useAutoDiscoveredFields<T extends RowData = RowData>({\n columns,\n customFieldDefs,\n}: UseAutoDiscoveredFieldsInput<T>): UseAutoDiscoveredFieldsResult {\n // Optional translator: this hook is a public entry point third-party modules\n // render, and it worked without an I18nProvider before it needed labels.\n const t = useOptionalT()\n const defaultGroupLabel = t?.('ui.columnChooser.defaultGroup', 'Columns') ?? 'Columns'\n const customFieldsGroupLabel = t?.('ui.columnChooser.customFieldsGroup', 'Custom Fields') ?? 'Custom Fields'\n return React.useMemo(() => {\n const filterFields: AdvancedFilterFieldDef[] = []\n const chooserFields: ColumnChooserField[] = []\n const seenFilterKeys = new Set<string>()\n const seenChooserKeys = new Set<string>()\n\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n const accessorKey = (col as any).accessorKey as string | undefined\n if (!accessorKey) continue\n\n const meta = (col as any).meta as ColumnMeta | undefined\n const label = resolveHeaderLabel(col)\n if (!label) continue\n const filterKey = meta?.filterKey ?? accessorKey\n\n // Advanced filter field\n if (meta?.filterable !== false && filterKey && !seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = inferFilterType(filterKey, meta)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label,\n type,\n group: meta?.filterGroup ?? meta?.columnChooserGroup,\n }\n if (meta?.filterIconName) field.iconName = meta.filterIconName\n if (meta?.filterOptions) field.options = meta.filterOptions\n if (meta?.filterLoadOptions) field.loadOptions = meta.filterLoadOptions\n filterFields.push(field)\n }\n\n // Column chooser field\n if (!seenChooserKeys.has(accessorKey)) {\n seenChooserKeys.add(accessorKey)\n chooserFields.push({\n key: accessorKey,\n label,\n group: meta?.columnChooserGroup ?? defaultGroupLabel,\n alwaysVisible: meta?.alwaysVisible ?? i === 0,\n defaultVisible: true,\n })\n }\n }\n\n // Append ALL custom field definitions (not just filterable/listVisible)\n // so that every entity field appears in both the column chooser and advanced filter\n for (const def of customFieldDefs) {\n if (!supportsCustomFieldColumn(def)) continue\n const filterKey = `cf_${def.key}`\n if (!seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = mapCustomFieldKindToFilterType(def.kind)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label: def.label || def.key,\n type,\n group: customFieldsGroupLabel,\n }\n if (type === 'select' && Array.isArray(def.options) && def.options.length) {\n field.options = normalizeCustomFieldFilterOptions(def.options)\n }\n filterFields.push(field)\n }\n if (!seenChooserKeys.has(filterKey)) {\n seenChooserKeys.add(filterKey)\n chooserFields.push({\n key: filterKey,\n label: def.label || def.key,\n group: def.group?.title ?? customFieldsGroupLabel,\n defaultVisible: false,\n })\n }\n }\n\n return { advancedFilterFields: filterFields, columnChooserFields: chooserFields }\n }, [columns, customFieldDefs, defaultGroupLabel, customFieldsGroupLabel])\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/primitives/alert.js
CHANGED
|
@@ -193,7 +193,7 @@ const AlertTitle = React.forwardRef(
|
|
|
193
193
|
);
|
|
194
194
|
AlertTitle.displayName = "AlertTitle";
|
|
195
195
|
const AlertDescription = React.forwardRef(
|
|
196
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx("
|
|
196
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm leading-5", className), ...props })
|
|
197
197
|
);
|
|
198
198
|
AlertDescription.displayName = "AlertDescription";
|
|
199
199
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/alert.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { AlertCircle, CheckCircle2, Info, Rocket, X, AlertTriangle } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type AlertStatus = 'error' | 'warning' | 'success' | 'information' | 'feature'\nexport type AlertStyle = 'filled' | 'light' | 'lighter' | 'stroke'\nexport type AlertSize = 'xs' | 'sm' | 'default'\n\n/**\n * Legacy `variant` values from the pre-Figma-`169:2358` API. Kept for BC \u2014\n * mapped to `status` inside the Alert component so existing call sites do\n * not need migration. The Figma `light` look (`#fecaca` saturated tint\n * for error \u2192 `state/{x}/light` token family) is heavier than the\n * pre-Figma look; legacy call sites that want the softer pre-Figma\n * appearance should switch explicitly to `style=\"lighter\"`.\n */\ntype LegacyVariant = 'default' | 'destructive' | 'success' | 'warning' | 'info'\n\nconst legacyVariantToStatus: Record<LegacyVariant, AlertStatus> = {\n default: 'information',\n destructive: 'error',\n success: 'success',\n warning: 'warning',\n info: 'information',\n}\n\nconst alertVariants = cva(\n 'relative flex w-full gap-2 transition-colors',\n {\n variants: {\n status: {\n error: '',\n warning: '',\n success: '',\n information: '',\n feature: '',\n },\n style: {\n filled: '',\n light: '',\n lighter: '',\n stroke: '',\n },\n size: {\n // Figma X-Small: px-[8px] py-[6px], radius-8, Paragraph/X Small (12/16)\n xs: 'min-h-8 items-center rounded-lg px-2 py-1.5 text-xs leading-4 tracking-tight',\n // Figma Small: px-[10px] py-[8px], radius-8, Paragraph/Small (14/20)\n sm: 'min-h-9 items-center rounded-lg px-2.5 py-2 text-sm leading-5 tracking-tight',\n // Figma Large: px-[12px] py-[10px], radius-12, Paragraph/Small (14/20) multi-line\n default: 'items-start rounded-xl px-3 py-2.5 text-sm leading-5 tracking-tight',\n },\n },\n compoundVariants: [\n // === filled (Figma: bg = state/{x}/base, text = static-white) ===\n { status: 'error', style: 'filled', className: 'border border-transparent bg-status-error-icon text-white' },\n { status: 'warning', style: 'filled', className: 'border border-transparent bg-status-warning-icon text-white' },\n { status: 'success', style: 'filled', className: 'border border-transparent bg-status-success-icon text-white' },\n { status: 'information', style: 'filled', className: 'border border-transparent bg-status-info-icon text-white' },\n { status: 'feature', style: 'filled', className: 'border border-transparent bg-status-neutral-icon text-white' },\n // === light (Figma: bg = state/{x}/light, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'light', className: 'border border-transparent bg-status-error-border text-foreground' },\n { status: 'warning', style: 'light', className: 'border border-transparent bg-status-warning-border text-foreground' },\n { status: 'success', style: 'light', className: 'border border-transparent bg-status-success-border text-foreground' },\n { status: 'information', style: 'light', className: 'border border-transparent bg-status-info-border text-foreground' },\n { status: 'feature', style: 'light', className: 'border border-transparent bg-status-neutral-border text-foreground' },\n // === lighter (Figma: bg = state/{x}/lighter, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'lighter', className: 'border border-transparent bg-status-error-bg text-foreground' },\n { status: 'warning', style: 'lighter', className: 'border border-transparent bg-status-warning-bg text-foreground' },\n { status: 'success', style: 'lighter', className: 'border border-transparent bg-status-success-bg text-foreground' },\n { status: 'information', style: 'lighter', className: 'border border-transparent bg-status-info-bg text-foreground' },\n { status: 'feature', style: 'lighter', className: 'border border-transparent bg-status-neutral-bg text-foreground' },\n // === stroke (Figma: bg = white, border = stroke/soft-200, text = strong-950, shadow = regular-shadow/medium) ===\n { style: 'stroke', className: 'border border-border bg-background text-foreground shadow-lg' },\n ],\n defaultVariants: {\n status: 'information',\n style: 'light',\n size: 'sm',\n },\n },\n)\n\n// Dismiss button (X) color per Figma: text/strong-950 (#171717 black) at\n// `opacity-40` for light/lighter/stroke \u2014 matches the Figma `close-line`\n// icon which is the dark icon-strong-950 fill rendered at 40% alpha\n// (visually \u2248 #5c5c5c gray). White at full opacity for filled so it\n// stays legible over the saturated bg.\nconst alertDismissVariants = cva(\n 'shrink-0 rounded-md p-0.5 transition-opacity focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n {\n variants: {\n style: {\n filled: 'text-white opacity-90 hover:opacity-100',\n light: 'text-foreground opacity-40 hover:opacity-100',\n lighter: 'text-foreground opacity-40 hover:opacity-100',\n stroke: 'text-foreground opacity-40 hover:opacity-100',\n },\n },\n defaultVariants: { style: 'light' },\n },\n)\n\nconst alertIconBadgeVariants = cva(\n 'inline-flex shrink-0 items-center justify-center rounded-full text-white',\n {\n variants: {\n status: {\n error: 'bg-status-error-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n information: 'bg-status-info-icon',\n feature: 'bg-status-neutral-icon',\n },\n size: {\n xs: 'size-4 [&>svg]:size-2.5',\n sm: 'size-5 [&>svg]:size-3',\n default: 'size-6 [&>svg]:size-3.5',\n },\n },\n defaultVariants: { status: 'information', size: 'sm' },\n },\n)\n\nconst defaultStatusIcons: Record<AlertStatus, React.ComponentType<{ className?: string; 'aria-hidden'?: 'true' }>> = {\n error: AlertCircle,\n warning: AlertTriangle,\n success: CheckCircle2,\n information: Info,\n feature: Rocket,\n}\n\nexport type AlertProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'role' | 'style'> &\n Omit<VariantProps<typeof alertVariants>, 'status' | 'style' | 'size'> & {\n status?: AlertStatus\n style?: AlertStyle\n size?: AlertSize\n /** Show the leading icon. Default `true`. */\n showIcon?: boolean\n /** Override the default per-status Lucide icon. */\n icon?: React.ReactNode\n /** Render a trailing close (X) button. Pair with `onDismiss`. */\n dismissible?: boolean\n onDismiss?: () => void\n dismissAriaLabel?: string\n /** Inline action slot (link buttons) rendered to the right of the body. */\n action?: React.ReactNode\n /**\n * @deprecated Use `status` + `style` instead. Kept for BC. Maps the\n * legacy single-prop variants onto `status` and picks up the new\n * `light` + `sm` defaults.\n */\n variant?: LegacyVariant\n }\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n status,\n style,\n size,\n showIcon = true,\n icon,\n dismissible = false,\n onDismiss,\n dismissAriaLabel = 'Dismiss',\n action,\n variant,\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedStatus: AlertStatus = status ?? (variant ? legacyVariantToStatus[variant] : 'information')\n const resolvedStyle: AlertStyle = style ?? 'light'\n const resolvedSize: AlertSize = size ?? 'sm'\n const IconComponent = defaultStatusIcons[resolvedStatus]\n const rawIcon = icon ?? <IconComponent aria-hidden=\"true\" />\n\n let iconSlot: React.ReactNode = null\n if (showIcon) {\n if (resolvedStyle === 'filled') {\n // Filled: plain icon over saturated bg \u2014 no badge wrap, inherit text-white.\n iconSlot = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n resolvedSize === 'default' ? '[&>svg]:size-5' : '[&>svg]:size-4',\n )}\n data-slot=\"alert-icon\"\n >\n {rawIcon}\n </span>\n )\n } else {\n // light / lighter / stroke: badge wrap with status color bg + white icon.\n iconSlot = (\n <span\n className={alertIconBadgeVariants({ status: resolvedStatus, size: resolvedSize })}\n data-slot=\"alert-icon-badge\"\n data-status={resolvedStatus}\n >\n {rawIcon}\n </span>\n )\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n className={cn(\n alertVariants({ status: resolvedStatus, style: resolvedStyle, size: resolvedSize }),\n className,\n )}\n data-slot=\"alert\"\n data-status={resolvedStatus}\n data-style={resolvedStyle}\n {...props}\n >\n {iconSlot}\n <div className=\"min-w-0 flex-1\">{children}</div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n {dismissible ? (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label={dismissAriaLabel}\n className={alertDismissVariants({ style: resolvedStyle })}\n data-slot=\"alert-dismiss\"\n >\n <X aria-hidden=\"true\" className=\"size-4\" />\n </button>\n ) : null}\n </div>\n )\n },\n)\nAlert.displayName = 'Alert'\n\n// Figma \"Label/Small\": Inter Medium 14 / 20.\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, ...props }, ref) => (\n <h5 ref={ref} className={cn('mb-1 text-sm font-medium leading-5', className)} {...props} />\n ),\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// Figma \"Paragraph/Small\": Inter Regular 14 / 20.\n// Renders a <div>, not a <p>: callers routinely nest paragraphs, lists and other\n// block elements inside the description, which is invalid inside <p> and makes\n// React fail hydration with \"In HTML, <p> cannot be a descendant of <p>\".\nconst AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('text-sm leading-5', className)} {...props} />\n ),\n)\nAlertDescription.displayName = 'AlertDescription'\n\nexport { Alert, AlertDescription, AlertTitle, alertVariants, alertIconBadgeVariants }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { AlertCircle, CheckCircle2, Info, Rocket, X, AlertTriangle } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type AlertStatus = 'error' | 'warning' | 'success' | 'information' | 'feature'\nexport type AlertStyle = 'filled' | 'light' | 'lighter' | 'stroke'\nexport type AlertSize = 'xs' | 'sm' | 'default'\n\n/**\n * Legacy `variant` values from the pre-Figma-`169:2358` API. Kept for BC \u2014\n * mapped to `status` inside the Alert component so existing call sites do\n * not need migration. The Figma `light` look (`#fecaca` saturated tint\n * for error \u2192 `state/{x}/light` token family) is heavier than the\n * pre-Figma look; legacy call sites that want the softer pre-Figma\n * appearance should switch explicitly to `style=\"lighter\"`.\n */\ntype LegacyVariant = 'default' | 'destructive' | 'success' | 'warning' | 'info'\n\nconst legacyVariantToStatus: Record<LegacyVariant, AlertStatus> = {\n default: 'information',\n destructive: 'error',\n success: 'success',\n warning: 'warning',\n info: 'information',\n}\n\nconst alertVariants = cva(\n 'relative flex w-full gap-2 transition-colors',\n {\n variants: {\n status: {\n error: '',\n warning: '',\n success: '',\n information: '',\n feature: '',\n },\n style: {\n filled: '',\n light: '',\n lighter: '',\n stroke: '',\n },\n size: {\n // Figma X-Small: px-[8px] py-[6px], radius-8, Paragraph/X Small (12/16)\n xs: 'min-h-8 items-center rounded-lg px-2 py-1.5 text-xs leading-4 tracking-tight',\n // Figma Small: px-[10px] py-[8px], radius-8, Paragraph/Small (14/20)\n sm: 'min-h-9 items-center rounded-lg px-2.5 py-2 text-sm leading-5 tracking-tight',\n // Figma Large: px-[12px] py-[10px], radius-12, Paragraph/Small (14/20) multi-line\n default: 'items-start rounded-xl px-3 py-2.5 text-sm leading-5 tracking-tight',\n },\n },\n compoundVariants: [\n // === filled (Figma: bg = state/{x}/base, text = static-white) ===\n { status: 'error', style: 'filled', className: 'border border-transparent bg-status-error-icon text-white' },\n { status: 'warning', style: 'filled', className: 'border border-transparent bg-status-warning-icon text-white' },\n { status: 'success', style: 'filled', className: 'border border-transparent bg-status-success-icon text-white' },\n { status: 'information', style: 'filled', className: 'border border-transparent bg-status-info-icon text-white' },\n { status: 'feature', style: 'filled', className: 'border border-transparent bg-status-neutral-icon text-white' },\n // === light (Figma: bg = state/{x}/light, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'light', className: 'border border-transparent bg-status-error-border text-foreground' },\n { status: 'warning', style: 'light', className: 'border border-transparent bg-status-warning-border text-foreground' },\n { status: 'success', style: 'light', className: 'border border-transparent bg-status-success-border text-foreground' },\n { status: 'information', style: 'light', className: 'border border-transparent bg-status-info-border text-foreground' },\n { status: 'feature', style: 'light', className: 'border border-transparent bg-status-neutral-border text-foreground' },\n // === lighter (Figma: bg = state/{x}/lighter, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'lighter', className: 'border border-transparent bg-status-error-bg text-foreground' },\n { status: 'warning', style: 'lighter', className: 'border border-transparent bg-status-warning-bg text-foreground' },\n { status: 'success', style: 'lighter', className: 'border border-transparent bg-status-success-bg text-foreground' },\n { status: 'information', style: 'lighter', className: 'border border-transparent bg-status-info-bg text-foreground' },\n { status: 'feature', style: 'lighter', className: 'border border-transparent bg-status-neutral-bg text-foreground' },\n // === stroke (Figma: bg = white, border = stroke/soft-200, text = strong-950, shadow = regular-shadow/medium) ===\n { style: 'stroke', className: 'border border-border bg-background text-foreground shadow-lg' },\n ],\n defaultVariants: {\n status: 'information',\n style: 'light',\n size: 'sm',\n },\n },\n)\n\n// Dismiss button (X) color per Figma: text/strong-950 (#171717 black) at\n// `opacity-40` for light/lighter/stroke \u2014 matches the Figma `close-line`\n// icon which is the dark icon-strong-950 fill rendered at 40% alpha\n// (visually \u2248 #5c5c5c gray). White at full opacity for filled so it\n// stays legible over the saturated bg.\nconst alertDismissVariants = cva(\n 'shrink-0 rounded-md p-0.5 transition-opacity focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n {\n variants: {\n style: {\n filled: 'text-white opacity-90 hover:opacity-100',\n light: 'text-foreground opacity-40 hover:opacity-100',\n lighter: 'text-foreground opacity-40 hover:opacity-100',\n stroke: 'text-foreground opacity-40 hover:opacity-100',\n },\n },\n defaultVariants: { style: 'light' },\n },\n)\n\nconst alertIconBadgeVariants = cva(\n 'inline-flex shrink-0 items-center justify-center rounded-full text-white',\n {\n variants: {\n status: {\n error: 'bg-status-error-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n information: 'bg-status-info-icon',\n feature: 'bg-status-neutral-icon',\n },\n size: {\n xs: 'size-4 [&>svg]:size-2.5',\n sm: 'size-5 [&>svg]:size-3',\n default: 'size-6 [&>svg]:size-3.5',\n },\n },\n defaultVariants: { status: 'information', size: 'sm' },\n },\n)\n\nconst defaultStatusIcons: Record<AlertStatus, React.ComponentType<{ className?: string; 'aria-hidden'?: 'true' }>> = {\n error: AlertCircle,\n warning: AlertTriangle,\n success: CheckCircle2,\n information: Info,\n feature: Rocket,\n}\n\nexport type AlertProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'role' | 'style'> &\n Omit<VariantProps<typeof alertVariants>, 'status' | 'style' | 'size'> & {\n status?: AlertStatus\n style?: AlertStyle\n size?: AlertSize\n /** Show the leading icon. Default `true`. */\n showIcon?: boolean\n /** Override the default per-status Lucide icon. */\n icon?: React.ReactNode\n /** Render a trailing close (X) button. Pair with `onDismiss`. */\n dismissible?: boolean\n onDismiss?: () => void\n dismissAriaLabel?: string\n /** Inline action slot (link buttons) rendered to the right of the body. */\n action?: React.ReactNode\n /**\n * @deprecated Use `status` + `style` instead. Kept for BC. Maps the\n * legacy single-prop variants onto `status` and picks up the new\n * `light` + `sm` defaults.\n */\n variant?: LegacyVariant\n }\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n status,\n style,\n size,\n showIcon = true,\n icon,\n dismissible = false,\n onDismiss,\n dismissAriaLabel = 'Dismiss',\n action,\n variant,\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedStatus: AlertStatus = status ?? (variant ? legacyVariantToStatus[variant] : 'information')\n const resolvedStyle: AlertStyle = style ?? 'light'\n const resolvedSize: AlertSize = size ?? 'sm'\n const IconComponent = defaultStatusIcons[resolvedStatus]\n const rawIcon = icon ?? <IconComponent aria-hidden=\"true\" />\n\n let iconSlot: React.ReactNode = null\n if (showIcon) {\n if (resolvedStyle === 'filled') {\n // Filled: plain icon over saturated bg \u2014 no badge wrap, inherit text-white.\n iconSlot = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n resolvedSize === 'default' ? '[&>svg]:size-5' : '[&>svg]:size-4',\n )}\n data-slot=\"alert-icon\"\n >\n {rawIcon}\n </span>\n )\n } else {\n // light / lighter / stroke: badge wrap with status color bg + white icon.\n iconSlot = (\n <span\n className={alertIconBadgeVariants({ status: resolvedStatus, size: resolvedSize })}\n data-slot=\"alert-icon-badge\"\n data-status={resolvedStatus}\n >\n {rawIcon}\n </span>\n )\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n className={cn(\n alertVariants({ status: resolvedStatus, style: resolvedStyle, size: resolvedSize }),\n className,\n )}\n data-slot=\"alert\"\n data-status={resolvedStatus}\n data-style={resolvedStyle}\n {...props}\n >\n {iconSlot}\n <div className=\"min-w-0 flex-1\">{children}</div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n {dismissible ? (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label={dismissAriaLabel}\n className={alertDismissVariants({ style: resolvedStyle })}\n data-slot=\"alert-dismiss\"\n >\n <X aria-hidden=\"true\" className=\"size-4\" />\n </button>\n ) : null}\n </div>\n )\n },\n)\nAlert.displayName = 'Alert'\n\n// Figma \"Label/Small\": Inter Medium 14 / 20.\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, ...props }, ref) => (\n <h5 ref={ref} className={cn('mb-1 text-sm font-medium leading-5', className)} {...props} />\n ),\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// Figma \"Paragraph/Small\": Inter Regular 14 / 20.\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p ref={ref} className={cn('text-sm leading-5', className)} {...props} />\n ),\n)\nAlertDescription.displayName = 'AlertDescription'\n\nexport { Alert, AlertDescription, AlertTitle, alertVariants, alertIconBadgeVariants }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,26 +3,7 @@ import { Slot } from "@radix-ui/react-slot";
|
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { cn } from "@open-mercato/shared/lib/utils";
|
|
5
5
|
const iconButtonVariants = cva(
|
|
6
|
-
|
|
7
|
-
// ghost variants ship `dark:bg-input/30` / `dark:hover:bg-accent/50`, which
|
|
8
|
-
// otherwise outrank the un-prefixed `aria-pressed:` background while the
|
|
9
|
-
// `aria-pressed:text-primary-foreground` still applies — leaving a dark icon
|
|
10
|
-
// on a dark surface (invisible pressed star/bell buttons on the dark theme).
|
|
11
|
-
//
|
|
12
|
-
// Every pressed rule is additionally gated on `not-disabled:` because a toggle
|
|
13
|
-
// can be pressed AND disabled at the same time (an active editor tool while the
|
|
14
|
-
// document is read-only). Without the gate the pressed rules outrank the
|
|
15
|
-
// `disabled:` surface — `dark:aria-pressed:bg-primary` is more specific than
|
|
16
|
-
// `disabled:bg-bg-disabled` — and a control the user cannot operate renders
|
|
17
|
-
// as a primary, actionable button.
|
|
18
|
-
//
|
|
19
|
-
// The gate MUST stay `not-disabled:` (`&:not(:disabled)`) and not `enabled:`
|
|
20
|
-
// (`&:enabled`): the CSS `:enabled` pseudo-class only matches form elements,
|
|
21
|
-
// so on an `asChild` host — this primitive is routinely rendered onto a link —
|
|
22
|
-
// no pressed rule could ever match and the pressed state would not paint at
|
|
23
|
-
// all. `:not(:disabled)` gates the same way on a real `<button>` while leaving
|
|
24
|
-
// a non-form host styled.
|
|
25
|
-
"inline-flex items-center justify-center cursor-pointer transition-all outline-none disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-disabled disabled:shadow-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:outline-none focus-visible:shadow-focus not-disabled:aria-pressed:bg-primary not-disabled:aria-pressed:text-primary-foreground not-disabled:aria-pressed:hover:bg-primary-hover dark:not-disabled:aria-pressed:bg-primary dark:not-disabled:aria-pressed:hover:bg-primary-hover",
|
|
6
|
+
"inline-flex items-center justify-center cursor-pointer transition-all outline-none disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-disabled disabled:shadow-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:outline-none focus-visible:shadow-focus aria-pressed:bg-primary aria-pressed:text-primary-foreground aria-pressed:hover:bg-primary-hover",
|
|
26
7
|
{
|
|
27
8
|
variants: {
|
|
28
9
|
variant: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/icon-button.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst iconButtonVariants = cva(\n
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst iconButtonVariants = cva(\n \"inline-flex items-center justify-center cursor-pointer transition-all outline-none disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-disabled disabled:shadow-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:outline-none focus-visible:shadow-focus aria-pressed:bg-primary aria-pressed:text-primary-foreground aria-pressed:hover:bg-primary-hover\",\n {\n variants: {\n variant: {\n primary:\n 'bg-primary text-primary-foreground shadow-xs hover:bg-primary-hover',\n destructive:\n 'bg-destructive text-white shadow-xs hover:bg-destructive/90',\n outline:\n 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',\n ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',\n white:\n 'bg-background text-muted-foreground shadow-xs hover:bg-accent hover:text-accent-foreground',\n modifiable:\n 'bg-transparent text-current hover:bg-foreground/10',\n },\n size: {\n xs: 'size-6',\n sm: 'size-7',\n default: 'size-8',\n lg: 'size-9',\n },\n fullRadius: {\n true: 'rounded-full',\n false: 'rounded-md',\n },\n },\n defaultVariants: {\n variant: 'outline',\n size: 'default',\n fullRadius: false,\n },\n }\n)\n\nexport function IconButton({\n className,\n variant,\n size,\n fullRadius,\n asChild = false,\n ...props\n}: React.ComponentProps<'button'> &\n VariantProps<typeof iconButtonVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot : 'button'\n return (\n <Comp\n data-slot=\"icon-button\"\n type={asChild ? undefined : 'button'}\n className={cn(iconButtonVariants({ variant, size, fullRadius, className }))}\n {...props}\n />\n )\n}\n\nexport { iconButtonVariants }\n"],
|
|
5
|
+
"mappings": "AAoDI;AAnDJ,SAAS,YAAY;AACrB,SAAS,WAA8B;AACvC,SAAS,UAAU;AAEnB,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SACE;AAAA,QACF,aACE;AAAA,QACF,SACE;AAAA,QACF,OAAO;AAAA,QACP,OACE;AAAA,QACF,YACE;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,MACN;AAAA,MACA,YAAY;AAAA,QACV,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,MACN,YAAY;AAAA,IACd;AAAA,EACF;AACF;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,GAAG;AACL,GACmE;AACjE,QAAM,OAAO,UAAU,OAAO;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAM,UAAU,SAAY;AAAA,MAC5B,WAAW,GAAG,mBAAmB,EAAE,SAAS,MAAM,YAAY,UAAU,CAAC,CAAC;AAAA,MACzE,GAAG;AAAA;AAAA,EACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -76,8 +76,6 @@ const Pagination = React.forwardRef(
|
|
|
76
76
|
page,
|
|
77
77
|
pageSize,
|
|
78
78
|
total,
|
|
79
|
-
totalIsCapped = false,
|
|
80
|
-
hasNextPage,
|
|
81
79
|
onPageChange,
|
|
82
80
|
onPageSizeChange,
|
|
83
81
|
pageSizeOptions = [10, 25, 50, 100],
|
|
@@ -93,25 +91,22 @@ const Pagination = React.forwardRef(
|
|
|
93
91
|
...props
|
|
94
92
|
}, ref) => {
|
|
95
93
|
const t = useT();
|
|
96
|
-
const resolvedFormatPageInfo = formatPageInfo ?? ((p, total2) =>
|
|
94
|
+
const resolvedFormatPageInfo = formatPageInfo ?? ((p, total2) => t("ui.pagination.info.pageOf", "Page {page} of {total}", { page: p, total: total2 }));
|
|
97
95
|
const resolvedFormatPageSizeLabel = formatPageSizeLabel ?? ((size) => t("ui.pagination.itemsPerPage.label", "{size} / page", { size }));
|
|
98
96
|
const totalPages = Math.max(1, Math.ceil(total / Math.max(1, pageSize)));
|
|
99
|
-
const safePage =
|
|
100
|
-
const listPages = Math.max(totalPages, safePage);
|
|
101
|
-
const canGoNext = totalIsCapped ? hasNextPage ?? safePage < listPages : safePage < totalPages;
|
|
97
|
+
const safePage = Math.min(Math.max(1, page), totalPages);
|
|
102
98
|
const items = React.useMemo(
|
|
103
|
-
() => buildPaginationItems(safePage,
|
|
104
|
-
[safePage,
|
|
99
|
+
() => buildPaginationItems(safePage, totalPages, siblingCount, boundaryCount),
|
|
100
|
+
[safePage, totalPages, siblingCount, boundaryCount]
|
|
105
101
|
);
|
|
106
102
|
const showPageSize = showPageSizeProp ?? Boolean(onPageSizeChange);
|
|
107
103
|
const goTo = React.useCallback(
|
|
108
104
|
(next) => {
|
|
109
105
|
if (disabled) return;
|
|
110
|
-
const
|
|
111
|
-
const bounded = Math.min(Math.max(1, next), upperBound);
|
|
106
|
+
const bounded = Math.min(Math.max(1, next), totalPages);
|
|
112
107
|
if (bounded !== safePage) onPageChange(bounded);
|
|
113
108
|
},
|
|
114
|
-
[disabled, onPageChange, safePage, totalPages
|
|
109
|
+
[disabled, onPageChange, safePage, totalPages]
|
|
115
110
|
);
|
|
116
111
|
return /* @__PURE__ */ jsxs(
|
|
117
112
|
"nav",
|
|
@@ -127,7 +122,7 @@ const Pagination = React.forwardRef(
|
|
|
127
122
|
{
|
|
128
123
|
"data-slot": "pagination-info",
|
|
129
124
|
className: "shrink-0 text-sm text-muted-foreground tabular-nums",
|
|
130
|
-
children: resolvedFormatPageInfo(safePage,
|
|
125
|
+
children: resolvedFormatPageInfo(safePage, totalPages)
|
|
131
126
|
}
|
|
132
127
|
) : /* @__PURE__ */ jsx("div", {}),
|
|
133
128
|
/* @__PURE__ */ jsxs(
|
|
@@ -202,13 +197,13 @@ const Pagination = React.forwardRef(
|
|
|
202
197
|
type: "button",
|
|
203
198
|
"data-slot": "pagination-next",
|
|
204
199
|
"aria-label": t("ui.pagination.next.ariaLabel", "Next page"),
|
|
205
|
-
disabled: disabled ||
|
|
200
|
+
disabled: disabled || safePage >= totalPages,
|
|
206
201
|
onClick: () => goTo(safePage + 1),
|
|
207
202
|
className: cn(navButtonVariants()),
|
|
208
203
|
children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "size-5" })
|
|
209
204
|
}
|
|
210
205
|
) : null,
|
|
211
|
-
showFirstLast
|
|
206
|
+
showFirstLast ? /* @__PURE__ */ jsx(
|
|
212
207
|
"button",
|
|
213
208
|
{
|
|
214
209
|
type: "button",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/pagination.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n ChevronLeft,\n ChevronRight,\n ChevronsLeft,\n ChevronsRight,\n} from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n CompactSelectTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectValue,\n} from './compact-select'\n\n/**\n * Page navigation primitive per Figma `Pagination Group [1.1]` (DS Open\n * Mercato componentSet `199985:4135`).\n *\n * Layout (Figma `Basic` variant):\n *\n * [Left] \"Page 2 of 16\"\n * [Center] \u23EE \u25C0 [1][2]\u2026[N-1][N] \u25B6 \u23ED\n * [Right] \"7 / page\" CompactSelect\n *\n * Figma's three boolean variant props are mapped 1:1 to React props:\n *\n * \uD83E\uDD47 First / Last \u2192 `showFirstLast` (default true)\n * \u23ED\uFE0F Next / Previous \u2192 `showPrevNext` (default true)\n * \uD83E\uDDEA Advanced \u2192 `showInfo` + `showPageSize` (default both true)\n *\n * The numeric page list uses the same ellipsis algorithm Material /\n * MUI / shadcn ship: `boundaryCount` pages at each end (default 1),\n * `siblingCount` pages on either side of the current page (default 1),\n * collapse the rest into `\u2026` placeholders.\n *\n * ```tsx\n * const [page, setPage] = React.useState(1)\n * const [pageSize, setPageSize] = React.useState(25)\n * <Pagination\n * page={page}\n * pageSize={pageSize}\n * total={items.length}\n * onPageChange={setPage}\n * onPageSizeChange={setPageSize}\n * />\n *\n * // Compact (no first/last, no page-size select)\n * <Pagination\n * page={page}\n * pageSize={20}\n * total={120}\n * onPageChange={setPage}\n * showFirstLast={false}\n * showPageSize={false}\n * />\n * ```\n */\n\n/**\n * Build a stable list of pages + ellipsis placeholders that fits a\n * pagination row of `boundaryCount + 2 + siblingCount * 2 + 1` cells.\n * Returns `number` entries (1-indexed) and `'ellipsis-left'` /\n * `'ellipsis-right'` placeholders that the renderer paints as `\u2026`.\n *\n * Exported for tests + consumer reuse (e.g. a server-side rendered\n * pagination indicator that needs the same shape).\n */\nexport function buildPaginationItems(\n page: number,\n totalPages: number,\n siblingCount: number,\n boundaryCount: number,\n): Array<number | 'ellipsis-left' | 'ellipsis-right'> {\n if (totalPages <= 0) return []\n // When everything fits, just list every page.\n const totalSlots = boundaryCount * 2 + siblingCount * 2 + 3\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1)\n }\n const startPages = Array.from({ length: boundaryCount }, (_, i) => i + 1)\n const endPages = Array.from(\n { length: boundaryCount },\n (_, i) => totalPages - boundaryCount + 1 + i,\n )\n const siblingStart = Math.max(\n Math.min(page - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1),\n boundaryCount + 2,\n )\n const siblingEnd = Math.min(\n Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2),\n endPages.length > 0 ? endPages[0] - 2 : totalPages - 1,\n )\n const middle: Array<number> = []\n for (let i = siblingStart; i <= siblingEnd; i += 1) middle.push(i)\n\n // Bridge between the start boundary and the sibling window. The\n // ellipsis is only worth showing when \u22652 page numbers fall between\n // them; if exactly 1 page is missing, render that single number\n // instead of \"\u2026\" (cleaner UX \u2014 same width, no information loss).\n const result: Array<number | 'ellipsis-left' | 'ellipsis-right'> = []\n for (const p of startPages) result.push(p)\n if (siblingStart > boundaryCount + 2) {\n result.push('ellipsis-left')\n } else if (siblingStart === boundaryCount + 2) {\n result.push(boundaryCount + 1)\n }\n for (const p of middle) result.push(p)\n const firstEnd = endPages[0] ?? totalPages\n if (siblingEnd < firstEnd - 2) {\n result.push('ellipsis-right')\n } else if (siblingEnd === firstEnd - 2) {\n result.push(firstEnd - 1)\n }\n for (const p of endPages) result.push(p)\n return result\n}\n\nconst cellVariants = cva(\n 'inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-sm font-medium outline-none transition-colors tabular-nums ' +\n 'focus-visible:shadow-focus ' +\n 'disabled:cursor-not-allowed disabled:opacity-50',\n {\n variants: {\n selected: {\n true: 'bg-muted text-foreground',\n false: 'bg-background text-foreground hover:bg-muted/40',\n },\n },\n defaultVariants: { selected: false },\n },\n)\n\nconst navButtonVariants = cva(\n 'inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground outline-none transition-colors ' +\n 'hover:bg-muted/40 hover:text-foreground ' +\n 'focus-visible:shadow-focus ' +\n 'disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground',\n)\n\nexport type PaginationProps = React.HTMLAttributes<HTMLDivElement> & {\n /** Current 1-indexed page. */\n page: number\n /** Items per page. */\n pageSize: number\n /** Total item count. Used to derive `Math.ceil(total / pageSize)` pages. */\n total: number\n /**\n * `total` is a floor, not an exact count (a capped list count,\n * `OM_LIST_COUNT_CAP`). The derived page count then only bounds the page\n * *list*: the current page is never clamped down to it, the last-page jump\n * is suppressed (it would present the floor as the end of the data), and\n * Next stays available past the floor when `hasNextPage` says so.\n */\n totalIsCapped?: boolean\n /**\n * Caller-provided \"a next page exists\" signal for the capped case, typically\n * short-page detection (the current page came back full). Ignored when\n * `totalIsCapped` is false; when omitted, Next ends at the known floor.\n */\n hasNextPage?: boolean\n /** Called when the user changes page. */\n onPageChange: (next: number) => void\n /** Called when the user changes page size. Optional \u2014 when omitted,\n * the \"X / page\" select is hidden. */\n onPageSizeChange?: (next: number) => void\n /** Page size options for the select. Default `[10, 25, 50, 100]`. */\n pageSizeOptions?: readonly number[]\n /** Show the left \"Page X of Y\" indicator. Default `true`. */\n showInfo?: boolean\n /** Show the right \"X / page\" select. Default `true` when\n * `onPageSizeChange` is provided; ignored otherwise. */\n showPageSize?: boolean\n /** Show \u23EE / \u23ED first / last buttons. Default `true`. */\n showFirstLast?: boolean\n /** Show \u25C0 / \u25B6 prev / next buttons. Default `true`. */\n showPrevNext?: boolean\n /** Pages on either side of the current page in the page list. Default `1`. */\n siblingCount?: number\n /** Pages pinned at each end of the page list. Default `1`. */\n boundaryCount?: number\n /** Block all interactions. Default `false`. */\n disabled?: boolean\n /** ARIA label for the navigation landmark. Default `\"Pagination\"`. */\n 'aria-label'?: string\n /** Format the \"Page X of Y\" label. */\n formatPageInfo?: (page: number, totalPages: number) => string\n /** Format the \"X / page\" label. */\n formatPageSizeLabel?: (pageSize: number) => string\n}\n\nexport const Pagination = React.forwardRef<HTMLDivElement, PaginationProps>(\n (\n {\n className,\n page,\n pageSize,\n total,\n totalIsCapped = false,\n hasNextPage,\n onPageChange,\n onPageSizeChange,\n pageSizeOptions = [10, 25, 50, 100],\n showInfo = true,\n showPageSize: showPageSizeProp,\n showFirstLast = true,\n showPrevNext = true,\n siblingCount = 1,\n boundaryCount = 1,\n disabled = false,\n formatPageInfo,\n formatPageSizeLabel,\n ...props\n },\n ref,\n ) => {\n const t = useT()\n const resolvedFormatPageInfo =\n formatPageInfo ??\n ((p: number, total: number) =>\n totalIsCapped\n ? t('ui.pagination.info.pageOfCapped', 'Page {page} of {total}+', { page: p, total })\n : t('ui.pagination.info.pageOf', 'Page {page} of {total}', { page: p, total }))\n const resolvedFormatPageSizeLabel =\n formatPageSizeLabel ??\n ((size: number) =>\n t('ui.pagination.itemsPerPage.label', '{size} / page', { size }))\n const totalPages = Math.max(1, Math.ceil(total / Math.max(1, pageSize)))\n // A capped total is a floor: never clamp the current page down to the\n // derived count \u2014 a page past the floor holds reachable rows.\n const safePage = totalIsCapped\n ? Math.max(1, page)\n : Math.min(Math.max(1, page), totalPages)\n const listPages = Math.max(totalPages, safePage)\n const canGoNext = totalIsCapped\n ? (hasNextPage ?? safePage < listPages)\n : safePage < totalPages\n const items = React.useMemo(\n () => buildPaginationItems(safePage, listPages, siblingCount, boundaryCount),\n [safePage, listPages, siblingCount, boundaryCount],\n )\n const showPageSize = showPageSizeProp ?? Boolean(onPageSizeChange)\n\n const goTo = React.useCallback(\n (next: number) => {\n if (disabled) return\n const upperBound = totalIsCapped ? Math.max(listPages, safePage + 1) : totalPages\n const bounded = Math.min(Math.max(1, next), upperBound)\n if (bounded !== safePage) onPageChange(bounded)\n },\n [disabled, onPageChange, safePage, totalPages, totalIsCapped, listPages],\n )\n\n return (\n <nav\n ref={ref}\n data-slot=\"pagination\"\n aria-label={props['aria-label'] ?? t('ui.pagination.landmark.ariaLabel', 'Pagination')}\n className={cn('flex w-full flex-wrap items-center justify-between gap-x-6 gap-y-2', className)}\n {...props}\n >\n {showInfo ? (\n <div\n data-slot=\"pagination-info\"\n className=\"shrink-0 text-sm text-muted-foreground tabular-nums\"\n >\n {/* When capped, report the best-known floor: a deep page proves at\n least that many pages exist. */}\n {resolvedFormatPageInfo(safePage, totalIsCapped ? listPages : totalPages)}\n </div>\n ) : (\n <div />\n )}\n\n <div\n data-slot=\"pagination-controls\"\n className=\"flex flex-wrap items-center justify-center gap-2\"\n >\n {showFirstLast ? (\n <button\n type=\"button\"\n data-slot=\"pagination-first\"\n aria-label={t('ui.pagination.first.ariaLabel', 'First page')}\n disabled={disabled || safePage <= 1}\n onClick={() => goTo(1)}\n className={cn(navButtonVariants())}\n >\n <ChevronsLeft aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n {showPrevNext ? (\n <button\n type=\"button\"\n data-slot=\"pagination-prev\"\n aria-label={t('ui.pagination.previous.ariaLabel', 'Previous page')}\n disabled={disabled || safePage <= 1}\n onClick={() => goTo(safePage - 1)}\n className={cn(navButtonVariants())}\n >\n <ChevronLeft aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n\n <ol\n data-slot=\"pagination-pages\"\n className=\"flex flex-wrap items-center justify-center gap-2 list-none\"\n >\n {items.map((entry, index) => {\n if (entry === 'ellipsis-left' || entry === 'ellipsis-right') {\n return (\n <li\n key={`${entry}-${index}`}\n data-slot=\"pagination-ellipsis\"\n aria-hidden=\"true\"\n className=\"inline-flex size-8 items-center justify-center text-sm text-muted-foreground\"\n >\n \u2026\n </li>\n )\n }\n const selected = entry === safePage\n return (\n <li key={entry}>\n <button\n type=\"button\"\n data-slot=\"pagination-page\"\n data-state={selected ? 'on' : 'off'}\n aria-current={selected ? 'page' : undefined}\n aria-label={\n selected\n ? t('ui.pagination.page.currentAriaLabel', 'Page {page}, current page', { page: entry })\n : t('ui.pagination.page.goToAriaLabel', 'Go to page {page}', { page: entry })\n }\n disabled={disabled}\n onClick={() => goTo(entry)}\n className={cn(cellVariants({ selected }))}\n >\n {entry}\n </button>\n </li>\n )\n })}\n </ol>\n\n {showPrevNext ? (\n <button\n type=\"button\"\n data-slot=\"pagination-next\"\n aria-label={t('ui.pagination.next.ariaLabel', 'Next page')}\n disabled={disabled || !canGoNext}\n onClick={() => goTo(safePage + 1)}\n className={cn(navButtonVariants())}\n >\n <ChevronRight aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n {/* The last-page jump is suppressed for capped totals: it would land\n on the floor page while presenting itself as the end of the data. */}\n {showFirstLast && !totalIsCapped ? (\n <button\n type=\"button\"\n data-slot=\"pagination-last\"\n aria-label={t('ui.pagination.last.ariaLabel', 'Last page')}\n disabled={disabled || safePage >= totalPages}\n onClick={() => goTo(totalPages)}\n className={cn(navButtonVariants())}\n >\n <ChevronsRight aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n </div>\n\n {showPageSize && onPageSizeChange ? (\n <div data-slot=\"pagination-page-size\" className=\"shrink-0\">\n <Select\n value={String(pageSize)}\n onValueChange={(next) => onPageSizeChange(Number(next))}\n disabled={disabled}\n >\n <CompactSelectTrigger aria-label={t('ui.pagination.itemsPerPage.ariaLabel', 'Items per page')}>\n <SelectValue />\n </CompactSelectTrigger>\n <SelectContent>\n {pageSizeOptions.map((option) => (\n <SelectItem key={option} value={String(option)}>\n {resolvedFormatPageSizeLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : (\n <div />\n )}\n </nav>\n )\n },\n)\nPagination.displayName = 'Pagination'\n\nexport { cellVariants as paginationCellVariants, navButtonVariants as paginationNavVariants }\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n ChevronLeft,\n ChevronRight,\n ChevronsLeft,\n ChevronsRight,\n} from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n CompactSelectTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectValue,\n} from './compact-select'\n\n/**\n * Page navigation primitive per Figma `Pagination Group [1.1]` (DS Open\n * Mercato componentSet `199985:4135`).\n *\n * Layout (Figma `Basic` variant):\n *\n * [Left] \"Page 2 of 16\"\n * [Center] \u23EE \u25C0 [1][2]\u2026[N-1][N] \u25B6 \u23ED\n * [Right] \"7 / page\" CompactSelect\n *\n * Figma's three boolean variant props are mapped 1:1 to React props:\n *\n * \uD83E\uDD47 First / Last \u2192 `showFirstLast` (default true)\n * \u23ED\uFE0F Next / Previous \u2192 `showPrevNext` (default true)\n * \uD83E\uDDEA Advanced \u2192 `showInfo` + `showPageSize` (default both true)\n *\n * The numeric page list uses the same ellipsis algorithm Material /\n * MUI / shadcn ship: `boundaryCount` pages at each end (default 1),\n * `siblingCount` pages on either side of the current page (default 1),\n * collapse the rest into `\u2026` placeholders.\n *\n * ```tsx\n * const [page, setPage] = React.useState(1)\n * const [pageSize, setPageSize] = React.useState(25)\n * <Pagination\n * page={page}\n * pageSize={pageSize}\n * total={items.length}\n * onPageChange={setPage}\n * onPageSizeChange={setPageSize}\n * />\n *\n * // Compact (no first/last, no page-size select)\n * <Pagination\n * page={page}\n * pageSize={20}\n * total={120}\n * onPageChange={setPage}\n * showFirstLast={false}\n * showPageSize={false}\n * />\n * ```\n */\n\n/**\n * Build a stable list of pages + ellipsis placeholders that fits a\n * pagination row of `boundaryCount + 2 + siblingCount * 2 + 1` cells.\n * Returns `number` entries (1-indexed) and `'ellipsis-left'` /\n * `'ellipsis-right'` placeholders that the renderer paints as `\u2026`.\n *\n * Exported for tests + consumer reuse (e.g. a server-side rendered\n * pagination indicator that needs the same shape).\n */\nexport function buildPaginationItems(\n page: number,\n totalPages: number,\n siblingCount: number,\n boundaryCount: number,\n): Array<number | 'ellipsis-left' | 'ellipsis-right'> {\n if (totalPages <= 0) return []\n // When everything fits, just list every page.\n const totalSlots = boundaryCount * 2 + siblingCount * 2 + 3\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1)\n }\n const startPages = Array.from({ length: boundaryCount }, (_, i) => i + 1)\n const endPages = Array.from(\n { length: boundaryCount },\n (_, i) => totalPages - boundaryCount + 1 + i,\n )\n const siblingStart = Math.max(\n Math.min(page - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1),\n boundaryCount + 2,\n )\n const siblingEnd = Math.min(\n Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2),\n endPages.length > 0 ? endPages[0] - 2 : totalPages - 1,\n )\n const middle: Array<number> = []\n for (let i = siblingStart; i <= siblingEnd; i += 1) middle.push(i)\n\n // Bridge between the start boundary and the sibling window. The\n // ellipsis is only worth showing when \u22652 page numbers fall between\n // them; if exactly 1 page is missing, render that single number\n // instead of \"\u2026\" (cleaner UX \u2014 same width, no information loss).\n const result: Array<number | 'ellipsis-left' | 'ellipsis-right'> = []\n for (const p of startPages) result.push(p)\n if (siblingStart > boundaryCount + 2) {\n result.push('ellipsis-left')\n } else if (siblingStart === boundaryCount + 2) {\n result.push(boundaryCount + 1)\n }\n for (const p of middle) result.push(p)\n const firstEnd = endPages[0] ?? totalPages\n if (siblingEnd < firstEnd - 2) {\n result.push('ellipsis-right')\n } else if (siblingEnd === firstEnd - 2) {\n result.push(firstEnd - 1)\n }\n for (const p of endPages) result.push(p)\n return result\n}\n\nconst cellVariants = cva(\n 'inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-sm font-medium outline-none transition-colors tabular-nums ' +\n 'focus-visible:shadow-focus ' +\n 'disabled:cursor-not-allowed disabled:opacity-50',\n {\n variants: {\n selected: {\n true: 'bg-muted text-foreground',\n false: 'bg-background text-foreground hover:bg-muted/40',\n },\n },\n defaultVariants: { selected: false },\n },\n)\n\nconst navButtonVariants = cva(\n 'inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground outline-none transition-colors ' +\n 'hover:bg-muted/40 hover:text-foreground ' +\n 'focus-visible:shadow-focus ' +\n 'disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground',\n)\n\nexport type PaginationProps = React.HTMLAttributes<HTMLDivElement> & {\n /** Current 1-indexed page. */\n page: number\n /** Items per page. */\n pageSize: number\n /** Total item count. Used to derive `Math.ceil(total / pageSize)` pages. */\n total: number\n /** Called when the user changes page. */\n onPageChange: (next: number) => void\n /** Called when the user changes page size. Optional \u2014 when omitted,\n * the \"X / page\" select is hidden. */\n onPageSizeChange?: (next: number) => void\n /** Page size options for the select. Default `[10, 25, 50, 100]`. */\n pageSizeOptions?: readonly number[]\n /** Show the left \"Page X of Y\" indicator. Default `true`. */\n showInfo?: boolean\n /** Show the right \"X / page\" select. Default `true` when\n * `onPageSizeChange` is provided; ignored otherwise. */\n showPageSize?: boolean\n /** Show \u23EE / \u23ED first / last buttons. Default `true`. */\n showFirstLast?: boolean\n /** Show \u25C0 / \u25B6 prev / next buttons. Default `true`. */\n showPrevNext?: boolean\n /** Pages on either side of the current page in the page list. Default `1`. */\n siblingCount?: number\n /** Pages pinned at each end of the page list. Default `1`. */\n boundaryCount?: number\n /** Block all interactions. Default `false`. */\n disabled?: boolean\n /** ARIA label for the navigation landmark. Default `\"Pagination\"`. */\n 'aria-label'?: string\n /** Format the \"Page X of Y\" label. */\n formatPageInfo?: (page: number, totalPages: number) => string\n /** Format the \"X / page\" label. */\n formatPageSizeLabel?: (pageSize: number) => string\n}\n\nexport const Pagination = React.forwardRef<HTMLDivElement, PaginationProps>(\n (\n {\n className,\n page,\n pageSize,\n total,\n onPageChange,\n onPageSizeChange,\n pageSizeOptions = [10, 25, 50, 100],\n showInfo = true,\n showPageSize: showPageSizeProp,\n showFirstLast = true,\n showPrevNext = true,\n siblingCount = 1,\n boundaryCount = 1,\n disabled = false,\n formatPageInfo,\n formatPageSizeLabel,\n ...props\n },\n ref,\n ) => {\n const t = useT()\n const resolvedFormatPageInfo =\n formatPageInfo ??\n ((p: number, total: number) =>\n t('ui.pagination.info.pageOf', 'Page {page} of {total}', { page: p, total }))\n const resolvedFormatPageSizeLabel =\n formatPageSizeLabel ??\n ((size: number) =>\n t('ui.pagination.itemsPerPage.label', '{size} / page', { size }))\n const totalPages = Math.max(1, Math.ceil(total / Math.max(1, pageSize)))\n const safePage = Math.min(Math.max(1, page), totalPages)\n const items = React.useMemo(\n () => buildPaginationItems(safePage, totalPages, siblingCount, boundaryCount),\n [safePage, totalPages, siblingCount, boundaryCount],\n )\n const showPageSize = showPageSizeProp ?? Boolean(onPageSizeChange)\n\n const goTo = React.useCallback(\n (next: number) => {\n if (disabled) return\n const bounded = Math.min(Math.max(1, next), totalPages)\n if (bounded !== safePage) onPageChange(bounded)\n },\n [disabled, onPageChange, safePage, totalPages],\n )\n\n return (\n <nav\n ref={ref}\n data-slot=\"pagination\"\n aria-label={props['aria-label'] ?? t('ui.pagination.landmark.ariaLabel', 'Pagination')}\n className={cn('flex w-full flex-wrap items-center justify-between gap-x-6 gap-y-2', className)}\n {...props}\n >\n {showInfo ? (\n <div\n data-slot=\"pagination-info\"\n className=\"shrink-0 text-sm text-muted-foreground tabular-nums\"\n >\n {resolvedFormatPageInfo(safePage, totalPages)}\n </div>\n ) : (\n <div />\n )}\n\n <div\n data-slot=\"pagination-controls\"\n className=\"flex flex-wrap items-center justify-center gap-2\"\n >\n {showFirstLast ? (\n <button\n type=\"button\"\n data-slot=\"pagination-first\"\n aria-label={t('ui.pagination.first.ariaLabel', 'First page')}\n disabled={disabled || safePage <= 1}\n onClick={() => goTo(1)}\n className={cn(navButtonVariants())}\n >\n <ChevronsLeft aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n {showPrevNext ? (\n <button\n type=\"button\"\n data-slot=\"pagination-prev\"\n aria-label={t('ui.pagination.previous.ariaLabel', 'Previous page')}\n disabled={disabled || safePage <= 1}\n onClick={() => goTo(safePage - 1)}\n className={cn(navButtonVariants())}\n >\n <ChevronLeft aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n\n <ol\n data-slot=\"pagination-pages\"\n className=\"flex flex-wrap items-center justify-center gap-2 list-none\"\n >\n {items.map((entry, index) => {\n if (entry === 'ellipsis-left' || entry === 'ellipsis-right') {\n return (\n <li\n key={`${entry}-${index}`}\n data-slot=\"pagination-ellipsis\"\n aria-hidden=\"true\"\n className=\"inline-flex size-8 items-center justify-center text-sm text-muted-foreground\"\n >\n \u2026\n </li>\n )\n }\n const selected = entry === safePage\n return (\n <li key={entry}>\n <button\n type=\"button\"\n data-slot=\"pagination-page\"\n data-state={selected ? 'on' : 'off'}\n aria-current={selected ? 'page' : undefined}\n aria-label={\n selected\n ? t('ui.pagination.page.currentAriaLabel', 'Page {page}, current page', { page: entry })\n : t('ui.pagination.page.goToAriaLabel', 'Go to page {page}', { page: entry })\n }\n disabled={disabled}\n onClick={() => goTo(entry)}\n className={cn(cellVariants({ selected }))}\n >\n {entry}\n </button>\n </li>\n )\n })}\n </ol>\n\n {showPrevNext ? (\n <button\n type=\"button\"\n data-slot=\"pagination-next\"\n aria-label={t('ui.pagination.next.ariaLabel', 'Next page')}\n disabled={disabled || safePage >= totalPages}\n onClick={() => goTo(safePage + 1)}\n className={cn(navButtonVariants())}\n >\n <ChevronRight aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n {showFirstLast ? (\n <button\n type=\"button\"\n data-slot=\"pagination-last\"\n aria-label={t('ui.pagination.last.ariaLabel', 'Last page')}\n disabled={disabled || safePage >= totalPages}\n onClick={() => goTo(totalPages)}\n className={cn(navButtonVariants())}\n >\n <ChevronsRight aria-hidden=\"true\" className=\"size-5\" />\n </button>\n ) : null}\n </div>\n\n {showPageSize && onPageSizeChange ? (\n <div data-slot=\"pagination-page-size\" className=\"shrink-0\">\n <Select\n value={String(pageSize)}\n onValueChange={(next) => onPageSizeChange(Number(next))}\n disabled={disabled}\n >\n <CompactSelectTrigger aria-label={t('ui.pagination.itemsPerPage.ariaLabel', 'Items per page')}>\n <SelectValue />\n </CompactSelectTrigger>\n <SelectContent>\n {pageSizeOptions.map((option) => (\n <SelectItem key={option} value={String(option)}>\n {resolvedFormatPageSizeLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : (\n <div />\n )}\n </nav>\n )\n },\n)\nPagination.displayName = 'Pagination'\n\nexport { cellVariants as paginationCellVariants, navButtonVariants as paginationNavVariants }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["total"]
|
|
7
7
|
}
|
|
@@ -1181,23 +1181,16 @@ function RichEditorAutoToolbar({
|
|
|
1181
1181
|
const hasOverflow = hiddenItems.length > 0;
|
|
1182
1182
|
const showMoreButton = hasOverflow || !!moreMenu;
|
|
1183
1183
|
return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
1184
|
-
/* @__PURE__ */
|
|
1184
|
+
/* @__PURE__ */ jsxs(
|
|
1185
1185
|
"div",
|
|
1186
1186
|
{
|
|
1187
|
+
ref: measureRef,
|
|
1187
1188
|
"aria-hidden": "true",
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
"div",
|
|
1192
|
-
|
|
1193
|
-
ref: measureRef,
|
|
1194
|
-
className: "invisible absolute left-0 top-0 flex flex-row flex-nowrap items-center gap-0.5",
|
|
1195
|
-
children: [
|
|
1196
|
-
items.map(renderToolbarItem),
|
|
1197
|
-
/* @__PURE__ */ jsx(RichEditorDropdownButton, { icon: /* @__PURE__ */ jsx(MoreVertical, {}), ariaLabel: labels.more, showChevron: false, menu: /* @__PURE__ */ jsx("div", {}) })
|
|
1198
|
-
]
|
|
1199
|
-
}
|
|
1200
|
-
)
|
|
1189
|
+
className: "pointer-events-none invisible absolute left-0 top-0 flex flex-row flex-nowrap items-center gap-0.5",
|
|
1190
|
+
children: [
|
|
1191
|
+
items.map(renderToolbarItem),
|
|
1192
|
+
/* @__PURE__ */ jsx(RichEditorDropdownButton, { icon: /* @__PURE__ */ jsx(MoreVertical, {}), ariaLabel: labels.more, showChevron: false, menu: /* @__PURE__ */ jsx("div", {}) })
|
|
1193
|
+
]
|
|
1201
1194
|
}
|
|
1202
1195
|
),
|
|
1203
1196
|
/* @__PURE__ */ jsxs(RichEditorToolbar, { ref: containerRef, className: "w-full flex-nowrap overflow-hidden", children: [
|