[7.x] Reverting PR 42545 (for now) (#44277) (#44285)

This commit is contained in:
Clint Andrew Hall 2019-08-28 12:47:01 -04:00 committed by GitHub
parent 7097cfb03d
commit 5add57b19d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 197 additions and 35139 deletions

View file

@ -7,29 +7,32 @@ files:
- 'x-pack/legacy/plugins/rollup/**/*.s+(a|c)ss'
- 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss'
- 'x-pack/legacy/plugins/canvas/**/*.s+(a|c)ss'
ignore:
- 'x-pack/legacy/plugins/canvas/external_runtime/**/*.s+(a|c)ss'
rules:
quotes:
- 2
- style: 'single'
-
style: 'single'
# } else { style on one line, like our JS
brace-style:
- 2
- style: '1tbs'
-
style: '1tbs'
variable-name-format:
- 2
- convention: 'camelcase'
-
convention: 'camelcase'
# Needs regex, right now we ignore
class-name-format: 0
# Order how you please
property-sort-order: 0
hex-notation:
- 2
- style: 'uppercase'
-
style: 'uppercase'
mixin-name-format:
- 2
- allow-leading-underscore: false
-
allow-leading-underscore: false
convention: 'camelcase'
# Use none instead of 0 for no border
border-zero:
@ -44,7 +47,8 @@ rules:
indentation: 2
function-name-format:
- 2
- allow-leading-underscore: false
-
allow-leading-underscore: false
convention: 'camelcase'
# This removes the need for ::hover
pseudo-element: 0
@ -58,20 +62,23 @@ rules:
force-attribute-nesting: 0
no-qualifying-elements:
- 2
- # Allows input[type=search]
-
# Allows input[type=search]
allow-element-with-attribute: 1
# Files can end without a newline
final-newline: 0
# We use some rare duplicate property values for browser variance
no-duplicate-properties:
- 2
- exclude:
-
exclude:
- 'font-size'
- 'word-break'
# Put a line-break between sections of CSS, but allow quicky one-liners for legibility
empty-line-between-blocks:
- 2
- allow-single-line-rulesets: 1
-
allow-single-line-rulesets: 1
# Warns are nice for deprecations and development
no-warn: 0
# Transition all is useful in certain situations and there's no recent info to suggest slowdown

View file

@ -415,7 +415,6 @@
"nock": "10.0.6",
"node-sass": "^4.9.4",
"normalize-path": "^3.0.0",
"null-loader": "^3.0.0",
"nyc": "^14.1.1",
"pixelmatch": "4.0.2",
"pkg-up": "^2.0.0",

View file

@ -577,14 +577,6 @@
'@types/zen-observable',
],
},
{
groupSlug: 'archiver',
groupName: 'archiver related packages',
packageNames: [
'archiver',
'@types/archiver',
],
},
{
groupSlug: 'base64-js',
groupName: 'base64-js related packages',

View file

@ -53,14 +53,6 @@ jest.mock(
}
);
// Disabling this test due to https://github.com/elastic/eui/issues/2242
jest.mock(
'../public/components/workpad_header/workpad_export/flyout/__examples__/external_embed_flyout.examples',
() => {
return 'Disabled Panel';
}
);
addSerializer(styleSheetSerializer);
// Initialize Storyshots and build the Jest Snapshots

View file

@ -6,16 +6,6 @@
import { CanvasWorkpad, CanvasElement, CanvasPage } from '../../types';
const BaseWorkpad: CanvasWorkpad = {
'@created': '2019-02-08T18:35:23.029Z',
'@timestamp': '2019-02-08T18:35:23.029Z',
assets: {
'asset-ada763f1-295e-4188-8e08-b5bed9e006a1': {
id: 'asset-ada763f1-295e-4188-8e08-b5bed9e006a1',
'@created': '2018-01-17T19:13:09.185Z',
type: 'dataurl',
value: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciLz4=',
},
},
name: 'base workpad',
id: 'base-workpad',
width: 0,

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RUNTIME_NAME } from '../../external_runtime/constants';
export const CANVAS_TYPE = 'canvas-workpad';
export const CUSTOM_ELEMENT_TYPE = 'canvas-element';
export const CANVAS_APP = 'canvas';
@ -35,7 +33,3 @@ export const CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR = `canvasLayout__stageContent`
export const DATATABLE_COLUMN_TYPES = ['string', 'number', 'null', 'boolean', 'date'];
export const LAUNCHED_FULLSCREEN = 'workpad-full-screen-launch';
export const LAUNCHED_FULLSCREEN_AUTOPLAY = 'workpad-full-screen-launch-with-autoplay';
export const API_ROUTE_SNAPSHOT_BASE = '/public/canvas';
export const API_ROUTE_SNAPSHOT_ZIP = `${API_ROUTE_SNAPSHOT_BASE}/zip`;
export const API_ROUTE_SNAPSHOT_RUNTIME = `${API_ROUTE_SNAPSHOT_BASE}/runtime`;
export const API_ROUTE_SNAPSHOT_RUNTIME_DOWNLOAD = `${API_ROUTE_SNAPSHOT_BASE}/${RUNTIME_NAME}.js`;

View file

@ -15,13 +15,3 @@ export const fetch = axios.create({
},
timeout: FETCH_TIMEOUT,
});
export const arrayBufferFetch = axios.create({
responseType: 'arraybuffer',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'kbn-xsrf': 'professionally-crafted-string-of-text',
},
timeout: FETCH_TIMEOUT,
});

View file

@ -1,54 +0,0 @@
# Canvas External Embeds
![Canvas External Runtime](demo.gif)
## Introduction
The external runtime is designed to render Canvas Workpads outside of Kibana in a different website or application. It uses the intermediate, "transient" state of a workpad, which is a JSON-blob state after element expressions are evaluated, but before the elements are rendered to the screen. This "transient" state, therefore, has no dependency or access to ES/Kibana data, making it lightweight and portable.
This directory contains the code necessary to build and test this external runtime.
## Building
Run `node scripts/external_runtime`. The runtime will be built and stored `external_runtime/build`.
## Development
To start the `webpack-dev-server` and test a workpad, simply run:
`/canvas: node scripts/external_runtime --dev --run`
A browser window should automatically open. If not, navigate to [`http://localhost:9001/`](http://localhost:9001).
### Customizing
The `index.html` file contains a call to the `CanvasEmbed` runtime. Currently, you can embed by object or by url:
```html
<script src="kbn_canvas.js"></script>
...
<div kbn-canvas-embed="canvas" kbn-canvas-height="400" kbn-canvas-url="workpad.json"></div>
<script type="text/javascript">
KbnCanvas.embed();
</script>
```
There are two test workpads available: `/test/test.json` and `/test/austin.json`.
### Options
The [`api/embed.tsx`]('./api/embed') file contains the base class with available options to configure the embed:
```typescript
height?: number;
width?: number;
page?: number;
```
More options are available, but have not yet been exposed, (e.g. toolbar hide, etc)
## Testing
You can load a Workpad in Canvas, click "Export" and then "Embed on a website". You can then download a ZIP file with the runtime, the workpad and a sample HTML file.
After extracting to a directory, you can then start a small web server to load the HTML file. The easiest way, if you have `python` installed, is to run `python -m SimpleHTTPServer 8000` from the extracted directory.

View file

@ -1,100 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { render } from 'react-dom';
import { App } from '../components/app';
import { CanvasRenderedWorkpad } from '../types';
export interface Options {
/** The preferred height to scale the embedded workpad. If only `height` is
* specified, `width` will be calculated by the workpad ratio. If both are
* specified, the ratio will be overriden by an absolute size. */
height?: number;
/** The preferred width to scale the embedded workpad. If only `width` is
* specified, `height` will be calculated by the workpad ratio. If both are
* specified, the ratio will be overriden by an absolute size. */
width?: number;
/** The initial page to display. */
page?: number;
}
const PREFIX = 'kbn-canvas';
const EMBED = `${PREFIX}-embed`;
const getAttributes = (element: Element, attributes: string[]) => {
const result: { [key: string]: string } = {};
attributes.forEach(attribute => {
const key = `${PREFIX}-${attribute}`;
const value = element.getAttribute(key);
if (value) {
result[attribute] = value;
element.removeAttribute(key);
}
});
return result;
};
const getWorkpad = async (url: string): Promise<CanvasRenderedWorkpad | null> => {
const workpadResponse = await fetch(url);
if (workpadResponse.ok) {
return await workpadResponse.json();
}
return null;
};
const updateArea = async (area: Element) => {
const { url, page: pageAttr, height: heightAttr, weight: widthAttr } = getAttributes(area, [
'url',
'page',
'height',
'width',
]);
if (url) {
const workpad = await getWorkpad(url);
if (workpad) {
const page = pageAttr ? parseInt(pageAttr, 10) : null;
let height = heightAttr ? parseInt(heightAttr, 10) : null;
let width = widthAttr ? parseInt(widthAttr, 10) : null;
if (height && !width) {
// If we have a height but no width, the width should honor the workpad ratio.
width = workpad.width * (height / workpad.height);
} else if (width && !height) {
// If we have a width but no height, the height should honor the workpad ratio.
height = workpad.height * (width / workpad.width);
}
const options = {
height: height || workpad.height,
width: width || workpad.width,
page: page ? page : workpad.page,
};
area.classList.add('kbnCanvas');
area.removeAttribute(EMBED);
render(<App workpad={workpad} {...options} />, area);
}
}
};
/**
* This is an abstract embedding component. It provides all of the scaling and
* other option handling for embedding strategies.
*/
export const embed = () => {
const embedAreas = document.querySelectorAll(`[${EMBED}]`);
const validAreas = Array.from(embedAreas).filter(area => area.getAttribute(EMBED) === 'canvas');
validAreas.forEach(updateArea);
};

View file

@ -1,10 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import 'whatwg-fetch';
import 'babel-polyfill';
export * from './embed';

View file

@ -1,56 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
// @ts-ignore Untyped package
import { RenderFunctionsRegistry } from 'data/interpreter';
import { Canvas } from './canvas';
import {
initialExternalEmbedState,
ExternalEmbedStateProvider,
ExternalEmbedState,
} from '../context';
// @ts-ignore Untyped local
import { renderFunctions } from '../../canvas_plugin_src/renderers';
import { CanvasRenderedWorkpad } from '../types';
interface Props {
height: number;
width: number;
page: number;
workpad: CanvasRenderedWorkpad;
}
/**
* The overall Embedded Workpad app; the highest-layer component.
*/
export const App = (props: Props) => {
const { workpad, page, height, width } = props;
// Register all of the rendering experessions with a bespoke registry.
const renderersRegistry = new RenderFunctionsRegistry();
renderFunctions.forEach((fn: Function | undefined) => {
if (fn) {
renderersRegistry.register(fn);
}
});
const initialState: ExternalEmbedState = {
...initialExternalEmbedState,
height,
page,
renderersRegistry,
width,
workpad,
};
return (
<ExternalEmbedStateProvider initialState={initialState}>
<Canvas />
</ExternalEmbedStateProvider>
);
};

View file

@ -1,26 +0,0 @@
:global html body .kbnCanvas {
height: auto;
}
:global .kbnCanvas :local .root {
position: relative;
overflow: hidden;
transition: height 1s;
}
.container {
composes: canvas from global;
composes: canvasContainer from global;
}
:global .kbnCanvas :local .container {
align-items: center;
display: flex;
justify-content: center;
pointer-events: none;
}
:global .kbnCanvas :local .page {
position: absolute;
transform-origin: center center;
}

View file

@ -1,86 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState } from 'react';
import { useExternalEmbedState, setPage, setScrubberVisible } from '../context';
import { Page } from './page';
import { Footer, FOOTER_HEIGHT } from './footer';
import { getTimeInterval } from '../../public/lib/time_interval';
// @ts-ignore CSS Module
import css from './canvas.module';
let timeout: number = 0;
/**
* The "stage" for a workpad, which composes the toolbar and other components.
*/
export const Canvas = () => {
const [
{ workpad, height: containerHeight, width: containerWidth, page, settings, refs },
dispatch,
] = useExternalEmbedState();
if (!workpad) {
return null;
}
const { toolbar, autoplay } = settings;
const { height, width, pages } = workpad;
const ratio = Math.max(width / containerWidth, height / containerHeight);
const transform = `scale3d(${containerHeight / (containerHeight * ratio)}, ${containerWidth /
(containerWidth * ratio)}, 1)`;
const pageStyle = {
height,
transform,
width,
};
if (autoplay.enabled && autoplay.interval) {
// We need to clear the timeout every time, even if it doesn't need to be or
// it's null. Since one could select a different page from the scrubber at
// any point, or change the interval, we need to make sure the interval is
// killed on React re-render-- otherwise the pages will start bouncing around
// as timeouts are accumulated.
clearTimeout(timeout);
timeout = setTimeout(
() => dispatch(setPage(page >= workpad.pages.length - 1 ? 0 : page + 1)),
getTimeInterval(autoplay.interval)
);
}
const [toolbarHidden, setToolbarHidden] = useState(toolbar.autohide);
const rootHeight = containerHeight + (toolbar.autohide ? 0 : FOOTER_HEIGHT);
const hideToolbar = (hidden: boolean) => {
if (settings.toolbar.autohide) {
if (hidden) {
// Hide the scrubber if we hide the toolbar.
dispatch(setScrubberVisible(false));
}
setToolbarHidden(hidden);
}
};
return (
<div
className={css.root}
style={{ height: rootHeight, width: containerWidth }}
onMouseEnter={() => hideToolbar(false)}
onMouseLeave={() => hideToolbar(true)}
ref={refs.stage}
>
<div className={css.container} style={{ height: containerHeight, width: containerWidth }}>
<div className={css.page} style={pageStyle}>
<Page page={pages[page]} />
</div>
</div>
<Footer hidden={toolbarHidden} />
</div>
);
};

