@burdenoff/microfe-bigconsole 2026.622.1 → 2026.624.2
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/dist/bigconsole/BigConsoleRoutes.js +1 -1
- package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
- package/dist/bigconsole/components/DrilldownModal.js +4 -4
- package/dist/bigconsole/components/DrilldownModal.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +2 -2
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +28 -28
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkCard.js +4 -4
- package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDataViewer.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkDataViewer.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkTableViewer.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkTableViewer.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +25 -25
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedParsersPanel.js +1 -1
- package/dist/bigconsole/components/datasink/LinkedParsersPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +3 -3
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +2 -2
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +2 -2
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
- package/dist/bigconsole/components/parser/DataSinkSelector.js +3 -3
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ImportParserDialog.js +26 -26
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserCard.js +5 -5
- package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +6 -6
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserStatusBadge.js +1 -1
- package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserTypeBadge.js +1 -1
- package/dist/bigconsole/components/parser/ParserTypeBadge.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineCard.js +2 -2
- package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +1 -1
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +1 -1
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +11 -11
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +17 -17
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetConfigDialog.js +14 -14
- package/dist/bigconsole/components/widgets/WidgetConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetHeader.js +4 -4
- package/dist/bigconsole/components/widgets/WidgetHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetMenu.js +3 -3
- package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +266 -238
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +2 -2
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
- package/dist/bigconsole/components/widgets/custom/CustomWidget.js +5 -5
- package/dist/bigconsole/components/widgets/custom/CustomWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js +1 -1
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi/KPIWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kpi/KPIWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListItem.js +3 -3
- package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +3 -3
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js +1 -1
- package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +2 -2
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +12 -12
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressBar.js +2 -2
- package/dist/bigconsole/components/widgets/progress/ProgressBar.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +2 -2
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/states/EmptyState.js +1 -1
- package/dist/bigconsole/components/widgets/states/EmptyState.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableHeader.js +2 -2
- package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TablePagination.js +4 -4
- package/dist/bigconsole/components/widgets/table/TablePagination.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js +5 -5
- package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/text-widget/TextWidget.js +1 -1
- package/dist/bigconsole/components/widgets/text-widget/TextWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +3 -3
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js +2 -2
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js.map +1 -1
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +3 -3
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowCard.js +2 -2
- package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +1 -1
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +2 -2
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +2 -2
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +3 -3
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +2 -2
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +4 -4
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +9 -9
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WidgetDemoPage.js +2 -2
- package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +5 -5
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +12 -12
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BigConsoleRenderer.js +2 -2
- package/dist/bigconsole/renderers/adapters/BigConsoleRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +5 -5
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js +2 -2
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetMenu.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetMenu.tsx"],"sourcesContent":["/**\n * WidgetMenu Component\n *\n * Dropdown menu with widget actions (edit, duplicate, delete, etc.)\n */\n\nimport { type FC, type ReactElement, useState, useRef, useEffect, useCallback, memo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Widget } from '../../types';\nimport { useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetMenuProps {\n /** The widget data */\n widget: Widget;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n}\n\ninterface MenuItem {\n id: string;\n label: string;\n icon: ReactElement;\n action: () => void;\n disabled?: boolean;\n destructive?: boolean;\n divider?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetMenu: FC<WidgetMenuProps> = memo(function WidgetMenu({\n widget,\n isEditMode,\n onRefresh,\n onExportCSV,\n onExportPNG,\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });\n const menuRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n // Store actions\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const copyWidget = useWidgetStore((state) => state.copyWidget);\n const cutWidget = useWidgetStore((state) => state.cutWidget);\n const removeWidget = useWidgetStore((state) => state.removeWidget);\n const openConfigPanel = useWidgetStore((state) => state.openConfigPanel);\n\n // API operations - skipFetch since parent page fetches\n const {\n duplicateWidget: duplicateWidgetApi,\n deleteWidget: deleteWidgetApi,\n refreshWidgetData,\n } = useWidgetOperations(widget.pageId, widget.dashboardId, { skipFetch: true });\n\n // Close menu when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n menuRef.current &&\n !menuRef.current.contains(event.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n // Close menu on escape\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n buttonRef.current?.focus();\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen]);\n\n // Toggle menu with collision detection\n const toggleMenu = useCallback((event: React.MouseEvent) => {\n event.stopPropagation();\n setIsOpen((prev) => {\n if (!prev && buttonRef.current) {\n // Calculate position when opening with collision detection\n const rect = buttonRef.current.getBoundingClientRect();\n const menuWidth = 160;\n const menuHeight = 320; // Approximate max height of menu\n const collisionPadding = 10;\n\n // Calculate initial position\n let top = rect.bottom + 4;\n let left = rect.right - menuWidth;\n\n // Check right overflow - ensure menu doesn't go past viewport right edge\n if (left + menuWidth > window.innerWidth - collisionPadding) {\n left = window.innerWidth - menuWidth - collisionPadding;\n }\n\n // Check left overflow - ensure menu doesn't go past viewport left edge\n if (left < collisionPadding) {\n left = collisionPadding;\n }\n\n // Check bottom overflow - flip to top if needed\n if (top + menuHeight > window.innerHeight - collisionPadding) {\n top = rect.top - menuHeight - 4;\n // If still overflows at top, position at top of viewport\n if (top < collisionPadding) {\n top = collisionPadding;\n }\n }\n\n setMenuPosition({\n top: top + window.scrollY,\n left: left + window.scrollX,\n });\n }\n return !prev;\n });\n }, []);\n\n // Menu items\n const menuItems: MenuItem[] = [\n {\n id: 'edit',\n label: 'Edit Widget',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n ),\n action: () => {\n selectWidget(widget.id);\n openConfigPanel();\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'refresh',\n label: 'Refresh Data',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n if (onRefresh) {\n onRefresh();\n } else {\n await refreshWidgetData(widget.id);\n }\n },\n disabled: false,\n },\n {\n id: 'exportCSV',\n label: 'Export CSV',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportCSV?.();\n },\n disabled: !onExportCSV,\n },\n {\n id: 'exportPNG',\n label: 'Export PNG',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportPNG?.();\n },\n disabled: !onExportPNG,\n },\n {\n id: 'divider1',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'copy',\n label: 'Copy',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n copyWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'cut',\n label: 'Cut',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z\"\n />\n </svg>\n ),\n action: () => {\n cutWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'duplicate',\n label: 'Duplicate',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n // Calculate new position (offset from original)\n const newPosition = {\n x: (widget.positionX ?? 0) + 1,\n y: (widget.positionY ?? 0) + 1,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n };\n await duplicateWidgetApi(widget.id, newPosition, `${widget.title} (Copy)`);\n },\n disabled: !isEditMode,\n },\n {\n id: 'divider2',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'delete',\n label: 'Delete',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n ),\n action: async () => {\n if (window.confirm('Are you sure you want to delete this widget?')) {\n setIsOpen(false);\n const success = await deleteWidgetApi(widget.id);\n if (!success) {\n // Fallback to local store removal if API fails\n removeWidget(widget.id);\n }\n } else {\n setIsOpen(false);\n }\n },\n disabled: !isEditMode,\n destructive: true,\n },\n ];\n\n return (\n <div className=\"relative\">\n {/* Menu Button */}\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={toggleMenu}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-muted\n transition-colors\n ${isOpen ? 'bg-bg-muted' : ''}\n `}\n title=\"Widget options\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <circle cx=\"12\" cy=\"19\" r=\"2\" />\n </svg>\n </button>\n\n {/* Dropdown Menu - Rendered via Portal to avoid overflow clipping */}\n {isOpen &&\n createPortal(\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: menuPosition.top,\n left: menuPosition.left,\n zIndex: 99999,\n }}\n className=\"\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n \"\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {menuItems.map((item) => {\n if (item.divider) {\n return <div key={item.id} className=\"my-1 border-t border-border-default\" />;\n }\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n item.action();\n }}\n disabled={item.disabled}\n className={`\n w-full flex items-center gap-2\n px-3 py-2 text-left text-sm\n transition-colors\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-bg-muted'}\n ${item.destructive ? 'text-state-error hover:text-state-error' : 'text-text-primary'}\n `}\n role=\"menuitem\"\n >\n {item.icon}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>,\n document.body\n )}\n </div>\n );\n});\n\nexport default WidgetMenu;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetMenu.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetMenu.tsx"],"sourcesContent":["/**\n * WidgetMenu Component\n *\n * Dropdown menu with widget actions (edit, duplicate, delete, etc.)\n */\n\nimport { type FC, type ReactElement, useState, useRef, useEffect, useCallback, memo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Widget } from '../../types';\nimport { useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetMenuProps {\n /** The widget data */\n widget: Widget;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n}\n\ninterface MenuItem {\n id: string;\n label: string;\n icon: ReactElement;\n action: () => void;\n disabled?: boolean;\n destructive?: boolean;\n divider?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetMenu: FC<WidgetMenuProps> = memo(function WidgetMenu({\n widget,\n isEditMode,\n onRefresh,\n onExportCSV,\n onExportPNG,\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });\n const menuRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n // Store actions\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const copyWidget = useWidgetStore((state) => state.copyWidget);\n const cutWidget = useWidgetStore((state) => state.cutWidget);\n const removeWidget = useWidgetStore((state) => state.removeWidget);\n const openConfigPanel = useWidgetStore((state) => state.openConfigPanel);\n\n // API operations - skipFetch since parent page fetches\n const {\n duplicateWidget: duplicateWidgetApi,\n deleteWidget: deleteWidgetApi,\n refreshWidgetData,\n } = useWidgetOperations(widget.pageId, widget.dashboardId, { skipFetch: true });\n\n // Close menu when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n menuRef.current &&\n !menuRef.current.contains(event.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n // Close menu on escape\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n buttonRef.current?.focus();\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen]);\n\n // Toggle menu with collision detection\n const toggleMenu = useCallback((event: React.MouseEvent) => {\n event.stopPropagation();\n setIsOpen((prev) => {\n if (!prev && buttonRef.current) {\n // Calculate position when opening with collision detection\n const rect = buttonRef.current.getBoundingClientRect();\n const menuWidth = 160;\n const menuHeight = 320; // Approximate max height of menu\n const collisionPadding = 10;\n\n // Calculate initial position\n let top = rect.bottom + 4;\n let left = rect.right - menuWidth;\n\n // Check right overflow - ensure menu doesn't go past viewport right edge\n if (left + menuWidth > window.innerWidth - collisionPadding) {\n left = window.innerWidth - menuWidth - collisionPadding;\n }\n\n // Check left overflow - ensure menu doesn't go past viewport left edge\n if (left < collisionPadding) {\n left = collisionPadding;\n }\n\n // Check bottom overflow - flip to top if needed\n if (top + menuHeight > window.innerHeight - collisionPadding) {\n top = rect.top - menuHeight - 4;\n // If still overflows at top, position at top of viewport\n if (top < collisionPadding) {\n top = collisionPadding;\n }\n }\n\n setMenuPosition({\n top: top + window.scrollY,\n left: left + window.scrollX,\n });\n }\n return !prev;\n });\n }, []);\n\n // Menu items\n const menuItems: MenuItem[] = [\n {\n id: 'edit',\n label: 'Edit Widget',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n ),\n action: () => {\n selectWidget(widget.id);\n openConfigPanel();\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'refresh',\n label: 'Refresh Data',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n if (onRefresh) {\n onRefresh();\n } else {\n await refreshWidgetData(widget.id);\n }\n },\n disabled: false,\n },\n {\n id: 'exportCSV',\n label: 'Export CSV',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportCSV?.();\n },\n disabled: !onExportCSV,\n },\n {\n id: 'exportPNG',\n label: 'Export PNG',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportPNG?.();\n },\n disabled: !onExportPNG,\n },\n {\n id: 'divider1',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'copy',\n label: 'Copy',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n copyWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'cut',\n label: 'Cut',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z\"\n />\n </svg>\n ),\n action: () => {\n cutWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'duplicate',\n label: 'Duplicate',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n // Calculate new position (offset from original)\n const newPosition = {\n x: (widget.positionX ?? 0) + 1,\n y: (widget.positionY ?? 0) + 1,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n };\n await duplicateWidgetApi(widget.id, newPosition, `${widget.title} (Copy)`);\n },\n disabled: !isEditMode,\n },\n {\n id: 'divider2',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'delete',\n label: 'Delete',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n ),\n action: async () => {\n if (window.confirm('Are you sure you want to delete this widget?')) {\n setIsOpen(false);\n const success = await deleteWidgetApi(widget.id);\n if (!success) {\n // Fallback to local store removal if API fails\n removeWidget(widget.id);\n }\n } else {\n setIsOpen(false);\n }\n },\n disabled: !isEditMode,\n destructive: true,\n },\n ];\n\n return (\n <div className=\"relative\">\n {/* Menu Button */}\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={toggleMenu}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n ${isOpen ? 'bg-bg-sunken' : ''}\n `}\n title=\"Widget options\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <circle cx=\"12\" cy=\"19\" r=\"2\" />\n </svg>\n </button>\n\n {/* Dropdown Menu - Rendered via Portal to avoid overflow clipping */}\n {isOpen &&\n createPortal(\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: menuPosition.top,\n left: menuPosition.left,\n zIndex: 99999,\n }}\n className=\"\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n \"\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {menuItems.map((item) => {\n if (item.divider) {\n return <div key={item.id} className=\"my-1 border-t border-border-default\" />;\n }\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n item.action();\n }}\n disabled={item.disabled}\n className={`\n w-full flex items-center gap-2\n px-3 py-2 text-left text-sm\n transition-colors\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-bg-sunken'}\n ${item.destructive ? 'text-state-error hover:text-state-error' : 'text-text-primary'}\n `}\n role=\"menuitem\"\n >\n {item.icon}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>,\n document.body\n )}\n </div>\n );\n});\n\nexport default WidgetMenu;\n"],"mappings":";;;;;;;;AA2CA,IAAa,IAAkC,EAAK,SAAoB,EACtE,WACA,eACA,cACA,gBACA,kBACC;CACD,IAAM,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAc,KAAmB,EAAS;EAAE,KAAK;EAAG,MAAM;EAAG,CAAC,EAC/D,IAAU,EAAuB,KAAK,EACtC,IAAY,EAA0B,KAAK,EAG3C,IAAe,GAAgB,MAAU,EAAM,aAAa,EAC5D,IAAa,GAAgB,MAAU,EAAM,WAAW,EACxD,IAAY,GAAgB,MAAU,EAAM,UAAU,EACtD,IAAe,GAAgB,MAAU,EAAM,aAAa,EAC5D,IAAkB,GAAgB,MAAU,EAAM,gBAAgB,EAGlE,EACJ,iBAAiB,GACjB,cAAc,GACd,yBACE,EAAoB,EAAO,QAAQ,EAAO,aAAa,EAAE,WAAW,IAAM,CAAC;AAyB/E,CAtBA,QAAgB;EACd,IAAM,KAAsB,MAAsB;AAChD,GACE,EAAQ,WACR,CAAC,EAAQ,QAAQ,SAAS,EAAM,OAAe,IAC/C,EAAU,WACV,CAAC,EAAU,QAAQ,SAAS,EAAM,OAAe,IAEjD,EAAU,GAAM;;AAQpB,SAJI,KACF,SAAS,iBAAiB,aAAa,EAAmB,QAG/C;AACX,YAAS,oBAAoB,aAAa,EAAmB;;IAE9D,CAAC,EAAO,CAAC,EAGZ,QAAgB;EACd,IAAM,KAAgB,MAAyB;AAC7C,GAAI,EAAM,QAAQ,aAChB,EAAU,GAAM,EAChB,EAAU,SAAS,OAAO;;AAQ9B,SAJI,KACF,SAAS,iBAAiB,WAAW,EAAa,QAGvC;AACX,YAAS,oBAAoB,WAAW,EAAa;;IAEtD,CAAC,EAAO,CAAC;CAGZ,IAAM,IAAa,GAAa,MAA4B;AAE1D,EADA,EAAM,iBAAiB,EACvB,GAAW,MAAS;AAClB,OAAI,CAAC,KAAQ,EAAU,SAAS;IAE9B,IAAM,IAAO,EAAU,QAAQ,uBAAuB,EAMlD,IAAM,EAAK,SAAS,GACpB,IAAO,EAAK,QAAQ;AAqBxB,IAlBI,IAAO,MAAY,OAAO,aAAa,OACzC,IAAO,OAAO,aAAa,MAAY,KAIrC,IAAO,OACT,IAAO,KAIL,IAAM,MAAa,OAAO,cAAc,OAC1C,IAAM,EAAK,MAAM,MAAa,GAE1B,IAAM,OACR,IAAM,MAIV,EAAgB;KACd,KAAK,IAAM,OAAO;KAClB,MAAM,IAAO,OAAO;KACrB,CAAC;;AAEJ,UAAO,CAAC;IACR;IACD,EAAE,CAAC,EAGA,IAAwB;EAC5B;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,cAAc;AAGZ,IAFA,EAAa,EAAO,GAAG,EACvB,GAAiB,EACjB,EAAU,GAAM;;GAElB,UAAU,CAAC;GACZ;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,QAAQ,YAAY;AAElB,IADA,EAAU,GAAM,EACZ,IACF,GAAW,GAEX,MAAM,EAAkB,EAAO,GAAG;;GAGtC,UAAU;GACX;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,cAAc;AAEZ,IADA,EAAU,GAAM,EAChB,KAAe;;GAEjB,UAAU,CAAC;GACZ;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,cAAc;AAEZ,IADA,EAAU,GAAM,EAChB,KAAe;;GAEjB,UAAU,CAAC;GACZ;EACD;GACE,IAAI;GACJ,OAAO;GACP,MAAM,kBAAA,GAAA,EAAK,CAAA;GACX,cAAc;GACd,SAAS;GACV;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,cAAc;AAEZ,IADA,EAAW,EAAO,GAAG,EACrB,EAAU,GAAM;;GAElB,UAAU,CAAC;GACZ;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,cAAc;AAEZ,IADA,EAAU,EAAO,GAAG,EACpB,EAAU,GAAM;;GAElB,UAAU,CAAC;GACZ;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,QAAQ,YAAY;AAClB,MAAU,GAAM;IAEhB,IAAM,IAAc;KAClB,IAAI,EAAO,aAAa,KAAK;KAC7B,IAAI,EAAO,aAAa,KAAK;KAC7B,OAAO,EAAO,iBAAiB;KAC/B,QAAQ,EAAO,kBAAkB;KAClC;AACD,UAAM,EAAmB,EAAO,IAAI,GAAa,GAAG,EAAO,MAAM,SAAS;;GAE5E,UAAU,CAAC;GACZ;EACD;GACE,IAAI;GACJ,OAAO;GACP,MAAM,kBAAA,GAAA,EAAK,CAAA;GACX,cAAc;GACd,SAAS;GACV;EACD;GACE,IAAI;GACJ,OAAO;GACP,MACE,kBAAC,OAAD;IAAK,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,WAAU;cACnE,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA;GAER,QAAQ,YAAY;AAClB,IAAI,OAAO,QAAQ,+CAA+C,IAChE,EAAU,GAAM,EACA,MAAM,EAAgB,EAAO,GAAG,IAG9C,EAAa,EAAO,GAAG,IAGzB,EAAU,GAAM;;GAGpB,UAAU,CAAC;GACX,aAAa;GACd;EACF;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,UAAD;GACE,KAAK;GACL,MAAK;GACL,SAAS;GACT,WAAW;;;;;;YAMP,IAAS,iBAAiB,GAAG;;GAEjC,OAAM;GACN,iBAAc;GACd,iBAAe;aAEf,kBAAC,OAAD;IAAK,WAAU;IAAU,MAAK;IAAe,SAAQ;cAArD;KACE,kBAAC,UAAD;MAAQ,IAAG;MAAK,IAAG;MAAI,GAAE;MAAM,CAAA;KAC/B,kBAAC,UAAD;MAAQ,IAAG;MAAK,IAAG;MAAK,GAAE;MAAM,CAAA;KAChC,kBAAC,UAAD;MAAQ,IAAG;MAAK,IAAG;MAAK,GAAE;MAAM,CAAA;KAC5B;;GACC,CAAA,EAGR,KACC,EACE,kBAAC,OAAD;GACE,KAAK;GACL,OAAO;IACL,UAAU;IACV,KAAK,EAAa;IAClB,MAAM,EAAa;IACnB,QAAQ;IACT;GACD,WAAU;GAOV,MAAK;GACL,oBAAiB;aAEhB,EAAU,KAAK,MACV,EAAK,UACA,kBAAC,OAAD,EAAmB,WAAU,uCAAwC,EAA3D,EAAK,GAAsD,GAI5E,kBAAC,UAAD;IAEE,MAAK;IACL,UAAU,MAAM;AAEd,KADA,EAAE,iBAAiB,EACnB,EAAK,QAAQ;;IAEf,UAAU,EAAK;IACf,WAAW;;;;oBAIT,EAAK,WAAW,kCAAkC,qBAAqB;oBACvE,EAAK,cAAc,4CAA4C,oBAAoB;;IAErF,MAAK;cAfP,CAiBG,EAAK,MACN,kBAAC,QAAD,EAAA,UAAO,EAAK,OAAa,CAAA,CAClB;MAlBF,EAAK,GAkBH,CAEX;GACE,CAAA,EACN,SAAS,KACV,CACC;;EAER"}
|