Skip to content
This repository was archived by the owner on Feb 6, 2026. It is now read-only.

Commit 86277e6

Browse files
committed
feat(web) - helpful hiding functionality for Map view
How does this PR change the system? This PR adds two hiding features to the Map view to make it easier to navigate and read. One hiding feature hides all credential components from the Map, while the other hides all components which have no connections to any other components. Both features are on by default, with a button to toggle them off. Both features are behind a feature flag so that we can have our team try them out before rolling anything out to users. This PR also includes some minor refactoring improvements and style fixes to the area the buttons were added to. Out of Scope: These hiding features may be better implemented via a different UI than individual buttons, and we may decide that one or both should be disabled by default instead of enabled by default. This PR also does not make any other changes to the Map besides these two hiding features. How was it tested? Aggressive manual testing with a wide variety of components and configurations. Does it require a docs change? Not yet, but if we roll out this feature we will want to update the docs to explain it to users.
1 parent 8e0cdf9 commit 86277e6

File tree

7 files changed

+190
-49
lines changed

7 files changed

+190
-49
lines changed

app/web/src/newhotness/Explore.vue

Lines changed: 83 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,14 @@
146146
</Transition>
147147
</div>
148148
</div>
149-
<div class="flex-none flex flex-row flex-wrap items-center gap-xs">
149+
<div :class="
150+
clsx(
151+
'flex-none flex flex-row flex-wrap items-center gap-xs',
152+
!showGrid && (mapRef?.selectedComponents.size ?? 0) > 0
153+
? 'pr-[440px]' // right padding to accommodate the ExploreGridTile in the Map view
154+
: 'max-w-[100vw]', // prevents the buttons from going offscreen on smaller window sizes
155+
)"
156+
>
150157
<TabGroupToggle ref="groupRef" :aOrB="urlGridOrMap === 'grid'" @toggle="storeViewMode">
151158
<template #a="{ selected, toggle }">
152159
<ExploreModeTile icon="grid" label="Grid" :selected="selected" @toggle="toggle" />
@@ -156,48 +163,46 @@
156163
</template>
157164
</TabGroupToggle>
158165
<template v-if="!showGrid">
159-
<button
160-
v-if="!ctx.onHead.value"
161-
:class="
162-
clsx(
163-
'flex flex-row gap-xs items-center border rounded-sm',
164-
'p-2xs mb-[-1px] h-7',
165-
'font-mono text-[13px] text-left truncate relative',
166-
themeClasses(
167-
'border-neutral-400 hover:border-action-500',
168-
'border-neutral-600 hover:border-action-300',
169-
),
170-
queryOnlyDiff
171-
? themeClasses('bg-action-200', 'bg-action-900')
172-
: themeClasses('bg-neutral-100', 'bg-neutral-900'),
173-
)
174-
"
166+
<NewButton
167+
tone="toggle"
168+
size="xs"
169+
label="See only diffs"
170+
truncateText
171+
icon="tilde-circle"
172+
:iconClasses="themeClasses('text-warning-500', 'text-warning-300')"
173+
:active="queryOnlyDiff"
175174
@click="toggleOnlyDiff"
176-
>
177-
<Icon name="tilde-circle" :class="themeClasses('text-warning-500', 'text-warning-300')" size="xs" />
178-
<span>See only diffs</span>
179-
</button>
180-
<button
181-
v-if="mapRef?.selectedComponents.size || 0 > 0"
182-
:class="
183-
clsx(
184-
'flex flex-row gap-xs items-center border rounded-sm',
185-
'p-2xs mb-[-1px] h-7',
186-
'font-mono text-[13px] text-left truncate relative',
187-
themeClasses(
188-
'border-neutral-400 hover:border-action-500',
189-
'border-neutral-600 hover:border-action-300',
190-
),
191-
queryHideSubscriptions
192-
? themeClasses('bg-action-200', 'bg-action-900')
193-
: themeClasses('bg-neutral-100', 'bg-neutral-900'),
194-
)
195-
"
175+
/>
176+
<NewButton
177+
v-if="(mapRef?.selectedComponents.size ?? 0) > 0"
178+
tone="toggle"
179+
size="xs"
180+
label="Hide unconnected components"
181+
truncateText
182+
icon="hide"
183+
:active="queryHideSubscriptions"
196184
@click="toggleHide"
197-
>
198-
<Icon name="hide" size="xs" />
199-
<span>Hide unconnected components</span>
200-
</button>
185+
/>
186+
<template v-if="featureFlagsStore.MAP_VIEW_UPGRADES">
187+
<NewButton
188+
tone="toggle"
189+
size="xs"
190+
label="Show credential components"
191+
truncateText
192+
icon="eye"
193+
:active="queryShowCredentials"
194+
@click="toggleShowCredentials"
195+
/>
196+
<NewButton
197+
tone="toggle"
198+
size="xs"
199+
label="Show components with no connections"
200+
truncateText
201+
icon="eye"
202+
:active="queryShowNoConnections"
203+
@click="toggleShowNoConnections"
204+
/>
205+
</template>
201206
</template>
202207
<div v-if="showGrid" class="ml-auto flex flex-row flex-wrap gap-xs">
203208
<DefaultSubscriptionsButton
@@ -567,6 +572,9 @@ import { ExploreGridRowData } from "./explore_grid/ExploreGridRow.vue";
567572
import { useDefaultSubscription } from "./logic_composables/default_subscriptions";
568573
import { useContext } from "./logic_composables/context";
569574
import { generateMockActions } from "./logic_composables/mock_data";
575+
import { useFeatureFlagsStore } from "@/store/feature_flags.store";
576+
577+
const featureFlagsStore = useFeatureFlagsStore();
570578
571579
const router = useRouter();
572580
const route = useRoute();
@@ -630,7 +638,6 @@ const queryOnlyDiff = computed(() => {
630638
};
631639
return query.showDiff === "1";
632640
});
633-
634641
const toggleOnlyDiff = () => {
635642
const query: SelectionsInQueryString = {
636643
...router.currentRoute.value?.query,
@@ -648,7 +655,6 @@ const queryHideSubscriptions = computed(() => {
648655
};
649656
return query.hideSubscriptions === "1";
650657
});
651-
652658
const toggleHide = () => {
653659
const query: SelectionsInQueryString = {
654660
...router.currentRoute.value?.query,
@@ -660,6 +666,40 @@ const toggleHide = () => {
660666
}
661667
router.replace({ query });
662668
};
669+
const queryShowCredentials = computed(() => {
670+
const query: SelectionsInQueryString = {
671+
...router.currentRoute.value?.query,
672+
};
673+
return query.showCredentials === "1";
674+
});
675+
const toggleShowCredentials = () => {
676+
const query: SelectionsInQueryString = {
677+
...router.currentRoute.value?.query,
678+
};
679+
if (queryShowCredentials.value) {
680+
delete query.showCredentials;
681+
} else {
682+
query.showCredentials = "1";
683+
}
684+
router.replace({ query });
685+
};
686+
const queryShowNoConnections = computed(() => {
687+
const query: SelectionsInQueryString = {
688+
...router.currentRoute.value?.query,
689+
};
690+
return query.showNoConnections === "1";
691+
});
692+
const toggleShowNoConnections = () => {
693+
const query: SelectionsInQueryString = {
694+
...router.currentRoute.value?.query,
695+
};
696+
if (queryShowNoConnections.value) {
697+
delete query.showNoConnections;
698+
} else {
699+
query.showNoConnections = "1";
700+
}
701+
router.replace({ query });
702+
};
663703
664704
const urlGridOrMap = computed((): "grid" | "map" => {
665705
const q: SelectionsInQueryString = router.currentRoute.value?.query;

app/web/src/newhotness/Map.vue

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ export type GraphData = {
5757
id="map"
5858
:class="
5959
clsx(
60-
'grid h-full',
60+
'grid h-full border-t',
61+
themeClasses('border-neutral-200 bg-white', 'border-neutral-800 bg-neutral-900'),
6162
showSkeleton && 'hidden', // Since the svgs need a target to be drawn, we need to have this in the DOM
6263
)
6364
"
@@ -247,12 +248,15 @@ import { pickBrandIconByString } from "./util";
247248
import ComponentContextMenu from "./ComponentContextMenu.vue";
248249
import { truncateString } from "./logic_composables/string_funcs";
249250
import MiniMap from "./MiniMap.vue";
251+
import { useFeatureFlagsStore } from "@/store/feature_flags.store";
250252
251253
const MAX_STRING_LENGTH = 18;
252254
253255
const router = useRouter();
254256
const { theme } = useTheme();
255257
258+
const featureFlagsStore = useFeatureFlagsStore();
259+
256260
const props = defineProps<{
257261
active: boolean;
258262
components: ComponentInList[];
@@ -955,6 +959,8 @@ const onEscape = () => {
955959
delete query.map;
956960
delete query.c;
957961
delete query.hideSubscriptions; // Also clear hideSubscriptions when closing map
962+
delete query.showCredentials; // Also clear showCredentials when closing map
963+
delete query.showNoConnections; // Also clear showNoConnections when closing map
958964
delete query.showDiff;
959965
query.grid = "1";
960966
router.push({ query });
@@ -1052,6 +1058,13 @@ const connections = useQuery<IncomingConnections[]>({
10521058
},
10531059
});
10541060
1061+
const isCredentialSchema = (schemaName: string | undefined) => {
1062+
if (schemaName) {
1063+
return schemaName.toLowerCase().includes("credential");
1064+
}
1065+
return false;
1066+
};
1067+
10551068
const mapData = computed(() => {
10561069
const nodes = new Set<string>();
10571070
const edges = new Set<string>();
@@ -1064,6 +1077,8 @@ const mapData = computed(() => {
10641077
const shouldHideUnconnected = router.currentRoute.value.query.hideSubscriptions === "1";
10651078
10661079
const showOnlyDiff = router.currentRoute.value.query.showDiff === "1";
1080+
const showCredentials = router.currentRoute.value.query.showCredentials === "1" || !featureFlagsStore.MAP_VIEW_UPGRADES;
1081+
const showNoConnections = router.currentRoute.value.query.showNoConnections === "1" || !featureFlagsStore.MAP_VIEW_UPGRADES;
10671082
10681083
const hasSelectedComponents = selectedComponents.value.size > 0;
10691084
@@ -1074,7 +1089,14 @@ const mapData = computed(() => {
10741089
connections.data.value.forEach((c) => {
10751090
const component = componentsById.value[c.id];
10761091
if (!component) return;
1092+
// If showOnlyDiff is enabled, only show components with diffs
10771093
if (showOnlyDiff && component.diffStatus === "None") return;
1094+
1095+
// If showCredentials is disabled, hide components which are credentials
1096+
if (!showCredentials && isCredentialSchema(component.schemaName)) {
1097+
return;
1098+
}
1099+
10781100
allComponents.set(c.id, component);
10791101
10801102
c.connections.forEach((e) => {
@@ -1085,6 +1107,41 @@ const mapData = computed(() => {
10851107
});
10861108
});
10871109
1110+
// If showNoConnections is disabled, filter out components which have no connections
1111+
if (!showNoConnections) {
1112+
allComponents.forEach((component) => {
1113+
let keepComponent = false;
1114+
for (const connection of allConnections) {
1115+
if (connection.includes(component.id)) {
1116+
if (showCredentials) {
1117+
// if showCredentials is true then this is a valid connection
1118+
keepComponent = true;
1119+
break;
1120+
} else {
1121+
// otherwise, filter out connections to a credential
1122+
const ids = connection.split("-");
1123+
const id1 = ids[0];
1124+
const id2 = ids[1];
1125+
if (
1126+
(id1 && isCredentialSchema(componentsById.value[id1]?.schemaName)) ||
1127+
(id2 && isCredentialSchema(componentsById.value[id2]?.schemaName))
1128+
) {
1129+
// one of the components connected via this connection is a credential, so ignore it
1130+
continue;
1131+
} else {
1132+
// this connection is a valid one
1133+
keepComponent = true;
1134+
break;
1135+
}
1136+
}
1137+
}
1138+
}
1139+
if (!keepComponent) {
1140+
allComponents.delete(component.id)
1141+
}
1142+
});
1143+
}
1144+
10881145
// If we're in hideSubscriptions mode and have selected components, filter the data
10891146
if (shouldHideUnconnected && hasSelectedComponents) {
10901147
// Get the connected component IDs (including selected ones)
@@ -1098,6 +1155,9 @@ const mapData = computed(() => {
10981155
// Add directly connected components
10991156
selectedComponents.value.forEach((selectedComp) => {
11001157
if (showOnlyDiff && selectedComp.diffStatus === "None") return;
1158+
if (!showCredentials && isCredentialSchema(selectedComp.schemaName)) {
1159+
return;
1160+
}
11011161
11021162
const selectedId = selectedComp.id;
11031163
@@ -1132,7 +1192,10 @@ const mapData = computed(() => {
11321192
} else {
11331193
// Normal mode: include all components
11341194
allComponents.forEach((component, componentId) => {
1135-
if (!showOnlyDiff || (showOnlyDiff && component.diffStatus !== "None")) {
1195+
if (
1196+
(showCredentials || !isCredentialSchema(component.schemaName)) &&
1197+
(!showOnlyDiff || component.diffStatus !== "None")
1198+
) {
11361199
nodes.add(componentId);
11371200
components[componentId] = component;
11381201
}

app/web/src/newhotness/Workspace.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@ export type SelectionsInQueryString = Partial<{
542542
retainSessionState: string; // If set, the component should load up with the last state it had on this tab. Used by Explore.vue
543543
hideSubscriptions: string; // Flag to hide unconnected components when navigating to map
544544
showDiff: string; // Flag to show only components with diff on the map
545+
showCredentials: string; // Flag to show or hide credential components from the map
546+
showNoConnections: string; // Flag to show or hide components with no subscriptions from the map
545547
}>;
546548
547549
const tokenFailStatus = ref();

app/web/src/newhotness/skeletons/ExploreMapSkeleton.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<template>
22
<DelayedComponent>
3-
<section :class="clsx('h-full w-full', themeClasses('bg-white', 'bg-neutral-950'))">
3+
<section :class="
4+
clsx(
5+
'h-full w-full border-t',
6+
themeClasses('border-neutral-200 bg-white', 'border-neutral-800 bg-neutral-900'),
7+
)
8+
">
49
<div
510
:class="
611
clsx(

app/web/src/pages/DebugPage.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ const getButtonTooltip = (variant: string) => {
231231
return "The nostyle variant is used to clear ALL styles";
232232
} else if (variant === "flat") {
233233
return "Similar to the neutral style, but meant to have the same bg color as the things it is sitting on top of (e.g. appear to not have a separate bg color)";
234+
} else if (variant === "toggle") {
235+
return "A special tone used only for the buttons at the top of the Explore page in the area below the search bar";
234236
} else {
235237
return undefined;
236238
}

app/web/src/store/feature_flags.store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const USER_FLAG_MAPPING = {
1919
SHOW_WS_DISCONNECT: "show-ws-disconnect",
2020
SHOW_POLICIES: "show-policies",
2121
SHOW_AUTHORING_NAV: "authoring-in-nav",
22+
MAP_VIEW_UPGRADES: "map-view-upgrades",
2223
} as const;
2324
const WORKSPACE_FLAG_MAPPING: Record<string, string> = {
2425
// STORE_FLAG_NAME: "posthogFlagName",

0 commit comments

Comments
 (0)