View file

@ -1,27 +0,0 @@
@import '@elastic/eui/src/global_styling/variables/_size.scss';
@import '@elastic/eui/src/global_styling/variables/_colors.scss';
:global .kbnCanvas :local .root .bar {
position: absolute;
}
.bar {
composes: euiBottomBar from global;
}
:global .kbnCanvas :local .bar {
transition: bottom 0.25s;
padding: $euiSizeM;
}
:global .kbnCanvas :local .title {
overflow: hidden;
min-width: 0;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
}
:global .kbnCanvas .euiIcon__fillNegative {
fill: $euiColorGhost !important;
}

View file

@ -1,61 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { useExternalEmbedState, setScrubberVisible } from '../../context';
import { Scrubber } from './scrubber';
import { Title } from './title';
import { PageControls } from './page_controls';
import { Settings } from './settings';
// @ts-ignore CSS Module
import css from './footer.module';
export const FOOTER_HEIGHT = 48;
interface Props {
hidden?: boolean;
}
/**
* The footer of the Embedded Workpad.
*/
export const Footer = ({ hidden = false }: Props) => {
const [{ workpad, settings }] = useExternalEmbedState();
if (!workpad) {
return null;
}
const { autohide } = settings.toolbar;
// If autohide is enabled, and the toolbar is hidden, set the scrubber
// visibility to hidden. This is useful for state changes where one
// sets the footer to hidden, and the scrubber would be left open with
// no toolbar.
if (autohide && hidden) {
setScrubberVisible(false);
}
return (
<div className={css.root} style={{ height: FOOTER_HEIGHT }}>
<Scrubber />
<div className={css.bar} style={{ bottom: autohide && hidden ? -FOOTER_HEIGHT : 0 }}>
<EuiFlexGroup gutterSize="none">
<EuiFlexItem className={css.title}>
<Title />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s">
<PageControls />
<Settings />
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</div>
</div>
);
};

View file

@ -1,7 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './footer';

View file

