(this.element = element)} className=\"panel-height-helper\" />}\n \n );\n }\n}\n\nconst mapStateToProps: MapStateToProps
= (state, props) => {\n return {\n angularComponent: getPanelStateForModel(state, props.panel)?.angularComponent,\n };\n};\n\nconst mapDispatchToProps: MapDispatchToProps = { setPanelAngularComponent };\n\nexport const PanelChromeAngular = connect(mapStateToProps, mapDispatchToProps)(PanelChromeAngularUnconnected);\n","import {\n ConfigOverrideRule,\n DynamicConfigValue,\n FieldColorModeId,\n FieldConfigSource,\n FieldMatcherID,\n} from '@grafana/data';\n\nexport const changeSeriesColorConfigFactory = (\n label: string,\n color: string,\n fieldConfig: FieldConfigSource\n): FieldConfigSource => {\n const { overrides } = fieldConfig;\n const currentIndex = fieldConfig.overrides.findIndex((override) => {\n return override.matcher.id === FieldMatcherID.byName && override.matcher.options === label;\n });\n\n if (currentIndex < 0) {\n return {\n ...fieldConfig,\n overrides: [...fieldConfig.overrides, createOverride(label, color)],\n };\n }\n\n const overridesCopy = Array.from(overrides);\n const existing = overridesCopy[currentIndex];\n const propertyIndex = existing.properties.findIndex((p) => p.id === 'color');\n\n if (propertyIndex < 0) {\n overridesCopy[currentIndex] = {\n ...existing,\n properties: [...existing.properties, createProperty(color)],\n };\n\n return {\n ...fieldConfig,\n overrides: overridesCopy,\n };\n }\n\n const propertiesCopy = Array.from(existing.properties);\n propertiesCopy[propertyIndex] = createProperty(color);\n\n overridesCopy[currentIndex] = {\n ...existing,\n properties: propertiesCopy,\n };\n\n return {\n ...fieldConfig,\n overrides: overridesCopy,\n };\n};\n\nconst createOverride = (label: string, color: string): ConfigOverrideRule => {\n return {\n matcher: {\n id: FieldMatcherID.byName,\n options: label,\n },\n properties: [createProperty(color)],\n };\n};\n\nconst createProperty = (color: string): DynamicConfigValue => {\n return {\n id: 'color',\n value: {\n mode: FieldColorModeId.Fixed,\n fixedColor: color,\n },\n };\n};\n","export enum PanelLogEvents {\n FIELD_CONFIG_OVERRIDES_CHANGED_EVENT = 'field config overrides changed',\n NEW_PANEL_OPTION_EVENT = 'new panel option',\n PANEL_OPTION_CHANGED_EVENT = 'panel option changed',\n NEW_DEFAULT_FIELD_CONFIG_EVENT = 'new default field config',\n DEFAULT_FIELD_CONFIG_CHANGED_EVENT = 'default field config changed',\n NEW_CUSTOM_FIELD_CONFIG_EVENT = 'new custom field config',\n CUSTOM_FIELD_CONFIG_CHANGED_EVENT = 'custom field config changed',\n MEASURE_PANEL_LOAD_TIME_EVENT = 'measure panel load time',\n THRESHOLDS_COUNT_CHANGED_EVENT = 'thresholds count changed',\n THRESHOLDS_MODE_CHANGED_EVENT = 'thresholds mode changed',\n MAPPINGS_COUNT_CHANGED_EVENT = 'mappings count changed',\n LINKS_COUNT_CHANGED_EVENT = 'links count changed',\n PANEL_ERROR = 'panel error',\n}\n\nexport const FIELD_CONFIG_OVERRIDES_KEY = 'overrides';\nexport const FIELD_CONFIG_CUSTOM_KEY = 'custom';\n","import { useEffect } from 'react';\n\nimport { faro } from '@grafana/faro-web-sdk';\nimport { config } from 'app/core/config';\nimport { PanelLogEvents } from 'app/core/log_events';\n\ninterface Props {\n panelType: string;\n panelId: number;\n panelTitle: string;\n}\n\nexport const PanelLoadTimeMonitor = (props: Props) => {\n const startLoadTime = performance.now();\n\n useEffect(() => {\n if (!config.grafanaJavascriptAgent.enabled) {\n return;\n }\n\n // This code will be run ASAP after Style and Layout information have\n // been calculated and the paint has occurred.\n // https://firefox-source-docs.mozilla.org/performance/bestpractices.html\n requestAnimationFrame(() => {\n setTimeout(() => {\n faro.api.pushMeasurement(\n {\n type: PanelLogEvents.MEASURE_PANEL_LOAD_TIME_EVENT,\n values: {\n start_loading_time_ms: startLoadTime,\n load_time_ms: performance.now() - startLoadTime,\n },\n },\n {\n context: {\n panel_type: props.panelType,\n panel_id: String(props.panelId),\n panel_title: props.panelTitle,\n },\n }\n );\n }, 0);\n });\n\n return;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return null;\n};\n","import { FieldConfigSource } from '@grafana/data';\nimport { faro } from '@grafana/faro-web-sdk';\nimport { FIELD_CONFIG_CUSTOM_KEY, FIELD_CONFIG_OVERRIDES_KEY, PanelLogEvents } from 'app/core/log_events';\n\ninterface PanelLogInfo {\n panelId: string;\n panelType: string;\n panelTitle: string;\n}\n\nexport class PanelOptionsLogger {\n private initialPanelOptions: unknown;\n private initialFieldConfig: FieldConfigSource;\n private panelLogInfo: PanelLogInfo;\n\n constructor(initialPanelOptions: unknown, initialFieldConfig: FieldConfigSource, panelLogInfo: PanelLogInfo) {\n this.initialPanelOptions = initialPanelOptions;\n this.initialFieldConfig = initialFieldConfig;\n this.panelLogInfo = panelLogInfo;\n }\n\n logChanges = (latestPanelOptions: unknown, latestFieldConfig: FieldConfigSource) => {\n this.logPanelOptionChanges(latestPanelOptions, this.initialPanelOptions);\n this.logFieldConfigChanges(latestFieldConfig, this.initialFieldConfig);\n\n //set the old values to the current values for next log diff\n this.initialPanelOptions = latestPanelOptions;\n this.initialFieldConfig = latestFieldConfig;\n };\n\n logPanelEvent = (eventName: string, newKey: string, newVal: string, oldVal?: string) => {\n const logObj = {\n key: newKey,\n newValue: newVal,\n oldValue: oldVal ?? '',\n panelTitle: this.panelLogInfo.panelTitle,\n panelId: this.panelLogInfo.panelId,\n panelType: this.panelLogInfo.panelType,\n };\n\n faro.api.pushEvent(eventName, logObj);\n };\n\n logPanelOptionChanges = (panelOptions: unknown, oldPanelOptions: unknown) => {\n if (typeof panelOptions !== 'object' || panelOptions === null) {\n return;\n }\n\n if (typeof oldPanelOptions !== 'object' || oldPanelOptions === null) {\n return;\n }\n\n const oldPanelOptionsUnknown: { [key: string]: unknown } = { ...oldPanelOptions };\n\n for (const [key, value] of Object.entries(panelOptions)) {\n const newValue: string = typeof value !== 'string' ? JSON.stringify(value) : value;\n const oldValue: string =\n typeof value !== 'string' ? JSON.stringify(oldPanelOptionsUnknown[key]) : String(oldPanelOptionsUnknown[key]);\n\n if (oldPanelOptionsUnknown[key] === undefined) {\n this.logPanelEvent(PanelLogEvents.NEW_PANEL_OPTION_EVENT, key, newValue);\n } else if (oldValue !== newValue) {\n this.logPanelEvent(PanelLogEvents.PANEL_OPTION_CHANGED_EVENT, key, newValue, oldValue);\n }\n }\n };\n\n logFieldConfigChanges = (fieldConfig: FieldConfigSource, oldFieldConfig: FieldConfigSource) => {\n // overrides are an array of objects, so stringify it all and log changes\n // in lack of an index, we can't tell which override changed\n const oldOverridesStr = JSON.stringify(oldFieldConfig.overrides);\n const newOverridesStr = JSON.stringify(fieldConfig.overrides);\n if (oldOverridesStr !== newOverridesStr) {\n this.logPanelEvent(\n PanelLogEvents.FIELD_CONFIG_OVERRIDES_CHANGED_EVENT,\n FIELD_CONFIG_OVERRIDES_KEY,\n newOverridesStr,\n oldOverridesStr\n );\n }\n\n const oldDefaults: { [key: string]: unknown } = { ...oldFieldConfig.defaults };\n\n // go through field config keys except custom, we treat that below\n for (const [key, value] of Object.entries(fieldConfig.defaults)) {\n if (key === FIELD_CONFIG_CUSTOM_KEY) {\n continue;\n }\n\n const newValue: string = typeof value !== 'string' ? JSON.stringify(value) : value;\n const oldValue: string = typeof value !== 'string' ? JSON.stringify(oldDefaults[key]) : String(oldDefaults[key]);\n\n if (oldDefaults[key] === undefined) {\n this.logPanelEvent(PanelLogEvents.NEW_DEFAULT_FIELD_CONFIG_EVENT, key, newValue);\n } else if (oldValue !== newValue) {\n this.logPanelEvent(PanelLogEvents.DEFAULT_FIELD_CONFIG_CHANGED_EVENT, key, newValue, oldValue);\n }\n }\n\n if (!fieldConfig.defaults.custom || oldDefaults.custom === undefined) {\n return;\n }\n\n const oldCustom: { [key: string]: unknown } = { ...oldDefaults.custom };\n\n // go through custom field config keys\n for (const [key, value] of Object.entries(fieldConfig.defaults.custom)) {\n if (oldDefaults.custom === null || oldCustom[key] === null) {\n continue;\n }\n\n const newValue: string = typeof value !== 'string' ? JSON.stringify(value) : value;\n const oldValue: string = typeof value !== 'string' ? JSON.stringify(oldCustom[key]) : String(oldCustom[key]);\n\n if (oldCustom[key] === undefined) {\n this.logPanelEvent(PanelLogEvents.NEW_CUSTOM_FIELD_CONFIG_EVENT, key, newValue);\n } else if (oldValue !== newValue) {\n this.logPanelEvent(PanelLogEvents.CUSTOM_FIELD_CONFIG_CHANGED_EVENT, key, newValue, oldValue);\n }\n }\n };\n}\n","import React, { PureComponent } from 'react';\nimport { Subscription } from 'rxjs';\n\nimport {\n AbsoluteTimeRange,\n AnnotationChangeEvent,\n AnnotationEventUIModel,\n CoreApp,\n DashboardCursorSync,\n DataFrame,\n EventFilterOptions,\n FieldConfigSource,\n getDataSourceRef,\n getDefaultTimeRange,\n LoadingState,\n PanelData,\n PanelPlugin,\n PanelPluginMeta,\n TimeRange,\n toDataFrameDTO,\n toUtc,\n} from '@grafana/data';\nimport { RefreshEvent } from '@grafana/runtime';\nimport { VizLegendOptions } from '@grafana/schema';\nimport {\n ErrorBoundary,\n PanelChrome,\n PanelContext,\n PanelContextProvider,\n SeriesVisibilityChangeMode,\n AdHocFilterItem,\n} from '@grafana/ui';\nimport config from 'app/core/config';\nimport { profiler } from 'app/core/profiler';\nimport { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';\nimport { getDatasourceSrv } from 'app/features/plugins/datasource_srv';\nimport { applyFilterFromTable } from 'app/features/variables/adhoc/actions';\nimport { onUpdatePanelSnapshotData } from 'app/plugins/datasource/grafana/utils';\nimport { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory';\nimport { dispatch } from 'app/store/store';\nimport { RenderEvent } from 'app/types/events';\n\nimport { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../annotations/api';\nimport { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';\nimport { getTimeSrv, TimeSrv } from '../services/TimeSrv';\nimport { DashboardModel, PanelModel } from '../state';\nimport { getPanelChromeProps } from '../utils/getPanelChromeProps';\nimport { loadSnapshotData } from '../utils/loadSnapshotData';\n\nimport { PanelHeaderMenuWrapper } from './PanelHeader/PanelHeaderMenuWrapper';\nimport { PanelLoadTimeMonitor } from './PanelLoadTimeMonitor';\nimport { seriesVisibilityConfigFactory } from './SeriesVisibilityConfigFactory';\nimport { liveTimer } from './liveTimer';\nimport { PanelOptionsLogger } from './panelOptionsLogger';\n\nconst DEFAULT_PLUGIN_ERROR = 'Error in plugin';\n\nexport interface Props {\n panel: PanelModel;\n dashboard: DashboardModel;\n plugin: PanelPlugin;\n isViewing: boolean;\n isEditing: boolean;\n isInView: boolean;\n isDraggable?: boolean;\n width: number;\n height: number;\n onInstanceStateChange: (value: unknown) => void;\n timezone?: string;\n hideMenu?: boolean;\n}\n\nexport interface State {\n isFirstLoad: boolean;\n renderCounter: number;\n errorMessage?: string;\n context: PanelContext;\n data: PanelData;\n liveTime?: TimeRange;\n}\n\nexport class PanelStateWrapper extends PureComponent {\n private readonly timeSrv: TimeSrv = getTimeSrv();\n private subs = new Subscription();\n private eventFilter: EventFilterOptions = { onlyLocal: true };\n private panelOptionsLogger: PanelOptionsLogger | undefined = undefined;\n\n constructor(props: Props) {\n super(props);\n\n // Can this eventBus be on PanelModel? when we have more complex event filtering, that may be a better option\n const eventBus = props.dashboard.events.newScopedBus(`panel:${props.panel.id}`, this.eventFilter);\n\n this.state = {\n isFirstLoad: true,\n renderCounter: 0,\n context: {\n eventsScope: '__global_',\n eventBus,\n app: this.getPanelContextApp(),\n sync: this.getSync,\n onSeriesColorChange: this.onSeriesColorChange,\n onToggleSeriesVisibility: this.onSeriesVisibilityChange,\n onAnnotationCreate: this.onAnnotationCreate,\n onAnnotationUpdate: this.onAnnotationUpdate,\n onAnnotationDelete: this.onAnnotationDelete,\n onInstanceStateChange: this.onInstanceStateChange,\n onToggleLegendSort: this.onToggleLegendSort,\n canAddAnnotations: props.dashboard.canAddAnnotations.bind(props.dashboard),\n canEditAnnotations: props.dashboard.canEditAnnotations.bind(props.dashboard),\n canDeleteAnnotations: props.dashboard.canDeleteAnnotations.bind(props.dashboard),\n onAddAdHocFilter: this.onAddAdHocFilter,\n onUpdateData: this.onUpdateData,\n },\n data: this.getInitialPanelDataState(),\n };\n\n if (config.featureToggles.panelMonitoring && this.getPanelContextApp() === CoreApp.PanelEditor) {\n const panelInfo = {\n panelId: String(props.panel.id),\n panelType: props.panel.type,\n panelTitle: props.panel.title,\n };\n\n this.panelOptionsLogger = new PanelOptionsLogger(props.panel.getOptions(), props.panel.fieldConfig, panelInfo);\n }\n }\n\n // Due to a mutable panel model we get the sync settings via function that proactively reads from the model\n getSync = () => (this.props.isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip);\n\n onInstanceStateChange = (value: unknown) => {\n this.props.onInstanceStateChange(value);\n\n this.setState({\n context: {\n ...this.state.context,\n instanceState: value,\n },\n });\n };\n\n getPanelContextApp() {\n if (this.props.isEditing) {\n return CoreApp.PanelEditor;\n }\n if (this.props.isViewing) {\n return CoreApp.PanelViewer;\n }\n\n return CoreApp.Dashboard;\n }\n\n onUpdateData = (frames: DataFrame[]): Promise => {\n return onUpdatePanelSnapshotData(this.props.panel, frames);\n };\n\n onSeriesColorChange = (label: string, color: string) => {\n this.onFieldConfigChange(changeSeriesColorConfigFactory(label, color, this.props.panel.fieldConfig));\n };\n\n onSeriesVisibilityChange = (label: string, mode: SeriesVisibilityChangeMode) => {\n this.onFieldConfigChange(\n seriesVisibilityConfigFactory(label, mode, this.props.panel.fieldConfig, this.state.data.series)\n );\n };\n\n onToggleLegendSort = (sortKey: string) => {\n const legendOptions: VizLegendOptions = this.props.panel.options.legend;\n\n // We don't want to do anything when legend options are not available\n if (!legendOptions) {\n return;\n }\n\n let sortDesc = legendOptions.sortDesc;\n let sortBy = legendOptions.sortBy;\n if (sortKey !== sortBy) {\n sortDesc = undefined;\n }\n\n // if already sort ascending, disable sorting\n if (sortDesc === false) {\n sortBy = undefined;\n sortDesc = undefined;\n } else {\n sortDesc = !sortDesc;\n sortBy = sortKey;\n }\n\n this.onOptionsChange({\n ...this.props.panel.options,\n legend: { ...legendOptions, sortBy, sortDesc },\n });\n };\n\n getInitialPanelDataState(): PanelData {\n return {\n state: LoadingState.NotStarted,\n series: [],\n timeRange: getDefaultTimeRange(),\n };\n }\n\n componentDidMount() {\n const { panel, dashboard } = this.props;\n\n // Subscribe to panel events\n this.subs.add(panel.events.subscribe(RefreshEvent, this.onRefresh));\n this.subs.add(panel.events.subscribe(RenderEvent, this.onRender));\n\n dashboard.panelInitialized(this.props.panel);\n\n // Move snapshot data into the query response\n if (this.hasPanelSnapshot) {\n this.setState({\n data: loadSnapshotData(panel, dashboard),\n isFirstLoad: false,\n });\n return;\n }\n\n if (!this.wantsQueryExecution) {\n this.setState({ isFirstLoad: false });\n }\n\n this.subs.add(\n panel\n .getQueryRunner()\n .getData({ withTransforms: true, withFieldConfig: true })\n .subscribe({\n next: (data) => this.onDataUpdate(data),\n })\n );\n\n // Listen for live timer events\n liveTimer.listen(this);\n }\n\n componentWillUnmount() {\n this.subs.unsubscribe();\n liveTimer.remove(this);\n }\n\n liveTimeChanged(liveTime: TimeRange) {\n const { data } = this.state;\n if (data.timeRange) {\n const delta = liveTime.to.valueOf() - data.timeRange.to.valueOf();\n if (delta < 100) {\n // 10hz\n console.log('Skip tick render', this.props.panel.title, delta);\n return;\n }\n }\n this.setState({ liveTime });\n }\n\n componentDidUpdate(prevProps: Props) {\n const { isInView, width, panel } = this.props;\n const { context } = this.state;\n\n const app = this.getPanelContextApp();\n\n if (context.app !== app) {\n this.setState({\n context: {\n ...context,\n app,\n },\n });\n }\n\n // View state has changed\n if (isInView !== prevProps.isInView) {\n if (isInView) {\n // Check if we need a delayed refresh\n if (panel.refreshWhenInView) {\n this.onRefresh();\n }\n }\n }\n\n // The timer depends on panel width\n if (width !== prevProps.width) {\n liveTimer.updateInterval(this);\n }\n }\n\n // Updates the response with information from the stream\n // The next is outside a react synthetic event so setState is not batched\n // So in this context we can only do a single call to setState\n onDataUpdate(data: PanelData) {\n const { dashboard, panel, plugin } = this.props;\n\n // Ignore this data update if we are now a non data panel\n if (plugin.meta.skipDataQuery) {\n this.setState({ data: this.getInitialPanelDataState() });\n return;\n }\n\n let { isFirstLoad } = this.state;\n let errorMessage: string | undefined;\n\n switch (data.state) {\n case LoadingState.Loading:\n // Skip updating state data if it is already in loading state\n // This is to avoid rendering partial loading responses\n if (this.state.data.state === LoadingState.Loading) {\n return;\n }\n break;\n case LoadingState.Error:\n const { error, errors } = data;\n if (errors?.length) {\n if (errors.length === 1) {\n errorMessage = errors[0].message;\n } else {\n errorMessage = 'Multiple errors found. Click for more details';\n }\n } else if (error) {\n if (errorMessage !== error.message) {\n errorMessage = error.message;\n }\n }\n break;\n case LoadingState.Done:\n // If we are doing a snapshot save data in panel model\n if (dashboard.snapshot) {\n panel.snapshotData = data.series.map((frame) => toDataFrameDTO(frame));\n }\n if (isFirstLoad) {\n isFirstLoad = false;\n }\n break;\n }\n\n this.setState({ isFirstLoad, errorMessage, data, liveTime: undefined });\n }\n\n onRefresh = () => {\n const { dashboard, panel, isInView, width } = this.props;\n\n if (!dashboard.snapshot && !isInView) {\n panel.refreshWhenInView = true;\n return;\n }\n\n const timeData = applyPanelTimeOverrides(panel, this.timeSrv.timeRange());\n\n // Issue Query\n if (this.wantsQueryExecution) {\n if (width < 0) {\n return;\n }\n\n panel.refreshWhenInView = false;\n panel.runAllPanelQueries({\n dashboardUID: dashboard.uid,\n dashboardTimezone: dashboard.getTimezone(),\n timeData,\n width,\n });\n } else {\n // The panel should render on refresh as well if it doesn't have a query, like clock panel\n this.setState({\n data: { ...this.state.data, timeRange: this.timeSrv.timeRange() },\n renderCounter: this.state.renderCounter + 1,\n liveTime: undefined,\n });\n }\n };\n\n onRender = () => {\n const stateUpdate = { renderCounter: this.state.renderCounter + 1 };\n this.setState(stateUpdate);\n };\n\n onOptionsChange = (options: any) => {\n this.props.panel.updateOptions(options);\n };\n\n onFieldConfigChange = (config: FieldConfigSource) => {\n this.props.panel.updateFieldConfig(config);\n };\n\n logPanelChangesOnError() {\n this.panelOptionsLogger!.logChanges(this.props.panel.getOptions(), this.props.panel.fieldConfig);\n }\n\n onPanelError = (error: Error) => {\n if (config.featureToggles.panelMonitoring && this.getPanelContextApp() === CoreApp.PanelEditor) {\n this.logPanelChangesOnError();\n }\n\n const errorMessage = error.message || DEFAULT_PLUGIN_ERROR;\n\n if (this.state.errorMessage !== errorMessage) {\n this.setState({ errorMessage });\n }\n };\n\n onPanelErrorRecover = () => {\n this.setState({ errorMessage: undefined });\n };\n\n onAnnotationCreate = async (event: AnnotationEventUIModel) => {\n const isRegion = event.from !== event.to;\n const anno = {\n dashboardUID: this.props.dashboard.uid,\n panelId: this.props.panel.id,\n isRegion,\n time: event.from,\n timeEnd: isRegion ? event.to : 0,\n tags: event.tags,\n text: event.description,\n };\n await saveAnnotation(anno);\n getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });\n this.state.context.eventBus.publish(new AnnotationChangeEvent(anno));\n };\n\n onAnnotationDelete = async (id: string) => {\n await deleteAnnotation({ id });\n getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });\n this.state.context.eventBus.publish(new AnnotationChangeEvent({ id }));\n };\n\n onAnnotationUpdate = async (event: AnnotationEventUIModel) => {\n const isRegion = event.from !== event.to;\n const anno = {\n id: event.id,\n dashboardUID: this.props.dashboard.uid,\n panelId: this.props.panel.id,\n isRegion,\n time: event.from,\n timeEnd: isRegion ? event.to : 0,\n tags: event.tags,\n text: event.description,\n };\n await updateAnnotation(anno);\n\n getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });\n this.state.context.eventBus.publish(new AnnotationChangeEvent(anno));\n };\n\n get hasPanelSnapshot() {\n const { panel } = this.props;\n return panel.snapshotData && panel.snapshotData.length;\n }\n\n get wantsQueryExecution() {\n return !(this.props.plugin.meta.skipDataQuery || this.hasPanelSnapshot);\n }\n\n onChangeTimeRange = (timeRange: AbsoluteTimeRange) => {\n this.timeSrv.setTime({\n from: toUtc(timeRange.from),\n to: toUtc(timeRange.to),\n });\n };\n\n shouldSignalRenderingCompleted(loadingState: LoadingState, pluginMeta: PanelPluginMeta) {\n return (\n loadingState === LoadingState.Done ||\n loadingState === LoadingState.Streaming ||\n loadingState === LoadingState.Error ||\n pluginMeta.skipDataQuery\n );\n }\n\n skipFirstRender(loadingState: LoadingState) {\n const { isFirstLoad } = this.state;\n return (\n this.wantsQueryExecution &&\n isFirstLoad &&\n (loadingState === LoadingState.Loading || loadingState === LoadingState.NotStarted)\n );\n }\n\n onAddAdHocFilter = (filter: AdHocFilterItem) => {\n const { key, value, operator } = filter;\n\n // When the datasource is null/undefined (for a default datasource), we use getInstanceSettings\n // to find the real datasource ref for the default datasource.\n const datasourceInstance = getDatasourceSrv().getInstanceSettings(this.props.panel.datasource);\n const datasourceRef = datasourceInstance && getDataSourceRef(datasourceInstance);\n if (!datasourceRef) {\n return;\n }\n\n dispatch(applyFilterFromTable({ datasource: datasourceRef, key, operator, value }));\n };\n\n renderPanelContent(innerWidth: number, innerHeight: number) {\n const { panel, plugin, dashboard } = this.props;\n const { renderCounter, data } = this.state;\n const { state: loadingState } = data;\n\n // do not render component until we have first data\n if (this.skipFirstRender(loadingState)) {\n return null;\n }\n\n // This is only done to increase a counter that is used by backend\n // image rendering to know when to capture image\n if (this.shouldSignalRenderingCompleted(loadingState, plugin.meta)) {\n profiler.renderingCompleted();\n }\n\n const PanelComponent = plugin.panel!;\n const timeRange = this.state.liveTime ?? data.timeRange ?? this.timeSrv.timeRange();\n const panelOptions = panel.getOptions();\n\n // Update the event filter (dashboard settings may have changed)\n // Yes this is called ever render for a function that is triggered on every mouse move\n this.eventFilter.onlyLocal = dashboard.graphTooltip === 0;\n\n return (\n <>\n \n \n {config.featureToggles.panelMonitoring && this.state.errorMessage === undefined && (\n \n )}\n \n >\n );\n }\n\n render() {\n const { dashboard, panel, width, height, plugin } = this.props;\n const { errorMessage, data } = this.state;\n const { transparent } = panel;\n\n const panelChromeProps = getPanelChromeProps({ ...this.props, data });\n\n // Shift the hover menu down if it's on the top row so it doesn't get clipped by topnav\n const hoverHeaderOffset = (panel.gridPos?.y ?? 0) === 0 ? -16 : undefined;\n\n const menu = (\n \n );\n\n return (\n \n {(innerWidth, innerHeight) => (\n <>\n \n {({ error }) => {\n if (error) {\n return null;\n }\n return this.renderPanelContent(innerWidth, innerHeight);\n }}\n \n >\n )}\n \n );\n }\n}\n","import React, { PureComponent } from 'react';\nimport { connect, ConnectedProps } from 'react-redux';\n\nimport { StoreState } from 'app/types';\n\nimport { initPanelState } from '../../panel/state/actions';\nimport { setPanelInstanceState } from '../../panel/state/reducers';\nimport { DashboardModel, PanelModel } from '../state';\n\nimport { LazyLoader } from './LazyLoader';\nimport { PanelChromeAngular } from './PanelChromeAngular';\nimport { PanelStateWrapper } from './PanelStateWrapper';\n\nexport interface OwnProps {\n panel: PanelModel;\n stateKey: string;\n dashboard: DashboardModel;\n isEditing: boolean;\n isViewing: boolean;\n isDraggable?: boolean;\n width: number;\n height: number;\n lazy?: boolean;\n timezone?: string;\n hideMenu?: boolean;\n}\n\nconst mapStateToProps = (state: StoreState, props: OwnProps) => {\n const panelState = state.panels[props.stateKey];\n if (!panelState) {\n return { plugin: undefined };\n }\n\n return {\n plugin: panelState.plugin,\n instanceState: panelState.instanceState,\n };\n};\n\nconst mapDispatchToProps = {\n initPanelState,\n setPanelInstanceState,\n};\n\nconst connector = connect(mapStateToProps, mapDispatchToProps);\n\nexport type Props = OwnProps & ConnectedProps;\n\nexport class DashboardPanelUnconnected extends PureComponent {\n static defaultProps: Partial = {\n lazy: true,\n };\n\n componentDidMount() {\n this.props.panel.isInView = !this.props.lazy;\n if (!this.props.lazy) {\n this.onPanelLoad();\n }\n }\n\n onInstanceStateChange = (value: unknown) => {\n this.props.setPanelInstanceState({ key: this.props.stateKey, value });\n };\n\n onVisibilityChange = (v: boolean) => {\n this.props.panel.isInView = v;\n };\n\n onPanelLoad = () => {\n if (!this.props.plugin) {\n this.props.initPanelState(this.props.panel);\n }\n };\n\n renderPanel = ({ isInView }: { isInView: boolean }) => {\n const {\n dashboard,\n panel,\n isViewing,\n isEditing,\n width,\n height,\n plugin,\n timezone,\n hideMenu,\n isDraggable = true,\n } = this.props;\n\n if (!plugin) {\n return null;\n }\n\n if (plugin && plugin.angularPanelCtrl) {\n return (\n \n );\n }\n\n return (\n \n );\n };\n\n render() {\n const { width, height, lazy } = this.props;\n\n return lazy ? (\n \n {this.renderPanel}\n \n ) : (\n this.renderPanel({ isInView: true })\n );\n }\n}\n\nexport const DashboardPanel = connector(DashboardPanelUnconnected);\n","import {\n applyFieldOverrides,\n arrayToDataFrame,\n getDefaultTimeRange,\n getProcessedDataFrames,\n LoadingState,\n PanelData,\n} from '@grafana/data';\nimport { config } from 'app/core/config';\n\nimport { SnapshotWorker } from '../../query/state/DashboardQueryRunner/SnapshotWorker';\nimport { getTimeSrv } from '../services/TimeSrv';\nimport { DashboardModel, PanelModel } from '../state';\n\nimport { applyPanelTimeOverrides } from './panel';\n\nexport function loadSnapshotData(panel: PanelModel, dashboard: DashboardModel): PanelData {\n const data = getProcessedDataFrames(panel.snapshotData);\n const worker = new SnapshotWorker();\n const options = { dashboard, range: getDefaultTimeRange() };\n const annotationEvents = worker.canWork(options) ? worker.getAnnotationsInSnapshot(dashboard, panel.id) : [];\n const annotations = [arrayToDataFrame(annotationEvents)];\n const timeData = applyPanelTimeOverrides(panel, getTimeSrv().timeRange());\n\n return {\n timeRange: timeData.timeRange,\n state: LoadingState.Done,\n series: applyFieldOverrides({\n data,\n fieldConfig: {\n defaults: {},\n overrides: [],\n },\n replaceVariables: panel.replaceVariables,\n fieldConfigRegistry: panel.plugin!.fieldConfigRegistry,\n theme: config.theme2,\n timeZone: dashboard.getTimezone(),\n }),\n structureRev: 1,\n annotations,\n };\n}\n"],"names":["LazyLoader","children","width","height","onLoad","onChange","id","loaded","setLoaded","isInView","setIsInView","wrapperRef","useEffectOnce","entry","wrapperEl","callbacks","c","entries","PanelLinks","panelLinks","onShowPanelLinks","styles","getStyles","getLinksContent","interpolatedLinks","Menu","link","idx","linkModel","PanelChrome","Icon","Dropdown","ToolbarButton","theme","PanelHeaderTitleItems","props","alertState","data","panelId","angularNotice","alertStateItem","Tooltip","timeshift","message","pluginType","angularNoticeTooltip","PanelHeaderNotices","getPanelChromeProps","descriptionInteractionReported","hasOverlayHeader","onShowPanelDescription","descriptionMarkdown","interpolatedDescription","linkSupplier","panelLink","args","onOpenInspector","e","tab","onOpenErrorInspect","onCancelQuery","padding","isAngularDatasource","isAngularPanel","showAngularNotice","titleItems","v","description","dragClass","title","PanelHeaderMenu","items","renderItems","item","getPanelMenu","dashboard","panel","angularComponent","onViewPanel","event","onEditPanel","onSharePanel","onAddLibraryPanel","onUnlinkLibraryPanel","onInspectPanel","onMore","onDuplicatePanel","onCopyPanel","onRemovePanel","onNavigateToExplore","openInNewWindow","url","config","store","onToggleLegend","menu","inspectMenu","currentTarget","target","createAlert","formValues","ruleFormUrl","onCreateAlert","subMenu","canEdit","isCreateAlertMenuOptionAvailable","scope","panelCtrl","angularMenuItems","reactItem","extensions","createExtensionContext","PanelHeaderMenuProvider","loadingState","setItems","state","PanelHeaderMenuWrapper","style","PanelChromeAngularUnconnected","Subscription","queryRunner","errorMessage","error","prevProps","prevState","plugin","headerHeight","chromePadding","setPanelAngularComponent","loader","template","transparent","panelChromeProps","hoverHeaderOffset","element","mapStateToProps","mapDispatchToProps","PanelChromeAngular","changeSeriesColorConfigFactory","label","color","fieldConfig","overrides","currentIndex","override","createOverride","overridesCopy","existing","propertyIndex","p","createProperty","propertiesCopy","PanelLogEvents","FIELD_CONFIG_OVERRIDES_KEY","FIELD_CONFIG_CUSTOM_KEY","PanelLoadTimeMonitor","startLoadTime","PanelOptionsLogger","initialPanelOptions","initialFieldConfig","panelLogInfo","latestPanelOptions","latestFieldConfig","eventName","newKey","newVal","oldVal","logObj","panelOptions","oldPanelOptions","oldPanelOptionsUnknown","key","value","newValue","oldValue","oldFieldConfig","oldOverridesStr","newOverridesStr","oldDefaults","oldCustom","DEFAULT_PLUGIN_ERROR","PanelStateWrapper","frames","mode","sortKey","legendOptions","sortDesc","sortBy","timeData","stateUpdate","options","isRegion","anno","timeRange","filter","operator","datasourceInstance","datasourceRef","eventBus","panelInfo","loadSnapshotData","liveTimer","liveTime","delta","context","app","isFirstLoad","errors","frame","pluginMeta","innerWidth","innerHeight","renderCounter","profiler","PanelComponent","PanelContext","ErrorBoundary","panelState","connector","DashboardPanelUnconnected","isViewing","isEditing","timezone","hideMenu","isDraggable","lazy","DashboardPanel","worker","annotationEvents","annotations"],"sourceRoot":""}