@genspectrum/dashboard-components 0.6.9 → 0.6.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.
Files changed (24) hide show
  1. package/custom-elements.json +3 -3
  2. package/dist/dashboard-components.js +141 -19
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +2 -2
  5. package/dist/style.css +31 -0
  6. package/package.json +4 -1
  7. package/src/preact/aggregatedData/aggregate.tsx +2 -0
  8. package/src/preact/components/chart.tsx +14 -1
  9. package/src/preact/components/fullscreen.tsx +57 -0
  10. package/src/preact/components/resize-container.tsx +5 -1
  11. package/src/preact/mutationComparison/mutation-comparison.tsx +2 -0
  12. package/src/preact/mutations/mutations.tsx +2 -0
  13. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_byDayOverall.json +4726 -0
  14. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json +11143 -0
  15. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byWeekOverall.json +9154 -0
  16. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +66 -22
  17. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +16 -7
  18. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +17 -0
  19. package/src/preact/mutationsOverTime/mutations-over-time.tsx +28 -4
  20. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +2 -0
  21. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +2 -0
  22. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +2 -0
  23. package/src/query/queryMutationsOverTime.ts +10 -1
  24. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +51 -1
@@ -976,14 +976,14 @@ declare global {
976
976
 
977
977
  declare global {
978
978
  interface HTMLElementTagNameMap {
979
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
979
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
980
980
  }
981
981
  }
982
982
 
983
983
 
984
984
  declare global {
985
985
  interface HTMLElementTagNameMap {
986
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
986
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
987
987
  }
988
988
  }
989
989
 
package/dist/style.css CHANGED
@@ -2777,6 +2777,18 @@ input.tab:checked + .tab-content,
2777
2777
  right: auto;
2778
2778
  bottom: var(--tooltip-tail-offset);
2779
2779
  }
2780
+ .iconify {
2781
+ display: inline-block;
2782
+ width: 1em;
2783
+ height: 1em;
2784
+ background-color: currentColor;
2785
+ -webkit-mask-image: var(--svg);
2786
+ mask-image: var(--svg);
2787
+ -webkit-mask-repeat: no-repeat;
2788
+ mask-repeat: no-repeat;
2789
+ -webkit-mask-size: 100% 100%;
2790
+ mask-size: 100% 100%;
2791
+ }
2780
2792
  .visible {
2781
2793
  visibility: visible;
2782
2794
  }
@@ -2884,6 +2896,9 @@ input.tab:checked + .tab-content,
2884
2896
  .mr-2 {
2885
2897
  margin-right: 0.5rem;
2886
2898
  }
2899
+ .mt-0\.5 {
2900
+ margin-top: 0.125rem;
2901
+ }
2887
2902
  .mt-1 {
2888
2903
  margin-top: 0.25rem;
2889
2904
  }
@@ -3183,6 +3198,12 @@ input.tab:checked + .tab-content,
3183
3198
  .duration-150 {
3184
3199
  transition-duration: 150ms;
3185
3200
  }
