@open-mercato/ui 0.5.1-develop.2860.07af3a6a9d → 0.5.1-develop.2874.77704bccbd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +204 -121
- package/dist/backend/AppShell.js +25 -28
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/ContextHelp.js +1 -1
- package/dist/backend/ContextHelp.js.map +1 -1
- package/dist/backend/CrudForm.js +12 -15
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +9 -10
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/FilterBar.js +6 -8
- package/dist/backend/FilterBar.js.map +2 -2
- package/dist/backend/FilterOverlay.js +10 -10
- package/dist/backend/FilterOverlay.js.map +2 -2
- package/dist/backend/FlashMessages.js +1 -1
- package/dist/backend/FlashMessages.js.map +2 -2
- package/dist/backend/JsonBuilder.js +6 -6
- package/dist/backend/JsonBuilder.js.map +1 -1
- package/dist/backend/NextStepCallout.js +1 -1
- package/dist/backend/NextStepCallout.js.map +1 -1
- package/dist/backend/PerspectiveSidebar.js +2 -2
- package/dist/backend/PerspectiveSidebar.js.map +2 -2
- package/dist/backend/ProfileDropdown.js +1 -1
- package/dist/backend/ProfileDropdown.js.map +1 -1
- package/dist/backend/RowActions.js +1 -1
- package/dist/backend/RowActions.js.map +1 -1
- package/dist/backend/UserMenu.js +2 -2
- package/dist/backend/UserMenu.js.map +1 -1
- package/dist/backend/WebhookSetupGuide.js +11 -11
- package/dist/backend/WebhookSetupGuide.js.map +2 -2
- package/dist/backend/charts/KpiCard.js +3 -3
- package/dist/backend/charts/KpiCard.js.map +1 -1
- package/dist/backend/columns/ColumnChooserPanel.js +1 -1
- package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js +3 -3
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +1 -1
- package/dist/backend/dashboard/DashboardScreen.js.map +1 -1
- package/dist/backend/date-range/DateRangeSelect.js +1 -1
- package/dist/backend/date-range/DateRangeSelect.js.map +1 -1
- package/dist/backend/date-range/InlineDateRangeSelect.js +1 -1
- package/dist/backend/date-range/InlineDateRangeSelect.js.map +1 -1
- package/dist/backend/detail/AccessDeniedMessage.js +1 -1
- package/dist/backend/detail/AccessDeniedMessage.js.map +1 -1
- package/dist/backend/detail/ActivitiesSection.js +5 -5
- package/dist/backend/detail/ActivitiesSection.js.map +1 -1
- package/dist/backend/detail/AddressEditor.js +3 -3
- package/dist/backend/detail/AddressEditor.js.map +2 -2
- package/dist/backend/detail/AddressTiles.js +3 -3
- package/dist/backend/detail/AddressTiles.js.map +2 -2
- package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +1 -1
- package/dist/backend/detail/CustomDataSection.js +1 -1
- package/dist/backend/detail/CustomDataSection.js.map +1 -1
- package/dist/backend/detail/InlineEditors.js +5 -5
- package/dist/backend/detail/InlineEditors.js.map +1 -1
- package/dist/backend/detail/NotesSection.js +6 -6
- package/dist/backend/detail/NotesSection.js.map +1 -1
- package/dist/backend/detail/TagsSection.js +1 -1
- package/dist/backend/detail/TagsSection.js.map +1 -1
- package/dist/backend/devtools/UmesDevToolsPanel.js +6 -6
- package/dist/backend/devtools/UmesDevToolsPanel.js.map +2 -2
- package/dist/backend/devtools/components/ConflictWarnings.js +3 -3
- package/dist/backend/devtools/components/ConflictWarnings.js.map +2 -2
- package/dist/backend/devtools/components/EnricherTiming.js +2 -2
- package/dist/backend/devtools/components/EnricherTiming.js.map +2 -2
- package/dist/backend/devtools/components/EventFlow.js +5 -5
- package/dist/backend/devtools/components/EventFlow.js.map +2 -2
- package/dist/backend/devtools/components/ExtensionPointList.js +3 -3
- package/dist/backend/devtools/components/ExtensionPointList.js.map +2 -2
- package/dist/backend/devtools/components/InterceptorActivity.js +6 -6
- package/dist/backend/devtools/components/InterceptorActivity.js.map +2 -2
- package/dist/backend/forms/ActionsDropdown.js +1 -1
- package/dist/backend/forms/ActionsDropdown.js.map +1 -1
- package/dist/backend/forms/FormActionButtons.js +2 -3
- package/dist/backend/forms/FormActionButtons.js.map +2 -2
- package/dist/backend/indexes/PartialIndexBanner.js +8 -8
- package/dist/backend/indexes/PartialIndexBanner.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +1 -1
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/DatePicker.js +3 -3
- package/dist/backend/inputs/DatePicker.js.map +1 -1
- package/dist/backend/inputs/DateTimePicker.js +3 -3
- package/dist/backend/inputs/DateTimePicker.js.map +1 -1
- package/dist/backend/inputs/EventSelect.js +1 -1
- package/dist/backend/inputs/EventSelect.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +1 -1
- package/dist/backend/inputs/LookupSelect.js.map +1 -1
- package/dist/backend/inputs/SwitchableMarkdownInput.js +1 -1
- package/dist/backend/inputs/SwitchableMarkdownInput.js.map +1 -1
- package/dist/backend/inputs/TagsInput.js +2 -2
- package/dist/backend/inputs/TagsInput.js.map +2 -2
- package/dist/backend/inputs/TimeInput.js +1 -1
- package/dist/backend/inputs/TimeInput.js.map +1 -1
- package/dist/backend/inputs/TimePicker.js +3 -3
- package/dist/backend/inputs/TimePicker.js.map +1 -1
- package/dist/backend/messages/MessageObjectDetail.js +1 -1
- package/dist/backend/messages/MessageObjectDetail.js.map +1 -1
- package/dist/backend/messages/MessageObjectPreview.js +1 -1
- package/dist/backend/messages/MessageObjectPreview.js.map +1 -1
- package/dist/backend/messages/message-compose-form-groups.js +3 -3
- package/dist/backend/messages/message-compose-form-groups.js.map +1 -1
- package/dist/backend/notifications/NotificationCountBadge.js +1 -1
- package/dist/backend/notifications/NotificationCountBadge.js.map +2 -2
- package/dist/backend/notifications/NotificationPanel.js +3 -3
- package/dist/backend/notifications/NotificationPanel.js.map +1 -1
- package/dist/backend/progress/ProgressTopBar.js +4 -4
- package/dist/backend/progress/ProgressTopBar.js.map +2 -2
- package/dist/backend/schedule/ScheduleAgenda.js +1 -1
- package/dist/backend/schedule/ScheduleAgenda.js.map +2 -2
- package/dist/backend/schedule/ScheduleCalendar.js +1 -1
- package/dist/backend/schedule/ScheduleCalendar.js.map +1 -1
- package/dist/backend/schedule/ScheduleGrid.js +1 -1
- package/dist/backend/schedule/ScheduleGrid.js.map +2 -2
- package/dist/backend/version-history/VersionHistoryPanel.js +4 -4
- package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
- package/dist/frontend/AuthFooter.js +1 -1
- package/dist/frontend/AuthFooter.js.map +1 -1
- package/dist/frontend/LanguageSwitcher.js +1 -1
- package/dist/frontend/LanguageSwitcher.js.map +1 -1
- package/dist/frontend/Layout.js +2 -2
- package/dist/frontend/Layout.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +2 -2
- package/dist/portal/PortalShell.js +15 -15
- package/dist/portal/PortalShell.js.map +2 -2
- package/dist/portal/components/PortalCard.js +2 -2
- package/dist/portal/components/PortalCard.js.map +2 -2
- package/dist/portal/components/PortalNotificationPanel.js +18 -18
- package/dist/portal/components/PortalNotificationPanel.js.map +2 -2
- package/dist/portal/components/PortalPageHeader.js +1 -1
- package/dist/portal/components/PortalPageHeader.js.map +2 -2
- package/dist/primitives/avatar.js +11 -1
- package/dist/primitives/avatar.js.map +2 -2
- package/dist/primitives/badge.js +1 -1
- package/dist/primitives/badge.js.map +1 -1
- package/dist/primitives/button.js +9 -5
- package/dist/primitives/button.js.map +2 -2
- package/dist/primitives/calendar.js +1 -1
- package/dist/primitives/calendar.js.map +1 -1
- package/dist/primitives/checkbox-field.js +63 -0
- package/dist/primitives/checkbox-field.js.map +7 -0
- package/dist/primitives/checkbox.js +31 -17
- package/dist/primitives/checkbox.js.map +2 -2
- package/dist/primitives/dialog.js +4 -4
- package/dist/primitives/dialog.js.map +1 -1
- package/dist/primitives/fancy-button.js +72 -0
- package/dist/primitives/fancy-button.js.map +7 -0
- package/dist/primitives/icon-button.js +20 -4
- package/dist/primitives/icon-button.js.map +2 -2
- package/dist/primitives/kbd.js +27 -0
- package/dist/primitives/kbd.js.map +7 -0
- package/dist/primitives/link-button.js +56 -0
- package/dist/primitives/link-button.js.map +7 -0
- package/dist/primitives/popover.js +1 -1
- package/dist/primitives/popover.js.map +1 -1
- package/dist/primitives/social-button.js +61 -0
- package/dist/primitives/social-button.js.map +7 -0
- package/dist/primitives/tabs.js +1 -1
- package/dist/primitives/tabs.js.map +1 -1
- package/dist/primitives/tag.js +45 -0
- package/dist/primitives/tag.js.map +7 -0
- package/dist/primitives/tooltip.js +1 -1
- package/dist/primitives/tooltip.js.map +1 -1
- package/package.json +3 -3
- package/src/backend/AppShell.tsx +25 -28
- package/src/backend/ContextHelp.tsx +1 -1
- package/src/backend/CrudForm.tsx +12 -15
- package/src/backend/DataTable.tsx +9 -10
- package/src/backend/FilterBar.tsx +6 -5
- package/src/backend/FilterOverlay.tsx +10 -10
- package/src/backend/FlashMessages.tsx +1 -1
- package/src/backend/JsonBuilder.tsx +6 -6
- package/src/backend/NextStepCallout.tsx +1 -1
- package/src/backend/PerspectiveSidebar.tsx +2 -2
- package/src/backend/ProfileDropdown.tsx +1 -1
- package/src/backend/RowActions.tsx +1 -1
- package/src/backend/UserMenu.tsx +2 -2
- package/src/backend/WebhookSetupGuide.tsx +11 -11
- package/src/backend/charts/KpiCard.tsx +3 -3
- package/src/backend/columns/ColumnChooserPanel.tsx +1 -1
- package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +3 -3
- package/src/backend/dashboard/DashboardScreen.tsx +1 -1
- package/src/backend/date-range/DateRangeSelect.tsx +1 -1
- package/src/backend/date-range/InlineDateRangeSelect.tsx +1 -1
- package/src/backend/detail/AccessDeniedMessage.tsx +1 -1
- package/src/backend/detail/ActivitiesSection.tsx +5 -5
- package/src/backend/detail/AddressEditor.tsx +3 -3
- package/src/backend/detail/AddressTiles.tsx +3 -3
- package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
- package/src/backend/detail/CustomDataSection.tsx +1 -1
- package/src/backend/detail/InlineEditors.tsx +5 -5
- package/src/backend/detail/NotesSection.tsx +6 -6
- package/src/backend/detail/TagsSection.tsx +1 -1
- package/src/backend/devtools/UmesDevToolsPanel.tsx +6 -6
- package/src/backend/devtools/components/ConflictWarnings.tsx +4 -4
- package/src/backend/devtools/components/EnricherTiming.tsx +2 -2
- package/src/backend/devtools/components/EventFlow.tsx +5 -5
- package/src/backend/devtools/components/ExtensionPointList.tsx +3 -3
- package/src/backend/devtools/components/InterceptorActivity.tsx +6 -6
- package/src/backend/forms/ActionsDropdown.tsx +1 -1
- package/src/backend/forms/FormActionButtons.tsx +4 -5
- package/src/backend/indexes/PartialIndexBanner.tsx +8 -8
- package/src/backend/inputs/ComboboxInput.tsx +1 -1
- package/src/backend/inputs/DatePicker.tsx +3 -3
- package/src/backend/inputs/DateTimePicker.tsx +3 -3
- package/src/backend/inputs/EventSelect.tsx +1 -1
- package/src/backend/inputs/LookupSelect.tsx +1 -1
- package/src/backend/inputs/SwitchableMarkdownInput.tsx +1 -1
- package/src/backend/inputs/TagsInput.tsx +2 -2
- package/src/backend/inputs/TimeInput.tsx +1 -1
- package/src/backend/inputs/TimePicker.tsx +3 -3
- package/src/backend/messages/MessageObjectDetail.tsx +1 -1
- package/src/backend/messages/MessageObjectPreview.tsx +1 -1
- package/src/backend/messages/message-compose-form-groups.tsx +3 -3
- package/src/backend/notifications/NotificationCountBadge.tsx +1 -1
- package/src/backend/notifications/NotificationPanel.tsx +3 -3
- package/src/backend/progress/ProgressTopBar.tsx +4 -4
- package/src/backend/schedule/ScheduleAgenda.tsx +1 -1
- package/src/backend/schedule/ScheduleCalendar.tsx +1 -1
- package/src/backend/schedule/ScheduleGrid.tsx +1 -1
- package/src/backend/version-history/VersionHistoryPanel.tsx +4 -4
- package/src/frontend/AuthFooter.tsx +1 -1
- package/src/frontend/LanguageSwitcher.tsx +1 -1
- package/src/frontend/Layout.tsx +2 -2
- package/src/index.ts +6 -1
- package/src/portal/PortalShell.tsx +15 -15
- package/src/portal/components/PortalCard.tsx +2 -2
- package/src/portal/components/PortalNotificationPanel.tsx +18 -18
- package/src/portal/components/PortalPageHeader.tsx +1 -1
- package/src/primitives/avatar.tsx +22 -0
- package/src/primitives/badge.tsx +1 -1
- package/src/primitives/button.tsx +12 -5
- package/src/primitives/calendar.tsx +1 -1
- package/src/primitives/checkbox-field.tsx +85 -0
- package/src/primitives/checkbox.tsx +44 -18
- package/src/primitives/dialog.tsx +4 -4
- package/src/primitives/fancy-button.tsx +89 -0
- package/src/primitives/icon-button.tsx +19 -2
- package/src/primitives/kbd.tsx +38 -0
- package/src/primitives/link-button.tsx +55 -0
- package/src/primitives/popover.tsx +1 -1
- package/src/primitives/social-button.tsx +80 -0
- package/src/primitives/tabs.tsx +1 -1
- package/src/primitives/tag.tsx +66 -0
- package/src/primitives/tooltip.tsx +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { ListFilter, Search } from "lucide-react";
|
|
4
5
|
import { Button } from "../primitives/button.js";
|
|
5
6
|
import { FilterOverlay } from "./FilterOverlay.js";
|
|
6
7
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
@@ -51,25 +52,22 @@ function FilterBar({
|
|
|
51
52
|
return Object.values(values).filter(isActive).length;
|
|
52
53
|
}, [values]);
|
|
53
54
|
const containerClass = `flex flex-col ${layout === "inline" ? "gap-1 sm:gap-2" : "gap-2"} w-full`;
|
|
54
|
-
const searchInput = onSearchChange ? /* @__PURE__ */ jsxs("div", { className: `relative w-full sm:w-
|
|
55
|
+
const searchInput = onSearchChange ? /* @__PURE__ */ jsxs("div", { className: `relative w-full sm:w-72 lg:w-80 ${searchAlign === "right" ? "sm:ml-auto" : ""}`, children: [
|
|
55
56
|
/* @__PURE__ */ jsx(
|
|
56
57
|
"input",
|
|
57
58
|
{
|
|
58
59
|
value: searchDraft,
|
|
59
60
|
onChange: (e) => setSearchDraft(e.target.value),
|
|
60
61
|
placeholder: resolvedSearchPlaceholder,
|
|
61
|
-
className: "h-9 w-full rounded border pl-8 pr-2 text-sm",
|
|
62
|
+
className: "h-9 w-full rounded-md border border-input bg-background pl-8 pr-2 text-sm shadow-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50",
|
|
62
63
|
suppressHydrationWarning: true
|
|
63
64
|
}
|
|
64
65
|
),
|
|
65
|
-
/* @__PURE__ */ jsx("
|
|
66
|
+
/* @__PURE__ */ jsx(Search, { "aria-hidden": "true", className: "absolute left-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" })
|
|
66
67
|
] }) : null;
|
|
67
68
|
const controls = /* @__PURE__ */ jsxs("div", { className: `flex flex-wrap items-center gap-2 ${searchAlign === "left" && searchInput ? "sm:ml-auto" : ""}`, children: [
|
|
68
|
-
filters.length > 0 && /* @__PURE__ */ jsxs(Button, { variant: "outline",
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
/* @__PURE__ */ jsx("path", { d: "M3 4h18" }),
|
|
71
|
-
/* @__PURE__ */ jsx("path", { d: "M6 8h12l-3 8H9L6 8z" })
|
|
72
|
-
] }),
|
|
69
|
+
filters.length > 0 && /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: () => setOpen(true), children: [
|
|
70
|
+
/* @__PURE__ */ jsx(ListFilter, { "aria-hidden": "true", className: "size-4 opacity-80" }),
|
|
73
71
|
activeCount ? t("ui.filterBar.filtersWithCount", "Filters {count}", { count: activeCount }) : t("ui.filterBar.filters", "Filters")
|
|
74
72
|
] }),
|
|
75
73
|
leadingItems,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/FilterBar.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Button } from '../primitives/button'\nimport { FilterDef, FilterOverlay, FilterValues } from './FilterOverlay'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type FilterBarProps = {\n searchValue?: string\n onSearchChange?: (v: string) => void\n searchPlaceholder?: string\n searchAlign?: 'left' | 'right'\n filters?: FilterDef[]\n values?: FilterValues\n onApply?: (values: FilterValues) => void\n onClear?: () => void\n className?: string\n leadingItems?: React.ReactNode\n trailingItems?: React.ReactNode\n layout?: 'stacked' | 'inline'\n filtersExtraContent?: React.ReactNode\n}\n\nexport function FilterBar({\n searchValue,\n onSearchChange,\n searchPlaceholder,\n searchAlign = 'left',\n filters = [],\n values = {},\n onApply,\n onClear,\n className,\n leadingItems,\n trailingItems,\n layout = 'stacked',\n filtersExtraContent,\n}: FilterBarProps) {\n const t = useT()\n const resolvedSearchPlaceholder = searchPlaceholder ?? t('ui.filterBar.searchPlaceholder', 'Search')\n const [open, setOpen] = React.useState(false)\n const [searchDraft, setSearchDraft] = React.useState(searchValue ?? '')\n const lastAppliedSearchRef = React.useRef(searchValue ?? '')\n\n React.useEffect(() => {\n const next = searchValue ?? ''\n lastAppliedSearchRef.current = next\n setSearchDraft((prev) => (prev === next ? prev : next))\n }, [searchValue])\n\n React.useEffect(() => {\n if (!onSearchChange) return\n const handle = window.setTimeout(() => {\n if (lastAppliedSearchRef.current === searchDraft) return\n lastAppliedSearchRef.current = searchDraft\n onSearchChange(searchDraft)\n }, 1000)\n return () => {\n window.clearTimeout(handle)\n }\n }, [searchDraft, onSearchChange])\n\n const activeCount = React.useMemo(() => {\n const isActive = (v: any) => {\n if (v == null) return false\n if (typeof v === 'string') return v.trim() !== ''\n if (Array.isArray(v)) return v.length > 0\n if (typeof v === 'object') return Object.values(v).some((x) => x != null && x !== '')\n return Boolean(v)\n }\n return Object.values(values).filter(isActive).length\n }, [values])\n\n const containerClass = `flex flex-col ${layout === 'inline' ? 'gap-1 sm:gap-2' : 'gap-2'} w-full`\n const searchInput = onSearchChange ? (\n <div className={`relative w-full sm:w-
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { ListFilter, Search } from 'lucide-react'\nimport { Button } from '../primitives/button'\nimport { FilterDef, FilterOverlay, FilterValues } from './FilterOverlay'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type FilterBarProps = {\n searchValue?: string\n onSearchChange?: (v: string) => void\n searchPlaceholder?: string\n searchAlign?: 'left' | 'right'\n filters?: FilterDef[]\n values?: FilterValues\n onApply?: (values: FilterValues) => void\n onClear?: () => void\n className?: string\n leadingItems?: React.ReactNode\n trailingItems?: React.ReactNode\n layout?: 'stacked' | 'inline'\n filtersExtraContent?: React.ReactNode\n}\n\nexport function FilterBar({\n searchValue,\n onSearchChange,\n searchPlaceholder,\n searchAlign = 'left',\n filters = [],\n values = {},\n onApply,\n onClear,\n className,\n leadingItems,\n trailingItems,\n layout = 'stacked',\n filtersExtraContent,\n}: FilterBarProps) {\n const t = useT()\n const resolvedSearchPlaceholder = searchPlaceholder ?? t('ui.filterBar.searchPlaceholder', 'Search')\n const [open, setOpen] = React.useState(false)\n const [searchDraft, setSearchDraft] = React.useState(searchValue ?? '')\n const lastAppliedSearchRef = React.useRef(searchValue ?? '')\n\n React.useEffect(() => {\n const next = searchValue ?? ''\n lastAppliedSearchRef.current = next\n setSearchDraft((prev) => (prev === next ? prev : next))\n }, [searchValue])\n\n React.useEffect(() => {\n if (!onSearchChange) return\n const handle = window.setTimeout(() => {\n if (lastAppliedSearchRef.current === searchDraft) return\n lastAppliedSearchRef.current = searchDraft\n onSearchChange(searchDraft)\n }, 1000)\n return () => {\n window.clearTimeout(handle)\n }\n }, [searchDraft, onSearchChange])\n\n const activeCount = React.useMemo(() => {\n const isActive = (v: any) => {\n if (v == null) return false\n if (typeof v === 'string') return v.trim() !== ''\n if (Array.isArray(v)) return v.length > 0\n if (typeof v === 'object') return Object.values(v).some((x) => x != null && x !== '')\n return Boolean(v)\n }\n return Object.values(values).filter(isActive).length\n }, [values])\n\n const containerClass = `flex flex-col ${layout === 'inline' ? 'gap-1 sm:gap-2' : 'gap-2'} w-full`\n const searchInput = onSearchChange ? (\n <div className={`relative w-full sm:w-72 lg:w-80 ${searchAlign === 'right' ? 'sm:ml-auto' : ''}`}>\n <input\n value={searchDraft}\n onChange={(e) => setSearchDraft(e.target.value)}\n placeholder={resolvedSearchPlaceholder}\n className=\"h-9 w-full rounded-md border border-input bg-background pl-8 pr-2 text-sm shadow-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50\"\n suppressHydrationWarning\n />\n <Search aria-hidden=\"true\" className=\"absolute left-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\" />\n </div>\n ) : null\n const controls = (\n <div className={`flex flex-wrap items-center gap-2 ${searchAlign === 'left' && searchInput ? 'sm:ml-auto' : ''}`}>\n {filters.length > 0 && (\n <Button variant=\"outline\" onClick={() => setOpen(true)}>\n <ListFilter aria-hidden=\"true\" className=\"size-4 opacity-80\" />\n {activeCount\n ? t('ui.filterBar.filtersWithCount', 'Filters {count}', { count: activeCount })\n : t('ui.filterBar.filters', 'Filters')\n }\n </Button>\n )}\n {leadingItems}\n {trailingItems}\n </div>\n )\n\n return (\n <div className={`${containerClass} ${className ?? ''}`}>\n <div className=\"flex flex-wrap items-center gap-2 w-full\">\n {searchAlign === 'left' ? searchInput : null}\n {controls}\n {searchAlign === 'right' ? searchInput : null}\n </div>\n {/* Active filter chips */}\n {filters.length > 0 && activeCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1\">\n {filters.map((f) => {\n const v = (values as any)[f.id]\n if (v == null || v === '' || (Array.isArray(v) && v.length === 0)) return null\n const toLabel = (val: any) => {\n if (typeof f.formatValue === 'function' && (typeof val === 'string' || typeof val === 'number')) {\n const formatted = f.formatValue(String(val))\n if (formatted) return formatted\n }\n if (f.type === 'select' && f.options) {\n const o = f.options.find((o) => o.value === val)\n return o ? o.label : String(val)\n }\n if (typeof val === 'object' && val.from == null && val.to == null) return null\n if (typeof val === 'object') {\n const from = val.from ?? ''\n const to = val.to ? ` \u2192 ${val.to}` : ''\n return `${from}${to}`.trim()\n }\n if (val === true) return t('common.yes', 'Yes')\n if (val === false) return t('common.no', 'No')\n return String(val)\n }\n const removeValue = (val?: any) => {\n const next = { ...(values || {}) }\n if (Array.isArray(v) && val !== undefined) next[f.id] = v.filter((x: any) => x !== val)\n else delete (next as any)[f.id]\n onApply?.(next)\n }\n if (Array.isArray(v)) {\n return v.map((item) => (\n <Button key={`${f.id}:${item}`} size=\"sm\" variant=\"outline\" className=\"max-w-[calc(100vw-4rem)] truncate\" onClick={() => removeValue(item)}>\n {f.label}: {toLabel(item)} \u00D7\n </Button>\n ))\n }\n const label = toLabel(v)\n if (!label) return null\n return (\n <Button key={f.id} size=\"sm\" variant=\"outline\" className=\"max-w-[calc(100vw-4rem)] truncate\" onClick={() => removeValue()}>\n {f.label}: {label} \u00D7\n </Button>\n )\n })}\n </div>\n )}\n <FilterOverlay\n title={t('ui.filterOverlay.title', 'Filters')}\n filters={filters}\n initialValues={values}\n open={open}\n onOpenChange={setOpen}\n onApply={(v) => onApply?.(v)}\n onClear={onClear}\n extraContent={filtersExtraContent}\n />\n </div>\n )\n}\n\nexport type { FilterDef, FilterValues } from './FilterOverlay'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["o"]
|
|
7
7
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Button } from "../primitives/button.js";
|
|
5
|
+
import { Checkbox } from "../primitives/checkbox.js";
|
|
5
6
|
import { ComboboxInput } from "./inputs/ComboboxInput.js";
|
|
6
7
|
import { TagsInput } from "./inputs/TagsInput.js";
|
|
7
8
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
@@ -141,8 +142,8 @@ function FilterOverlay({
|
|
|
141
142
|
}
|
|
142
143
|
return map;
|
|
143
144
|
}, [stableFilters]);
|
|
144
|
-
return /* @__PURE__ */ jsx(Fragment, { children: open && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-
|
|
145
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/
|
|
145
|
+
return /* @__PURE__ */ jsx(Fragment, { children: open && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-modal", children: [
|
|
146
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/20", onClick: () => onOpenChange(false), role: "presentation" }),
|
|
146
147
|
/* @__PURE__ */ jsxs("div", { className: "absolute left-0 top-0 h-full w-full sm:w-[380px] bg-background shadow-xl border-r flex flex-col", children: [
|
|
147
148
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b", children: [
|
|
148
149
|
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: defaultTitle }),
|
|
@@ -201,17 +202,16 @@ function FilterOverlay({
|
|
|
201
202
|
f.type === "select" && /* @__PURE__ */ jsx("div", { className: "space-y-1", children: f.multiple ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: (f.options || dynamicOptions[f.id] || []).map((opt) => {
|
|
202
203
|
const arr = Array.isArray(values[f.id]) ? values[f.id] : [];
|
|
203
204
|
const checked = arr.includes(opt.value);
|
|
204
|
-
return /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2", children: [
|
|
205
|
+
return /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 cursor-pointer", children: [
|
|
205
206
|
/* @__PURE__ */ jsx(
|
|
206
|
-
|
|
207
|
+
Checkbox,
|
|
207
208
|
{
|
|
208
|
-
type: "checkbox",
|
|
209
209
|
checked,
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
else
|
|
214
|
-
setValue(f.id, Array.from(
|
|
210
|
+
onCheckedChange: (next) => {
|
|
211
|
+
const set = new Set(arr);
|
|
212
|
+
if (next === true) set.add(opt.value);
|
|
213
|
+
else set.delete(opt.value);
|
|
214
|
+
setValue(f.id, Array.from(set));
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/FilterOverlay.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Button } from '../primitives/button'\nimport { ComboboxInput } from './inputs/ComboboxInput'\nimport { TagsInput, type TagsInputOption } from './inputs/TagsInput'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type FilterOption = { value: string; label: string; description?: string | null }\n\nexport type FilterDef = {\n id: string\n label: string\n type: 'text' | 'select' | 'checkbox' | 'dateRange' | 'tags' | 'combobox'\n options?: FilterOption[]\n // Optional async loader for options (used by select/tags/combobox)\n loadOptions?: (query?: string) => Promise<FilterOption[]>\n multiple?: boolean\n placeholder?: string\n group?: string\n formatValue?: (value: string) => string\n formatDescription?: (value: string) => string | null | undefined\n}\n\nexport type FilterValues = Record<string, any>\n\nexport type FilterOverlayProps = {\n title?: string\n filters: FilterDef[]\n initialValues: FilterValues\n open: boolean\n onOpenChange: (open: boolean) => void\n onApply: (values: FilterValues) => void\n onClear?: () => void\n extraContent?: React.ReactNode\n}\n\nconst EMPTY_FILTER_VALUES: FilterValues = {}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction normalizeKeys(source: FilterValues | null | undefined): string[] {\n if (!source) return []\n return Object.keys(source).filter((key) => source[key] !== undefined)\n}\n\nfunction areFieldValuesEqual(a: any, b: any): boolean {\n if (a === b) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (!areFieldValuesEqual(a[i], b[i])) return false\n }\n return true\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = normalizeKeys(a as FilterValues)\n const keysB = normalizeKeys(b as FilterValues)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!keysB.includes(key)) return false\n if (!areFieldValuesEqual((a as FilterValues)[key], (b as FilterValues)[key])) return false\n }\n return true\n }\n return false\n}\n\nfunction areFilterValuesEqual(a?: FilterValues | null, b?: FilterValues | null): boolean {\n if (a === b) return true\n const keysA = normalizeKeys(a || EMPTY_FILTER_VALUES)\n const keysB = normalizeKeys(b || EMPTY_FILTER_VALUES)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!keysB.includes(key)) return false\n if (!areFieldValuesEqual(a?.[key], b?.[key])) return false\n }\n return true\n}\n\nexport function FilterOverlay({\n title,\n filters,\n initialValues,\n open,\n onOpenChange,\n onApply,\n onClear,\n extraContent,\n}: FilterOverlayProps) {\n const t = useT()\n const defaultTitle = title ?? t('ui.filters.title', 'Filters')\n const [values, setValues] = React.useState<FilterValues>(initialValues)\n React.useEffect(() => {\n setValues((prev) => (areFilterValuesEqual(prev, initialValues) ? prev : initialValues))\n }, [initialValues])\n const filtersSignature = React.useMemo(\n () => filters.map((f) => `${f.id}:${f.type}:${Boolean((f as any).loadOptions)}:${(f.options || []).length}`).join('|'),\n [filters]\n )\n const lastLoadedSignatureRef = React.useRef<string | null>(null)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stableFilters = React.useMemo(() => filters, [filtersSignature])\n\n // Load dynamic options for filters that request it\n const [dynamicOptions, setDynamicOptions] = React.useState<Record<string, FilterOption[]>>({})\n React.useEffect(() => {\n if (!open) return\n if (lastLoadedSignatureRef.current === filtersSignature) return\n lastLoadedSignatureRef.current = filtersSignature\n setDynamicOptions({})\n let cancelled = false\n const loadAll = async () => {\n const loaders = filters\n .filter((f): f is FilterDef & { loadOptions: (query?: string) => Promise<FilterOption[]> } => (f as any).loadOptions != null)\n .map(async (f) => {\n try {\n const opts = await (f as any).loadOptions()\n if (!cancelled) setDynamicOptions((prev) => ({ ...prev, [f.id]: opts }))\n } catch {\n // ignore\n }\n })\n await Promise.all(loaders)\n }\n loadAll()\n return () => {\n cancelled = true\n }\n }, [filters, filtersSignature, open])\n React.useEffect(() => {\n if (!open) {\n lastLoadedSignatureRef.current = null\n }\n }, [open])\n\n const setValue = (id: string, v: any) => setValues((prev) => ({ ...prev, [id]: v }))\n\n const handleApply = () => {\n onApply(values)\n onOpenChange(false)\n }\n\n const handleClear = () => {\n setValues({})\n onClear?.()\n }\n\n const tagLoaders = React.useMemo(() => {\n const map = new Map<string, (q?: string) => Promise<Array<string | TagsInputOption>>>()\n for (const f of stableFilters) {\n if (f.type === 'tags' && typeof f.loadOptions === 'function') {\n const fieldId = f.id\n const load = f.loadOptions as (query?: string) => Promise<FilterOption[]>\n map.set(fieldId, async (q?: string) => {\n const query = (q ?? '').trim()\n if (!query.length) return []\n try {\n const opts = await load(query)\n setDynamicOptions((prev) => ({ ...prev, [fieldId]: opts }))\n return opts.map((o) => ({ value: o.value, label: o.label, description: o.description ?? null }))\n } catch {\n return []\n }\n })\n }\n }\n return map\n }, [stableFilters])\n\n const comboboxLoaders = React.useMemo(() => {\n const map = new Map<string, (q?: string) => Promise<FilterOption[]>>()\n for (const f of stableFilters) {\n if (f.type === 'combobox' && typeof f.loadOptions === 'function') {\n map.set(f.id, async (query?: string) => {\n try {\n const opts = await f.loadOptions?.(query)\n setDynamicOptions((prev) => ({ ...prev, [f.id]: opts ?? [] }))\n return opts ?? []\n } catch {\n return []\n }\n })\n }\n }\n return map\n }, [stableFilters])\n\n return (\n <>\n {open && (\n <div className=\"fixed inset-0 z-50\">\n <div className=\"absolute inset-0 bg-black/30\" onClick={() => onOpenChange(false)} role=\"presentation\" />\n <div className=\"absolute left-0 top-0 h-full w-full sm:w-[380px] bg-background shadow-xl border-r flex flex-col\">\n <div className=\"flex items-center justify-between p-4 border-b\">\n <h2 className=\"text-base font-semibold\">{defaultTitle}</h2>\n <Button variant=\"muted\" size=\"sm\" onClick={() => onOpenChange(false)}>{t('common.close')}</Button>\n </div>\n {/* Top actions: duplicate Clear/Apply */}\n <div className=\"px-4 py-2 border-b flex items-center justify-between gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleClear}>{t('ui.filters.actions.clear', 'Clear')}</Button>\n <Button size=\"sm\" onClick={handleApply}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\" className=\"opacity-80\"><path d=\"M3 4h18\"/><path d=\"M6 8h12l-3 8H9L6 8z\"/></svg>\n {t('ui.filters.actions.apply', 'Apply')}\n </Button>\n </div>\n <div className=\"flex-1 overflow-auto p-4 space-y-4\">\n {extraContent ? <div className=\"space-y-2 rounded-md border bg-muted/30 p-3\">{extraContent}</div> : null}\n {filters.map((f) => (\n <div key={f.id} className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{f.label}</div>\n {f.type === 'text' && (\n <input\n type=\"text\"\n className=\"w-full h-11 rounded border px-2 text-sm\"\n placeholder={f.placeholder}\n value={values[f.id] ?? ''}\n onChange={(e) => setValue(f.id, e.target.value || undefined)}\n />\n )}\n {f.type === 'dateRange' && (\n <div className=\"grid grid-cols-1 gap-2\">\n <div>\n <div className=\"text-xs text-muted-foreground mb-1\">{t('ui.filters.dateRange.from', 'From')}</div>\n <input\n type=\"date\"\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id]?.from ?? ''}\n onChange={(e) => setValue(f.id, { ...(values[f.id] ?? {}), from: e.target.value || undefined })}\n />\n </div>\n <div>\n <div className=\"text-xs text-muted-foreground mb-1\">{t('ui.filters.dateRange.to', 'To')}</div>\n <input\n type=\"date\"\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id]?.to ?? ''}\n onChange={(e) => setValue(f.id, { ...(values[f.id] ?? {}), to: e.target.value || undefined })}\n />\n </div>\n </div>\n )}\n {f.type === 'select' && (\n <div className=\"space-y-1\">\n {f.multiple ? (\n <div className=\"flex flex-col gap-1\">\n {(f.options || dynamicOptions[f.id] || []).map((opt) => {\n const arr: string[] = Array.isArray(values[f.id]) ? values[f.id] : []\n const checked = arr.includes(opt.value)\n return (\n <label key={opt.value} className=\"inline-flex items-center gap-2\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => {\n const next = new Set(arr)\n if (e.target.checked) next.add(opt.value)\n else next.delete(opt.value)\n setValue(f.id, Array.from(next))\n }}\n />\n <span className=\"text-sm\">{opt.label}</span>\n </label>\n )\n })}\n </div>\n ) : (\n <select\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id] ?? ''}\n onChange={(e) => setValue(f.id, e.target.value || undefined)}\n >\n <option value=\"\">{t('ui.forms.select.emptyOption', '\u2014')}</option>\n {(f.options || dynamicOptions[f.id] || []).map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )}\n </div>\n )}\n {f.type === 'combobox' && (() => {\n const staticOptions = f.options || []\n const dynamic = dynamicOptions[f.id] || []\n const optionMap = new Map<string, FilterOption>()\n staticOptions.forEach((opt) => optionMap.set(opt.value, opt))\n dynamic.forEach((opt) => optionMap.set(opt.value, opt))\n const currentValue = typeof values[f.id] === 'string' ? values[f.id] : ''\n const suggestions = Array.from(optionMap.values()).map((opt) => ({\n value: opt.value,\n label: opt.label,\n description: opt.description ?? null,\n }))\n const loadSuggestions = comboboxLoaders.get(f.id)\n return (\n <div className=\"flex items-start gap-2\">\n <div className=\"min-w-0 flex-1\">\n <ComboboxInput\n value={currentValue}\n onChange={(next) => setValue(f.id, next.trim().length ? next : undefined)}\n suggestions={suggestions}\n loadSuggestions={loadSuggestions}\n resolveLabel={(value) => f.formatValue?.(value) ?? optionMap.get(value)?.label ?? value}\n resolveDescription={(value) => optionMap.get(value)?.description ?? null}\n placeholder={f.placeholder}\n allowCustomValues={false}\n />\n </div>\n {currentValue ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"shrink-0\"\n onClick={() => setValue(f.id, undefined)}\n >\n {t('ui.filters.actions.clear', 'Clear')}\n </Button>\n ) : null}\n </div>\n )\n })()}\n {f.type === 'tags' && (() => {\n const arr: string[] = Array.isArray(values[f.id]) ? values[f.id] : []\n const staticOptions = f.options || []\n const dynamic = dynamicOptions[f.id] || []\n const optionMap = new Map<string, FilterOption>()\n staticOptions.forEach((opt) => optionMap.set(opt.value, opt))\n dynamic.forEach((opt) => optionMap.set(opt.value, opt))\n const loadSuggestions = tagLoaders.get(f.id)\n const resolveTagLabel = f.formatValue\n ? (val: string) => f.formatValue!(val)\n : (val: string) => optionMap.get(val)?.label ?? val\n const resolveTagDescription = f.formatDescription\n ? (val: string) => f.formatDescription!(val) ?? null\n : (val: string) => optionMap.get(val)?.description ?? null\n const suggestionList: TagsInputOption[] = Array.from(optionMap.values()).map((opt) => ({\n value: opt.value,\n label: opt.label,\n description: opt.description ?? null,\n }))\n return (\n <TagsInput\n value={arr}\n suggestions={suggestionList}\n loadSuggestions={loadSuggestions}\n allowCustomValues={false}\n resolveLabel={resolveTagLabel}\n resolveDescription={resolveTagDescription}\n placeholder={f.placeholder}\n onChange={(next) => setValue(f.id, next.length ? next : undefined)}\n />\n )\n })()}\n {f.type === 'checkbox' && (\n <div>\n <select\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id] === true ? 'true' : values[f.id] === false ? 'false' : ''}\n onChange={(e) => {\n const v = e.target.value\n if (v === '') setValue(f.id, undefined)\n else if (v === 'true') setValue(f.id, true)\n else if (v === 'false') setValue(f.id, false)\n }}\n >\n <option value=\"\">{t('ui.forms.select.emptyOption', '\u2014')}</option>\n <option value=\"true\">{t('common.yes', 'Yes')}</option>\n <option value=\"false\">{t('common.no', 'No')}</option>\n </select>\n </div>\n )}\n </div>\n ))}\n </div>\n <div className=\"p-4 border-t flex items-center justify-between gap-2\">\n <Button variant=\"outline\" onClick={handleClear}>{t('ui.filters.actions.clear', 'Clear')}</Button>\n <Button onClick={handleApply}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\" className=\"opacity-80\"><path d=\"M3 4h18\"/><path d=\"M6 8h12l-3 8H9L6 8z\"/></svg>\n {t('ui.filters.actions.apply', 'Apply')}\n </Button>\n </div>\n </div>\n </div>\n )}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Button } from '../primitives/button'\nimport { Checkbox } from '../primitives/checkbox'\nimport { ComboboxInput } from './inputs/ComboboxInput'\nimport { TagsInput, type TagsInputOption } from './inputs/TagsInput'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type FilterOption = { value: string; label: string; description?: string | null }\n\nexport type FilterDef = {\n id: string\n label: string\n type: 'text' | 'select' | 'checkbox' | 'dateRange' | 'tags' | 'combobox'\n options?: FilterOption[]\n // Optional async loader for options (used by select/tags/combobox)\n loadOptions?: (query?: string) => Promise<FilterOption[]>\n multiple?: boolean\n placeholder?: string\n group?: string\n formatValue?: (value: string) => string\n formatDescription?: (value: string) => string | null | undefined\n}\n\nexport type FilterValues = Record<string, any>\n\nexport type FilterOverlayProps = {\n title?: string\n filters: FilterDef[]\n initialValues: FilterValues\n open: boolean\n onOpenChange: (open: boolean) => void\n onApply: (values: FilterValues) => void\n onClear?: () => void\n extraContent?: React.ReactNode\n}\n\nconst EMPTY_FILTER_VALUES: FilterValues = {}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction normalizeKeys(source: FilterValues | null | undefined): string[] {\n if (!source) return []\n return Object.keys(source).filter((key) => source[key] !== undefined)\n}\n\nfunction areFieldValuesEqual(a: any, b: any): boolean {\n if (a === b) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (!areFieldValuesEqual(a[i], b[i])) return false\n }\n return true\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keysA = normalizeKeys(a as FilterValues)\n const keysB = normalizeKeys(b as FilterValues)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!keysB.includes(key)) return false\n if (!areFieldValuesEqual((a as FilterValues)[key], (b as FilterValues)[key])) return false\n }\n return true\n }\n return false\n}\n\nfunction areFilterValuesEqual(a?: FilterValues | null, b?: FilterValues | null): boolean {\n if (a === b) return true\n const keysA = normalizeKeys(a || EMPTY_FILTER_VALUES)\n const keysB = normalizeKeys(b || EMPTY_FILTER_VALUES)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!keysB.includes(key)) return false\n if (!areFieldValuesEqual(a?.[key], b?.[key])) return false\n }\n return true\n}\n\nexport function FilterOverlay({\n title,\n filters,\n initialValues,\n open,\n onOpenChange,\n onApply,\n onClear,\n extraContent,\n}: FilterOverlayProps) {\n const t = useT()\n const defaultTitle = title ?? t('ui.filters.title', 'Filters')\n const [values, setValues] = React.useState<FilterValues>(initialValues)\n React.useEffect(() => {\n setValues((prev) => (areFilterValuesEqual(prev, initialValues) ? prev : initialValues))\n }, [initialValues])\n const filtersSignature = React.useMemo(\n () => filters.map((f) => `${f.id}:${f.type}:${Boolean((f as any).loadOptions)}:${(f.options || []).length}`).join('|'),\n [filters]\n )\n const lastLoadedSignatureRef = React.useRef<string | null>(null)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stableFilters = React.useMemo(() => filters, [filtersSignature])\n\n // Load dynamic options for filters that request it\n const [dynamicOptions, setDynamicOptions] = React.useState<Record<string, FilterOption[]>>({})\n React.useEffect(() => {\n if (!open) return\n if (lastLoadedSignatureRef.current === filtersSignature) return\n lastLoadedSignatureRef.current = filtersSignature\n setDynamicOptions({})\n let cancelled = false\n const loadAll = async () => {\n const loaders = filters\n .filter((f): f is FilterDef & { loadOptions: (query?: string) => Promise<FilterOption[]> } => (f as any).loadOptions != null)\n .map(async (f) => {\n try {\n const opts = await (f as any).loadOptions()\n if (!cancelled) setDynamicOptions((prev) => ({ ...prev, [f.id]: opts }))\n } catch {\n // ignore\n }\n })\n await Promise.all(loaders)\n }\n loadAll()\n return () => {\n cancelled = true\n }\n }, [filters, filtersSignature, open])\n React.useEffect(() => {\n if (!open) {\n lastLoadedSignatureRef.current = null\n }\n }, [open])\n\n const setValue = (id: string, v: any) => setValues((prev) => ({ ...prev, [id]: v }))\n\n const handleApply = () => {\n onApply(values)\n onOpenChange(false)\n }\n\n const handleClear = () => {\n setValues({})\n onClear?.()\n }\n\n const tagLoaders = React.useMemo(() => {\n const map = new Map<string, (q?: string) => Promise<Array<string | TagsInputOption>>>()\n for (const f of stableFilters) {\n if (f.type === 'tags' && typeof f.loadOptions === 'function') {\n const fieldId = f.id\n const load = f.loadOptions as (query?: string) => Promise<FilterOption[]>\n map.set(fieldId, async (q?: string) => {\n const query = (q ?? '').trim()\n if (!query.length) return []\n try {\n const opts = await load(query)\n setDynamicOptions((prev) => ({ ...prev, [fieldId]: opts }))\n return opts.map((o) => ({ value: o.value, label: o.label, description: o.description ?? null }))\n } catch {\n return []\n }\n })\n }\n }\n return map\n }, [stableFilters])\n\n const comboboxLoaders = React.useMemo(() => {\n const map = new Map<string, (q?: string) => Promise<FilterOption[]>>()\n for (const f of stableFilters) {\n if (f.type === 'combobox' && typeof f.loadOptions === 'function') {\n map.set(f.id, async (query?: string) => {\n try {\n const opts = await f.loadOptions?.(query)\n setDynamicOptions((prev) => ({ ...prev, [f.id]: opts ?? [] }))\n return opts ?? []\n } catch {\n return []\n }\n })\n }\n }\n return map\n }, [stableFilters])\n\n return (\n <>\n {open && (\n <div className=\"fixed inset-0 z-modal\">\n <div className=\"absolute inset-0 bg-black/20\" onClick={() => onOpenChange(false)} role=\"presentation\" />\n <div className=\"absolute left-0 top-0 h-full w-full sm:w-[380px] bg-background shadow-xl border-r flex flex-col\">\n <div className=\"flex items-center justify-between p-4 border-b\">\n <h2 className=\"text-base font-semibold\">{defaultTitle}</h2>\n <Button variant=\"muted\" size=\"sm\" onClick={() => onOpenChange(false)}>{t('common.close')}</Button>\n </div>\n {/* Top actions: duplicate Clear/Apply */}\n <div className=\"px-4 py-2 border-b flex items-center justify-between gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleClear}>{t('ui.filters.actions.clear', 'Clear')}</Button>\n <Button size=\"sm\" onClick={handleApply}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\" className=\"opacity-80\"><path d=\"M3 4h18\"/><path d=\"M6 8h12l-3 8H9L6 8z\"/></svg>\n {t('ui.filters.actions.apply', 'Apply')}\n </Button>\n </div>\n <div className=\"flex-1 overflow-auto p-4 space-y-4\">\n {extraContent ? <div className=\"space-y-2 rounded-md border bg-muted/30 p-3\">{extraContent}</div> : null}\n {filters.map((f) => (\n <div key={f.id} className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{f.label}</div>\n {f.type === 'text' && (\n <input\n type=\"text\"\n className=\"w-full h-11 rounded border px-2 text-sm\"\n placeholder={f.placeholder}\n value={values[f.id] ?? ''}\n onChange={(e) => setValue(f.id, e.target.value || undefined)}\n />\n )}\n {f.type === 'dateRange' && (\n <div className=\"grid grid-cols-1 gap-2\">\n <div>\n <div className=\"text-xs text-muted-foreground mb-1\">{t('ui.filters.dateRange.from', 'From')}</div>\n <input\n type=\"date\"\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id]?.from ?? ''}\n onChange={(e) => setValue(f.id, { ...(values[f.id] ?? {}), from: e.target.value || undefined })}\n />\n </div>\n <div>\n <div className=\"text-xs text-muted-foreground mb-1\">{t('ui.filters.dateRange.to', 'To')}</div>\n <input\n type=\"date\"\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id]?.to ?? ''}\n onChange={(e) => setValue(f.id, { ...(values[f.id] ?? {}), to: e.target.value || undefined })}\n />\n </div>\n </div>\n )}\n {f.type === 'select' && (\n <div className=\"space-y-1\">\n {f.multiple ? (\n <div className=\"flex flex-col gap-1\">\n {(f.options || dynamicOptions[f.id] || []).map((opt) => {\n const arr: string[] = Array.isArray(values[f.id]) ? values[f.id] : []\n const checked = arr.includes(opt.value)\n return (\n <label key={opt.value} className=\"inline-flex items-center gap-2 cursor-pointer\">\n <Checkbox\n checked={checked}\n onCheckedChange={(next) => {\n const set = new Set(arr)\n if (next === true) set.add(opt.value)\n else set.delete(opt.value)\n setValue(f.id, Array.from(set))\n }}\n />\n <span className=\"text-sm\">{opt.label}</span>\n </label>\n )\n })}\n </div>\n ) : (\n <select\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id] ?? ''}\n onChange={(e) => setValue(f.id, e.target.value || undefined)}\n >\n <option value=\"\">{t('ui.forms.select.emptyOption', '\u2014')}</option>\n {(f.options || dynamicOptions[f.id] || []).map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )}\n </div>\n )}\n {f.type === 'combobox' && (() => {\n const staticOptions = f.options || []\n const dynamic = dynamicOptions[f.id] || []\n const optionMap = new Map<string, FilterOption>()\n staticOptions.forEach((opt) => optionMap.set(opt.value, opt))\n dynamic.forEach((opt) => optionMap.set(opt.value, opt))\n const currentValue = typeof values[f.id] === 'string' ? values[f.id] : ''\n const suggestions = Array.from(optionMap.values()).map((opt) => ({\n value: opt.value,\n label: opt.label,\n description: opt.description ?? null,\n }))\n const loadSuggestions = comboboxLoaders.get(f.id)\n return (\n <div className=\"flex items-start gap-2\">\n <div className=\"min-w-0 flex-1\">\n <ComboboxInput\n value={currentValue}\n onChange={(next) => setValue(f.id, next.trim().length ? next : undefined)}\n suggestions={suggestions}\n loadSuggestions={loadSuggestions}\n resolveLabel={(value) => f.formatValue?.(value) ?? optionMap.get(value)?.label ?? value}\n resolveDescription={(value) => optionMap.get(value)?.description ?? null}\n placeholder={f.placeholder}\n allowCustomValues={false}\n />\n </div>\n {currentValue ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"shrink-0\"\n onClick={() => setValue(f.id, undefined)}\n >\n {t('ui.filters.actions.clear', 'Clear')}\n </Button>\n ) : null}\n </div>\n )\n })()}\n {f.type === 'tags' && (() => {\n const arr: string[] = Array.isArray(values[f.id]) ? values[f.id] : []\n const staticOptions = f.options || []\n const dynamic = dynamicOptions[f.id] || []\n const optionMap = new Map<string, FilterOption>()\n staticOptions.forEach((opt) => optionMap.set(opt.value, opt))\n dynamic.forEach((opt) => optionMap.set(opt.value, opt))\n const loadSuggestions = tagLoaders.get(f.id)\n const resolveTagLabel = f.formatValue\n ? (val: string) => f.formatValue!(val)\n : (val: string) => optionMap.get(val)?.label ?? val\n const resolveTagDescription = f.formatDescription\n ? (val: string) => f.formatDescription!(val) ?? null\n : (val: string) => optionMap.get(val)?.description ?? null\n const suggestionList: TagsInputOption[] = Array.from(optionMap.values()).map((opt) => ({\n value: opt.value,\n label: opt.label,\n description: opt.description ?? null,\n }))\n return (\n <TagsInput\n value={arr}\n suggestions={suggestionList}\n loadSuggestions={loadSuggestions}\n allowCustomValues={false}\n resolveLabel={resolveTagLabel}\n resolveDescription={resolveTagDescription}\n placeholder={f.placeholder}\n onChange={(next) => setValue(f.id, next.length ? next : undefined)}\n />\n )\n })()}\n {f.type === 'checkbox' && (\n <div>\n <select\n className=\"w-full h-11 rounded border px-2 text-sm\"\n value={values[f.id] === true ? 'true' : values[f.id] === false ? 'false' : ''}\n onChange={(e) => {\n const v = e.target.value\n if (v === '') setValue(f.id, undefined)\n else if (v === 'true') setValue(f.id, true)\n else if (v === 'false') setValue(f.id, false)\n }}\n >\n <option value=\"\">{t('ui.forms.select.emptyOption', '\u2014')}</option>\n <option value=\"true\">{t('common.yes', 'Yes')}</option>\n <option value=\"false\">{t('common.no', 'No')}</option>\n </select>\n </div>\n )}\n </div>\n ))}\n </div>\n <div className=\"p-4 border-t flex items-center justify-between gap-2\">\n <Button variant=\"outline\" onClick={handleClear}>{t('ui.filters.actions.clear', 'Clear')}</Button>\n <Button onClick={handleApply}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\" className=\"opacity-80\"><path d=\"M3 4h18\"/><path d=\"M6 8h12l-3 8H9L6 8z\"/></svg>\n {t('ui.filters.actions.apply', 'Apply')}\n </Button>\n </div>\n </div>\n </div>\n )}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -123,7 +123,7 @@ function FlashMessagesInner() {
|
|
|
123
123
|
info: "bg-status-info-icon"
|
|
124
124
|
};
|
|
125
125
|
const color = colorMap[kind];
|
|
126
|
-
return /* @__PURE__ */ jsx("div", { className: "pointer-events-none fixed left-3 right-3 top-3 z-
|
|
126
|
+
return /* @__PURE__ */ jsx("div", { className: "pointer-events-none fixed left-3 right-3 top-3 z-toast sm:left-auto sm:right-4 sm:w-[380px]", children: /* @__PURE__ */ jsx("div", { className: `pointer-events-auto rounded px-3 py-2 text-white shadow-md ${color}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
127
127
|
/* @__PURE__ */ jsx("div", { className: "text-sm", children: msg }),
|
|
128
128
|
/* @__PURE__ */ jsx(
|
|
129
129
|
IconButton,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/FlashMessages.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { X } from 'lucide-react'\nimport { IconButton } from '../primitives/icon-button'\n\nexport type FlashKind = 'success' | 'error' | 'warning' | 'info'\n\n// Programmatic API to show a flash message without navigation.\n// Consumers can import { flash } and call flash('text', 'error').\nexport function flash(message: string, type: FlashKind = 'info') {\n if (typeof window === 'undefined') return\n const evt = new CustomEvent('flash', { detail: { message, type } })\n window.dispatchEvent(evt)\n}\n\ntype HistoryMethod = History['pushState']\n\nfunction useLocationKey() {\n const [locationKey, setLocationKey] = React.useState(() => {\n if (typeof window === 'undefined') return ''\n return window.location.href\n })\n const locationKeyRef = React.useRef(locationKey)\n\n React.useEffect(() => {\n locationKeyRef.current = locationKey\n }, [locationKey])\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n\n let active = true\n const scheduleUpdate = (href: string) => {\n const run = () => {\n if (!active) return\n if (locationKeyRef.current === href) return\n locationKeyRef.current = href\n setLocationKey(href)\n }\n if (typeof queueMicrotask === 'function') {\n queueMicrotask(run)\n } else {\n setTimeout(run, 0)\n }\n }\n const updateLocation = () => {\n if (!active) return\n const href = window.location.href\n if (href === locationKeyRef.current) return\n scheduleUpdate(href)\n }\n\n const deferredUpdateLocation = () => {\n setTimeout(updateLocation, 0)\n }\n\n const originalPush: HistoryMethod = window.history.pushState.bind(window.history)\n const originalReplace: HistoryMethod = window.history.replaceState.bind(window.history)\n\n const pushState: HistoryMethod = (...args) => {\n originalPush(...args)\n deferredUpdateLocation()\n }\n\n const replaceState: HistoryMethod = (...args) => {\n originalReplace(...args)\n deferredUpdateLocation()\n }\n\n window.history.pushState = pushState\n window.history.replaceState = replaceState\n window.addEventListener('popstate', updateLocation)\n window.addEventListener('hashchange', updateLocation)\n updateLocation()\n\n return () => {\n active = false\n window.history.pushState = originalPush\n window.history.replaceState = originalReplace\n window.removeEventListener('popstate', updateLocation)\n window.removeEventListener('hashchange', updateLocation)\n }\n }, [])\n\n return locationKey\n}\n\nfunction FlashMessagesInner() {\n const [msg, setMsg] = React.useState<string | null>(null)\n const [kind, setKind] = React.useState<FlashKind>('info')\n const locationKey = useLocationKey()\n const dismissTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const clearDismissTimer = React.useCallback(() => {\n if (dismissTimerRef.current) {\n clearTimeout(dismissTimerRef.current)\n dismissTimerRef.current = null\n }\n }, [])\n\n const showFlash = React.useCallback((message: string, type: FlashKind) => {\n clearDismissTimer()\n setMsg(message)\n setKind(type)\n dismissTimerRef.current = setTimeout(() => {\n dismissTimerRef.current = null\n setMsg(null)\n }, 3000)\n }, [clearDismissTimer])\n\n React.useEffect(() => {\n return () => {\n clearDismissTimer()\n }\n }, [clearDismissTimer])\n\n // Read flash from URL on any navigation change (client-side too)\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const url = new URL(window.location.href)\n const message = url.searchParams.get('flash')\n const type = (url.searchParams.get('type') as FlashKind | null) || 'success'\n if (message) {\n showFlash(message, type)\n url.searchParams.delete('flash')\n url.searchParams.delete('type')\n window.history.replaceState({}, '', url.toString())\n }\n }, [locationKey, showFlash])\n\n // Listen for programmatic flash events\n React.useEffect(() => {\n const handler = (e: Event) => {\n const ce = e as CustomEvent<{ message?: string; type?: FlashKind }>\n const text = ce.detail?.message\n const t = ce.detail?.type || 'info'\n if (!text) return\n showFlash(text, t)\n }\n window.addEventListener('flash', handler as EventListener)\n return () => window.removeEventListener('flash', handler as EventListener)\n }, [showFlash])\n\n if (!msg) return null\n\n const colorMap: Record<FlashKind, string> = {\n success: 'bg-status-success-icon',\n error: 'bg-status-error-icon',\n warning: 'bg-status-warning-icon',\n info: 'bg-status-info-icon',\n }\n const color = colorMap[kind]\n\n return (\n <div className=\"pointer-events-none fixed left-3 right-3 top-3 z-
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { X } from 'lucide-react'\nimport { IconButton } from '../primitives/icon-button'\n\nexport type FlashKind = 'success' | 'error' | 'warning' | 'info'\n\n// Programmatic API to show a flash message without navigation.\n// Consumers can import { flash } and call flash('text', 'error').\nexport function flash(message: string, type: FlashKind = 'info') {\n if (typeof window === 'undefined') return\n const evt = new CustomEvent('flash', { detail: { message, type } })\n window.dispatchEvent(evt)\n}\n\ntype HistoryMethod = History['pushState']\n\nfunction useLocationKey() {\n const [locationKey, setLocationKey] = React.useState(() => {\n if (typeof window === 'undefined') return ''\n return window.location.href\n })\n const locationKeyRef = React.useRef(locationKey)\n\n React.useEffect(() => {\n locationKeyRef.current = locationKey\n }, [locationKey])\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n\n let active = true\n const scheduleUpdate = (href: string) => {\n const run = () => {\n if (!active) return\n if (locationKeyRef.current === href) return\n locationKeyRef.current = href\n setLocationKey(href)\n }\n if (typeof queueMicrotask === 'function') {\n queueMicrotask(run)\n } else {\n setTimeout(run, 0)\n }\n }\n const updateLocation = () => {\n if (!active) return\n const href = window.location.href\n if (href === locationKeyRef.current) return\n scheduleUpdate(href)\n }\n\n const deferredUpdateLocation = () => {\n setTimeout(updateLocation, 0)\n }\n\n const originalPush: HistoryMethod = window.history.pushState.bind(window.history)\n const originalReplace: HistoryMethod = window.history.replaceState.bind(window.history)\n\n const pushState: HistoryMethod = (...args) => {\n originalPush(...args)\n deferredUpdateLocation()\n }\n\n const replaceState: HistoryMethod = (...args) => {\n originalReplace(...args)\n deferredUpdateLocation()\n }\n\n window.history.pushState = pushState\n window.history.replaceState = replaceState\n window.addEventListener('popstate', updateLocation)\n window.addEventListener('hashchange', updateLocation)\n updateLocation()\n\n return () => {\n active = false\n window.history.pushState = originalPush\n window.history.replaceState = originalReplace\n window.removeEventListener('popstate', updateLocation)\n window.removeEventListener('hashchange', updateLocation)\n }\n }, [])\n\n return locationKey\n}\n\nfunction FlashMessagesInner() {\n const [msg, setMsg] = React.useState<string | null>(null)\n const [kind, setKind] = React.useState<FlashKind>('info')\n const locationKey = useLocationKey()\n const dismissTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const clearDismissTimer = React.useCallback(() => {\n if (dismissTimerRef.current) {\n clearTimeout(dismissTimerRef.current)\n dismissTimerRef.current = null\n }\n }, [])\n\n const showFlash = React.useCallback((message: string, type: FlashKind) => {\n clearDismissTimer()\n setMsg(message)\n setKind(type)\n dismissTimerRef.current = setTimeout(() => {\n dismissTimerRef.current = null\n setMsg(null)\n }, 3000)\n }, [clearDismissTimer])\n\n React.useEffect(() => {\n return () => {\n clearDismissTimer()\n }\n }, [clearDismissTimer])\n\n // Read flash from URL on any navigation change (client-side too)\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const url = new URL(window.location.href)\n const message = url.searchParams.get('flash')\n const type = (url.searchParams.get('type') as FlashKind | null) || 'success'\n if (message) {\n showFlash(message, type)\n url.searchParams.delete('flash')\n url.searchParams.delete('type')\n window.history.replaceState({}, '', url.toString())\n }\n }, [locationKey, showFlash])\n\n // Listen for programmatic flash events\n React.useEffect(() => {\n const handler = (e: Event) => {\n const ce = e as CustomEvent<{ message?: string; type?: FlashKind }>\n const text = ce.detail?.message\n const t = ce.detail?.type || 'info'\n if (!text) return\n showFlash(text, t)\n }\n window.addEventListener('flash', handler as EventListener)\n return () => window.removeEventListener('flash', handler as EventListener)\n }, [showFlash])\n\n if (!msg) return null\n\n const colorMap: Record<FlashKind, string> = {\n success: 'bg-status-success-icon',\n error: 'bg-status-error-icon',\n warning: 'bg-status-warning-icon',\n info: 'bg-status-info-icon',\n }\n const color = colorMap[kind]\n\n return (\n <div className=\"pointer-events-none fixed left-3 right-3 top-3 z-toast sm:left-auto sm:right-4 sm:w-[380px]\">\n <div className={`pointer-events-auto rounded px-3 py-2 text-white shadow-md ${color}`}>\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-sm\">{msg}</div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-white/90 hover:text-white hover:bg-white/10\"\n onClick={() => setMsg(null)}\n aria-label=\"Dismiss\"\n >\n <X size={16} />\n </IconButton>\n </div>\n </div>\n </div>\n )\n}\n\nexport function FlashMessages() {\n return (\n <React.Suspense fallback={null}>\n <FlashMessagesInner />\n </React.Suspense>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -109,7 +109,7 @@ function JsonBuilder({
|
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
111
|
placeholder: '{"key": "value"}',
|
|
112
|
-
className: "w-full rounded border px-3 py-2 min-h-[300px] text-sm font-mono focus:outline-none focus:ring-2 focus:ring-
|
|
112
|
+
className: "w-full rounded border px-3 py-2 min-h-[300px] text-sm font-mono focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
113
113
|
disabled
|
|
114
114
|
}
|
|
115
115
|
),
|
|
@@ -208,7 +208,7 @@ function JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }) {
|
|
|
208
208
|
{
|
|
209
209
|
value: type,
|
|
210
210
|
onChange: (e) => handleTypeChange(e.target.value),
|
|
211
|
-
className: "text-xs border rounded px-1 py-0.5 bg-muted text-foreground focus:ring-1 focus:ring-
|
|
211
|
+
className: "text-xs border rounded px-1 py-0.5 bg-muted text-foreground focus-visible:ring-1 focus-visible:ring-ring",
|
|
212
212
|
children: [
|
|
213
213
|
/* @__PURE__ */ jsx("option", { value: "string", children: "String" }),
|
|
214
214
|
/* @__PURE__ */ jsx("option", { value: "number", children: "Number" }),
|
|
@@ -222,7 +222,7 @@ function JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }) {
|
|
|
222
222
|
type === "string" && /* @__PURE__ */ jsx(
|
|
223
223
|
"input",
|
|
224
224
|
{
|
|
225
|
-
className: "flex-1 min-w-0 sm:min-w-[120px] text-sm border rounded px-2 py-0.5 focus:outline-none focus:border-
|
|
225
|
+
className: "flex-1 min-w-0 sm:min-w-[120px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring",
|
|
226
226
|
value: data,
|
|
227
227
|
onChange: (e) => onChange(e.target.value),
|
|
228
228
|
disabled: readOnly
|
|
@@ -232,7 +232,7 @@ function JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }) {
|
|
|
232
232
|
"input",
|
|
233
233
|
{
|
|
234
234
|
type: "number",
|
|
235
|
-
className: "flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus:outline-none focus:border-
|
|
235
|
+
className: "flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring",
|
|
236
236
|
value: data,
|
|
237
237
|
onChange: (e) => onChange(parseFloat(e.target.value) || 0),
|
|
238
238
|
disabled: readOnly
|
|
@@ -241,7 +241,7 @@ function JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }) {
|
|
|
241
241
|
type === "boolean" && /* @__PURE__ */ jsxs(
|
|
242
242
|
"select",
|
|
243
243
|
{
|
|
244
|
-
className: "flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus:outline-none focus:border-
|
|
244
|
+
className: "flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring",
|
|
245
245
|
value: String(data),
|
|
246
246
|
onChange: (e) => onChange(e.target.value === "true"),
|
|
247
247
|
disabled: readOnly,
|
|
@@ -272,7 +272,7 @@ function JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }) {
|
|
|
272
272
|
/* @__PURE__ */ jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsx(
|
|
273
273
|
"input",
|
|
274
274
|
{
|
|
275
|
-
className: "w-full sm:w-[100px] text-xs font-mono border-b border-transparent hover:border-gray-300 focus:border-
|
|
275
|
+
className: "w-full sm:w-[100px] text-xs font-mono border-b border-transparent hover:border-gray-300 focus-visible:border-ring bg-transparent focus-visible:outline-none text-right pr-1",
|
|
276
276
|
value: key,
|
|
277
277
|
onChange: (e) => handleKeyRename(key, e.target.value),
|
|
278
278
|
disabled: readOnly
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/JsonBuilder.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '../primitives/button'\nimport { IconButton } from '../primitives/icon-button'\nimport { Plus, Trash2, ChevronRight, ChevronDown, Code, LayoutList } from 'lucide-react'\n\nfunction cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(' ')\n}\n\nexport type JsonBuilderProps = {\n value: any\n onChange: (value: any) => void\n disabled?: boolean\n error?: string\n}\n\ntype JsonNodeType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null'\n\nfunction getJsonType(value: any): JsonNodeType {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'array'\n return typeof value as JsonNodeType\n}\n\nexport function JsonBuilder({\n value,\n onChange,\n disabled,\n error\n}: JsonBuilderProps) {\n const [mode, setMode] = React.useState<'raw' | 'builder'>('raw')\n const [rawString, setRawString] = React.useState(() => {\n if (value === null) return '{}'\n return typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value || '{}')\n })\n const [parseError, setParseError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n if (value === null) {\n if (!disabled) {\n onChange({})\n }\n setRawString('{}')\n setParseError(null)\n return\n }\n if (typeof value === 'object') {\n setRawString(JSON.stringify(value, null, 2))\n setParseError(null)\n }\n }, [value, disabled, onChange])\n\n const handleRawChange = (str: string) => {\n setRawString(str)\n try {\n if (str.trim() === '') {\n onChange({})\n setParseError(null)\n } else {\n const parsed = JSON.parse(str)\n onChange(parsed)\n setParseError(null)\n }\n } catch (e) {\n onChange(str)\n setParseError(\"Invalid JSON\")\n }\n }\n\n const switchToBuilder = () => {\n try {\n if (typeof value === 'string') {\n JSON.parse(value)\n }\n setMode('builder')\n } catch (e) {\n alert(\"Cannot switch to Builder mode: Invalid JSON\")\n }\n }\n\n return (\n <div className=\"space-y-4 border rounded-md p-4 bg-card\">\n <div className=\"flex items-center space-x-2 border-b pb-2 mb-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={cn(mode === 'raw' && \"bg-muted text-foreground\")}\n onClick={() => setMode('raw')}\n >\n <Code className=\"w-4 h-4\" />\n Raw JSON\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={cn(mode === 'builder' && \"bg-muted text-foreground\")}\n onClick={switchToBuilder}\n >\n <LayoutList className=\"w-4 h-4\" />\n Builder\n </Button>\n </div>\n\n {mode === 'raw' ? (\n <div className=\"space-y-2\">\n <textarea\n value={rawString}\n onChange={(e) => handleRawChange(e.target.value)}\n onBlur={() => {\n try {\n const parsed = JSON.parse(rawString)\n setRawString(JSON.stringify(parsed, null, 2))\n } catch { }\n }}\n placeholder='{\"key\": \"value\"}'\n className=\"w-full rounded border px-3 py-2 min-h-[300px] text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500\"\n disabled={disabled}\n />\n {parseError && (\n <div className=\"text-xs text-red-600\">Invalid JSON format</div>\n )}\n </div>\n ) : (\n <div className=\"min-h-[300px] text-sm overflow-x-auto\">\n {typeof value === 'object' && value !== null ? (\n <JsonNode\n data={value}\n onChange={onChange}\n readOnly={disabled}\n isRoot\n />\n ) : (\n <div className=\"text-muted-foreground italic p-4 text-center\">\n Value is not an object or array. Switch to Raw to edit.\n </div>\n )}\n </div>\n )}\n\n {error && <div className=\"text-xs text-red-600\">{error}</div>}\n </div>\n )\n}\n\ninterface JsonNodeProps {\n data: any\n onChange: (val: any) => void\n onDelete?: () => void\n readOnly?: boolean\n label?: string\n isRoot?: boolean\n}\n\nfunction JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }: JsonNodeProps) {\n const type = getJsonType(data)\n const isContainer = type === 'object' || type === 'array'\n const [collapsed, setCollapsed] = React.useState(false)\n\n const handleTypeChange = (newType: JsonNodeType) => {\n let newVal: any = ''\n switch (newType) {\n case 'string': newVal = \"\"; break;\n case 'number': newVal = 0; break;\n case 'boolean': newVal = false; break;\n case 'object': newVal = {}; break;\n case 'array': newVal = []; break;\n case 'null': newVal = null; break;\n }\n onChange(newVal)\n }\n\n const handleAddKey = () => {\n if (type === 'object') {\n const newKey = `newKey_${Object.keys(data).length}`\n onChange({ ...data, [newKey]: \"\" })\n } else if (type === 'array') {\n onChange([...data, \"\"])\n }\n }\n\n const handleChildChange = (key: string | number, newVal: any) => {\n if (type === 'object') {\n onChange({ ...data, [key]: newVal })\n } else if (type === 'array') {\n const arr = [...data]\n arr[Number(key)] = newVal\n onChange(arr)\n }\n }\n\n const handleKeyRename = (oldKey: string, newKey: string) => {\n if (oldKey === newKey) return\n const keys = Object.keys(data)\n const newData: any = {}\n keys.forEach(k => {\n if (k === oldKey) {\n newData[newKey] = data[k]\n } else {\n newData[k] = data[k]\n }\n })\n onChange(newData)\n }\n\n const handleChildDelete = (key: string | number) => {\n if (type === 'object') {\n const newData = { ...data }\n delete newData[key as string]\n onChange(newData)\n } else if (type === 'array') {\n onChange(data.filter((_: any, i: number) => i !== key))\n }\n }\n\n return (\n <div className={cn(\"pl-0\", !isRoot && \"pl-4 border-l border-border ml-1\")}>\n <div className=\"flex items-start gap-2 py-1 group\">\n\n {isContainer && (\n <IconButton type=\"button\" variant=\"ghost\" size=\"xs\" className=\"mt-1 text-muted-foreground hover:text-foreground\" onClick={() => setCollapsed(!collapsed)}>\n {collapsed ? <ChevronRight className=\"w-3 h-3\" /> : <ChevronDown className=\"w-3 h-3\" />}\n </IconButton>\n )}\n {!isContainer && !isRoot && <div className=\"w-3\" />} {/* Spacer */}\n\n {label !== undefined && !isRoot && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-muted-foreground font-mono\">\n {label}\n </span>\n <span className=\"text-muted-foreground text-xs\">:</span>\n </div>\n )}\n\n <div className=\"flex-1 flex gap-2 items-center flex-wrap\">\n\n {!readOnly && (\n <select\n value={type}\n onChange={(e) => handleTypeChange(e.target.value as JsonNodeType)}\n className=\"text-xs border rounded px-1 py-0.5 bg-muted text-foreground focus:ring-1 focus:ring-blue-500\"\n >\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n <option value=\"null\">Null</option>\n </select>\n )}\n\n {type === 'string' && (\n <input\n className=\"flex-1 min-w-0 sm:min-w-[120px] text-sm border rounded px-2 py-0.5 focus:outline-none focus:border-blue-500\"\n value={data}\n onChange={e => onChange(e.target.value)}\n disabled={readOnly}\n />\n )}\n {type === 'number' && (\n <input\n type=\"number\"\n className=\"flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus:outline-none focus:border-blue-500\"\n value={data}\n onChange={e => onChange(parseFloat(e.target.value) || 0)}\n disabled={readOnly}\n />\n )}\n {type === 'boolean' && (\n <select\n className=\"flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus:outline-none focus:border-blue-500\"\n value={String(data)}\n onChange={e => onChange(e.target.value === 'true')}\n disabled={readOnly}\n >\n <option value=\"true\">true</option>\n <option value=\"false\">false</option>\n </select>\n )}\n {type === 'null' && <span className=\"text-xs text-muted-foreground\">null</span>}\n {isContainer && (\n <span className=\"text-xs text-muted-foreground\">\n {type === 'object' ? `{ ${Object.keys(data).length} items }` : `[ ${data.length} items ]`}\n </span>\n )}\n\n {onDelete && !readOnly && (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className=\"text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity\"\n title=\"Remove item\"\n onClick={onDelete}\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </IconButton>\n )}\n </div>\n </div>\n\n {isContainer && !collapsed && (\n <div className=\"flex flex-col gap-1 w-full pl-2\">\n {type === 'object' && Object.entries(data).map(([key, val], idx) => (\n <div key={idx} className=\"flex\">\n <div className=\"pt-2\">\n {/* Key Renamer */}\n <input\n className=\"w-full sm:w-[100px] text-xs font-mono border-b border-transparent hover:border-gray-300 focus:border-blue-500 bg-transparent focus:outline-none text-right pr-1\"\n value={key}\n onChange={(e) => handleKeyRename(key, e.target.value)}\n disabled={readOnly}\n />\n </div>\n <div className=\"flex-1\">\n <JsonNode\n data={val}\n onChange={(v) => handleChildChange(key, v)}\n onDelete={() => handleChildDelete(key)}\n readOnly={readOnly}\n />\n </div>\n </div>\n ))}\n\n {type === 'array' && (data as any[]).map((val, idx) => (\n <JsonNode\n key={idx}\n label={String(idx)}\n data={val}\n onChange={(v) => handleChildChange(idx, v)}\n onDelete={() => handleChildDelete(idx)}\n readOnly={readOnly}\n />\n ))}\n\n {!readOnly && (\n <div className=\"pl-4 mt-1\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleAddKey}\n className=\"h-6 text-xs\"\n >\n <Plus className=\"w-3 h-3 mr-1\" />\n Add {type === 'object' ? 'Property' : 'Item'}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '../primitives/button'\nimport { IconButton } from '../primitives/icon-button'\nimport { Plus, Trash2, ChevronRight, ChevronDown, Code, LayoutList } from 'lucide-react'\n\nfunction cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(' ')\n}\n\nexport type JsonBuilderProps = {\n value: any\n onChange: (value: any) => void\n disabled?: boolean\n error?: string\n}\n\ntype JsonNodeType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null'\n\nfunction getJsonType(value: any): JsonNodeType {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'array'\n return typeof value as JsonNodeType\n}\n\nexport function JsonBuilder({\n value,\n onChange,\n disabled,\n error\n}: JsonBuilderProps) {\n const [mode, setMode] = React.useState<'raw' | 'builder'>('raw')\n const [rawString, setRawString] = React.useState(() => {\n if (value === null) return '{}'\n return typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value || '{}')\n })\n const [parseError, setParseError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n if (value === null) {\n if (!disabled) {\n onChange({})\n }\n setRawString('{}')\n setParseError(null)\n return\n }\n if (typeof value === 'object') {\n setRawString(JSON.stringify(value, null, 2))\n setParseError(null)\n }\n }, [value, disabled, onChange])\n\n const handleRawChange = (str: string) => {\n setRawString(str)\n try {\n if (str.trim() === '') {\n onChange({})\n setParseError(null)\n } else {\n const parsed = JSON.parse(str)\n onChange(parsed)\n setParseError(null)\n }\n } catch (e) {\n onChange(str)\n setParseError(\"Invalid JSON\")\n }\n }\n\n const switchToBuilder = () => {\n try {\n if (typeof value === 'string') {\n JSON.parse(value)\n }\n setMode('builder')\n } catch (e) {\n alert(\"Cannot switch to Builder mode: Invalid JSON\")\n }\n }\n\n return (\n <div className=\"space-y-4 border rounded-md p-4 bg-card\">\n <div className=\"flex items-center space-x-2 border-b pb-2 mb-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={cn(mode === 'raw' && \"bg-muted text-foreground\")}\n onClick={() => setMode('raw')}\n >\n <Code className=\"w-4 h-4\" />\n Raw JSON\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={cn(mode === 'builder' && \"bg-muted text-foreground\")}\n onClick={switchToBuilder}\n >\n <LayoutList className=\"w-4 h-4\" />\n Builder\n </Button>\n </div>\n\n {mode === 'raw' ? (\n <div className=\"space-y-2\">\n <textarea\n value={rawString}\n onChange={(e) => handleRawChange(e.target.value)}\n onBlur={() => {\n try {\n const parsed = JSON.parse(rawString)\n setRawString(JSON.stringify(parsed, null, 2))\n } catch { }\n }}\n placeholder='{\"key\": \"value\"}'\n className=\"w-full rounded border px-3 py-2 min-h-[300px] text-sm font-mono focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n disabled={disabled}\n />\n {parseError && (\n <div className=\"text-xs text-red-600\">Invalid JSON format</div>\n )}\n </div>\n ) : (\n <div className=\"min-h-[300px] text-sm overflow-x-auto\">\n {typeof value === 'object' && value !== null ? (\n <JsonNode\n data={value}\n onChange={onChange}\n readOnly={disabled}\n isRoot\n />\n ) : (\n <div className=\"text-muted-foreground italic p-4 text-center\">\n Value is not an object or array. Switch to Raw to edit.\n </div>\n )}\n </div>\n )}\n\n {error && <div className=\"text-xs text-red-600\">{error}</div>}\n </div>\n )\n}\n\ninterface JsonNodeProps {\n data: any\n onChange: (val: any) => void\n onDelete?: () => void\n readOnly?: boolean\n label?: string\n isRoot?: boolean\n}\n\nfunction JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }: JsonNodeProps) {\n const type = getJsonType(data)\n const isContainer = type === 'object' || type === 'array'\n const [collapsed, setCollapsed] = React.useState(false)\n\n const handleTypeChange = (newType: JsonNodeType) => {\n let newVal: any = ''\n switch (newType) {\n case 'string': newVal = \"\"; break;\n case 'number': newVal = 0; break;\n case 'boolean': newVal = false; break;\n case 'object': newVal = {}; break;\n case 'array': newVal = []; break;\n case 'null': newVal = null; break;\n }\n onChange(newVal)\n }\n\n const handleAddKey = () => {\n if (type === 'object') {\n const newKey = `newKey_${Object.keys(data).length}`\n onChange({ ...data, [newKey]: \"\" })\n } else if (type === 'array') {\n onChange([...data, \"\"])\n }\n }\n\n const handleChildChange = (key: string | number, newVal: any) => {\n if (type === 'object') {\n onChange({ ...data, [key]: newVal })\n } else if (type === 'array') {\n const arr = [...data]\n arr[Number(key)] = newVal\n onChange(arr)\n }\n }\n\n const handleKeyRename = (oldKey: string, newKey: string) => {\n if (oldKey === newKey) return\n const keys = Object.keys(data)\n const newData: any = {}\n keys.forEach(k => {\n if (k === oldKey) {\n newData[newKey] = data[k]\n } else {\n newData[k] = data[k]\n }\n })\n onChange(newData)\n }\n\n const handleChildDelete = (key: string | number) => {\n if (type === 'object') {\n const newData = { ...data }\n delete newData[key as string]\n onChange(newData)\n } else if (type === 'array') {\n onChange(data.filter((_: any, i: number) => i !== key))\n }\n }\n\n return (\n <div className={cn(\"pl-0\", !isRoot && \"pl-4 border-l border-border ml-1\")}>\n <div className=\"flex items-start gap-2 py-1 group\">\n\n {isContainer && (\n <IconButton type=\"button\" variant=\"ghost\" size=\"xs\" className=\"mt-1 text-muted-foreground hover:text-foreground\" onClick={() => setCollapsed(!collapsed)}>\n {collapsed ? <ChevronRight className=\"w-3 h-3\" /> : <ChevronDown className=\"w-3 h-3\" />}\n </IconButton>\n )}\n {!isContainer && !isRoot && <div className=\"w-3\" />} {/* Spacer */}\n\n {label !== undefined && !isRoot && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-muted-foreground font-mono\">\n {label}\n </span>\n <span className=\"text-muted-foreground text-xs\">:</span>\n </div>\n )}\n\n <div className=\"flex-1 flex gap-2 items-center flex-wrap\">\n\n {!readOnly && (\n <select\n value={type}\n onChange={(e) => handleTypeChange(e.target.value as JsonNodeType)}\n className=\"text-xs border rounded px-1 py-0.5 bg-muted text-foreground focus-visible:ring-1 focus-visible:ring-ring\"\n >\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n <option value=\"null\">Null</option>\n </select>\n )}\n\n {type === 'string' && (\n <input\n className=\"flex-1 min-w-0 sm:min-w-[120px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring\"\n value={data}\n onChange={e => onChange(e.target.value)}\n disabled={readOnly}\n />\n )}\n {type === 'number' && (\n <input\n type=\"number\"\n className=\"flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring\"\n value={data}\n onChange={e => onChange(parseFloat(e.target.value) || 0)}\n disabled={readOnly}\n />\n )}\n {type === 'boolean' && (\n <select\n className=\"flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring\"\n value={String(data)}\n onChange={e => onChange(e.target.value === 'true')}\n disabled={readOnly}\n >\n <option value=\"true\">true</option>\n <option value=\"false\">false</option>\n </select>\n )}\n {type === 'null' && <span className=\"text-xs text-muted-foreground\">null</span>}\n {isContainer && (\n <span className=\"text-xs text-muted-foreground\">\n {type === 'object' ? `{ ${Object.keys(data).length} items }` : `[ ${data.length} items ]`}\n </span>\n )}\n\n {onDelete && !readOnly && (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className=\"text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity\"\n title=\"Remove item\"\n onClick={onDelete}\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </IconButton>\n )}\n </div>\n </div>\n\n {isContainer && !collapsed && (\n <div className=\"flex flex-col gap-1 w-full pl-2\">\n {type === 'object' && Object.entries(data).map(([key, val], idx) => (\n <div key={idx} className=\"flex\">\n <div className=\"pt-2\">\n {/* Key Renamer */}\n <input\n className=\"w-full sm:w-[100px] text-xs font-mono border-b border-transparent hover:border-gray-300 focus-visible:border-ring bg-transparent focus-visible:outline-none text-right pr-1\"\n value={key}\n onChange={(e) => handleKeyRename(key, e.target.value)}\n disabled={readOnly}\n />\n </div>\n <div className=\"flex-1\">\n <JsonNode\n data={val}\n onChange={(v) => handleChildChange(key, v)}\n onDelete={() => handleChildDelete(key)}\n readOnly={readOnly}\n />\n </div>\n </div>\n ))}\n\n {type === 'array' && (data as any[]).map((val, idx) => (\n <JsonNode\n key={idx}\n label={String(idx)}\n data={val}\n onChange={(v) => handleChildChange(idx, v)}\n onDelete={() => handleChildDelete(idx)}\n readOnly={readOnly}\n />\n ))}\n\n {!readOnly && (\n <div className=\"pl-4 mt-1\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleAddKey}\n className=\"h-6 text-xs\"\n >\n <Plus className=\"w-3 h-3 mr-1\" />\n Add {type === 'object' ? 'Property' : 'Item'}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -10,7 +10,7 @@ const STEP_STYLES = {
|
|
|
10
10
|
completed: "border-emerald-500/30 bg-emerald-500/10 text-emerald-200"
|
|
11
11
|
};
|
|
12
12
|
const STATUS_STYLES = {
|
|
13
|
-
default: "border-border bg-background/
|
|
13
|
+
default: "border-border bg-background/80 text-foreground",
|
|
14
14
|
info: "border-primary/20 bg-primary/5 text-primary",
|
|
15
15
|
success: "border-emerald-500/20 bg-emerald-500/5 text-emerald-200",
|
|
16
16
|
warning: "border-amber-500/20 bg-amber-500/5 text-amber-200",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/NextStepCallout.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\n\ntype NextStepCalloutStep = {\n id: string\n label: React.ReactNode\n state?: 'pending' | 'active' | 'completed'\n}\n\ntype NextStepCalloutStatusTone = 'default' | 'info' | 'success' | 'warning' | 'danger' | 'muted'\n\ntype NextStepCalloutStatus = {\n tone?: NextStepCalloutStatusTone\n icon?: React.ReactNode\n label: React.ReactNode\n badge?: React.ReactNode\n progressValue?: number | null\n progressDescription?: React.ReactNode\n errorMessage?: React.ReactNode\n}\n\ntype NextStepCalloutProps = {\n icon?: React.ReactNode\n title: React.ReactNode\n description?: React.ReactNode\n steps?: NextStepCalloutStep[]\n actionLabel: React.ReactNode\n actionIcon?: React.ReactNode\n onAction?: () => void\n disabled?: boolean\n disabledMessage?: React.ReactNode\n busy?: boolean\n status?: NextStepCalloutStatus | null\n className?: string\n}\n\nconst STEP_STYLES: Record<NonNullable<NextStepCalloutStep['state']>, string> = {\n pending: '',\n active: 'border-primary/40 bg-primary/10 text-primary',\n completed: 'border-emerald-500/30 bg-emerald-500/10 text-emerald-200',\n}\n\nconst STATUS_STYLES: Record<NextStepCalloutStatusTone, string> = {\n default: 'border-border bg-background/
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\n\ntype NextStepCalloutStep = {\n id: string\n label: React.ReactNode\n state?: 'pending' | 'active' | 'completed'\n}\n\ntype NextStepCalloutStatusTone = 'default' | 'info' | 'success' | 'warning' | 'danger' | 'muted'\n\ntype NextStepCalloutStatus = {\n tone?: NextStepCalloutStatusTone\n icon?: React.ReactNode\n label: React.ReactNode\n badge?: React.ReactNode\n progressValue?: number | null\n progressDescription?: React.ReactNode\n errorMessage?: React.ReactNode\n}\n\ntype NextStepCalloutProps = {\n icon?: React.ReactNode\n title: React.ReactNode\n description?: React.ReactNode\n steps?: NextStepCalloutStep[]\n actionLabel: React.ReactNode\n actionIcon?: React.ReactNode\n onAction?: () => void\n disabled?: boolean\n disabledMessage?: React.ReactNode\n busy?: boolean\n status?: NextStepCalloutStatus | null\n className?: string\n}\n\nconst STEP_STYLES: Record<NonNullable<NextStepCalloutStep['state']>, string> = {\n pending: '',\n active: 'border-primary/40 bg-primary/10 text-primary',\n completed: 'border-emerald-500/30 bg-emerald-500/10 text-emerald-200',\n}\n\nconst STATUS_STYLES: Record<NextStepCalloutStatusTone, string> = {\n default: 'border-border bg-background/80 text-foreground',\n info: 'border-primary/20 bg-primary/5 text-primary',\n success: 'border-emerald-500/20 bg-emerald-500/5 text-emerald-200',\n warning: 'border-amber-500/20 bg-amber-500/5 text-amber-200',\n danger: 'border-destructive/20 bg-destructive/5 text-destructive',\n muted: 'border-zinc-500/20 bg-zinc-500/5 text-zinc-200',\n}\n\nexport function NextStepCallout({\n icon,\n title,\n description,\n steps,\n actionLabel,\n actionIcon,\n onAction,\n disabled = false,\n disabledMessage,\n busy = false,\n status,\n className,\n}: NextStepCalloutProps) {\n return (\n <div className={cn('rounded-xl border border-dashed border-primary/30 bg-primary/5 px-6 py-8', className)}>\n <div className=\"mx-auto flex max-w-2xl flex-col items-center text-center\">\n {icon ? (\n <div className=\"mb-4 flex h-14 w-14 items-center justify-center rounded-full border border-primary/30 bg-background text-primary\">\n {icon}\n </div>\n ) : null}\n <h4 className=\"text-lg font-semibold\">{title}</h4>\n {description ? (\n <p className=\"mt-2 text-sm text-muted-foreground\">{description}</p>\n ) : null}\n {steps && steps.length > 0 ? (\n <div className=\"mt-4 flex flex-wrap items-center justify-center gap-2\">\n {steps.map((step) => (\n <Badge\n key={step.id}\n variant=\"outline\"\n className={cn(step.state ? STEP_STYLES[step.state] : STEP_STYLES.pending)}\n >\n {step.label}\n </Badge>\n ))}\n </div>\n ) : null}\n <Button\n type=\"button\"\n size=\"lg\"\n className=\"mt-6 min-w-72\"\n onClick={onAction}\n disabled={disabled || busy}\n >\n {actionIcon}\n {actionLabel}\n </Button>\n {disabledMessage ? (\n <p className=\"mt-3 text-xs text-muted-foreground\">{disabledMessage}</p>\n ) : null}\n {status ? (\n <div className={cn('mt-4 w-full rounded-lg border px-4 py-3 text-left', STATUS_STYLES[status.tone ?? 'default'])}>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n {status.icon ? <span className=\"shrink-0\">{status.icon}</span> : null}\n <span>{status.label}</span>\n </div>\n {status.badge ? status.badge : null}\n </div>\n {typeof status.progressValue === 'number' ? (\n <Progress value={status.progressValue} className=\"mt-3 h-2\" />\n ) : busy ? (\n <div className=\"mt-3 h-2 rounded-full bg-secondary\" />\n ) : null}\n {status.progressDescription ? (\n <p className=\"mt-2 text-xs text-muted-foreground\">{status.progressDescription}</p>\n ) : null}\n {status.errorMessage ? (\n <p className=\"mt-2 text-xs text-destructive\">{status.errorMessage}</p>\n ) : null}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": ";AA0EU,cAqBF,YArBE;AAvEV,SAAS,UAAU;AACnB,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAmCzB,MAAM,cAAyE;AAAA,EAC7E,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AACb;AAEA,MAAM,gBAA2D;AAAA,EAC/D,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AACT;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AACF,GAAyB;AACvB,SACE,oBAAC,SAAI,WAAW,GAAG,4EAA4E,SAAS,GACtG,+BAAC,SAAI,WAAU,4DACZ;AAAA,WACC,oBAAC,SAAI,WAAU,oHACZ,gBACH,IACE;AAAA,IACJ,oBAAC,QAAG,WAAU,yBAAyB,iBAAM;AAAA,IAC5C,cACC,oBAAC,OAAE,WAAU,sCAAsC,uBAAY,IAC7D;AAAA,IACH,SAAS,MAAM,SAAS,IACvB,oBAAC,SAAI,WAAU,yDACZ,gBAAM,IAAI,CAAC,SACV;AAAA,MAAC;AAAA;AAAA,QAEC,SAAQ;AAAA,QACR,WAAW,GAAG,KAAK,QAAQ,YAAY,KAAK,KAAK,IAAI,YAAY,OAAO;AAAA,QAEvE,eAAK;AAAA;AAAA,MAJD,KAAK;AAAA,IAKZ,CACD,GACH,IACE;AAAA,IACJ;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS;AAAA,QACT,UAAU,YAAY;AAAA,QAErB;AAAA;AAAA,UACA;AAAA;AAAA;AAAA,IACH;AAAA,IACC,kBACC,oBAAC,OAAE,WAAU,sCAAsC,2BAAgB,IACjE;AAAA,IACH,SACC,qBAAC,SAAI,WAAW,GAAG,qDAAqD,cAAc,OAAO,QAAQ,SAAS,CAAC,GAC7G;AAAA,2BAAC,SAAI,WAAU,qDACb;AAAA,6BAAC,SAAI,WAAU,+CACZ;AAAA,iBAAO,OAAO,oBAAC,UAAK,WAAU,YAAY,iBAAO,MAAK,IAAU;AAAA,UACjE,oBAAC,UAAM,iBAAO,OAAM;AAAA,WACtB;AAAA,QACC,OAAO,QAAQ,OAAO,QAAQ;AAAA,SACjC;AAAA,MACC,OAAO,OAAO,kBAAkB,WAC/B,oBAAC,YAAS,OAAO,OAAO,eAAe,WAAU,YAAW,IAC1D,OACF,oBAAC,SAAI,WAAU,sCAAqC,IAClD;AAAA,MACH,OAAO,sBACN,oBAAC,OAAE,WAAU,sCAAsC,iBAAO,qBAAoB,IAC5E;AAAA,MACH,OAAO,eACN,oBAAC,OAAE,WAAU,iCAAiC,iBAAO,cAAa,IAChE;AAAA,OACN,IACE;AAAA,KACN,GACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -289,8 +289,8 @@ function PerspectiveSidebar({
|
|
|
289
289
|
if (!open) return null;
|
|
290
290
|
const isNew = mode.type === "new";
|
|
291
291
|
const isShare = mode.type === "share";
|
|
292
|
-
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-
|
|
293
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/
|
|
292
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-modal", children: [
|
|
293
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/20", onClick: () => onOpenChange(false), role: "presentation" }),
|
|
294
294
|
/* @__PURE__ */ jsxs("div", { className: "fixed right-0 top-0 h-full w-full sm:w-80 bg-background shadow-xl border-l flex flex-col", children: [
|
|
295
295
|
/* @__PURE__ */ jsx("div", { className: "flex items-center p-4 border-b", children: /* @__PURE__ */ jsxs(
|
|
296
296
|
Button,
|