Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cfb463e

Browse files
Add button for selecting pivot fields via dropdown (#4020)
* add ui button component * update global button component * add measure and dimension selectors * add new addPivotField function * use new measure/dimension selectors * add new type prop to DragList and conditionally render AddField button * update DragList usage * simplify DragList props
1 parent d2ca586 commit cfb463e

File tree

8 files changed

+169
-55
lines changed

8 files changed

+169
-55
lines changed

web-common/src/components/button/Button.svelte

+21-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
| "highlighted"
99
| "text"
1010
| "link"
11-
| "brand";
11+
| "brand"
12+
| "add";
1213
1314
export let type: ButtonType = "primary";
1415
export let status: "info" | "error" = "info";
@@ -24,6 +25,7 @@
2425
export let small = false;
2526
export let noStroke = false;
2627
export let dashed = false;
28+
export let rounded = false;
2729
export let builders: Builder[] = [];
2830
2931
const dispatch = createEventDispatcher();
@@ -45,6 +47,7 @@
4547
class:small
4648
class:dashed
4749
class:compact
50+
class:rounded
4851
class:danger={status === "error"}
4952
class:no-stroke={noStroke}
5053
type={submitForm ? "submit" : "button"}
@@ -63,11 +66,7 @@
6366
@apply text-xs leading-snug font-normal;
6467
@apply gap-x-2 min-w-fit;
6568
@apply rounded-[2px];
66-
@apply px-3 h-7 min-h-[28px];
67-
}
68-
69-
button:focus {
70-
@apply outline-none ring-2 ring-slate-800;
69+
@apply h-7 min-h-[28px];
7170
}
7271
7372
button:disabled {
@@ -98,11 +97,15 @@
9897
9998
.secondary:hover,
10099
.secondary:disabled,
101-
.secondary.selected {
100+
.secondary.selected,
101+
.add:hover,
102+
.add:disabled,
103+
.add.selected {
102104
@apply bg-slate-100;
103105
}
104106
105-
.secondary:active {
107+
.secondary:active,
108+
.add:active {
106109
@apply bg-slate-200;
107110
}
108111
@@ -150,6 +153,7 @@
150153
@apply text-ellipsis overflow-hidden whitespace-nowrap flex-grow-0 flex-shrink-0;
151154
}
152155
156+
.rounded,
153157
.circle {
154158
@apply rounded-full;
155159
}
@@ -221,4 +225,13 @@
221225
.dashed {
222226
@apply border border-dashed;
223227
}
228+
229+
/* ADD BUTTON STYLES */
230+
231+
.add {
232+
@apply w-[34px] h-[26px] rounded-2xl;
233+
@apply flex items-center justify-center;
234+
@apply border border-dashed border-slate-300;
235+
@apply bg-white;
236+
}
224237
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script lang="ts" context="module">
2+
import Add from "@rilldata/web-common/components/icons/Add.svelte";
3+
import Button from "@rilldata/web-common/components/button/Button.svelte";
4+
import * as DropdownMenu from "@rilldata/web-common/components/dropdown-menu/";
5+
import { getStateManagers } from "../state-managers/state-managers";
6+
import { metricsExplorerStore } from "../stores/dashboard-stores";
7+
import type { PivotChipData } from "./types";
8+
</script>
9+
10+
<script lang="ts">
11+
export let type: "rows" | "columns" | null = null;
12+
13+
const {
14+
selectors: {
15+
pivot: { dimensions, measures },
16+
},
17+
metricsViewName,
18+
} = getStateManagers();
19+
20+
let open = false;
21+
22+
function handleSelectValue(data: PivotChipData) {
23+
metricsExplorerStore.addPivotField($metricsViewName, data, type === "rows");
24+
}
25+
</script>
26+
27+
<DropdownMenu.Root bind:open>
28+
<DropdownMenu.Trigger asChild let:builder>
29+
<Button builders={[builder]} type="add" selected={open}>
30+
<Add size="17px" />
31+
</Button>
32+
</DropdownMenu.Trigger>
33+
34+
<DropdownMenu.Content class="h-80 w-64 overflow-scroll" align="start">
35+
{#if type === "columns"}
36+
<DropdownMenu.Label>Measures</DropdownMenu.Label>
37+
<DropdownMenu.Group>
38+
{#each $measures as measure}
39+
<DropdownMenu.Item
40+
on:click={() => {
41+
handleSelectValue(measure);
42+
}}
43+
>
44+
{measure.title}
45+
</DropdownMenu.Item>
46+
{/each}
47+
</DropdownMenu.Group>
48+
<DropdownMenu.Separator />
49+
{/if}
50+
<DropdownMenu.Label>Dimensions</DropdownMenu.Label>
51+
<DropdownMenu.Group>
52+
{#each $dimensions as dimension}
53+
<DropdownMenu.Item
54+
on:click={() => {
55+
handleSelectValue(dimension);
56+
}}
57+
>
58+
{dimension.title}
59+
</DropdownMenu.Item>
60+
{/each}
61+
</DropdownMenu.Group>
62+
</DropdownMenu.Content>
63+
</DropdownMenu.Root>

web-common/src/features/dashboards/pivot/DragList.svelte

+24-12
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
<script context="module" lang="ts">
2+
import AddField from "./AddField.svelte";
3+
import PivotChip from "./PivotChip.svelte";
24
import { dndzone } from "svelte-dnd-action";
35
import { flip } from "svelte/animate";
46
import { createEventDispatcher } from "svelte";
57
import type { PivotChipData } from "./types";
68
import { PivotChipType } from "./types";
7-
import PivotChip from "./PivotChip.svelte";
89
import type { TimeGrain } from "@rilldata/web-common/lib/time/types";
910
</script>
1011

1112
<script lang="ts">
1213
export let items: PivotChipData[] = [];
13-
export let style: "vertical" | "horizontal" = "vertical";
14-
export let removable = false;
14+
export let placeholder: string | null = null;
15+
export let type: "rows" | "columns" | null = null;
16+
17+
const removable = Boolean(type);
18+
const horizontal = Boolean(type);
1519
1620
const dispatch = createEventDispatcher();
1721
const flipDurationMs = 200;
1822
19-
let listClasses: string;
20-
21-
$: if (style === "horizontal") {
22-
listClasses = "flex flex-row bg-slate-50 w-full p-2 gap-x-2 h-10";
23-
} else {
24-
listClasses = "flex flex-col gap-y-2 py-2";
25-
}
26-
2723
function handleConsider(e: CustomEvent<{ items: PivotChipData[] }>) {
2824
items = e.detail.items;
2925
}
@@ -49,11 +45,15 @@
4945
</script>
5046

5147
<div
52-
class="{listClasses} rounded-sm"
48+
class="container"
49+
class:horizontal
5350
use:dndzone={{ items, flipDurationMs }}
5451
on:consider={handleConsider}
5552
on:finalize={handleFinalize}
5653
>
54+
{#if !items.length && placeholder}
55+
<p class="text-gray-500">{placeholder}</p>
56+
{/if}
5757
{#each items as item (item.id)}
5858
<div class="item" animate:flip={{ duration: flipDurationMs }}>
5959
<PivotChip
@@ -69,13 +69,25 @@
6969
/>
7070
</div>
7171
{/each}
72+
{#if removable}
73+
<AddField {type} />
74+
{/if}
7275
</div>
7376

7477
<style type="postcss">
7578
.item {
7679
@apply text-center h-6;
7780
}
7881
82+
.container {
83+
@apply flex flex-col gap-y-2 py-2 rounded-sm;
84+
}
85+
86+
.horizontal {
87+
@apply flex flex-row bg-slate-50 w-full p-2 gap-x-2 h-10;
88+
@apply items-center;
89+
}
90+
7991
div {
8092
outline: none !important;
8193
}

web-common/src/features/dashboards/pivot/PivotDrag.svelte

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
<div class="flex flex-col gap-1 items-start">
2626
<button class="flex gap-1" on:click={toggleCollapse}>
2727
<span class="header">{title}</span>
28-
2928
<div class="transition-transform" class:-rotate-180={!collapsed}>
3029
<CaretDownIcon size="12px" />
3130
</div>

web-common/src/features/dashboards/pivot/PivotHeader.svelte

+4-5
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,19 @@
3131
<div class="header-row">
3232
<span class="row-label"> <Column size="16px" /> Columns</span>
3333
<DragList
34-
removable
34+
type="columns"
3535
items={$columns.dimension.concat($columns.measure)}
36-
style="horizontal"
36+
placeholder="Drag dimensions here"
3737
on:update={updateColumn}
3838
/>
3939
</div>
4040
<div class="header-row">
4141
<span class="row-label"> <Row size="16px" /> Rows</span>
42-
4342
<DragList
44-
removable
43+
type="rows"
44+
placeholder="Drag dimensions or measures here"
4545
on:update={updateRows}
4646
items={$rows.dimension}
47-
style="horizontal"
4847
/>
4948
</div>
5049
</div>

web-common/src/features/dashboards/pivot/PivotSidebar.svelte

+3-27
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,12 @@
88
const stateManagers = getStateManagers();
99
const {
1010
selectors: {
11-
measures: { visibleMeasures },
12-
dimensions: { visibleDimensions },
13-
pivot: { columns, rows },
11+
pivot: { measures, dimensions },
1412
},
1513
} = stateManagers;
1614
1715
const timeControlsStore = useTimeControlStore(getStateManagers());
1816
19-
// Todo: Move to external selectors
20-
$: measures = $visibleMeasures
21-
.filter((m) => !$columns.measure.find((c) => c.id === m.name))
22-
.map((measure) => ({
23-
id: measure.name || "Unknown",
24-
title: measure.label || measure.name || "Unknown",
25-
type: PivotChipType.Measure,
26-
}));
27-
28-
$: dimensions = $visibleDimensions
29-
.filter((d) => {
30-
return !(
31-
$columns.dimension.find((c) => c.id === d.name) ||
32-
$rows.dimension.find((r) => r.id === d.name)
33-
);
34-
})
35-
.map((dimension) => ({
36-
id: dimension.name || dimension.column || "Unknown",
37-
title: dimension.label || dimension.name || dimension.column || "Unknown",
38-
type: PivotChipType.Dimension,
39-
}));
40-
4117
$: timeGrainOptions = getAllowedTimeGrains(
4218
new Date($timeControlsStore.timeStart!),
4319
new Date($timeControlsStore.timeEnd!),
@@ -53,8 +29,8 @@
5329
<div class="sidebar">
5430
<div class="container">
5531
<PivotDrag title="Time" items={timeGrainOptions} />
56-
<PivotDrag title="Measures" items={measures} />
57-
<PivotDrag title="Dimensions" items={dimensions} />
32+
<PivotDrag title="Measures" items={$measures} />
33+
<PivotDrag title="Dimensions" items={$dimensions} />
5834
</div>
5935
</div>
6036

Original file line numberDiff line numberDiff line change
@@ -1,7 +1,47 @@
11
import type { DashboardDataSources } from "./types";
2+
import { PivotChipType } from "../../pivot/types";
23

34
export const pivotSelectors = {
45
showPivot: ({ dashboard }: DashboardDataSources) => dashboard.pivot.active,
56
rows: ({ dashboard }: DashboardDataSources) => dashboard.pivot.rows,
67
columns: ({ dashboard }: DashboardDataSources) => dashboard.pivot.columns,
8+
measures: ({ metricsSpecQueryResult, dashboard }: DashboardDataSources) => {
9+
const measures =
10+
metricsSpecQueryResult.data?.measures?.filter(
11+
(d) => d.name && dashboard.visibleMeasureKeys.has(d.name),
12+
) ?? [];
13+
const columns = dashboard.pivot.columns;
14+
15+
return measures
16+
.filter((m) => !columns.measure.find((c) => c.id === m.name))
17+
.map((measure) => ({
18+
id: measure.name || "Unknown",
19+
title: measure.label || measure.name || "Unknown",
20+
type: PivotChipType.Measure,
21+
}));
22+
},
23+
dimensions: ({ metricsSpecQueryResult, dashboard }: DashboardDataSources) => {
24+
{
25+
const dimensions =
26+
metricsSpecQueryResult.data?.dimensions?.filter(
27+
(d) => d.name && dashboard.visibleDimensionKeys.has(d.name),
28+
) ?? [];
29+
const columns = dashboard.pivot.columns;
30+
const rows = dashboard.pivot.rows;
31+
32+
return dimensions
33+
.filter((d) => {
34+
return !(
35+
columns.dimension.find((c) => c.id === d.name) ||
36+
rows.dimension.find((r) => r.id === d.name)
37+
);
38+
})
39+
.map((dimension) => ({
40+
id: dimension.name || dimension.column || "Unknown",
41+
title:
42+
dimension.label || dimension.name || dimension.column || "Unknown",
43+
type: PivotChipType.Dimension,
44+
}));
45+
}
46+
},
747
};

web-common/src/features/dashboards/stores/dashboard-stores.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ const metricViewReducers = {
242242
});
243243

244244
metricsExplorer.pivot.rows = {
245-
...metricsExplorer.pivot.rows,
246245
dimension: dimensions,
247246
};
248247
});
@@ -262,13 +261,26 @@ const metricViewReducers = {
262261
});
263262

264263
metricsExplorer.pivot.columns = {
265-
...metricsExplorer.pivot.columns,
266264
dimension: dimensions,
267265
measure: measures,
268266
};
269267
});
270268
},
271269

270+
addPivotField(name: string, value: PivotChipData, rows: boolean) {
271+
updateMetricsExplorerByName(name, (metricsExplorer) => {
272+
if (value.type === PivotChipType.Measure) {
273+
metricsExplorer.pivot.columns.measure.push(value);
274+
} else {
275+
if (rows) {
276+
metricsExplorer.pivot.rows.dimension.push(value);
277+
} else {
278+
metricsExplorer.pivot.columns.dimension.push(value);
279+
}
280+
}
281+
});
282+
},
283+
272284
setPivotExpanded(name: string, expanded: ExpandedState) {
273285
updateMetricsExplorerByName(name, (metricsExplorer) => {
274286
metricsExplorer.pivot = { ...metricsExplorer.pivot, expanded };

0 commit comments

Comments
 (0)