Browse Source

Adds code splitting to the minimap

main
Eric Amodio 1 year ago
parent
commit
2302e82a2f
5 changed files with 210 additions and 175 deletions
  1. +2
    -1
      package.json
  2. +3
    -0
      src/webviews/apps/plus/graph/graph.html
  3. +181
    -168
      src/webviews/apps/plus/graph/minimap/minimap.ts
  4. +19
    -6
      webpack.config.js
  5. +5
    -0
      yarn.lock

+ 2
- 1
package.json View File

@ -13110,7 +13110,8 @@
"webpack": "5.75.0",
"webpack-bundle-analyzer": "4.8.0",
"webpack-cli": "5.0.1",
"webpack-node-externals": "3.0.0"
"webpack-node-externals": "3.0.0",
"webpack-require-from": "1.8.6"
},
"resolutions": {
"node-fetch": "2.6.9",

+ 3
- 0
src/webviews/apps/plus/graph/graph.html View File

@ -2,6 +2,9 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<script type="application/javascript" nonce="#{cspNonce}">
window.webpackResourceBasePath = '#{webroot}/';
</script>
</head>
<body class="graph-app" data-vscode-context='{ "preventDefaultContextMenuItems": true }'>

+ 181
- 168
src/webviews/apps/plus/graph/minimap/minimap.ts View File

@ -1,6 +1,5 @@
import { css, customElement, FASTElement, html, observable, ref } from '@microsoft/fast-element';
import type { Chart, DataItem, RegionOptions } from 'billboard.js';
import { bb, selection, spline, zoom } from 'billboard.js';
import { groupByMap } from '../../../../../system/array';
import { first, flatMap, map, some, union } from '../../../../../system/iterable';
import { pluralize } from '../../../../../system/string';
@ -387,7 +386,7 @@ export class GraphMinimap extends FASTElement {
override connectedCallback(): void {
super.connectedCallback();
this.loadChart();
this.dataChanged(undefined, undefined, false);
}
override disconnectedCallback(): void {
@ -516,7 +515,14 @@ export class GraphMinimap extends FASTElement {
} satisfies RegionOptions;
}
private _loading: Promise<void> | undefined;
private loadChart() {
if (this._loading == null) {
this._loading = this.loadChartCore().then(() => (this._loading = undefined));
}
}
private async loadChartCore() {
if (!this.data?.size) {
this._chart?.destroy();
this._chart = undefined!;
@ -582,148 +588,152 @@ export class GraphMinimap extends FASTElement {
const yMax = p98 + Math.min(changesMax - p98, p98 * 0.02) + 100;
if (this._chart == null) {
this._chart = bb.generate({
bindto: this.chart,
data: {
x: 'date',
xSort: false,
axes: {
activity: 'y',
additions: 'y',
deletions: 'y',
},
columns: [
['date', ...dates],
['activity', ...activity],
// ['additions', ...additions],
// ['deletions', ...deletions],
],
names: {
activity: 'Activity',
// additions: 'Additions',
// deletions: 'Deletions',
try {
const { bb, selection, spline, zoom } = await import(
/* webpackChunkName: "billboard" */ 'billboard.js'
);
this._chart = bb.generate({
bindto: this.chart,
data: {
x: 'date',
xSort: false,
axes: {
activity: 'y',
additions: 'y',
deletions: 'y',
},
columns: [
['date', ...dates],
['activity', ...activity],
// ['additions', ...additions],
// ['deletions', ...deletions],
],
names: {
activity: 'Activity',
// additions: 'Additions',
// deletions: 'Deletions',
},
// hide: ['additions', 'deletions'],
onclick: d => {
if (d.id !== 'activity') return;
const date = new Date(d.x);
const day = getDay(date);
const sha = this.searchResults?.get(day)?.sha ?? this.data?.get(day)?.sha;
queueMicrotask(() => {
this.$emit('selected', {
date: date,
sha: sha,
} satisfies GraphMinimapDaySelectedEventDetail);
});
},
selection: {
enabled: selection(),
grouped: true,
multiple: false,
// isselectable: d => {
// if (d.id !== 'activity') return false;
// return (this.data?.get(getDay(new Date(d.x)))?.commits ?? 0) > 0;
// },
},
colors: {
activity: 'var(--color-graph-minimap-line0)',
// additions: 'rgba(73, 190, 71, 0.7)',
// deletions: 'rgba(195, 32, 45, 0.7)',
},
groups: [['additions', 'deletions']],
types: {
activity: spline(),
// additions: bar(),
// deletions: bar(),
},
},
// hide: ['additions', 'deletions'],
onclick: d => {
if (d.id !== 'activity') return;
const date = new Date(d.x);
const day = getDay(date);
const sha = this.searchResults?.get(day)?.sha ?? this.data?.get(day)?.sha;
queueMicrotask(() => {
this.$emit('selected', {
date: date,
sha: sha,
} satisfies GraphMinimapDaySelectedEventDetail);
});
area: {
linearGradient: true,
front: true,
below: true,
zerobased: true,
},
selection: {
enabled: selection(),
grouped: true,
multiple: false,
// isselectable: d => {
// if (d.id !== 'activity') return false;
// return (this.data?.get(getDay(new Date(d.x)))?.commits ?? 0) > 0;
axis: {
x: {
show: false,
localtime: true,
type: 'timeseries',
},
y: {
min: 0,
max: yMax,
show: true,
padding: {
// top: 10,
bottom: 8,
},
},
// y2: {
// min: y2Min,
// max: yMax,
// show: true,
// // padding: {
// // top: 10,
// // bottom: 0,
// // },
// },
},
colors: {
activity: 'var(--color-graph-minimap-line0)',
// additions: 'rgba(73, 190, 71, 0.7)',
// deletions: 'rgba(195, 32, 45, 0.7)',
bar: {
zerobased: false,
width: { max: 3 },
},
groups: [['additions', 'deletions']],
types: {
activity: spline(),
// additions: bar(),
// deletions: bar(),
clipPath: false,
grid: {
front: false,
focus: {
show: true,
},
},
},
area: {
linearGradient: true,
front: true,
below: true,
zerobased: true,
},
axis: {
x: {
legend: {
show: false,
localtime: true,
type: 'timeseries',
},
y: {
min: 0,
max: yMax,
show: true,
padding: {
// top: 10,
bottom: 8,
},
line: {
point: true,
zerobased: true,
},
// y2: {
// min: y2Min,
// max: yMax,
// show: true,
// // padding: {
// // top: 10,
// // bottom: 0,
// // },
// },
},
bar: {
zerobased: false,
width: { max: 3 },
},
clipPath: false,
grid: {
front: false,
focus: {
point: {
show: true,
select: {
r: 5,
},
focus: {
only: true,
expand: {
enabled: true,
r: 3,
},
},
sensitivity: 100,
},
},
legend: {
show: false,
},
line: {
point: true,
zerobased: true,
},
point: {
show: true,
select: {
r: 5,
regions: regions,
resize: {
auto: true,
},
focus: {
only: true,
expand: {
enabled: true,
r: 3,
spline: {
interpolation: {
type: 'catmull-rom',
},
},
sensitivity: 100,
},
regions: regions,
resize: {
auto: true,
},
spline: {
interpolation: {
type: 'catmull-rom',
},
},
tooltip: {
contents: (data, _defaultTitleFormat, _defaultValueFormat, _color) => {
const date = new Date(data[0].x);
const stat = this.data?.get(getDay(date));
const markers = this.markers?.get(getDay(date));
let groups;
if (markers?.length) {
groups = groupByMap(markers, m => m.type);
}
return /*html*/ `<div class="bb-tooltip">
tooltip: {
contents: (data, _defaultTitleFormat, _defaultValueFormat, _color) => {
const date = new Date(data[0].x);
const stat = this.data?.get(getDay(date));
const markers = this.markers?.get(getDay(date));
let groups;
if (markers?.length) {
groups = groupByMap(markers, m => m.type);
}
return /*html*/ `<div class="bb-tooltip">
<div class="header">
<span class="header--title">${formatDate(date, 'MMMM Do, YYYY')}</span>
<span class="header--description">(${capitalize(fromNow(date))})</span>
@ -791,48 +801,51 @@ export class GraphMinimap extends FASTElement {
: ''
}
</div>`;
},
position: (_data, width, _height, element, pos) => {
const { x } = pos;
const rect = (element as HTMLElement).getBoundingClientRect();
let left = rect.right - x;
if (left + width > rect.right) {
left = rect.right - width;
}
return { top: 0, left: left };
},
},
position: (_data, width, _height, element, pos) => {
const { x } = pos;
const rect = (element as HTMLElement).getBoundingClientRect();
let left = rect.right - x;
if (left + width > rect.right) {
left = rect.right - width;
}
return { top: 0, left: left };
transition: {
duration: 0,
},
},
transition: {
duration: 0,
},
zoom: {
enabled: zoom(),
rescale: false,
resetButton: {
text: '',
zoom: {
enabled: zoom(),
rescale: false,
resetButton: {
text: '',
},
type: 'wheel',
onzoom: () => {
// Reset the active day when zooming because it fails to update properly
queueMicrotask(() => this.activeDayChanged());
},
},
type: 'wheel',
onzoom: () => {
// Reset the active day when zooming because it fails to update properly
queueMicrotask(() => this.activeDayChanged());
onafterinit: function () {
const xAxis = this.$.main.selectAll<Element, any>('.bb-axis-x').node();
xAxis?.remove();
const yAxis = this.$.main.selectAll<Element, any>('.bb-axis-y').node();
yAxis?.remove();
const grid = this.$.main.selectAll<Element, any>('.bb-grid').node();
grid?.removeAttribute('clip-path');
// Move the regions to be on top of the bars
const bars = this.$.main.selectAll<Element, any>('.bb-chart-bars').node();
const regions = this.$.main.selectAll<Element, any>('.bb-regions').node();
bars?.insertAdjacentElement('afterend', regions!);
},
},
onafterinit: function () {
const xAxis = this.$.main.selectAll<Element, any>('.bb-axis-x').node();
xAxis?.remove();
const yAxis = this.$.main.selectAll<Element, any>('.bb-axis-y').node();
yAxis?.remove();
const grid = this.$.main.selectAll<Element, any>('.bb-grid').node();
grid?.removeAttribute('clip-path');
// Move the regions to be on top of the bars
const bars = this.$.main.selectAll<Element, any>('.bb-chart-bars').node();
const regions = this.$.main.selectAll<Element, any>('.bb-regions').node();
bars?.insertAdjacentElement('afterend', regions!);
},
});
});
} catch (ex) {
debugger;
}
} else {
this._chart.load({
columns: [

+ 19
- 6
webpack.config.js View File

@ -7,7 +7,9 @@ const CircularDependencyPlugin = require('circular-dependency-plugin');
const { CleanWebpackPlugin: CleanPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const CspHtmlPlugin = require('csp-html-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const esbuild = require('esbuild');
const { EsbuildPlugin } = require('esbuild-loader');
const { generateFonts } = require('fantasticon');
const ForkTsCheckerPlugin = require('fork-ts-checker-webpack-plugin');
const fs = require('fs');
@ -17,9 +19,8 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
const { validate } = require('schema-utils');
const TerserPlugin = require('terser-webpack-plugin');
const { WebpackError, optimize } = require('webpack');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { EsbuildPlugin } = require('esbuild-loader');
const { WebpackError, optimize, webpack } = require('webpack');
const WebpackRequireFromPlugin = require('webpack-require-from');
module.exports =
/**
@ -138,10 +139,10 @@ function getExtensionConfig(target, mode, env) {
target: target,
devtool: mode === 'production' ? false : 'source-map',
output: {
path: target === 'webworker' ? path.join(__dirname, 'dist', 'browser') : path.join(__dirname, 'dist'),
libraryTarget: 'commonjs2',
filename: 'gitlens.js',
chunkFilename: 'feature-[name].js',
filename: 'gitlens.js',
libraryTarget: 'commonjs2',
path: target === 'webworker' ? path.join(__dirname, 'dist', 'browser') : path.join(__dirname, 'dist'),
},
optimization: {
minimizer: [
@ -301,6 +302,9 @@ function getWebviewsConfig(mode, env) {
configFile: path.join(basePath, 'tsconfig.json'),
},
}),
new WebpackRequireFromPlugin({
variableName: 'webpackResourceBasePath',
}),
new MiniCssExtractPlugin({ filename: '[name].css' }),
getHtmlPlugin('commitDetails', false, mode, env),
getHtmlPlugin('graph', true, mode, env),
@ -363,6 +367,7 @@ function getWebviewsConfig(mode, env) {
target: 'web',
devtool: mode === 'production' ? false : 'source-map',
output: {
chunkFilename: 'feature-[name].js',
filename: '[name].js',
libraryTarget: 'module',
path: path.join(__dirname, 'dist', 'webviews'),
@ -421,6 +426,14 @@ function getWebviewsConfig(mode, env) {
}),
]
: [],
splitChunks: {
// Disable all non-async code splitting
// chunks: () => false,
cacheGroups: {
default: false,
vendors: false,
},
},
},
module: {
rules: [

+ 5
- 0
yarn.lock View File

@ -7298,6 +7298,11 @@ webpack-node-externals@3.0.0:
resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917"
integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==
webpack-require-from@1.8.6:
version "1.8.6"
resolved "https://registry.yarnpkg.com/webpack-require-from/-/webpack-require-from-1.8.6.tgz#47dc065257c522abdf39715556c4f027ea749bed"
integrity sha512-QmRsOkOYPKeNXp4uVc7qxnPrFQPrP4bhOc/gl4QenTFNgXdEbF1U8VC+jM/Sljb0VzJLNgyNiHlVkuHjcmDtBQ==
webpack-sources@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"

Loading…
Cancel
Save