@ -1,62 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiButtonEmpty, EuiText } from '@elastic/eui';
import { useExternalEmbedState, setScrubberVisible, setPage, setAutoplay } from '../../context';
/**
* The page count and paging controls within the footer of the Embedded Workpad.
*/
export const PageControls = () => {
const [{ workpad, footer, page }, dispatch] = useExternalEmbedState();
if (!workpad) {
return null;
}
const { isScrubberVisible } = footer;
const toggleScrubber = () => {
dispatch(setAutoplay(false));
dispatch(setScrubberVisible(!isScrubberVisible));
};
const setPageNumber = (number: number) => dispatch(setPage(number));
const currentPage = page + 1;
const totalPages = workpad.pages.length;
return (
<EuiFlexGroup alignItems="center" gutterSize="none" style={{ margin: '0 12px' }}>
<EuiFlexItem grow={false}>
<EuiButtonIcon
color="ghost"
onClick={() => setPageNumber(page - 1)}
iconType="arrowLeft"
disabled={currentPage <= 1}
aria-label="Previous Page"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="ghost" size="s" onClick={toggleScrubber}>
<EuiText color="ghost" size="s">
Page {currentPage}
{totalPages > 1 ? ` of ${totalPages}` : null}
</EuiText>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
color="ghost"
onClick={() => setPageNumber(page + 1)}
iconType="arrowRight"
disabled={currentPage >= totalPages}
aria-label="Next Page"
/>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -1,15 +0,0 @@
@import '@elastic/eui/src/global_styling/variables/_size.scss';
:global .kbnCanvas :local .root {
margin: 0 $euiSizeS;
cursor: pointer;
}
:global .kbnCanvas :local .root :global .canvasPage {
position: relative;
}
:global .kbnCanvas :local .preview {
pointer-events: none;
transform-origin: top left;
}

View file

@ -1,55 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { useExternalEmbedState } from '../../context';
import { Page } from '../page';
import { setPage } from '../../context/actions';
import { CanvasRenderedPage } from '../../types';
// @ts-ignore CSS Module
import css from './page_preview.module';
interface Props {
number: number;
height: number;
page: CanvasRenderedPage;
}
/**
* The small preview of the page shown within the `Scrubber`.
*/
export const PagePreview = ({ number, page, height }: Props) => {
const [{ workpad }, dispatch] = useExternalEmbedState();
if (!workpad) {
return null;
}
const onClick = (index: number) => dispatch(setPage(index));
const { height: workpadHeight, width: workpadWidth } = workpad;
const scale = height / workpadHeight;
const style = {
height: workpadHeight * scale,
width: workpadWidth * scale,
};
const transform = {
...style,
transform: `scale3d(${scale}, ${scale}, 1)`,
};
return (
<div
className={css.root}
onClick={() => onClick(number)}
onKeyPress={() => onClick(number)}
style={style}
>
<div className={css.preview} style={transform}>
<Page page={page} />
</div>
</div>
);
};

View file

@ -1,26 +0,0 @@
@import '@elastic/eui/src/global_styling/variables/_size.scss';
@import '@elastic/eui/src/global_styling/variables/_colors.scss';
@import '@elastic/eui/src/global_styling/mixins/_helpers.scss';
:global .kbnCanvas :local .root {
background: $euiColorGhost;
position: absolute;
bottom: -172px;
left: 0;
right: 0;
padding: $euiSizeS 0 ($euiSizeL * 2) 0;
transition: bottom 0.25s;
height: 172px;
}
:global .kbnCanvas :local .visible {
bottom: 0;
}
:global .kbnCanvas :local .slideContainer {
@include euiScrollBar;
display: flex;
overflow-x: auto;
overflow-y: hidden;
width: 100%;
}

View file

@ -1,41 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import classnames from 'classnames';
import { useExternalEmbedState } from '../../context';
import { PagePreview } from './page_preview';
// @ts-ignore CSS Module
import css from './scrubber.module';
const THUMBNAIL_HEIGHT = 100;
/**
* The panel of previews of the pages in the workpad, allowing one to select and
* navigate to a specific page.
*/
export const Scrubber = () => {
const [{ workpad, footer }] = useExternalEmbedState();
if (!workpad) {
return null;
}
const { pages } = workpad;
const { isScrubberVisible } = footer;
const className = isScrubberVisible ? classnames(css.root, css.visible) : css.root;
const slides = pages.map((page, index) => (
<PagePreview key={page.id} page={page} number={index} height={THUMBNAIL_HEIGHT} />
));
return (
<div className={className}>
<div className={css.slideContainer}>{slides}</div>
</div>
);
};

View file

@ -1,38 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiHorizontalRule, EuiSwitch } from '@elastic/eui';
import { useExternalEmbedState, setAutoplay, setAutoplayInterval } from '../../../context';
import { createTimeInterval } from '../../../../public/lib/time_interval';
// @ts-ignore Untyped local
import { CustomInterval } from '../../../../public/components/workpad_header/control_settings/custom_interval';
/**
* The panel used to configure Autolay in Embedded Workpads.
*/
export const AutoplaySettings = () => {
const [{ settings }, dispatch] = useExternalEmbedState();
const { autoplay } = settings;
return (
<div style={{ padding: 16 }}>
<EuiSwitch
name="cycle"
id="cycle"
label="Cycle Slides"
checked={autoplay.enabled}
onChange={() => dispatch(setAutoplay(!autoplay.enabled))}
/>
<EuiHorizontalRule margin="m" />
<CustomInterval
defaultValue={autoplay.interval}
onSubmit={(value: number) => dispatch(setAutoplayInterval(createTimeInterval(value)))}
/>
</div>
);
};

View file

@ -1,7 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './settings';

View file

@ -1,98 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiPopover, EuiContextMenu } from '@elastic/eui';
import { useExternalEmbedState } from '../../../context';
// @ts-ignore Untyped local
import { CustomInterval } from '../../../../public/components/workpad_header/control_settings/custom_interval';
import { ToolbarSettings } from './toolbar_settings';
import { AutoplaySettings } from './autoplay_settings';
// @ts-ignore CSS Module
import css from './settings.module';
/**
* The Settings Popover for External Workpads.
*/
export const Settings = () => {
const [{ workpad, refs }] = useExternalEmbedState();
if (!workpad) {
return null;
}
const [isPopoverOpen, setPopoverOpen] = useState(false);
const button = (
<EuiButtonIcon
color="ghost"
iconType="gear"
aria-label="Settings"
onClick={() => setPopoverOpen(!isPopoverOpen)}
/>
);
const flattenPanelTree = (tree: any, array: any[] = []) => {
array.push(tree);
if (tree.items) {
tree.items.forEach((item: any) => {
if (item.panel) {
flattenPanelTree(item.panel, array);
item.panel = item.panel.id;
}
});
}
return array;
};
const panels = flattenPanelTree({
id: 0,
title: 'Settings',
items: [
{
name: 'Auto Play',
icon: 'play',
panel: {
id: 1,
title: 'Auto Play',
content: <AutoplaySettings />,
},
},
{
name: 'Toolbar',
icon: 'boxesHorizontal',
panel: {
id: 2,
title: 'Toolbar',
content: <ToolbarSettings onChange={() => setPopoverOpen(false)} />,
},
},
],
});
return (
<EuiFlexGroup alignItems="flexEnd" justifyContent="center" direction="column" gutterSize="none">
<EuiFlexItem grow={false}>
{/*
//@ts-ignore EuiPopover missing insert property */}
<EuiPopover
closePopover={() => setPopoverOpen(false)}
id="settings"
isOpen={isPopoverOpen}
button={button}
panelPaddingSize="none"
withTitle
anchorPosition="upRight"
insert={{ sibling: refs.stage.current, position: 'after' }}
>
<EuiContextMenu initialPanelId={0} panels={panels} />
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -1,42 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiSwitch, EuiFormRow } from '@elastic/eui';
import { useExternalEmbedState, setToolbarAutohide } from '../../../context';
// @ts-ignore CSS Module
import css from './settings.module';
interface Props {
onChange?: () => void;
}
/**
* The settings panel for the Toolbar of an Embedded Workpad.
*/
export const ToolbarSettings = ({ onChange = () => {} }: Props) => {
const [{ settings }, dispatch] = useExternalEmbedState();
const { toolbar } = settings;
return (
<div style={{ padding: 16 }}>
<EuiFormRow helpText="Hide the toolbar when the mouse is not within the Canvas?">
<EuiSwitch
name="toolbarHide"
id="toolbarHide"
label="Hide Toolbar"
checked={toolbar.autohide}
onChange={() => {
onChange();
dispatch(setToolbarAutohide(!toolbar.autohide));
}}
/>
</EuiFormRow>
</div>
);
};

View file

@ -1,33 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui';
import { useExternalEmbedState } from '../../context';
/**
* The title of the workpad displayed in the right-hand of the footer.
*/
export const Title = () => {
const [{ workpad }] = useExternalEmbedState();
if (!workpad) {
return null;
}
return (
<EuiFlexGroup gutterSize="s" justifyContent="flexStart" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type="logoKibana" size="m" />
</EuiFlexItem>
<EuiFlexItem grow={false} style={{ minWidth: 0 }}>
<EuiText color="ghost" size="s">
<div className="eui-textTruncate">{workpad.name}</div>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -1,6 +0,0 @@
.root {
composes: canvasPage from global;
composes: canvasInteractivePage from global;
composes: kbn-resetFocusState from global;
overflow: 'hidden';
}

View file

@ -1,37 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { RenderedElement } from './rendered_element';
import { useExternalEmbedState } from '../context';
import { CanvasRenderedPage, CanvasRenderedElement } from '../types';
// @ts-ignore CSS Module
import css from './page.module';
interface Props {
page: CanvasRenderedPage;
}
export const Page = (props: Props) => {
const [{ workpad }] = useExternalEmbedState();
if (!workpad) {
return null;
}
const { height, width, id } = workpad;
const { elements, style } = props.page;
const output = elements.map((element: CanvasRenderedElement, index) => (
<RenderedElement key={element.id} element={element} number={index + 1} />
));
return (
<div id={`page-${id}`} className={css.root} style={{ height, width, ...style }}>
{output}
</div>
);
};

View file

@ -1,19 +0,0 @@
:global .kbnCanvas :local .root,
:global .kbnCanvas :local .render {
height: 100%;
width: 100%;
}
.container {
composes: canvas__element from global;
composes: canvasElement from global;
}
.content {
composes: canvasElement__content from global;
}
.renderContainer {
composes: canvasWorkpad--element_render from global;
composes: canvasRenderEl from global;
}

View file

@ -1,88 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
// @ts-ignore Untyped library
import Style from 'style-it';
// @ts-ignore Untyped local
import { Positionable } from '../../public/components/positionable/positionable';
// @ts-ignore Untyped local
import { elementToShape } from '../../public/components/workpad_page/utils';
import { CanvasRenderedElement } from '../types';
import { ExternalEmbedContext } from '../context';
// @ts-ignore CSS Module
import css from './rendered_element.module';
interface Props {
element: CanvasRenderedElement;
number?: number;
}
/**
* A Rendered Element is different from an Element added to a Canvas Workpad. A
* Rendered Element has actually be evaluated already to gather any data from
* datasources, and is just a simple expression to render the result. This
* component renders that "transient" element state.
*/
export class RenderedElement extends React.PureComponent<Props> {
static contextType = ExternalEmbedContext;
protected ref: React.RefObject<HTMLDivElement>;
constructor(props: Props) {
super(props);
this.ref = React.createRef();
}
componentDidMount() {
const [{ renderersRegistry }] = this.context;
const { element } = this.props;
const { expressionRenderable } = element;
const { value } = expressionRenderable;
const { as } = value;
const fn = renderersRegistry.get(as);
try {
// TODO: These are stubbed, but may need implementation.
fn.render(this.ref.current, value.value, {
done: () => {},
onDestroy: () => {},
onResize: () => {},
setFilter: () => {},
getFilter: () => {},
});
} catch (e) {
// eslint-disable-next-line no-console
console.log(e.message);
}
}
render() {
const { element, number } = this.props;
const shape = elementToShape(element, number || 1);
const { id, expressionRenderable, position } = element;
const { value } = expressionRenderable;
const { as, css: elementCSS, containerStyle } = value;
const { height, width } = position;
return (
<Positionable height={height} width={width} transformMatrix={shape.transformMatrix}>
<div className={css.root}>
{Style.it(
elementCSS,
<div className={css.container} style={{ ...containerStyle }}>
<div className={css.content}>
<div className={css.renderContainer}>
<div key={id} ref={this.ref} data-renderer={as} className={css.render} />
</div>
</div>
</div>
)}
</div>
</Positionable>
);
}
}

View file

@ -1,13 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export const KIBANA_ROOT: string;
export const LIBRARY_NAME: string;
export const RUNTIME_FILE: string;
export const RUNTIME_NAME: string;
export const RUNTIME_OUTPUT: string;
export const RUNTIME_SRC: string;
export const STATS_OUTPUT: string;

View file

@ -1,25 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
const path = require('path');
const LIBRARY_NAME = 'KbnCanvas';
const RUNTIME_NAME = 'kbn_canvas';
const KIBANA_ROOT = path.resolve(__dirname, '../../../../..');
const RUNTIME_SRC = path.resolve(KIBANA_ROOT, 'x-pack/legacy/plugins/canvas/external_runtime');
const RUNTIME_OUTPUT = path.resolve(RUNTIME_SRC, 'build');
const RUNTIME_FILE = path.resolve(RUNTIME_OUTPUT, RUNTIME_NAME + '.js');
const STATS_OUTPUT = path.resolve(RUNTIME_OUTPUT, 'webpack_stats.json');
module.exports = {
KIBANA_ROOT,
LIBRARY_NAME,
RUNTIME_FILE,
RUNTIME_NAME,
RUNTIME_OUTPUT,
RUNTIME_SRC,
STATS_OUTPUT,
};

View file

@ -1,100 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { CanvasRenderedWorkpad } from '../types';
/**
* This enumeration applies a strong type to all of the actions that can be
* triggered from the interface.
*/
export enum ExternalEmbedActions {
SET_WORKPAD = 'SET_WORKPAD',
SET_PAGE = 'SET_PAGE',
SET_SCRUBBER_VISIBLE = 'SET_SCRUBBER_VISIBLE',
SET_AUTOPLAY = 'SET_AUTOPLAY',
SET_AUTOPLAY_ANIMATE = 'SET_AUTOPLAY_ANIMATE',
SET_AUTOPLAY_INTERVAL = 'SET_AUTOPLAY_INTERVAL',
SET_TOOLBAR_AUTOHIDE = 'SET_TOOLBAR_AUTOHIDE',
}
interface FluxAction<T, P> {
type: T;
payload: P;
}
const createAction = <T extends ExternalEmbedActions, P>(
type: T,
payload: P
): FluxAction<T, P> => ({
type,
payload,
});
/**
* Set the current `CanvasRenderedWorkpad`.
* @param workpad A `CanvasRenderedWorkpad` to display.
*/
export const setWorkpad = (workpad: CanvasRenderedWorkpad) =>
createAction(ExternalEmbedActions.SET_WORKPAD, { workpad });
/**
* Set the current page to display
* @param page The zero-indexed page to display.
*/
export const setPage = (page: number) => createAction(ExternalEmbedActions.SET_PAGE, { page });
/**
* Set the visibility of the page scrubber.
* @param visible True if it should be visible, false otherwise.
*/
export const setScrubberVisible = (visible: boolean) => {
return createAction(ExternalEmbedActions.SET_SCRUBBER_VISIBLE, { visible });
};
/**
* Set whether the slides should automatically advance.
* @param autoplay True if it should automatically advance, false otherwise.
*/
export const setAutoplay = (autoplay: boolean) =>
createAction(ExternalEmbedActions.SET_AUTOPLAY, { autoplay });
/**
* Set whether the slides should animate when advanced.
* @param animate True if it should animate when advanced, false otherwise.
*/
export const setAutoplayAnimate = (animate: boolean) =>
createAction(ExternalEmbedActions.SET_AUTOPLAY_ANIMATE, { animate });
/**
* Set the interval in which slide will advance. This is a `string` identical to
* that used in Canvas proper: `1m`, `2s`, etc.
* @param autoplay The interval in which slides should advance.
*/
export const setAutoplayInterval = (interval: string) =>
createAction(ExternalEmbedActions.SET_AUTOPLAY_INTERVAL, { interval });
/**
* Set if the toolbar should be hidden if the mouse is not within the bounds of the
* embedded workpad.
* @param autohide True if the toolbar should hide, false otherwise.
*/
export const setToolbarAutohide = (autohide: boolean) =>
createAction(ExternalEmbedActions.SET_TOOLBAR_AUTOHIDE, { autohide });
const actions = {
setWorkpad,
setPage,
setScrubberVisible,
setAutoplay,
setAutoplayAnimate,
setAutoplayInterval,
setToolbarAutohide,
};
/**
* Strongly-types the correlation between an `action` and its return.
*/
export type ExternalEmbedAction = ReturnType<typeof actions[keyof typeof actions]>;

View file

@ -1,8 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './state';
export * from './actions';

View file

@ -1,105 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ExternalEmbedState } from './state';
import { ExternalEmbedAction, ExternalEmbedActions } from './actions';
/**
* The Action Reducer for the Embedded Canvas Workpad interface.
*/
export const reducer = (state: ExternalEmbedState, action: ExternalEmbedAction) => {
switch (action.type) {
case ExternalEmbedActions.SET_WORKPAD: {
return {
...state,
workpad: action.payload.workpad,
};
}
case ExternalEmbedActions.SET_PAGE: {
return {
...state,
page: action.payload.page,
};
}
case ExternalEmbedActions.SET_SCRUBBER_VISIBLE: {
const { footer } = state;
return {
...state,
footer: {
...footer,
isScrubberVisible: action.payload.visible,
},
};
}
case ExternalEmbedActions.SET_AUTOPLAY: {
const { settings } = state;
const { autoplay } = settings;
return {
...state,
settings: {
...settings,
autoplay: {
...autoplay,
enabled: action.payload.autoplay,
},
},
};
}
case ExternalEmbedActions.SET_AUTOPLAY_ANIMATE: {
const { settings } = state;
const { autoplay } = settings;
const { animate } = action.payload;
return {
...state,
settings: {
...settings,
autoplay: {
...autoplay,
animate,
},
},
};
}
case ExternalEmbedActions.SET_AUTOPLAY_INTERVAL: {
const { settings } = state;
const { autoplay } = settings;
const { interval } = action.payload;
return {
...state,
settings: {
...settings,
autoplay: {
...autoplay,
interval,
},
},
};
}
case ExternalEmbedActions.SET_TOOLBAR_AUTOHIDE: {
const { settings } = state;
const { toolbar } = settings;
const { autohide } = action.payload;
return {
...state,
settings: {
...settings,
toolbar: {
...toolbar,
autohide,
},
},
};
}
default: {
return state;
}
}
};

View file

@ -1,86 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, {
createContext,
useContext,
Dispatch,
useReducer,
ReactChild,
RefObject,
} from 'react';
import { CanvasRenderedWorkpad } from '../types';
import { reducer } from './reducer';
import { ExternalEmbedAction } from './actions';
export interface ExternalEmbedState {
renderersRegistry: {
register: (fn: Function) => void;
get: (name: string) => Function;
} | null;
workpad: CanvasRenderedWorkpad | null;
page: number;
height: number;
width: number;
footer: {
isScrubberVisible: boolean;
};
settings: {
autoplay: {
enabled: boolean;
interval: string;
animate: boolean;
};
toolbar: {
autohide: boolean;
};
};
refs: {
stage: RefObject<HTMLDivElement>;
};
}
type StateType = [ExternalEmbedState, Dispatch<ExternalEmbedAction>];
export const initialExternalEmbedState: ExternalEmbedState = {
renderersRegistry: null,
workpad: null,
page: 0,
height: 0,
width: 0,
footer: {
isScrubberVisible: false,
},
settings: {
autoplay: {
enabled: false,
interval: '5s',
animate: false,
},
toolbar: {
autohide: false,
},
},
refs: {
stage: React.createRef(),
},
};
export const ExternalEmbedContext = createContext<StateType>([initialExternalEmbedState, () => {}]);
export const ExternalEmbedStateProvider = ({
initialState,
children,
}: {
initialState: ExternalEmbedState;
children: ReactChild;
}) => (
<ExternalEmbedContext.Provider value={useReducer(reducer, initialState)}>
{children}
</ExternalEmbedContext.Provider>
);
export const useExternalEmbedState = () => useContext<StateType>(ExternalEmbedContext);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

View file

@ -1,14 +0,0 @@
<html>
<head>
<script src="kbn_canvas.js"></script>
</head>
<body>
<div kbn-canvas-embed="canvas" kbn-canvas-height="400" kbn-canvas-url="/test/austin.json"></div>
</body>
<script type="text/javascript">
KbnCanvas.embed();
</script>
</html>

View file

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './api';
import '../../../../../built_assets/css/plugins/kibana/index.light.css';
import '../../../../../built_assets/css/plugins/canvas/style/index.light.css';
import '@elastic/eui/dist/eui_theme_light.css';
import '@kbn/ui-framework/dist/kui_light.css';
const css = require.context(
'../../../../../built_assets/css',
true,
/\.\/plugins\/(?!canvas).*light\.css/
);
css.keys().forEach(filename => {
css(filename);
});
const uiStyles = require.context(
'../../../../../src/legacy/ui/public/styles',
false,
/[\/\\](?!mixins|variables|_|\.|bootstrap_(light|dark))[^\/\\]+\.less/
);
uiStyles.keys().forEach(key => uiStyles(key));

View file

@ -1,25 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// eslint-disable-next-line
const autoprefixer = require('autoprefixer');
const prefixer = require('postcss-prefix-selector');
module.exports = {
plugins: [
prefixer({
prefix: '.kbnCanvas',
transform: function(prefix, selector, prefixedSelector) {
if (selector === 'body' || selector === 'html') {
return prefix;
} else {
return prefixedSelector;
}
},
}),
autoprefixer(),
],
};

View file

@ -1,14 +0,0 @@
<html>
<head>
<script src="kbn_canvas.js"></script>
</head>
<body>
<div kbn-canvas-embed="canvas" kbn-canvas-height="400" kbn-canvas-url="workpad.json"></div>
</body>
<script type="text/javascript">
KbnCanvas.embed();
</script>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -1,57 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// @ts-ignore Unlinked Webpack Type
import ContainerStyle from 'types/interpreter';
import { CSSProperties } from 'react';
import { SavedObject, SavedObjectAttributes } from 'src/core/server';
import { CanvasElement, CanvasPage, CanvasWorkpad } from '../types';
/**
* Represents a Canvas Element whose expression has been evaluated and now
* exists in a transient, ready-to-render state.
*/
export interface CanvasRenderedElement extends CanvasElement {
expressionRenderable: CanvasRenderable;
}
/**
* Represents a Page within a Canvas Workpad that is made up of ready-to-
* render Elements.
*/
export interface CanvasRenderedPage extends CanvasPage {
elements: CanvasRenderedElement[];
groups: CanvasRenderedElement[][];
}
/**
* A Canvas Workpad made up of ready-to-render Elements.
*/
export interface CanvasRenderedWorkpad extends CanvasWorkpad {
pages: CanvasRenderedPage[];
}
export type CanvasRenderedWorkpadSavedObject = SavedObject<
CanvasRenderedWorkpad & SavedObjectAttributes
>;
/**
* Represents the success/failure of the initial evaluation of a Canvas
* Element, as well as the Function and CSS necessary to render the Element
* upon a stage.
*/
export interface CanvasRenderable {
error: string;
state: 'ready' | 'error';
value: {
as: string;
containerStyle: ContainerStyle;
css: CSSProperties;
type: 'render';
value: any;
};
}

View file

@ -1,182 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
const path = require('path');
const webpack = require('webpack');
const { KIBANA_ROOT, RUNTIME_OUTPUT, LIBRARY_NAME, RUNTIME_NAME } = require('./constants');
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
context: KIBANA_ROOT,
entry: {
[RUNTIME_NAME]: require.resolve('./index.ts'),
},
mode: isProd ? 'production' : 'development',
plugins: isProd ? [new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 })] : [],
output: {
path: RUNTIME_OUTPUT,
filename: '[name].js',
library: LIBRARY_NAME,
},
// Include a require alias for legacy UI code and styles
resolve: {
alias: {
ui: path.resolve(KIBANA_ROOT, 'src/legacy/ui/public'),
'data/interpreter': path.resolve(
KIBANA_ROOT,
'src/plugins/data/public/expressions/interpreter'
),
'kbn/interpreter': path.resolve(KIBANA_ROOT, 'packages/kbn-interpreter/target/common'),
'types/interpreter': path.resolve(
KIBANA_ROOT,
'src/legacy/core_plugins/interpreter/public/types'
),
},
extensions: ['.js', '.json', '.ts', '.tsx', '.scss'],
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: 'babel-loader',
options: {
presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
},
sideEffects: false,
},
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
},
},
],
sideEffects: false,
},
{
test: /\.css$/,
exclude: /components/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
config: {
path: require.resolve('./postcss.config.js'),
},
},
},
{
loader: 'string-replace-loader',
options: {
search: '__REPLACE_WITH_PUBLIC_PATH__',
replace: '/',
flags: 'g',
},
},
],
},
{
test: /\.module\.s(a|c)ss$/,
loader: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
camelCase: true,
sourceMap: !isProd,
},
},
{
loader: 'postcss-loader',
options: {
path: path.resolve(KIBANA_ROOT, 'src/optimize/postcss.config.js'),
},
},
{
loader: 'sass-loader',
options: {
sourceMap: !isProd,
},
},
],
},
{
test: /\.less$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{
loader: 'postcss-loader',
options: {
config: {
path: require.resolve('./postcss.config.js'),
},
},
},
{ loader: 'less-loader' },
],
},
{
test: /\.scss$/,
exclude: /\.module.(s(a|c)ss)$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{
loader: 'postcss-loader',
options: {
config: {
path: require.resolve('./postcss.config.js'),
},
},
},
{ loader: 'sass-loader' },
],
},
{
test: require.resolve('jquery'),
loader: 'expose-loader?jQuery!expose-loader?$',
},
{
test: /\.(woff|woff2|ttf|eot|svg|ico)(\?|$)/,
loader: 'url-loader',
sideEffects: false,
},
{
test: /\.html$/,
loader: 'html-loader',
exclude: /node_modules/,
},
{
test: [
require.resolve('@elastic/eui/es/components/code_editor'),
require.resolve('@elastic/eui/es/components/drag_and_drop'),
require.resolve('@elastic/eui/packages/react-datepicker'),
require.resolve('highlight.js'),
/canvas_plugin_src\/renderers\/advanced_filter/,
/canvas_plugin_src\/renderers\/dropdown_filter/,
/canvas_plugin_src\/renderers\/embeddable.tsx/,
/canvas_plugin_src\/renderers\/time_filter/,
],
use: 'null-loader',
},
],
},
node: {
fs: 'empty',
child_process: 'empty',
},
};

