ShareDrawer: Share link panel (#90549)
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
|
getTimeZone,
|
||||||
InterpolateFunction,
|
InterpolateFunction,
|
||||||
LinkModel,
|
LinkModel,
|
||||||
PanelMenuItem,
|
PanelMenuItem,
|
||||||
PanelPlugin,
|
PanelPlugin,
|
||||||
PluginExtensionPanelContext,
|
PluginExtensionPanelContext,
|
||||||
PluginExtensionPoints,
|
PluginExtensionPoints,
|
||||||
getTimeZone,
|
|
||||||
urlUtil,
|
urlUtil,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { config, getPluginLinkExtensions, locationService } from '@grafana/runtime';
|
import { config, getPluginLinkExtensions, locationService } from '@grafana/runtime';
|
||||||
import { LocalValueVariable, SceneGridRow, VizPanel, VizPanelMenu, sceneGraph } from '@grafana/scenes';
|
import { LocalValueVariable, sceneGraph, SceneGridRow, VizPanel, VizPanelMenu } from '@grafana/scenes';
|
||||||
import { DataQuery, OptionsWithLegend } from '@grafana/schema';
|
import { DataQuery, OptionsWithLegend } from '@grafana/schema';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
@@ -21,7 +21,9 @@ import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils';
|
|||||||
import { addDataTrailPanelAction } from 'app/features/trails/Integrations/dashboardIntegration';
|
import { addDataTrailPanelAction } from 'app/features/trails/Integrations/dashboardIntegration';
|
||||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||||
|
|
||||||
|
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
|
import { SharePanelInternally } from '../sharing/panel-share/SharePanelInternally';
|
||||||
import { DashboardInteractions } from '../utils/interactions';
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
||||||
import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
||||||
@@ -79,15 +81,42 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
if (config.featureToggles.newDashboardSharingComponent) {
|
||||||
text: t('panel.header-menu.share', `Share`),
|
const subMenu: PanelMenuItem[] = [];
|
||||||
iconClassName: 'share-alt',
|
subMenu.push({
|
||||||
onClick: () => {
|
text: t('share-panel.menu.share-link-title', 'Share link'),
|
||||||
DashboardInteractions.panelMenuItemClicked('share');
|
iconClassName: 'link',
|
||||||
dashboard.showModal(new ShareModal({ panelRef: panel.getRef() }));
|
shortcut: 'p u',
|
||||||
},
|
onClick: () => {
|
||||||
shortcut: 'p s',
|
const drawer = new ShareDrawer({
|
||||||
});
|
title: t('share-panel.drawer.share-link-title', 'Link settings'),
|
||||||
|
body: new SharePanelInternally({ panelRef: panel.getRef() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
dashboard.showModal(drawer);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
type: 'submenu',
|
||||||
|
text: t('panel.header-menu.share', 'Share'),
|
||||||
|
iconClassName: 'share-alt',
|
||||||
|
subMenu,
|
||||||
|
onClick: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push({
|
||||||
|
text: t('panel.header-menu.share', 'Share'),
|
||||||
|
iconClassName: 'share-alt',
|
||||||
|
onClick: () => {
|
||||||
|
DashboardInteractions.panelMenuItemClicked('share');
|
||||||
|
dashboard.showModal(new ShareModal({ panelRef: panel.getRef() }));
|
||||||
|
},
|
||||||
|
shortcut: 'p s',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (dashboard.state.isEditing && !isRepeat && !isEditingPanel) {
|
if (dashboard.state.isEditing && !isRepeat && !isEditingPanel) {
|
||||||
moreSubMenu.push({
|
moreSubMenu.push({
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { SetPanelAttentionEvent } from '@grafana/data';
|
import { SetPanelAttentionEvent } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { config, locationService } from '@grafana/runtime';
|
||||||
import { sceneGraph, VizPanel } from '@grafana/scenes';
|
import { sceneGraph, VizPanel } from '@grafana/scenes';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
|
import { t } from 'app/core/internationalization';
|
||||||
import { KeybindingSet } from 'app/core/services/KeybindingSet';
|
import { KeybindingSet } from 'app/core/services/KeybindingSet';
|
||||||
|
|
||||||
|
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
|
import { SharePanelInternally } from '../sharing/panel-share/SharePanelInternally';
|
||||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||||
import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
||||||
import { getPanelIdForVizPanel } from '../utils/utils';
|
import { getPanelIdForVizPanel } from '../utils/utils';
|
||||||
@@ -45,12 +48,26 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Panel share
|
// Panel share
|
||||||
keybindings.addBinding({
|
if (config.featureToggles.newDashboardSharingComponent) {
|
||||||
key: 'p s',
|
keybindings.addBinding({
|
||||||
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
|
key: 'p u',
|
||||||
scene.showModal(new ShareModal({ panelRef: vizPanel.getRef() }));
|
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
|
||||||
}),
|
const drawer = new ShareDrawer({
|
||||||
});
|
title: t('share-panel.drawer.share-link-title', 'Link settings'),
|
||||||
|
body: new SharePanelInternally({ panelRef: vizPanel.getRef() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.showModal(drawer);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
keybindings.addBinding({
|
||||||
|
key: 'p s',
|
||||||
|
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
|
||||||
|
scene.showModal(new ShareModal({ panelRef: vizPanel.getRef() }));
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Panel inspect
|
// Panel inspect
|
||||||
keybindings.addBinding({
|
keybindings.addBinding({
|
||||||
|
|||||||
+45
-38
@@ -2,12 +2,12 @@ import { css } from '@emotion/css';
|
|||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { SceneComponentProps, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
import { SceneComponentProps, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
||||||
import { Alert, ClipboardButton, Divider, Label, Spinner, Stack, Switch, Text, useStyles2 } from '@grafana/ui';
|
import { Alert, ClipboardButton, Divider, Text, useStyles2 } from '@grafana/ui';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
import { ThemePicker } from 'app/features/dashboard/components/ShareModal/ThemePicker';
|
|
||||||
|
|
||||||
|
import ShareInternallyConfiguration from '../../ShareInternallyConfiguration';
|
||||||
import { ShareLinkTab } from '../../ShareLinkTab';
|
import { ShareLinkTab } from '../../ShareLinkTab';
|
||||||
import { getShareLinkConfiguration } from '../utils';
|
import { getShareLinkConfiguration, updateShareLinkConfiguration } from '../utils';
|
||||||
|
|
||||||
export class ShareInternally extends ShareLinkTab {
|
export class ShareInternally extends ShareLinkTab {
|
||||||
static Component = ShareInternallyRenderer;
|
static Component = ShareInternallyRenderer;
|
||||||
@@ -20,6 +20,39 @@ export class ShareInternally extends ShareLinkTab {
|
|||||||
useShortUrl,
|
useShortUrl,
|
||||||
selectedTheme: theme,
|
selectedTheme: theme,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.onToggleLockedTime = this.onToggleLockedTime.bind(this);
|
||||||
|
this.onUrlShorten = this.onUrlShorten.bind(this);
|
||||||
|
this.onThemeChange = this.onThemeChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onToggleLockedTime() {
|
||||||
|
const useLockedTime = !this.state.useLockedTime;
|
||||||
|
updateShareLinkConfiguration({
|
||||||
|
useAbsoluteTimeRange: useLockedTime,
|
||||||
|
useShortUrl: this.state.useShortUrl,
|
||||||
|
theme: this.state.selectedTheme,
|
||||||
|
});
|
||||||
|
await super.onToggleLockedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onUrlShorten() {
|
||||||
|
const useShortUrl = !this.state.useShortUrl;
|
||||||
|
updateShareLinkConfiguration({
|
||||||
|
useShortUrl,
|
||||||
|
useAbsoluteTimeRange: this.state.useLockedTime,
|
||||||
|
theme: this.state.selectedTheme,
|
||||||
|
});
|
||||||
|
await super.onUrlShorten();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onThemeChange(value: string) {
|
||||||
|
updateShareLinkConfiguration({
|
||||||
|
theme: value,
|
||||||
|
useShortUrl: this.state.useShortUrl,
|
||||||
|
useAbsoluteTimeRange: this.state.useLockedTime,
|
||||||
|
});
|
||||||
|
await super.onThemeChange(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,41 +75,15 @@ function ShareInternallyRenderer({ model }: SceneComponentProps<ShareInternally>
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<Stack justifyContent="space-between">
|
<ShareInternallyConfiguration
|
||||||
<Stack gap={2} direction="column">
|
useLockedTime={useLockedTime}
|
||||||
<Stack gap={1} direction="column">
|
onToggleLockedTime={model.onToggleLockedTime}
|
||||||
<Stack gap={1} alignItems="start">
|
useShortUrl={useShortUrl}
|
||||||
<Switch
|
onUrlShorten={model.onUrlShorten}
|
||||||
label={t('link.share.time-range-label', 'Lock time range')}
|
selectedTheme={selectedTheme}
|
||||||
id="share-current-time-range"
|
onChangeTheme={model.onThemeChange}
|
||||||
value={useLockedTime}
|
isLoading={isBuildUrlLoading}
|
||||||
onChange={model.onToggleLockedTime}
|
/>
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
description={t(
|
|
||||||
'link.share.time-range-description',
|
|
||||||
'Change the current relative time range to an absolute time range'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Trans i18nKey="link.share.time-range-label">Lock time range</Trans>
|
|
||||||
</Label>
|
|
||||||
</Stack>
|
|
||||||
<Stack gap={1} alignItems="start">
|
|
||||||
<Switch
|
|
||||||
id="share-short-url"
|
|
||||||
value={useShortUrl}
|
|
||||||
label={t('link.share.short-url-label', 'Shorten link')}
|
|
||||||
onChange={model.onUrlShorten}
|
|
||||||
/>
|
|
||||||
<Label>
|
|
||||||
<Trans i18nKey="link.share.short-url-label">Shorten link</Trans>
|
|
||||||
</Label>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
<ThemePicker selectedTheme={selectedTheme} onChange={model.onThemeChange} />
|
|
||||||
</Stack>
|
|
||||||
{isBuildUrlLoading && <Spinner />}
|
|
||||||
</Stack>
|
|
||||||
<Divider spacing={1} />
|
<Divider spacing={1} />
|
||||||
<ClipboardButton
|
<ClipboardButton
|
||||||
icon="link"
|
icon="link"
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { Label, Spinner, Stack, Switch } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { t, Trans } from '../../../core/internationalization';
|
||||||
|
import { ThemePicker } from '../../dashboard/components/ShareModal/ThemePicker';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
useLockedTime: boolean;
|
||||||
|
onToggleLockedTime: () => void;
|
||||||
|
useShortUrl: boolean;
|
||||||
|
onUrlShorten: () => void;
|
||||||
|
selectedTheme: string;
|
||||||
|
onChangeTheme: (v: string) => void;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ShareInternallyConfiguration({
|
||||||
|
useLockedTime,
|
||||||
|
onToggleLockedTime,
|
||||||
|
useShortUrl,
|
||||||
|
onUrlShorten,
|
||||||
|
onChangeTheme,
|
||||||
|
selectedTheme,
|
||||||
|
isLoading,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<Stack justifyContent="space-between">
|
||||||
|
<Stack gap={2} direction="column">
|
||||||
|
<Stack gap={1} direction="column">
|
||||||
|
<Stack gap={1} alignItems="start">
|
||||||
|
<Switch
|
||||||
|
label={t('link.share.time-range-label', 'Lock time range')}
|
||||||
|
id="share-current-time-range"
|
||||||
|
value={useLockedTime}
|
||||||
|
onChange={onToggleLockedTime}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
description={t(
|
||||||
|
'link.share.time-range-description',
|
||||||
|
'Change the current relative time range to an absolute time range'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Trans i18nKey="link.share.time-range-label">Lock time range</Trans>
|
||||||
|
</Label>
|
||||||
|
</Stack>
|
||||||
|
<Stack gap={1} alignItems="start">
|
||||||
|
<Switch
|
||||||
|
id="share-short-url"
|
||||||
|
value={useShortUrl}
|
||||||
|
label={t('link.share.short-url-label', 'Shorten link')}
|
||||||
|
onChange={onUrlShorten}
|
||||||
|
/>
|
||||||
|
<Label>
|
||||||
|
<Trans i18nKey="link.share.short-url-label">Shorten link</Trans>
|
||||||
|
</Label>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<ThemePicker selectedTheme={selectedTheme} onChange={onChangeTheme} />
|
||||||
|
</Stack>
|
||||||
|
{isLoading && <Spinner />}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ import { DashboardInteractions } from '../utils/interactions';
|
|||||||
import { getDashboardUrl } from '../utils/urlBuilders';
|
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||||
import { getDashboardSceneFor } from '../utils/utils';
|
import { getDashboardSceneFor } from '../utils/utils';
|
||||||
|
|
||||||
import { updateShareLinkConfiguration } from './ShareButton/utils';
|
|
||||||
import { SceneShareTabState } from './types';
|
import { SceneShareTabState } from './types';
|
||||||
export interface ShareLinkTabState extends SceneShareTabState, ShareOptions {
|
export interface ShareLinkTabState extends SceneShareTabState, ShareOptions {
|
||||||
panelRef?: SceneObjectRef<VizPanel>;
|
panelRef?: SceneObjectRef<VizPanel>;
|
||||||
@@ -50,6 +49,10 @@ export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> {
|
|||||||
this.addActivationHandler(() => {
|
this.addActivationHandler(() => {
|
||||||
this.buildUrl();
|
this.buildUrl();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.onToggleLockedTime = this.onToggleLockedTime.bind(this);
|
||||||
|
this.onUrlShorten = this.onUrlShorten.bind(this);
|
||||||
|
this.onThemeChange = this.onThemeChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildUrl() {
|
async buildUrl() {
|
||||||
@@ -94,37 +97,22 @@ export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> {
|
|||||||
return t('share-modal.tab-title.link', 'Link');
|
return t('share-modal.tab-title.link', 'Link');
|
||||||
}
|
}
|
||||||
|
|
||||||
onToggleLockedTime = async () => {
|
async onToggleLockedTime() {
|
||||||
const useLockedTime = !this.state.useLockedTime;
|
const useLockedTime = !this.state.useLockedTime;
|
||||||
updateShareLinkConfiguration({
|
|
||||||
useAbsoluteTimeRange: useLockedTime,
|
|
||||||
useShortUrl: this.state.useShortUrl,
|
|
||||||
theme: this.state.selectedTheme,
|
|
||||||
});
|
|
||||||
this.setState({ useLockedTime });
|
this.setState({ useLockedTime });
|
||||||
await this.buildUrl();
|
await this.buildUrl();
|
||||||
};
|
}
|
||||||
|
|
||||||
onUrlShorten = async () => {
|
async onUrlShorten() {
|
||||||
const useShortUrl = !this.state.useShortUrl;
|
const useShortUrl = !this.state.useShortUrl;
|
||||||
this.setState({ useShortUrl });
|
this.setState({ useShortUrl });
|
||||||
updateShareLinkConfiguration({
|
|
||||||
useShortUrl,
|
|
||||||
useAbsoluteTimeRange: this.state.useLockedTime,
|
|
||||||
theme: this.state.selectedTheme,
|
|
||||||
});
|
|
||||||
await this.buildUrl();
|
await this.buildUrl();
|
||||||
};
|
}
|
||||||
|
|
||||||
onThemeChange = async (value: string) => {
|
async onThemeChange(value: string) {
|
||||||
this.setState({ selectedTheme: value });
|
this.setState({ selectedTheme: value });
|
||||||
updateShareLinkConfiguration({
|
|
||||||
theme: value,
|
|
||||||
useShortUrl: this.state.useShortUrl,
|
|
||||||
useAbsoluteTimeRange: this.state.useLockedTime,
|
|
||||||
});
|
|
||||||
await this.buildUrl();
|
await this.buildUrl();
|
||||||
};
|
}
|
||||||
|
|
||||||
getShareUrl = () => {
|
getShareUrl = () => {
|
||||||
return this.state.shareUrl;
|
return this.state.shareUrl;
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
|
import { SceneComponentProps, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
||||||
|
import { Alert, ClipboardButton, Divider, LinkButton, Stack, Text, useStyles2 } from '@grafana/ui';
|
||||||
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
|
|
||||||
|
import { getDashboardSceneFor } from '../../utils/utils';
|
||||||
|
import ShareInternallyConfiguration from '../ShareInternallyConfiguration';
|
||||||
|
import { ShareLinkTab } from '../ShareLinkTab';
|
||||||
|
|
||||||
|
export class SharePanelInternally extends ShareLinkTab {
|
||||||
|
static Component = SharePanelInternallyRenderer;
|
||||||
|
|
||||||
|
constructor({ panelRef }: { panelRef?: SceneObjectRef<VizPanel> }) {
|
||||||
|
super({
|
||||||
|
panelRef,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SharePanelInternallyRenderer({ model }: SceneComponentProps<SharePanelInternally>) {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
const { useLockedTime, useShortUrl, selectedTheme, isBuildUrlLoading, imageUrl } = model.useState();
|
||||||
|
const dashboard = getDashboardSceneFor(model);
|
||||||
|
const isDashboardSaved = Boolean(dashboard.state.uid);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.configDescription}>
|
||||||
|
<Text variant="body">
|
||||||
|
<Trans i18nKey="link.share-panel.config-description">
|
||||||
|
Create a personalized, direct link to share your panel within your organization, with the following
|
||||||
|
customization settings:
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<ShareInternallyConfiguration
|
||||||
|
useLockedTime={useLockedTime}
|
||||||
|
onToggleLockedTime={() => model.onToggleLockedTime()}
|
||||||
|
useShortUrl={useShortUrl}
|
||||||
|
onUrlShorten={() => model.onUrlShorten()}
|
||||||
|
selectedTheme={selectedTheme}
|
||||||
|
onChangeTheme={(t) => model.onThemeChange(t)}
|
||||||
|
isLoading={isBuildUrlLoading}
|
||||||
|
/>
|
||||||
|
<Divider spacing={1} />
|
||||||
|
<Stack gap={2} direction="column">
|
||||||
|
<div className={styles.buttonsContainer}>
|
||||||
|
<Stack gap={1} flex={1} direction={{ xs: 'column', sm: 'row' }}>
|
||||||
|
<ClipboardButton
|
||||||
|
icon="link"
|
||||||
|
variant="primary"
|
||||||
|
fill="outline"
|
||||||
|
disabled={isBuildUrlLoading}
|
||||||
|
getText={model.getShareUrl}
|
||||||
|
onClipboardCopy={model.onCopy}
|
||||||
|
>
|
||||||
|
<Trans i18nKey="link.share.copy-link-button">Copy link</Trans>
|
||||||
|
</ClipboardButton>
|
||||||
|
<LinkButton
|
||||||
|
href={imageUrl}
|
||||||
|
icon="external-link-alt"
|
||||||
|
target="_blank"
|
||||||
|
variant="secondary"
|
||||||
|
fill="solid"
|
||||||
|
disabled={!config.rendererAvailable || !isDashboardSaved}
|
||||||
|
>
|
||||||
|
<Trans i18nKey="link.share-panel.render-image">Render image</Trans>
|
||||||
|
</LinkButton>
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
{!isDashboardSaved && (
|
||||||
|
<Alert severity="info" title={t('share-modal.link.save-alert', 'Dashboard is not saved')} bottomSpacing={0}>
|
||||||
|
<Trans i18nKey="share-modal.link.save-dashboard">
|
||||||
|
To render a panel image, you must save the dashboard first.
|
||||||
|
</Trans>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{!config.rendererAvailable && (
|
||||||
|
<Alert
|
||||||
|
severity="info"
|
||||||
|
title={t('share-modal.link.render-alert', 'Image renderer plugin not installed')}
|
||||||
|
bottomSpacing={0}
|
||||||
|
>
|
||||||
|
<Trans i18nKey="share-modal.link.render-instructions">
|
||||||
|
To render a panel image, you must install the{' '}
|
||||||
|
<a
|
||||||
|
href="https://grafana.com/grafana/plugins/grafana-image-renderer"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="external-link"
|
||||||
|
>
|
||||||
|
Grafana image renderer plugin
|
||||||
|
</a>
|
||||||
|
. Please contact your Grafana administrator to install the plugin.
|
||||||
|
</Trans>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
configDescription: css({
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}),
|
||||||
|
buttonsContainer: css({
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -953,6 +953,10 @@
|
|||||||
"short-url-label": "Shorten link",
|
"short-url-label": "Shorten link",
|
||||||
"time-range-description": "Change the current relative time range to an absolute time range",
|
"time-range-description": "Change the current relative time range to an absolute time range",
|
||||||
"time-range-label": "Lock time range"
|
"time-range-label": "Lock time range"
|
||||||
|
},
|
||||||
|
"share-panel": {
|
||||||
|
"config-description": "Create a personalized, direct link to share your panel within your organization, with the following customization settings:",
|
||||||
|
"render-image": "Render image"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
@@ -2009,6 +2013,14 @@
|
|||||||
"copy-button": "Copy to Clipboard"
|
"copy-button": "Copy to Clipboard"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"share-panel": {
|
||||||
|
"drawer": {
|
||||||
|
"share-link-title": "Link settings"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"share-link-title": "Share link"
|
||||||
|
}
|
||||||
|
},
|
||||||
"share-playlist": {
|
"share-playlist": {
|
||||||
"checkbox-description": "Panel heights will be adjusted to fit screen size",
|
"checkbox-description": "Panel heights will be adjusted to fit screen size",
|
||||||
"checkbox-label": "Autofit",
|
"checkbox-label": "Autofit",
|
||||||
|
|||||||
@@ -953,6 +953,10 @@
|
|||||||
"short-url-label": "Ŝĥőřŧęʼn ľįʼnĸ",
|
"short-url-label": "Ŝĥőřŧęʼn ľįʼnĸ",
|
||||||
"time-range-description": "Cĥäʼnģę ŧĥę čūřřęʼnŧ řęľäŧįvę ŧįmę řäʼnģę ŧő äʼn äþşőľūŧę ŧįmę řäʼnģę",
|
"time-range-description": "Cĥäʼnģę ŧĥę čūřřęʼnŧ řęľäŧįvę ŧįmę řäʼnģę ŧő äʼn äþşőľūŧę ŧįmę řäʼnģę",
|
||||||
"time-range-label": "Ŀőčĸ ŧįmę řäʼnģę"
|
"time-range-label": "Ŀőčĸ ŧįmę řäʼnģę"
|
||||||
|
},
|
||||||
|
"share-panel": {
|
||||||
|
"config-description": "Cřęäŧę ä pęřşőʼnäľįžęđ, đįřęčŧ ľįʼnĸ ŧő şĥäřę yőūř päʼnęľ ŵįŧĥįʼn yőūř őřģäʼnįžäŧįőʼn, ŵįŧĥ ŧĥę ƒőľľőŵįʼnģ čūşŧőmįžäŧįőʼn şęŧŧįʼnģş:",
|
||||||
|
"render-image": "Ŗęʼnđęř įmäģę"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
@@ -2009,6 +2013,14 @@
|
|||||||
"copy-button": "Cőpy ŧő Cľįpþőäřđ"
|
"copy-button": "Cőpy ŧő Cľįpþőäřđ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"share-panel": {
|
||||||
|
"drawer": {
|
||||||
|
"share-link-title": "Ŀįʼnĸ şęŧŧįʼnģş"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"share-link-title": "Ŝĥäřę ľįʼnĸ"
|
||||||
|
}
|
||||||
|
},
|
||||||
"share-playlist": {
|
"share-playlist": {
|
||||||
"checkbox-description": "Päʼnęľ ĥęįģĥŧş ŵįľľ þę äđĵūşŧęđ ŧő ƒįŧ şčřęęʼn şįžę",
|
"checkbox-description": "Päʼnęľ ĥęįģĥŧş ŵįľľ þę äđĵūşŧęđ ŧő ƒįŧ şčřęęʼn şįžę",
|
||||||
"checkbox-label": "Åūŧőƒįŧ",
|
"checkbox-label": "Åūŧőƒįŧ",
|
||||||
|
|||||||
Reference in New Issue
Block a user