@bundle-stats/ui 4.16.0-beta.0 → 4.16.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assets/icons.svg +6 -1
- package/lib/assets/icons.svg.js +3 -0
- package/lib/assets/icons.svg.js.map +1 -1
- package/lib/components/bundle-assets/bundle-assets.js +10 -3
- package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
- package/lib/components/bundle-assets-totals/bundle-assets-totals.js +21 -9
- package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
- package/lib/components/bundle-modules/bundle-modules.js +9 -2
- package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib/components/bundle-packages/bundle-packages.js +9 -2
- package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
- package/lib/components/entry-info/entry-info.js +2 -2
- package/lib/components/entry-info/entry-info.js.map +1 -1
- package/lib/components/insights/insights.module.css +1 -0
- package/lib/components/metrics-table-export/index.js +18 -0
- package/lib/components/metrics-table-export/index.js.map +1 -0
- package/lib/components/metrics-table-export/metrics-table-export.js +82 -0
- package/lib/components/metrics-table-export/metrics-table-export.js.map +1 -0
- package/lib/components/metrics-table-options/index.js.map +1 -1
- package/lib/components/metrics-table-options/metrics-table-options.js +7 -15
- package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
- package/lib/components/preview-source/index.js +18 -0
- package/lib/components/preview-source/index.js.map +1 -0
- package/lib/components/preview-source/preview-source.js +28 -0
- package/lib/components/preview-source/preview-source.js.map +1 -0
- package/lib/components/preview-source/preview-source.module.css +40 -0
- package/lib/css/variables.css +5 -0
- package/lib/i18n.js +3 -0
- package/lib/i18n.js.map +1 -1
- package/lib/ui/button/button.js +2 -1
- package/lib/ui/button/button.js.map +1 -1
- package/lib/ui/copy-to-clipboard/copy-to-clipboard.js +2 -6
- package/lib/ui/copy-to-clipboard/copy-to-clipboard.js.map +1 -1
- package/lib/ui/copy-to-clipboard/copy-to-clipboard.module.css +5 -48
- package/lib/ui/dialog/dialog.js +27 -0
- package/lib/ui/dialog/dialog.js.map +1 -0
- package/lib/ui/dialog/dialog.module.css +89 -0
- package/lib/ui/dialog/index.js +18 -0
- package/lib/ui/dialog/index.js.map +1 -0
- package/lib/ui/icon/icon.js +1 -0
- package/lib/ui/icon/icon.js.map +1 -1
- package/lib/ui/index.js +7 -1
- package/lib/ui/index.js.map +1 -1
- package/lib/ui/markdown/index.js +18 -0
- package/lib/ui/markdown/index.js.map +1 -0
- package/lib/ui/markdown/markdown.js +40 -0
- package/lib/ui/markdown/markdown.js.map +1 -0
- package/lib/ui/markdown/markdown.module.css +7 -0
- package/lib/ui/skeleton/skeleton.module.css +3 -3
- package/lib/ui/tabs/tabs.js +2 -2
- package/lib/ui/tabs/tabs.js.map +1 -1
- package/lib/ui/tabs/tabs.module.css +1 -0
- package/lib/ui/textarea/index.js +18 -0
- package/lib/ui/textarea/index.js.map +1 -0
- package/lib/ui/textarea/textarea.js +16 -0
- package/lib/ui/textarea/textarea.js.map +1 -0
- package/lib/ui/textarea/textarea.module.css +57 -0
- package/lib-esm/assets/icons.svg +6 -1
- package/lib-esm/assets/icons.svg.js +3 -0
- package/lib-esm/assets/icons.svg.js.map +1 -1
- package/lib-esm/components/bundle-assets/bundle-assets.js +10 -3
- package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
- package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +21 -9
- package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
- package/lib-esm/components/bundle-modules/bundle-modules.js +9 -2
- package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib-esm/components/bundle-packages/bundle-packages.js +9 -2
- package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
- package/lib-esm/components/entry-info/entry-info.js +2 -2
- package/lib-esm/components/entry-info/entry-info.js.map +1 -1
- package/lib-esm/components/insights/insights.module.css +1 -0
- package/lib-esm/components/metrics-table-export/index.js +2 -0
- package/lib-esm/components/metrics-table-export/index.js.map +1 -0
- package/lib-esm/components/metrics-table-export/metrics-table-export.js +60 -0
- package/lib-esm/components/metrics-table-export/metrics-table-export.js.map +1 -0
- package/lib-esm/components/metrics-table-options/index.js.map +1 -1
- package/lib-esm/components/metrics-table-options/metrics-table-options.js +7 -15
- package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
- package/lib-esm/components/preview-source/index.js +2 -0
- package/lib-esm/components/preview-source/index.js.map +1 -0
- package/lib-esm/components/preview-source/preview-source.js +21 -0
- package/lib-esm/components/preview-source/preview-source.js.map +1 -0
- package/lib-esm/components/preview-source/preview-source.module.css +40 -0
- package/lib-esm/css/variables.css +5 -0
- package/lib-esm/i18n.js +3 -0
- package/lib-esm/i18n.js.map +1 -1
- package/lib-esm/ui/button/button.js +1 -0
- package/lib-esm/ui/button/button.js.map +1 -1
- package/lib-esm/ui/copy-to-clipboard/copy-to-clipboard.js +2 -6
- package/lib-esm/ui/copy-to-clipboard/copy-to-clipboard.js.map +1 -1
- package/lib-esm/ui/copy-to-clipboard/copy-to-clipboard.module.css +5 -48
- package/lib-esm/ui/dialog/dialog.js +19 -0
- package/lib-esm/ui/dialog/dialog.js.map +1 -0
- package/lib-esm/ui/dialog/dialog.module.css +89 -0
- package/lib-esm/ui/dialog/index.js +2 -0
- package/lib-esm/ui/dialog/index.js.map +1 -0
- package/lib-esm/ui/icon/icon.js +1 -0
- package/lib-esm/ui/icon/icon.js.map +1 -1
- package/lib-esm/ui/index.js +3 -0
- package/lib-esm/ui/index.js.map +1 -1
- package/lib-esm/ui/markdown/index.js +2 -0
- package/lib-esm/ui/markdown/index.js.map +1 -0
- package/lib-esm/ui/markdown/markdown.js +10 -0
- package/lib-esm/ui/markdown/markdown.js.map +1 -0
- package/lib-esm/ui/markdown/markdown.module.css +7 -0
- package/lib-esm/ui/skeleton/skeleton.module.css +3 -3
- package/lib-esm/ui/tabs/tabs.js +2 -2
- package/lib-esm/ui/tabs/tabs.js.map +1 -1
- package/lib-esm/ui/tabs/tabs.module.css +1 -0
- package/lib-esm/ui/textarea/index.js +2 -0
- package/lib-esm/ui/textarea/index.js.map +1 -0
- package/lib-esm/ui/textarea/textarea.js +9 -0
- package/lib-esm/ui/textarea/textarea.js.map +1 -0
- package/lib-esm/ui/textarea/textarea.module.css +57 -0
- package/package.json +14 -13
- package/src/assets/icons/download.svg +4 -0
- package/src/assets/icons.svg +6 -1
- package/src/assets/icons.svg.jsx +9 -0
- package/src/components/bundle-assets/bundle-assets.jsx +20 -7
- package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +54 -35
- package/src/components/bundle-modules/bundle-modules.tsx +15 -2
- package/src/components/bundle-packages/bundle-packages.jsx +15 -2
- package/src/components/entry-info/entry-info.tsx +2 -2
- package/src/components/insights/insights.module.css +1 -0
- package/src/components/metrics-table-export/index.ts +1 -0
- package/src/components/metrics-table-export/metrics-table-export.stories.tsx +30 -0
- package/src/components/metrics-table-export/metrics-table-export.tsx +102 -0
- package/src/components/metrics-table-options/metrics-table-options.stories.tsx +22 -0
- package/src/components/metrics-table-options/metrics-table-options.tsx +25 -0
- package/src/components/preview-source/index.ts +1 -0
- package/src/components/preview-source/preview-source.module.css +40 -0
- package/src/components/preview-source/preview-source.stories.tsx +53 -0
- package/src/components/preview-source/preview-source.tsx +56 -0
- package/src/css/variables.css +5 -0
- package/src/i18n.js +4 -0
- package/src/ui/button/button.tsx +2 -1
- package/src/ui/copy-to-clipboard/copy-to-clipboard.module.css +5 -48
- package/src/ui/copy-to-clipboard/copy-to-clipboard.tsx +4 -8
- package/src/ui/dialog/dialog.module.css +89 -0
- package/src/ui/dialog/dialog.stories.tsx +67 -0
- package/src/ui/dialog/dialog.tsx +62 -0
- package/src/ui/dialog/index.ts +1 -0
- package/src/ui/icon/icon.tsx +1 -0
- package/src/ui/index.js +3 -0
- package/src/ui/markdown/index.ts +1 -0
- package/src/ui/markdown/markdown.module.css +7 -0
- package/src/ui/markdown/markdown.stories.tsx +29 -0
- package/src/ui/markdown/markdown.tsx +24 -0
- package/src/ui/skeleton/skeleton.module.css +3 -3
- package/src/ui/tabs/tabs.jsx +2 -2
- package/src/ui/tabs/tabs.module.css +1 -0
- package/src/ui/textarea/index.ts +1 -0
- package/src/ui/textarea/textarea.module.css +57 -0
- package/src/ui/textarea/textarea.stories.tsx +37 -0
- package/src/ui/textarea/textarea.tsx +17 -0
- package/types/components/metrics-table-export/index.d.ts +1 -0
- package/types/components/metrics-table-export/metrics-table-export.d.ts +10 -0
- package/types/components/metrics-table-options/index.d.ts +1 -1
- package/types/components/metrics-table-options/metrics-table-options.d.ts +9 -17
- package/types/components/preview-source/index.d.ts +1 -0
- package/types/components/preview-source/preview-source.d.ts +9 -0
- package/types/i18n.d.ts +3 -0
- package/types/ui/button/button.d.ts +26 -0
- package/types/ui/dialog/dialog.d.ts +13 -0
- package/types/ui/dialog/index.d.ts +1 -0
- package/types/ui/icon/icon.d.ts +2 -0
- package/types/ui/index.d.ts +3 -0
- package/types/ui/markdown/index.d.ts +1 -0
- package/types/ui/markdown/markdown.d.ts +7 -0
- package/types/ui/tabs/tabs.d.ts +3 -3
- package/types/ui/textarea/index.d.ts +1 -0
- package/types/ui/textarea/textarea.d.ts +6 -0
- package/src/components/metrics-table-options/metrics-table-options.jsx +0 -30
- /package/src/components/metrics-table-options/{index.js → index.ts} +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import I18N from '../../i18n';
|
|
5
|
+
import { Dropdown, DropdownItem } from '../../ui/dropdown';
|
|
6
|
+
import { Separator } from '../../layout/separator';
|
|
7
|
+
|
|
8
|
+
type MetricsTableOptionsProps = {
|
|
9
|
+
onViewAllClick?: () => void;
|
|
10
|
+
onResetClick?: () => void;
|
|
11
|
+
onExportClick?: () => void;
|
|
12
|
+
} & ComponentProps<typeof Dropdown>;
|
|
13
|
+
|
|
14
|
+
export const MetricsTableOptions = (props: MetricsTableOptionsProps) => {
|
|
15
|
+
const { onViewAllClick, onResetClick, onExportClick, ...restProps } = props;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Dropdown glyph="more-vertical" {...restProps}>
|
|
19
|
+
{onResetClick && <DropdownItem onClick={onResetClick}>{I18N.RESET_FILTERS}</DropdownItem>}
|
|
20
|
+
{onViewAllClick && <DropdownItem onClick={onViewAllClick}>{I18N.VIEW_ALL}</DropdownItem>}
|
|
21
|
+
{((onResetClick || onViewAllClick) && onExportClick) && <Separator />}
|
|
22
|
+
{onExportClick && <DropdownItem onClick={onExportClick}>{I18N.EXPORT}</DropdownItem>}
|
|
23
|
+
</Dropdown>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './preview-source';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.toolbar {
|
|
6
|
+
position: absolute;
|
|
7
|
+
z-index: 2;
|
|
8
|
+
top: var(--space-xsmall);
|
|
9
|
+
right: var(--space-xsmall);
|
|
10
|
+
background: var(--color-background);
|
|
11
|
+
border-radius: var(--radius-medium);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** fix control group with nested components */
|
|
15
|
+
.downloadTooltip > a {
|
|
16
|
+
border-top-left-radius: inherit;
|
|
17
|
+
border-bottom-left-radius: inherit;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.downloadTooltip > a:hover,
|
|
21
|
+
.downloadTooltip > a:active,
|
|
22
|
+
.downloadTooltip > a:focus {
|
|
23
|
+
box-shadow: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Source content */
|
|
27
|
+
.content {
|
|
28
|
+
position: relative;
|
|
29
|
+
z-index: 1;
|
|
30
|
+
height: 100%;
|
|
31
|
+
background: var(--color-highlight);
|
|
32
|
+
color: var(--color-text);
|
|
33
|
+
font-family: var(--font-family-fixed);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Empty / loading state */
|
|
37
|
+
.isEmpty .content {
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
background: var(--color-highlight);
|
|
40
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { getWrapperDecorator } from '../../stories';
|
|
4
|
+
import { PreviewSource } from '.';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof PreviewSource> = {
|
|
7
|
+
title: 'Components/PreviewSource',
|
|
8
|
+
component: PreviewSource,
|
|
9
|
+
decorators: [getWrapperDecorator()],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof meta>;
|
|
15
|
+
|
|
16
|
+
const SOURCE = JSON.stringify(
|
|
17
|
+
[
|
|
18
|
+
{
|
|
19
|
+
label: 'Hey',
|
|
20
|
+
runs: [
|
|
21
|
+
{ value: 1, name: 'a' },
|
|
22
|
+
{ value: 2, name: 'b' },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
null,
|
|
27
|
+
2,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const Default: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
source: JSON.stringify(
|
|
33
|
+
[
|
|
34
|
+
{
|
|
35
|
+
label: 'Hey',
|
|
36
|
+
runs: [
|
|
37
|
+
{ value: 1, name: 'a' },
|
|
38
|
+
{ value: 2, name: 'b' },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
null,
|
|
43
|
+
2,
|
|
44
|
+
),
|
|
45
|
+
download: 'bundle-stats--assets.json',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Empty: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
download: 'bundle-stats--assets.json',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import cx from 'classnames';
|
|
4
|
+
|
|
5
|
+
import { Button, BUTTON_GLYPHS } from '../../ui/button';
|
|
6
|
+
import { CopyToClipboard } from '../../ui/copy-to-clipboard';
|
|
7
|
+
import { ControlGroup } from '../../ui/control-group';
|
|
8
|
+
import { Tooltip } from '../../ui/tooltip';
|
|
9
|
+
import { Textarea } from '../../ui/textarea';
|
|
10
|
+
import css from './preview-source.module.css';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_ROWS = 10;
|
|
13
|
+
|
|
14
|
+
type PreviewSourceProps = {
|
|
15
|
+
source?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Textarea rows
|
|
18
|
+
*/
|
|
19
|
+
rows?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Link download attribute
|
|
22
|
+
* @example
|
|
23
|
+
* bundle-stats--assets.json
|
|
24
|
+
*/
|
|
25
|
+
download?: string;
|
|
26
|
+
} & ComponentProps<'div'>;
|
|
27
|
+
|
|
28
|
+
export const PreviewSource = (props: PreviewSourceProps) => {
|
|
29
|
+
const { className = '', source, rows = DEFAULT_ROWS, download = false, ...restProps } = props;
|
|
30
|
+
|
|
31
|
+
const isEmpty = !source;
|
|
32
|
+
const rootClassName = cx(css.root, className, isEmpty && css.isEmpty);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className={rootClassName} {...restProps}>
|
|
36
|
+
{source && (
|
|
37
|
+
<ControlGroup className={css.toolbar}>
|
|
38
|
+
<CopyToClipboard outline size="small" text={source} />
|
|
39
|
+
{download && (
|
|
40
|
+
<Tooltip title={`Download ${download}`} className={css.downloadTooltip}>
|
|
41
|
+
<Button
|
|
42
|
+
outline
|
|
43
|
+
rightGlyph={BUTTON_GLYPHS.DOWNLOAD}
|
|
44
|
+
size="small"
|
|
45
|
+
as="a"
|
|
46
|
+
href={`data:application/json,${encodeURIComponent(source)}`}
|
|
47
|
+
download={download}
|
|
48
|
+
/>
|
|
49
|
+
</Tooltip>
|
|
50
|
+
)}
|
|
51
|
+
</ControlGroup>
|
|
52
|
+
)}
|
|
53
|
+
<Textarea readOnly rows={rows} value={source} className={css.content} />
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
package/src/css/variables.css
CHANGED
|
@@ -176,6 +176,7 @@
|
|
|
176
176
|
--color-outline-warning: var(--color-yellow-200);
|
|
177
177
|
--color-outline-danger: var(--color-red-100);
|
|
178
178
|
|
|
179
|
+
--color-backdrop: rgba(0, 0, 0, 0.05);
|
|
179
180
|
--color-shadow: rgba(0, 0, 0, 0.2);
|
|
180
181
|
|
|
181
182
|
--transition-duration-in: 0.2s;
|
|
@@ -200,6 +201,10 @@
|
|
|
200
201
|
|
|
201
202
|
--outline-hover: 0 0 0 var(--space-xxxsmall) var(--color-highlight);
|
|
202
203
|
|
|
204
|
+
--skeleton-inline-background: linear-gradient(transparent 10%, currentColor 10.1%, currentColor 89.9%, transparent 90%);
|
|
205
|
+
--skeleton-block-background: currentColor;
|
|
206
|
+
--skeleton-opacity: 0.06;
|
|
207
|
+
|
|
203
208
|
/** Layout */
|
|
204
209
|
--max-width: 88rem;
|
|
205
210
|
--entry-info-width: 64rem;
|
package/src/i18n.js
CHANGED
|
@@ -21,6 +21,10 @@ export default {
|
|
|
21
21
|
|
|
22
22
|
RESET_FILTERS: 'Reset filters',
|
|
23
23
|
VIEW_ALL: 'View all entries',
|
|
24
|
+
EXPORT: 'Export',
|
|
25
|
+
|
|
26
|
+
COPY_TO_CLIPBOARD: 'Copy to clipboard',
|
|
27
|
+
COPY_TO_CLIPBOARD_DONE: 'Copied to clipboard',
|
|
24
28
|
|
|
25
29
|
GITHUB_LINK_TITLE: 'View BundleStats project on GitHub',
|
|
26
30
|
REPORT_ISSUE: 'Report issue',
|
package/src/ui/button/button.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
2
|
import cx from 'classnames';
|
|
4
3
|
import { Button as ButtonBaseComponent } from 'ariakit/button';
|
|
5
4
|
|
|
@@ -21,6 +20,8 @@ export const BUTTON_KIND = {
|
|
|
21
20
|
SUCCES: 'success',
|
|
22
21
|
} as const;
|
|
23
22
|
|
|
23
|
+
export const BUTTON_GLYPHS = BaseIcon.ICONS;
|
|
24
|
+
|
|
24
25
|
type Size = (typeof BUTTON_SIZE)[keyof typeof BUTTON_SIZE];
|
|
25
26
|
type Kind = (typeof BUTTON_KIND)[keyof typeof BUTTON_KIND];
|
|
26
27
|
|
|
@@ -1,58 +1,15 @@
|
|
|
1
1
|
.root {
|
|
2
|
-
padding: var(--space-xxxsmall);
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
.wrapper {
|
|
6
|
-
display: flex;
|
|
7
|
-
gap: var(--space-xxsmall);
|
|
8
|
-
align-items: center;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.content {
|
|
12
|
-
flex: 1 1 auto;
|
|
13
2
|
white-space: nowrap;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.icon {
|
|
18
|
-
flex: 0 0 auto;
|
|
19
|
-
position: relative;
|
|
20
|
-
width: var(--space-small);
|
|
21
|
-
height: var(--space-small);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.iconCopy,
|
|
25
|
-
.iconDone {
|
|
26
|
-
position: absolute;
|
|
27
|
-
top: 0;
|
|
28
|
-
left: 0;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.iconCopy {
|
|
32
|
-
color: var(--color-text-ultra-light);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.iconDone {
|
|
36
|
-
color: var(--color-success);
|
|
3
|
+
/** override tooltip styles */
|
|
4
|
+
color: var(--color-text-ultra-light) !important;
|
|
37
5
|
}
|
|
38
6
|
|
|
39
7
|
/** done state */
|
|
40
|
-
.
|
|
41
|
-
opacity: 1;
|
|
42
|
-
transition: var(--ui-transition-out);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.iconDone {
|
|
46
|
-
opacity: 0;
|
|
8
|
+
.root svg {
|
|
47
9
|
transition: var(--ui-transition-out);
|
|
48
10
|
}
|
|
49
11
|
|
|
50
|
-
.done
|
|
51
|
-
|
|
52
|
-
transition: var(--ui-transition-in);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.done .iconDone {
|
|
56
|
-
opacity: 1;
|
|
12
|
+
.done svg {
|
|
13
|
+
color: var(--color-success);
|
|
57
14
|
transition: var(--ui-transition-in);
|
|
58
15
|
}
|
|
@@ -3,6 +3,7 @@ import React, { useCallback, useState } from 'react';
|
|
|
3
3
|
import cx from 'classnames';
|
|
4
4
|
import { useCopyToClipboard } from 'react-use';
|
|
5
5
|
|
|
6
|
+
import I18N from '../../i18n';
|
|
6
7
|
import { Button } from '../button';
|
|
7
8
|
import { Icon } from '../icon';
|
|
8
9
|
import { Tooltip } from '../tooltip';
|
|
@@ -38,20 +39,15 @@ export const CopyToClipboard = (props: CopyToClipboardProps) => {
|
|
|
38
39
|
|
|
39
40
|
return (
|
|
40
41
|
<Tooltip
|
|
41
|
-
title={doneTimeout ?
|
|
42
|
+
title={doneTimeout ? I18N.COPY_TO_CLIPBOARD_DONE : I18N.COPY_TO_CLIPBOARD}
|
|
42
43
|
as={Button}
|
|
43
44
|
type="button"
|
|
45
|
+
rightGlyph={doneTimeout ? Icon.ICONS.CLIPBOARD_CHECK : Icon.ICONS.CLIPBOARD}
|
|
44
46
|
onClick={handleOnClick}
|
|
45
47
|
className={rootClassName}
|
|
46
48
|
{...restProps}
|
|
47
49
|
>
|
|
48
|
-
|
|
49
|
-
{children && <span className={css.content}>{children}</span>}
|
|
50
|
-
<span className={css.icon}>
|
|
51
|
-
<Icon className={css.iconCopy} glyph={Icon.ICONS.CLIPBOARD} />
|
|
52
|
-
<Icon className={css.iconDone} glyph={Icon.ICONS.CLIPBOARD_CHECK} />
|
|
53
|
-
</span>
|
|
54
|
-
</span>
|
|
50
|
+
{children}
|
|
55
51
|
</Tooltip>
|
|
56
52
|
);
|
|
57
53
|
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
.backdrop {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
z-index: 99999 !important;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.backdrop::before {
|
|
9
|
+
content: ' ';
|
|
10
|
+
position: absolute;
|
|
11
|
+
left: 0;
|
|
12
|
+
top: 0;
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
background: var(--color-backdrop);
|
|
16
|
+
filter:
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.dialog {
|
|
20
|
+
--dialog-header-height: var(--space-medium);
|
|
21
|
+
--dialog-vspace: var(--space-xsmall);
|
|
22
|
+
--dialog-padding: var(--space-xsmall);
|
|
23
|
+
|
|
24
|
+
width: calc(100vw - 2 * var(--dialog-padding));
|
|
25
|
+
position: relative;
|
|
26
|
+
border-radius: var(--radius-medium);
|
|
27
|
+
padding: var(--dialog-padding);
|
|
28
|
+
background: var(--color-background);
|
|
29
|
+
box-shadow: 0 0 var(--space-medium) var(--color-shadow);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.header {
|
|
33
|
+
min-height: var(--dialog-header-height);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.header:empty {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.headerClose {
|
|
41
|
+
position: absolute;
|
|
42
|
+
right: var(--space-xxsmall);
|
|
43
|
+
top: var(--space-xxsmall);
|
|
44
|
+
padding: var(--space-xxxsmall);
|
|
45
|
+
flex: 0 0 auto;
|
|
46
|
+
color: var(--color-outline-dark);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.headerClose:hover,
|
|
50
|
+
.headerClose:focus,
|
|
51
|
+
.headerClose:active {
|
|
52
|
+
color: var(--color-text-ultra-light);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.content {
|
|
56
|
+
width: 100%;
|
|
57
|
+
max-height: calc(100vh - 2 * var(--space-medium) - 2 * var(--dialog-padding) - var(--dialog-header-height) - var(--dialog-vspace));
|
|
58
|
+
padding-bottom: 0.3em; /* extra space to compensate for the title line-height and allow to show focus state correctly on buttons */
|
|
59
|
+
overflow: auto;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Width variation */
|
|
63
|
+
.width--narrow {
|
|
64
|
+
max-width: 360px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.width--medium {
|
|
68
|
+
max-width: 640px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.width--wide {
|
|
72
|
+
max-width: 1024px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.width--extra-wide {
|
|
76
|
+
max-width: calc(var(--max-width) + 2 * var(--space-medium));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Center variation */
|
|
80
|
+
.center {
|
|
81
|
+
text-align: center;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@media (min-width: 768px) {
|
|
85
|
+
.dialog {
|
|
86
|
+
--dialog-vspace: var(--space-medium);
|
|
87
|
+
--dialog-padding: var(--space-large);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { Fragment } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import { Dropdown, DropdownItem } from '../dropdown';
|
|
5
|
+
import { Button } from '../button';
|
|
6
|
+
import { Dialog, useDialogState } from '.';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Dialog> = {
|
|
9
|
+
title: 'UI/Dialog',
|
|
10
|
+
component: Dialog,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
render: function DefaultDialog() {
|
|
19
|
+
const dialog = useDialogState();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<Button type="button" onClick={dialog.toggle}>
|
|
24
|
+
Open
|
|
25
|
+
</Button>
|
|
26
|
+
<Dialog title="Title" state={dialog}>
|
|
27
|
+
<div style={{ maxWidth: 480 }}>
|
|
28
|
+
<Dropdown label="Select option">
|
|
29
|
+
<DropdownItem>Option 1</DropdownItem>
|
|
30
|
+
<DropdownItem>Option 2</DropdownItem>
|
|
31
|
+
<DropdownItem>Option 3</DropdownItem>
|
|
32
|
+
</Dropdown>
|
|
33
|
+
<br />
|
|
34
|
+
<br />
|
|
35
|
+
{[1, 2, 3].map((item) => (
|
|
36
|
+
<Fragment key={item}>
|
|
37
|
+
<p>
|
|
38
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vulputate, lectus
|
|
39
|
+
vitae tristique facilisis, quam ex vulputate risus, vitae mattis eros diam quis
|
|
40
|
+
velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames
|
|
41
|
+
ac turpis egestas. Sed et velit tortor. Suspendisse risus ipsum, convallis
|
|
42
|
+
ultricies justo vel, auctor semper est. Sed viverra pulvinar odio eget pharetra.
|
|
43
|
+
In eleifend felis nec odio convallis, eleifend rhoncus diam maximus. Cras diam
|
|
44
|
+
libero, egestas blandit vestibulum nec, volutpat a mi. Etiam blandit turpis ac
|
|
45
|
+
nulla molestie vehicula. Duis bibendum a nisl sollicitudin lobortis. Vestibulum
|
|
46
|
+
lobortis eget justo ac elementum. Vestibulum vel lorem arcu. Etiam sagittis luctus
|
|
47
|
+
justo eu interdum. Ut lobortis tempus turpis, vitae auctor elit congue id.
|
|
48
|
+
</p>
|
|
49
|
+
<br />
|
|
50
|
+
<p>
|
|
51
|
+
Nulla eu ullamcorper quam. Mauris rhoncus tristique dui in rutrum. Ut eget justo a
|
|
52
|
+
lorem efficitur consequat. Nunc sed erat at ipsum convallis iaculis at ac lacus.
|
|
53
|
+
Etiam pellentesque felis quis maximus ultrices. Vivamus non pretium quam. Aenean
|
|
54
|
+
vel nunc fringilla, lacinia felis a, vehicula purus. Vestibulum in varius purus.
|
|
55
|
+
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
|
|
56
|
+
himenaeos. Quisque laoreet ultrices ligula eget iaculis. Vivamus lacinia ante
|
|
57
|
+
mattis lorem ultricies consequat. Integer dui odio, semper ac neque vitae,
|
|
58
|
+
elementum hendrerit ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
59
|
+
</p>
|
|
60
|
+
</Fragment>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
</Dialog>
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import cx from 'classnames';
|
|
4
|
+
import { Dialog as AriakitDialog, DialogDismiss as AriakitDialogDismiss } from 'ariakit/dialog';
|
|
5
|
+
|
|
6
|
+
import { Stack } from '../../layout/stack';
|
|
7
|
+
import { BUTTON_GLYPHS, Button } from '../button';
|
|
8
|
+
import css from './dialog.module.css';
|
|
9
|
+
|
|
10
|
+
export { useDialogState } from 'ariakit/dialog';
|
|
11
|
+
|
|
12
|
+
type AriakitDialogProps = ComponentProps<typeof AriakitDialog>;
|
|
13
|
+
|
|
14
|
+
type DialogProps = {
|
|
15
|
+
title?: ReactNode;
|
|
16
|
+
width?: 'narrow' | 'medium' | 'wide' | 'extra-wide';
|
|
17
|
+
center?: boolean;
|
|
18
|
+
modal?: AriakitDialogProps['modal'];
|
|
19
|
+
state: AriakitDialogProps['state'];
|
|
20
|
+
} & Omit<ComponentProps<'div'>, 'title'>;
|
|
21
|
+
|
|
22
|
+
export const Dialog = (props: DialogProps) => {
|
|
23
|
+
const {
|
|
24
|
+
state,
|
|
25
|
+
className = '',
|
|
26
|
+
title = '',
|
|
27
|
+
width = 'medium',
|
|
28
|
+
center = false,
|
|
29
|
+
modal = false,
|
|
30
|
+
children,
|
|
31
|
+
} = props;
|
|
32
|
+
|
|
33
|
+
const rootClassName = cx(css.dialog, width && css[`width--${width}`], center && css.center);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<AriakitDialog
|
|
37
|
+
state={state}
|
|
38
|
+
modal={modal}
|
|
39
|
+
backdrop
|
|
40
|
+
backdropProps={{
|
|
41
|
+
className: css.backdrop,
|
|
42
|
+
}}
|
|
43
|
+
portal
|
|
44
|
+
className={rootClassName}
|
|
45
|
+
>
|
|
46
|
+
{!modal && (
|
|
47
|
+
<AriakitDialogDismiss as={Button} glyph={BUTTON_GLYPHS.CLOSE} className={css.headerClose}>
|
|
48
|
+
{/* null content to prevent DialogDismiss to render the default "x" */}
|
|
49
|
+
{null}
|
|
50
|
+
</AriakitDialogDismiss>
|
|
51
|
+
)}
|
|
52
|
+
<Stack space="medium">
|
|
53
|
+
{title && (
|
|
54
|
+
<header className={css.header}>
|
|
55
|
+
{typeof title === 'string' ? <h2>{title}</h2> : title}
|
|
56
|
+
</header>
|
|
57
|
+
)}
|
|
58
|
+
<div className={cx(css.content, className)}>{children}</div>
|
|
59
|
+
</Stack>
|
|
60
|
+
</AriakitDialog>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dialog';
|
package/src/ui/icon/icon.tsx
CHANGED
package/src/ui/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { Button } from './button';
|
|
|
3
3
|
export { CopyToClipboard } from './copy-to-clipboard';
|
|
4
4
|
export { Container } from './container';
|
|
5
5
|
export { ControlGroup } from './control-group';
|
|
6
|
+
export { Dialog } from './dialog';
|
|
6
7
|
export { Dropdown } from './dropdown';
|
|
7
8
|
export { EmptySet } from './empty-set';
|
|
8
9
|
export { FileName } from './file-name';
|
|
@@ -12,8 +13,10 @@ export { Icon } from './icon';
|
|
|
12
13
|
export { Input } from './input';
|
|
13
14
|
export { InputSearch } from './input-search';
|
|
14
15
|
export { Loader } from './loader';
|
|
16
|
+
export { Markdown } from './markdown';
|
|
15
17
|
export { Skeleton } from './skeleton';
|
|
16
18
|
export { Table } from './table';
|
|
17
19
|
export { Tabs } from './tabs';
|
|
18
20
|
export { Tag } from './tag';
|
|
21
|
+
export { Textarea } from './textarea';
|
|
19
22
|
export { Tooltip } from './tooltip';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './markdown';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Markdown } from '.';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Markdown> = {
|
|
5
|
+
title: 'UI/Markdown',
|
|
6
|
+
component: Markdown,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
content: '**One line** _markdown_ content',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const MULTI_LINE_MARKDOWN = `
|
|
20
|
+
**Lorem ipsum** dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Habitant morbi tristique senectus et netus et malesuada.
|
|
21
|
+
|
|
22
|
+
Eget nunc scelerisque viverra mauris in. Ipsum dolor sit amet consectetur adipiscing.
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
export const MultiLine: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
content: MULTI_LINE_MARKDOWN,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { ComponentProps, ElementType, useMemo } from 'react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import snarkdown from 'snarkdown';
|
|
4
|
+
|
|
5
|
+
import css from './markdown.module.css';
|
|
6
|
+
|
|
7
|
+
type MarkdownProps = {
|
|
8
|
+
content?: string;
|
|
9
|
+
as?: ElementType;
|
|
10
|
+
} & ComponentProps<'div'>;
|
|
11
|
+
|
|
12
|
+
export const Markdown = (props: MarkdownProps) => {
|
|
13
|
+
const { className = '', content = '', as: Component = 'div', ...restProps } = props;
|
|
14
|
+
|
|
15
|
+
const html = useMemo(() => snarkdown(content), [content]);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Component
|
|
19
|
+
className={cx(css.root, className)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.root {
|
|
2
|
-
background:
|
|
3
|
-
opacity:
|
|
2
|
+
background: var(--skeleton-inline-background);
|
|
3
|
+
opacity: var(--skeleton-opacity);
|
|
4
4
|
pointer-events: none;
|
|
5
5
|
min-width: 6em;
|
|
6
6
|
border-radius: var(--radius-medium);
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
.block {
|
|
17
|
-
background:
|
|
17
|
+
background: var(--skeleton-block-background);
|
|
18
18
|
}
|
package/src/ui/tabs/tabs.jsx
CHANGED
|
@@ -4,7 +4,7 @@ import cx from 'classnames';
|
|
|
4
4
|
|
|
5
5
|
import css from './tabs.module.css';
|
|
6
6
|
|
|
7
|
-
const Item = ({ isTabActive, as: Component, className
|
|
7
|
+
const Item = ({ isTabActive, as: Component = 'span', className = '', ...restProps }) => (
|
|
8
8
|
<Component className={cx(css.item, className, isTabActive && css.itemActive)} {...restProps} />
|
|
9
9
|
);
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ Item.propTypes = {
|
|
|
20
20
|
as: PropTypes.elementType,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export const Tabs = ({ className, ...restProps }) => (
|
|
23
|
+
export const Tabs = ({ className = '', ...restProps }) => (
|
|
24
24
|
<nav className={cx(css.root, className)} {...restProps} />
|
|
25
25
|
);
|
|
26
26
|
|