View file

@ -1,89 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots components/Export/WorkpadExport disabled 1`] = `
<div>
<div
className="euiPopover euiPopover--anchorDownCenter"
container={null}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
>
<div
className="euiPopover euiPopover--anchorDownCenter"
container={null}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
className="euiPopover__anchor"
>
<div
className="euiPopover__anchor"
<span
className="euiToolTipAnchor"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<span
className="euiToolTipAnchor"
onMouseOut={[Function]}
onMouseOver={[Function]}
<button
aria-label="Share this workpad"
className="euiButtonIcon euiButtonIcon--primary"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<button
aria-label="Share this workpad"
className="euiButtonIcon euiButtonIcon--primary"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</button>
</span>
</div>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</button>
</span>
</div>
</div>
`;
exports[`Storyshots components/Export/WorkpadExport enabled 1`] = `
<div>
<div
className="euiPopover euiPopover--anchorDownCenter"
container={null}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
>
<div
className="euiPopover euiPopover--anchorDownCenter"
container={null}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
className="euiPopover__anchor"
>
<div
className="euiPopover__anchor"
<span
className="euiToolTipAnchor"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<span
className="euiToolTipAnchor"
onMouseOut={[Function]}
onMouseOver={[Function]}
<button
aria-label="Share this workpad"
className="euiButtonIcon euiButtonIcon--primary"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<button
aria-label="Share this workpad"
className="euiButtonIcon euiButtonIcon--primary"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</button>
</span>
</div>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</button>
</span>
</div>
</div>
`;

View file

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import React from 'react';
import { ExternalEmbedFlyout } from '../external_embed_flyout';
storiesOf('components/Export/ExternalEmbedFlyout', module)
.addParameters({
info: {
inline: true,
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '20px 30px',
width: '620px',
},
},
},
})
.add('default', () => (
<ExternalEmbedFlyout
onCopy={action('onCopy')}
onExport={action('onExport')}
onClose={action('onClose')}
/>
));

View file

@ -1,191 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import {
EuiText,
EuiSpacer,
EuiCode,
EuiCallOut,
EuiCodeBlock,
EuiButton,
EuiSteps,
EuiDescriptionList,
EuiDescriptionListTitle,
EuiDescriptionListDescription,
EuiHorizontalRule,
EuiFlyout,
EuiFlyoutHeader,
EuiFlyoutBody,
EuiTitle,
EuiLink,
} from '@elastic/eui';
import { OnCopyFn, OnExportFn, OnCloseFn } from '../workpad_export';
import { Clipboard } from '../../../clipboard';
interface Props {
onCopy: OnCopyFn;
onExport: OnExportFn;
onClose: OnCloseFn;
}
const staticEmbedSteps = (onExport: OnExportFn, onCopy: OnCopyFn, onClose: OnCloseFn) => [
{
title: 'Download workpad',
children: (
<EuiText size="s">
<p>The workpad will be exported as a single JSON file for embedding in another site.</p>
<EuiSpacer size="s" />
<EuiButton
onClick={() => {
onExport('embed');
}}
size="s"
>
Download workpad
</EuiButton>
</EuiText>
),
},
{
title: 'Download runtime',
children: (
<EuiText size="s">
<p>
In order to render the Workpad, you also need to include the Canvas Embed Runtime. You can
skip this step if the runtime is already included on your website.
</p>
<EuiSpacer size="s" />
<EuiButton
onClick={() => {
onExport('runtime');
}}
size="s"
>
Download runtime
</EuiButton>
</EuiText>
),
},
{
title: 'Add snippets to website',
children: (
<div>
<EuiText size="s">
<p>
The Workpad is embedded in the HTML of the site by using an HTML placeholder. Parameters
for the runtime are included inline. See the full list of embed parameters below. You
can include more than one workpad on the page .
</p>
</EuiText>
<EuiSpacer size="s" />
<Clipboard
content={HTML}
onCopy={() => {
onCopy('embed');
}}
>
<EuiCodeBlock
className="canvasWorkpadExport__reportingConfig"
paddingSize="s"
fontSize="s"
language="html"
>
{HTML}
</EuiCodeBlock>
</Clipboard>
<EuiSpacer />
<EuiText>
<h4>Embed parameters</h4>
<p>There are a number of inline parameters to configure the embedded Workpad.</p>
</EuiText>
<EuiHorizontalRule />
<EuiDescriptionList>
<EuiDescriptionListTitle>
<EuiCode>kbn-canvas-embed="canvas"</EuiCode> (required)
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
The type of embed. In this case, a Canvas Workpad.
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiCode>kbn-canvas-url</EuiCode> (required)
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
The URL of the Workpad Snapshot JSON file.
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiCode>kbn-canvas-height</EuiCode>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
The height of the Workpad. Defaults to the Workpad height.
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiCode>kbn-canvas-width</EuiCode>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
The width of the Workpad. Defaults to the Workpad width.
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiCode>kbn-canvas-page</EuiCode>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
The page to display. Defaults to the page specified by the Workpad.
</EuiDescriptionListDescription>
</EuiDescriptionList>
</div>
),
},
];
const HTML = `<!-- Include Runtime -->
<script src="kbnCanvas.js"></script>
<!-- Placeholder -->
<div kbn-canvas-embed="canvas" kbn-canvas-url="workpad.json" />
<!-- Call Runtime -->
<script type="text/javascript">
KbnCanvas.embed();
</script>`;
export const ExternalEmbedFlyout = ({ onCopy, onExport, onClose }: Props) => (
<EuiFlyout onClose={() => onClose('embed')} maxWidth>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2 id="flyoutTitle">Embed on a website</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiText size="s">
<p>
Follow these steps to embed a static version of this workpad on an external website. It
will be a visual snapshot of the current workpad, and will not have access to live data.
</p>
</EuiText>
<EuiSpacer />
<EuiCallOut
size="s"
title={
<div>
To try embedding, you can{' '}
<EuiLink
style={{ textDecoration: 'underline' }}
onClick={() => {
onExport('zip');
}}
>
download an example ZIP file
</EuiLink>{' '}
containing this workpad, the Canvas runtime, and a sample HTML file.
</div>
}
iconType="iInCircle"
></EuiCallOut>
<EuiSpacer />
<EuiSteps steps={staticEmbedSteps(onExport, onCopy, onClose)} />
</EuiFlyoutBody>
</EuiFlyout>
);

View file

@ -4,36 +4,24 @@
* you may not use this file except in compliance with the Elastic License.
*/
import chrome from 'ui/chrome';
import { connect } from 'react-redux';
import { compose, withProps } from 'recompose';
import { jobCompletionNotifications } from '../../../../../reporting/public/lib/job_completion_notifications';
// @ts-ignore Untyped local
import { getWorkpad, getPages, getRenderedWorkpad } from '../../../state/selectors/workpad';
import { getWorkpad, getPages } from '../../../state/selectors/workpad';
// @ts-ignore Untyped local
import { getReportingBrowserType } from '../../../state/selectors/app';
// @ts-ignore Untyped local
import { notify } from '../../../lib/notify';
import { getWindow } from '../../../lib/get_window';
// @ts-ignore Untyped local
import {
downloadWorkpad,
downloadRenderedWorkpad,
downloadEmbedRuntime,
downloadZippedEmbed,
// @ts-ignore Untyped local
} from '../../../lib/download_workpad';
import { downloadWorkpad } from '../../../lib/download_workpad';
import { WorkpadExport as Component, Props as ComponentProps } from './workpad_export';
import { getPdfUrl, createPdf } from './utils';
import { CanvasWorkpad } from '../../../../types';
import { CanvasRenderedWorkpad } from '../../../../external_runtime/types';
// @ts-ignore Untyped local.
import { fetch, arrayBufferFetch } from '../../../../common/lib/fetch';
import { API_ROUTE_SNAPSHOT_ZIP } from '../../../../common/lib/constants';
const mapStateToProps = (state: any) => ({
workpad: getWorkpad(state),
renderedWorkpad: getRenderedWorkpad(state),
pageCount: getPages(state).length,
enabled: getReportingBrowserType(state) === 'chromium',
});
@ -51,7 +39,6 @@ const getAbsoluteUrl = (path: string) => {
interface Props {
workpad: CanvasWorkpad;
renderedWorkpad: CanvasRenderedWorkpad;
pageCount: number;
enabled: boolean;
}
@ -59,7 +46,7 @@ interface Props {
export const WorkpadExport = compose<ComponentProps, Props>(
connect(mapStateToProps),
withProps(
({ workpad, pageCount, enabled, renderedWorkpad }: Props): ComponentProps => ({
({ workpad, pageCount, enabled }: Props): ComponentProps => ({
enabled,
getExportUrl: type => {
if (type === 'pdf') {
@ -76,11 +63,8 @@ export const WorkpadExport = compose<ComponentProps, Props>(
case 'reportingConfig':
notify.info(`Copied reporting configuration to clipboard`);
break;
case 'embed':
notify.info(`Copied embed code to clipboard`);
break;
default:
throw new Error(`Unknown copy type: ${type}`);
throw new Error(`Unknown export type: ${type}`);
}
},
onExport: type => {
@ -100,19 +84,7 @@ export const WorkpadExport = compose<ComponentProps, Props>(
});
case 'json':
downloadWorkpad(workpad.id);
return;
case 'embed':
downloadRenderedWorkpad(renderedWorkpad);
return;
case 'runtime':
downloadEmbedRuntime();
return;
case 'zip':
const basePath = chrome.getBasePath();
arrayBufferFetch
.post(`${basePath}${API_ROUTE_SNAPSHOT_ZIP}`, JSON.stringify(renderedWorkpad))
.then(blob => downloadZippedEmbed(blob.data));
return;
break;
default:
throw new Error(`Unknown export type: ${type}`);
}

View file

@ -4,25 +4,22 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent, useState } from 'react';
import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import { EuiButtonIcon, EuiContextMenu, EuiIcon } from '@elastic/eui';
// @ts-ignore Untyped local
import { Popover } from '../../popover';
import { DisabledPanel } from './disabled_panel';
import { PDFPanel } from './pdf_panel';
import { ExternalEmbedFlyout } from './flyout/external_embed_flyout';
type ClosePopoverFn = () => void;
type CopyTypes = 'pdf' | 'reportingConfig' | 'embed';
type ExportTypes = 'pdf' | 'json' | 'embed' | 'runtime' | 'zip';
type CopyTypes = 'pdf' | 'reportingConfig';
type ExportTypes = 'pdf' | 'json';
type ExportUrlTypes = 'pdf';
type CloseTypes = 'embed';
export type OnCopyFn = (type: CopyTypes) => void;
export type OnExportFn = (type: ExportTypes) => void;
export type OnCloseFn = (type: CloseTypes) => void;
export type GetExportUrlFn = (type: ExportUrlTypes) => string;
export interface Props {
@ -45,13 +42,6 @@ export const WorkpadExport: FunctionComponent<Props> = ({
onExport,
getExportUrl,
}) => {
const [showFlyout, setShowFlyout] = useState(false);
const onClose = (type: CloseTypes) => {
if (type === 'embed') {
setShowFlyout(false);
}
};
// TODO: Fix all of this magic from EUI; this code is boilerplate from
// EUI examples and isn't easily typed.
const flattenPanelTree = (tree: any, array: any[] = []) => {
@ -116,14 +106,6 @@ export const WorkpadExport: FunctionComponent<Props> = ({
),
},
},
{
name: 'Embed on a website',
icon: <EuiIcon type="globe" size="m" />,
onClick: () => {
setShowFlyout(true);
closePopover();
},
},
],
});
@ -131,27 +113,17 @@ export const WorkpadExport: FunctionComponent<Props> = ({
<EuiButtonIcon iconType="share" aria-label="Share this workpad" onClick={togglePopover} />
);
const flyout = showFlyout ? (
<ExternalEmbedFlyout onClose={onClose} onCopy={onCopy} onExport={onExport} />
) : null;
return (
<div>
<Popover
button={exportControl}
panelPaddingSize="none"
tooltip="Share workpad"
tooltipPosition="bottom"
>
{({ closePopover }: { closePopover: ClosePopoverFn }) => (
<EuiContextMenu
initialPanelId={0}
panels={flattenPanelTree(getPanelTree(closePopover))}
/>
)}
</Popover>
{flyout}
</div>
<Popover
button={exportControl}
panelPaddingSize="none"
tooltip="Share workpad"
tooltipPosition="bottom"
>
{({ closePopover }: { closePopover: ClosePopoverFn }) => (
<EuiContextMenu initialPanelId={0} panels={flattenPanelTree(getPanelTree(closePopover))} />
)}
</Popover>
);
};

View file

@ -8,11 +8,61 @@ import { shallowEqual } from 'recompose';
import { getNodes, getSelectedPage } from '../../state/selectors/workpad';
import { addElement, removeElements, setMultiplePositions } from '../../state/actions/elements';
import { selectToplevelNodes } from '../../state/actions/transient';
import { matrixToAngle, multiply, rotateZ, translate } from '../../lib/aeroelastic/matrix';
import { arrayToMap, flatten, identity } from '../../lib/aeroelastic/functional';
import { getLocalTransformMatrix } from '../../lib/aeroelastic/layout_functions';
import { matrixToAngle } from '../../lib/aeroelastic/matrix';
import { isGroupId, elementToShape } from './utils';
export * from './utils';
export const isGroupId = id => id.startsWith('group');
const headerData = id =>
isGroupId(id)
? { id, type: 'group', subtype: 'persistentGroup' }
: { id, type: 'rectangleElement', subtype: '' };
const transformData = ({ top, left, width, height, angle }, z) =>
multiply(
translate(left + width / 2, top + height / 2, z), // painter's algo: latest item (highest z) goes to top
rotateZ((-angle / 180) * Math.PI) // minus angle as transform:matrix3d uses a left-handed coordinate system
);
const simplePosition = ({ id, position, filter }, z) => ({
...headerData(id),
width: position.width,
height: position.height,
transformMatrix: transformData(position, z),
filter,
});
export const simplePositioning = ({ elements }) => ({ elements: elements.map(simplePosition) });
/**
* elementToShape
*
* converts a `kibana-canvas` element to an `aeroelastic` shape.
*
* Shape: the layout algorithms need to deal with objects through their geometric properties, excluding other aspects,
* such as what's inside the element, eg. image or scatter plot. This representation is, at its core, a transform matrix
* that establishes a new local coordinate system https://drafts.csswg.org/css-transforms/#local-coordinate-system plus a
* size descriptor. There are two versions of the transform matrix:
* - `transformMatrix` is analogous to the SVG https://drafts.csswg.org/css-transforms/#current-transformation-matrix
* - `localTransformMatrix` is analogous to the SVG https://drafts.csswg.org/css-transforms/#transformation-matrix
*
* Element: it also needs to represent the geometry, primarily because of the need to persist it in `redux` and on the
* server, and to accept such data from the server. The redux and server representations will need to change as more general
* projections such as 3D are added. The element also needs to maintain its content, such as an image or a plot.
*
* While all elements on the current page also exist as shapes, there are shapes that are not elements: annotations.
* For example, `rotation_handle`, `border_resize_handle` and `border_connection` are modeled as shapes by the layout
* library, simply for generality.
*/
export const elementToShape = ({ id, position }, z) => ({
...headerData(id),
parent: (position && position.parent) || null,
transformMatrix: transformData(position, z),
a: position.width / 2, // we currently specify half-width, half-height as it leads to
b: position.height / 2, // more regular math (like ellipsis radii rather than diameters)
});
const shapeToElement = shape => ({
left: shape.transformMatrix[12] - shape.a,

View file

@ -1,58 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { multiply, rotateZ, translate } from '../../lib/aeroelastic/matrix';
export const isGroupId = id => id.startsWith('group');
const headerData = id =>
isGroupId(id)
? { id, type: 'group', subtype: 'persistentGroup' }
: { id, type: 'rectangleElement', subtype: '' };
const transformData = ({ top, left, width, height, angle }, z) =>
multiply(
translate(left + width / 2, top + height / 2, z), // painter's algo: latest item (highest z) goes to top
rotateZ((-angle / 180) * Math.PI) // minus angle as transform:matrix3d uses a left-handed coordinate system
);
/**
* elementToShape
*
* converts a `kibana-canvas` element to an `aeroelastic` shape.
*
* Shape: the layout algorithms need to deal with objects through their geometric properties, excluding other aspects,
* such as what's inside the element, eg. image or scatter plot. This representation is, at its core, a transform matrix
* that establishes a new local coordinate system https://drafts.csswg.org/css-transforms/#local-coordinate-system plus a
* size descriptor. There are two versions of the transform matrix:
* - `transformMatrix` is analogous to the SVG https://drafts.csswg.org/css-transforms/#current-transformation-matrix
* - `localTransformMatrix` is analogous to the SVG https://drafts.csswg.org/css-transforms/#transformation-matrix
*
* Element: it also needs to represent the geometry, primarily because of the need to persist it in `redux` and on the
* server, and to accept such data from the server. The redux and server representations will need to change as more general
* projections such as 3D are added. The element also needs to maintain its content, such as an image or a plot.
*
* While all elements on the current page also exist as shapes, there are shapes that are not elements: annotations.
* For example, `rotation_handle`, `border_resize_handle` and `border_connection` are modeled as shapes by the layout
* library, simply for generality.
*/
export const elementToShape = ({ id, position }, z) => ({
...headerData(id),
parent: (position && position.parent) || null,
transformMatrix: transformData(position, z),
a: position.width / 2, // we currently specify half-width, half-height as it leads to
b: position.height / 2, // more regular math (like ellipsis radii rather than diameters)
});
const simplePosition = ({ id, position, filter }, z) => ({
...headerData(id),
width: position.width,
height: position.height,
transformMatrix: transformData(position, z),
filter,
});
export const simplePositioning = ({ elements }) => ({ elements: elements.map(simplePosition) });

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import fileSaver from 'file-saver';
import chrome from 'ui/chrome';
import { API_ROUTE_SNAPSHOT_RUNTIME_DOWNLOAD } from '../../common/lib/constants';
import { notify } from './notify';
import * as workpadService from './workpad_service';
@ -18,35 +16,3 @@ export const downloadWorkpad = async workpadId => {
notify.error(err, { title: `Couldn't download workpad` });
}
};
export const downloadRenderedWorkpad = async renderedWorkpad => {
try {
const jsonBlob = new Blob([JSON.stringify(renderedWorkpad)], { type: 'application/json' });
fileSaver.saveAs(
jsonBlob,
`canvas-embed-workpad-${renderedWorkpad.name}-${renderedWorkpad.id}.json`
);
} catch (err) {
notify.error(err, { title: `Couldn't download rendered workpad` });
}
};
export const downloadEmbedRuntime = async () => {
try {
const basePath = chrome.getBasePath();
const path = `${basePath}${API_ROUTE_SNAPSHOT_RUNTIME_DOWNLOAD}`;
window.open(path);
return;
} catch (err) {
notify.error(err, { title: `Couldn't download embed runtime` });
}
};
export const downloadZippedEmbed = async data => {
try {
const zip = new Blob([data], { type: 'octet/stream' });
fileSaver.saveAs(zip, 'canvas-workpad-embed.zip');
} catch (err) {
notify.error(err, { title: `Couldn't download ZIP file` });
}
};