3201
+ .mdi--fullscreen {
3202
+ --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M5 5h5v2H7v3H5zm9 0h5v5h-2V7h-3zm3 9h2v5h-5v-2h3zm-7 3v2H5v-5h2v3z'/%3E%3C/svg%3E");
3203
+ }
3204
+ .mdi--fullscreen-exit {
3205
+ --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M14 14h5v2h-3v3h-2zm-9 0h5v5H8v-3H5zm3-9h2v5H5V8h3zm11 3v2h-5V5h2v3z'/%3E%3C/svg%3E");
3206
+ }
3186
3207
  @media (min-width: 640px) {
3187
3208
 
3188
3209
  .sm\:modal-middle {
@@ -3206,6 +3227,16 @@ input.tab:checked + .tab-content,
3206
3227
  --tw-border-opacity: 1;
3207
3228
  border-color: rgb(156 163 175 / var(--tw-border-opacity));
3208
3229
  }
3230
+ .hover\:scale-110:hover {
3231
+ --tw-scale-x: 1.1;
3232
+ --tw-scale-y: 1.1;
3233
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
3234
+ }
3235
+ .hover\:scale-90:hover {
3236
+ --tw-scale-x: .9;
3237
+ --tw-scale-y: .9;
3238
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
3239
+ }
3209
3240
  .hover\:bg-gray-100:hover {
3210
3241
  --tw-bg-opacity: 1;
3211
3242
  background-color: rgb(243 244 246 / var(--tw-bg-opacity));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.6.9",
3
+ "version": "0.6.10",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -67,6 +67,9 @@
67
67
  "dayjs": "^1.11.10",
68
68
  "flatpickr": "^4.6.13",
69
69
  "gridjs": "^6.2.0",
70
+ "@iconify-json/mdi": "^1.1.67",
71
+ "@iconify-json/mdi-light": "^1.1.10",
72
+ "@iconify/tailwind": "^1.1.1",
70
73
  "lit": "^3.1.3",
71
74
  "object-hash": "^3.0.0",
72
75
  "preact": "^10.20.1",
@@ -8,6 +8,7 @@ import { LapisUrlContext } from '../LapisUrlContext';
8
8
  import { CsvDownloadButton } from '../components/csv-download-button';
9
9
  import { ErrorBoundary } from '../components/error-boundary';
10
10
  import { ErrorDisplay } from '../components/error-display';
11
+ import { Fullscreen } from '../components/fullscreen';
11
12
  import Info from '../components/info';
12
13
  import { LoadingDisplay } from '../components/loading-display';
13
14
  import { NoDataDisplay } from '../components/no-data-display';
@@ -105,6 +106,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({ data }) => {
105
106
  <div class='flex flex-row'>
106
107
  <CsvDownloadButton className='mx-1 btn btn-xs' getData={() => data} filename='aggregate.csv' />
107
108
  <Info>Info for aggregate</Info>
109
+ <Fullscreen />
108
110
  </div>
109
111
  );
110
112
  };
@@ -1,5 +1,5 @@
1
1
  import { Chart, type ChartConfiguration } from 'chart.js';
2
- import { useEffect, useRef } from 'preact/hooks';
2
+ import { type MutableRef, useEffect, useRef } from 'preact/hooks';
3
3
 
4
4
  export interface GsChartProps {
5
5
  configuration: ChartConfiguration;
@@ -21,6 +21,8 @@ const GsChart = ({ configuration }: GsChartProps) => {
21
21
 
22
22
  chartRef.current = new Chart(ctx, configuration);
23
23
 
24
+ resizeChartAfterFullscreenChange(chartRef, ctx, configuration);
25
+
24
26
  return () => {
25
27
  chartRef.current?.destroy();
26
28
  };
@@ -30,3 +32,14 @@ const GsChart = ({ configuration }: GsChartProps) => {
30
32
  };
31
33
 
32
34
  export default GsChart;
35
+
36
+ const resizeChartAfterFullscreenChange = (
37
+ chartRef: MutableRef<Chart | null>,
38
+ ctx: CanvasRenderingContext2D,
39
+ configuration: ChartConfiguration,
40
+ ) => {
41
+ document.addEventListener('fullscreenchange', () => {
42
+ chartRef.current?.destroy();
43
+ chartRef.current = new Chart(ctx, configuration);
44
+ });
45
+ };
@@ -0,0 +1,57 @@
1
+ import { useEffect, useRef, useState } from 'preact/hooks';
2
+
3
+ export const Fullscreen = () => {
4
+ const element = useRef<HTMLButtonElement>(null);
5
+ const isFullscreen = useFullscreenStatus();
6
+ return (
7
+ <button
8
+ ref={element}
9
+ onClick={async () => {
10
+ if (element.current) {
11
+ if (isFullscreen) {
12
+ await document.exitFullscreen();
13
+ } else {
14
+ const componentRoot = findComponentRoot(element.current);
15
+ if (componentRoot) {
16
+ await componentRoot.requestFullscreen();
17
+ }
18
+ }
19
+ }
20
+ }}
21
+ className={`mt-0.5 iconify text-2xl ${isFullscreen ? 'mdi--fullscreen-exit hover:scale-90' : 'mdi--fullscreen hover:scale-110'}`}
22
+ title={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
23
+ />
24
+ );
25
+ };
26
+
27
+ function findComponentRoot(element: HTMLElement) {
28
+ return findShadowRoot(element)?.children[0];
29
+ }
30
+
31
+ function findShadowRoot(element: HTMLElement) {
32
+ let current: Node = element;
33
+ while (current) {
34
+ if (current instanceof ShadowRoot) {
35
+ return current;
36
+ }
37
+ if (current.parentNode === null) {
38
+ return null;
39
+ }
40
+ current = current.parentNode;
41
+ }
42
+ return null;
43
+ }
44
+
45
+ function useFullscreenStatus(): boolean {
46
+ const [isFullscreen, setIsFullscreen] = useState<boolean>(document.fullscreenElement !== null);
47
+ useEffect(() => {
48
+ const handleFullscreenChange = () => {
49
+ setIsFullscreen(document.fullscreenElement !== null);
50
+ };
51
+ document.addEventListener('fullscreenchange', handleFullscreenChange);
52
+ return () => {
53
+ document.removeEventListener('fullscreenchange', handleFullscreenChange);
54
+ };
55
+ }, []);
56
+ return isFullscreen;
57
+ }
@@ -10,5 +10,9 @@ export interface ResizeContainerProps {
10
10
  }
11
11
 
12
12
  export const ResizeContainer: FunctionComponent<ResizeContainerProps> = ({ children, size }) => {
13
- return <div style={size}>{children}</div>;
13
+ return (
14
+ <div style={size} className='bg-white'>
15
+ {children}
16
+ </div>
17
+ );
14
18
  };
@@ -11,6 +11,7 @@ import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '..
11
11
  import { CsvDownloadButton } from '../components/csv-download-button';
12
12
  import { ErrorBoundary } from '../components/error-boundary';
13
13
  import { ErrorDisplay } from '../components/error-display';
14
+ import { Fullscreen } from '../components/fullscreen';
14
15
  import Info from '../components/info';
15
16
  import { LoadingDisplay } from '../components/loading-display';
16
17
  import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
@@ -189,6 +190,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
189
190
  filename='mutation_comparison.csv'
190
191
  />
191
192
  <Info>Info for mutation comparison</Info>
193
+ <Fullscreen />
192
194
  </>
193
195
  );
194
196
  };
@@ -18,6 +18,7 @@ import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '..
18
18
  import { CsvDownloadButton } from '../components/csv-download-button';
19
19
  import { ErrorBoundary } from '../components/error-boundary';
20
20
  import { ErrorDisplay } from '../components/error-display';
21
+ import { Fullscreen } from '../components/fullscreen';
21
22
  import Info from '../components/info';
22
23
  import { LoadingDisplay } from '../components/loading-display';
23
24
  import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
@@ -208,6 +209,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
208
209
  />
209
210
  )}
210
211
  <Info>Info for mutations</Info>
212
+ <Fullscreen />
211
213
  </>
212
214
  );
213
215
  };