View file

@ -364,34 +364,3 @@ export function getRefreshInterval(state) {
export function getAutoplay(state) {
return get(state, 'transient.autoplay');
}
export function getRenderedWorkpad(state) {
const currentPages = getPages(state);
const args = get(state, ['transient', 'resolvedArgs']);
const renderedPages = currentPages.map(page => {
const { elements, ...rest } = page;
return {
...rest,
elements: elements.map(element => {
const { id, position } = element;
const arg = args[id];
if (!arg) {
return null;
}
const { expressionRenderable } = arg;
return { id, position, expressionRenderable };
}),
};
});
const workpad = getWorkpad(state);
// eslint-disable-next-line no-unused-vars
const { pages, ...rest } = workpad;
return {
pages: renderedPages,
...rest,
};
}

View file

@ -1,114 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
const fs = require('fs');
const path = require('path');
const { pipeline } = require('stream');
const { promisify } = require('util');
const del = require('del');
const { run } = require('@kbn/dev-utils');
const execa = require('execa');
const asyncPipeline = promisify(pipeline);
const {
RUNTIME_SRC,
KIBANA_ROOT,
STATS_OUTPUT,
RUNTIME_FILE,
} = require('../external_runtime/constants');
run(
async ({ log, flags }) => {
const options = {
cwd: KIBANA_ROOT,
stdio: ['ignore', 'inherit', 'inherit'],
buffer: false,
};
log.info('pre-req: Ensuring Kibana SCSS is built.');
// Ensure SASS has been built completely before building the runtime.
execa.sync(process.execPath, ['scripts/build_sass'], {
...options,
});
const webpackConfig = path.resolve(RUNTIME_SRC, 'webpack.config.js');
const clean = () => {
log.info('Deleting previous build.');
del.sync([RUNTIME_FILE], { force: true });
};
if (flags.clean) {
clean();
}
const env = {};
if (!flags.dev) {
env.NODE_ENV = 'production';
}
if (flags.run) {
log.info('Starting Webpack Dev Server...');
execa.sync(
'yarn',
[
'webpack-dev-server',
'--config',
webpackConfig,
'--progress',
'--hide-modules',
'--display-entrypoints',
'false',
'--content-base',
RUNTIME_SRC,
],
options
);
return;
}
if (flags.stats) {
log.info('Writing Webpack stats...');
const output = execa(
require.resolve('webpack/bin/webpack'),
['--config', webpackConfig, '--profile', '--json'],
{
...options,
env,
stdio: ['ignore', 'pipe', 'inherit'],
}
);
await asyncPipeline(output.stdout, fs.createWriteStream(STATS_OUTPUT));
log.success('...output written to', STATS_OUTPUT);
return;
}
clean();
log.info('Building Canvas Snapshot Runtime...');
execa.sync('yarn', ['webpack', '--config', webpackConfig, '--hide-modules', '--progress'], {
...options,
env,
});
log.success('...runtime built!');
},
{
description: `
Build script for the Canvas Snapshot Runtime.
`,
flags: {
boolean: ['run', 'clean', 'help', 'stats', 'dev'],
help: `
--run Run a server with the runtime
--dev Build and/or create stats in development mode.
--stats Output Webpack statistics to a stats.json file.
--clean Delete the existing build
`,
},
}
);

View file

@ -7,11 +7,9 @@
import { workpad } from './workpad';
import { esFields } from './es_fields';
import { customElements } from './custom_elements';
import { workpadSnapshots } from './workpad_snapshots';
export function routes(server) {
customElements(server);
esFields(server);
workpad(server);
workpadSnapshots(server);
}

View file

@ -1,64 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Server, RouteOptions } from 'hapi';
import archiver from 'archiver';
import {
API_ROUTE_SNAPSHOT_RUNTIME,
API_ROUTE_SNAPSHOT_RUNTIME_DOWNLOAD,
API_ROUTE_SNAPSHOT_ZIP,
} from '../../common/lib/constants';
import { RUNTIME_FILE, RUNTIME_NAME, RUNTIME_SRC } from '../../external_runtime/constants';
const PUBLIC_OPTIONS: RouteOptions = {
auth: false,
};
export function workpadSnapshots(server: Server) {
// get runtime
server.route({
method: 'GET',
path: API_ROUTE_SNAPSHOT_RUNTIME,
handler: {
file: RUNTIME_FILE,
},
options: PUBLIC_OPTIONS,
});
// download runtime
server.route({
method: 'GET',
path: API_ROUTE_SNAPSHOT_RUNTIME_DOWNLOAD,
handler(_request, handler) {
// @ts-ignore No type for inert Hapi handler
const file = handler.file(RUNTIME_FILE);
file.type('application/octet-stream');
return file;
},
options: PUBLIC_OPTIONS,
});
server.route({
method: 'POST',
path: API_ROUTE_SNAPSHOT_ZIP,
handler(request, handler) {
const workpad = request.payload;
const archive = archiver('zip');
archive.append(JSON.stringify(workpad), { name: 'workpad.json' });
archive.file(`${RUNTIME_SRC}/template.html`, { name: 'index.html' });
archive.file(RUNTIME_FILE, { name: `${RUNTIME_NAME}.js` });
const response = handler.response(archive);
response.header('content-type', 'application/zip');
archive.finalize();
return response;
},
});
}

View file

@ -6,13 +6,6 @@
import { ElementPosition } from './elements';
export interface CanvasAsset {
'@created': string;
id: string;
type: 'dataurl';
value: string;
}
export interface CanvasElement {
id: string;
position: ElementPosition;
@ -32,16 +25,13 @@ export interface CanvasPage {
}
export interface CanvasWorkpad {
'@created': string;
'@timestamp': string;
assets: { [id: string]: CanvasAsset };
colors: string[];
css: string;
height: number;
id: string;
isWriteable: boolean;
name: string;
id: string;
width: number;
height: number;
css: string;
page: number;
pages: CanvasPage[];
width: number;
colors: string[];
isWriteable: boolean;
}

View file

@ -44,7 +44,6 @@
"@storybook/react": "^5.0.5",
"@storybook/theming": "^5.0.5",
"@types/angular": "1.6.50",
"@types/archiver": "^3.0.0",
"@types/base64-js": "^1.2.5",
"@types/boom": "^7.2.0",
"@types/cheerio": "^0.22.10",
@ -152,7 +151,6 @@
"mustache": "^2.3.0",
"mutation-observer": "^1.0.3",
"node-fetch": "^2.6.0",
"null-loader": "^3.0.0",
"pdf-image": "2.0.0",
"pdfjs-dist": "^2.0.943",
"pixelmatch": "4.0.2",
@ -184,7 +182,6 @@
"@babel/runtime": "^7.5.5",
"@elastic/ctags-langserver": "^0.1.8",
"@elastic/datemath": "5.0.2",
"@elastic/filesaver": "1.1.2",
"@elastic/eui": "13.6.1",
"@elastic/javascript-typescript-langserver": "^0.2.2",
"@elastic/lsp-extension": "^0.1.2",
@ -219,7 +216,6 @@
"apollo-link-state": "^0.4.1",
"apollo-server-errors": "^2.0.2",
"apollo-server-hapi": "^1.3.6",
"archiver": "3.1.1",
"axios": "^0.19.0",
"bluebird": "3.5.5",
"boom": "^7.2.0",
@ -305,7 +301,6 @@
"pngjs": "3.4.0",
"polished": "^1.9.2",
"popper.js": "^1.14.3",
"postcss-prefix-selector": "^1.7.2",
"prop-types": "^15.6.0",
"proper-lockfile": "^3.2.0",
"puid": "1.0.7",
@ -366,7 +361,6 @@
"uuid": "3.3.2",
"venn.js": "0.2.20",
"vscode-languageserver": "^5.2.1",
"webpack": "4.33.0",
"wellknown": "^0.5.0",
"xml2js": "^0.4.19",
"xregexp": "4.2.4"

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import execa from 'execa';
import { resolve } from 'path';
import { writeFileSync } from 'fs';
import pluginHelpers from '@kbn/plugin-helpers';
@ -22,13 +21,7 @@ export default (gulp, { buildTarget }) => {
const log = new ToolingLog({
level: 'info',
writeTo: process.stdout,
});
execa.sync(process.execPath, ['legacy/plugins/canvas/scripts/external_runtime'], {
cwd: resolve(__dirname, '..'),
stdio: ['ignore', 'inherit', 'inherit'],
buffer: false,
writeTo: process.stdout
});
writeFileSync(
@ -36,7 +29,7 @@ export default (gulp, { buildTarget }) => {
await generateNoticeFromSource({
productName: 'Kibana X-Pack',
log,
directory: buildRoot,
directory: buildRoot
})
);
});

176
yarn.lock
View file

@ -3020,13 +3020,6 @@
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.50.tgz#8b6599088d80f68ef0cad7d3a2062248ebe72b3d"
integrity sha512-D3KB0PdaxdwtA44yOpK+NtptTscKWgUzXmf8fiLaaVxnX+b7QQ+dUMsyeVDweCQ6VX4PMwkd6x2hJ0X+ISIsoQ==
"@types/archiver@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-3.0.0.tgz#c0a53e0ed3b7aef626ce683d081d7821d8c638b4"
integrity sha512-orghAMOF+//wSg4ru2znk6jt0eIPvKTtMVLH7XcYcjbcRyAXRClDlh27QVdqnAvVM37yu9xDP6Nh7egRhNr8tQ==
dependencies:
"@types/glob" "*"
"@types/argparse@1.0.33":
version "1.0.33"
resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.33.tgz#2728669427cdd74a99e53c9f457ca2866a37c52d"
@ -5249,35 +5242,6 @@ archiver-utils@^2.0.0:
normalize-path "^3.0.0"
readable-stream "^2.0.0"
archiver-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
dependencies:
glob "^7.1.4"
graceful-fs "^4.2.0"
lazystream "^1.0.0"
lodash.defaults "^4.2.0"
lodash.difference "^4.5.0"
lodash.flatten "^4.4.0"
lodash.isplainobject "^4.0.6"
lodash.union "^4.6.0"
normalize-path "^3.0.0"
readable-stream "^2.0.0"
archiver@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-3.1.1.tgz#9db7819d4daf60aec10fe86b16cb9258ced66ea0"
integrity sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==
dependencies:
archiver-utils "^2.1.0"
async "^2.6.3"
buffer-crc32 "^0.2.1"
glob "^7.1.4"
readable-stream "^3.4.0"
tar-stream "^2.1.0"
zip-stream "^2.1.2"
archiver@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-3.0.0.tgz#50b2628cf032adcbf35d35d111b5324db95bfb69"
@ -5729,13 +5693,6 @@ async@^2.0.0, async@^2.1.4:
dependencies:
lodash "^4.14.0"
async@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
dependencies:
lodash "^4.17.14"
async@~0.2.9:
version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
@ -6554,13 +6511,6 @@ bl@^1.0.0:
dependencies:
readable-stream "^2.0.5"
bl@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==
dependencies:
readable-stream "^3.0.1"
blob@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
@ -6983,7 +6933,7 @@ buffer-alloc@^1.2.0:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
buffer-crc32@^0.2.1, buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
@ -8398,16 +8348,6 @@ compress-commons@^1.2.0:
normalize-path "^2.0.0"
readable-stream "^2.0.0"
compress-commons@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-2.1.1.tgz#9410d9a534cf8435e3fbbb7c6ce48de2dc2f0610"
integrity sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==
dependencies:
buffer-crc32 "^0.2.13"
crc32-stream "^3.0.1"
normalize-path "^3.0.0"
readable-stream "^2.3.6"
compressible@~2.0.16:
version "2.0.17"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
@ -8838,14 +8778,6 @@ crc32-stream@^2.0.0:
crc "^3.4.4"
readable-stream "^2.0.0"
crc32-stream@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-3.0.1.tgz#cae6eeed003b0e44d739d279de5ae63b171b4e85"
integrity sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==
dependencies:
crc "^3.4.4"
readable-stream "^3.4.0"
crc@^3.4.4:
version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
@ -10730,7 +10662,7 @@ encoding@^0.1.11:
dependencies:
iconv-lite "~0.4.13"
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
@ -13859,11 +13791,6 @@ graceful-fs@^4.1.0, graceful-fs@^4.1.15, graceful-fs@^4.1.9:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
graceful-fs@^4.2.0:
version "4.2.2"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
graceful-fs@~1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364"
@ -18642,7 +18569,7 @@ lodash@4.17.13, lodash@4.17.15:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93"
integrity sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==
lodash@>4.17.4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.4, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.1, lodash@~4.17.10, lodash@~4.17.5:
lodash@>4.17.4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.4, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.1, lodash@~4.17.10, lodash@~4.17.5:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@ -20577,14 +20504,6 @@ null-check@^1.0.0:
resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=
null-loader@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/null-loader/-/null-loader-3.0.0.tgz#3e2b6c663c5bda8c73a54357d8fa0708dc61b245"
integrity sha512-hf5sNLl8xdRho4UPBOOeoIwT3WhjYcMUQm0zj44EhD6UscMAz72o2udpoDFBgykucdEDGIcd6SXbc/G6zssbzw==
dependencies:
loader-utils "^1.2.3"
schema-utils "^1.0.0"
num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
@ -22093,13 +22012,6 @@ postcss-modules-values@^2.0.0:
icss-replace-symbols "^1.1.0"
postcss "^7.0.6"
postcss-prefix-selector@^1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/postcss-prefix-selector/-/postcss-prefix-selector-1.7.2.tgz#3adeed903985734298f19d8f5e0b657f9d90d43c"
integrity sha512-ddmzjWNmGs7E/nyolJ021/Gk6oBLRQLyyXKGV4Mu+Y0gquo+XlXSDP0/Y2J8C/cad/GLyftf2H0XtuDFQZxN3w==
dependencies:
postcss "^7.0.0"
postcss-selector-parser@^6.0.0:
version "6.0.2"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
@ -23937,7 +23849,7 @@ readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@^3.0.1, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0:
readable-stream@^3.0.6, readable-stream@^3.1.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
@ -27054,17 +26966,6 @@ tar-stream@^1.5.0:
to-buffer "^1.1.1"
xtend "^4.0.0"
tar-stream@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==
dependencies:
bl "^3.0.0"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@4.4.10:
version "4.4.10"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
@ -29780,36 +29681,6 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
source-list-map "^2.0.0"
source-map "~0.6.1"
webpack@4.33.0, webpack@^4.29.0:
version "4.33.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.33.0.tgz#c30fc4307db432e5c5e3333aaa7c16a15a3b277e"
integrity sha512-ggWMb0B2QUuYso6FPZKUohOgfm+Z0sVFs8WwWuSH1IAvkWs428VDNmOlAxvHGTB9Dm/qOB/qtE5cRx5y01clxw==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
"@webassemblyjs/wasm-edit" "1.8.5"
"@webassemblyjs/wasm-parser" "1.8.5"
acorn "^6.0.5"
acorn-dynamic-import "^4.0.0"
ajv "^6.1.0"
ajv-keywords "^3.1.0"
chrome-trace-event "^1.0.0"
enhanced-resolve "^4.1.0"
eslint-scope "^4.0.0"
json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
loader-utils "^1.1.0"
memory-fs "~0.4.1"
micromatch "^3.1.8"
mkdirp "~0.5.0"
neo-async "^2.5.0"
node-libs-browser "^2.0.0"
schema-utils "^1.0.0"
tapable "^1.1.0"
terser-webpack-plugin "^1.1.0"
watchpack "^1.5.0"
webpack-sources "^1.3.0"
webpack@4.39.2, webpack@^4.39.2:
version "4.39.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.39.2.tgz#c9aa5c1776d7c309d1b3911764f0288c8c2816aa"
@ -29839,6 +29710,36 @@ webpack@4.39.2, webpack@^4.39.2:
watchpack "^1.6.0"
webpack-sources "^1.4.1"
webpack@^4.29.0:
version "4.33.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.33.0.tgz#c30fc4307db432e5c5e3333aaa7c16a15a3b277e"
integrity sha512-ggWMb0B2QUuYso6FPZKUohOgfm+Z0sVFs8WwWuSH1IAvkWs428VDNmOlAxvHGTB9Dm/qOB/qtE5cRx5y01clxw==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
"@webassemblyjs/wasm-edit" "1.8.5"
"@webassemblyjs/wasm-parser" "1.8.5"
acorn "^6.0.5"
acorn-dynamic-import "^4.0.0"
ajv "^6.1.0"
ajv-keywords "^3.1.0"
chrome-trace-event "^1.0.0"
enhanced-resolve "^4.1.0"
eslint-scope "^4.0.0"
json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
loader-utils "^1.1.0"
memory-fs "~0.4.1"
micromatch "^3.1.8"
mkdirp "~0.5.0"
neo-async "^2.5.0"
node-libs-browser "^2.0.0"
schema-utils "^1.0.0"
tapable "^1.1.0"
terser-webpack-plugin "^1.1.0"
watchpack "^1.5.0"
webpack-sources "^1.3.0"
websocket-driver@>=0.5.1:
version "0.7.0"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"
@ -30815,15 +30716,6 @@ zip-stream@^2.0.1:
compress-commons "^1.2.0"
readable-stream "^2.0.0"
zip-stream@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.2.tgz#841efd23214b602ff49c497cba1a85d8b5fbc39c"
integrity sha512-ykebHGa2+uzth/R4HZLkZh3XFJzivhVsjJt8bN3GvBzLaqqrUdRacu+c4QtnUgjkkQfsOuNE1JgLKMCPNmkKgg==
dependencies:
archiver-utils "^2.1.0"
compress-commons "^2.1.1"
readable-stream "^3.4.0"
zlib@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0"