Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat group modules #1

Closed
wants to merge 11 commits into from
9 changes: 9 additions & 0 deletions apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ function initializeStore(client: SkottHttpClient, dataStoreRef: AppStore) {
data: nextDataValue,
};

if (nextDataValue.groupedGraph) {
/**
* Enforce the grouped graph view if the grouped graph is available
*
* "Full" graph might be way too heavy to render fast, so we default to the grouped graph which should be smaller.
*/
appStateValue.ui.network.graph = "grouped";
}

dataStoreRef.setInitialState(appStateValue);
})
.catch((exception) => {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/core/network/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export type NetworkActions =
| {
action: "update_configuration";
payload: NetworkLayout;
};
}
| { action: "toggle_graph"; payload: "full" | "grouped" };
13 changes: 13 additions & 0 deletions apps/web/src/core/network/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ function toggleDependencies(): AppReducer {
});
}

if (event.action === "toggle_graph") {
return Option.some({
data: state.data,
ui: {
...state.ui,
network: {
...state.ui.network,
graph: event.payload,
},
},
});
}

if (event.action === "update_configuration") {
return Option.some({
data: state.data,
Expand Down
12 changes: 12 additions & 0 deletions apps/web/src/core/network/update-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,15 @@ export function updateConfiguration(appStore: AppStore) {
});
};
}

export function toggleGraph(appStore: AppStore) {
return function (params: { graph: "full" | "grouped" }) {
appStore.dispatch(
{
action: "toggle_graph",
payload: params.graph,
},
{ notify: true }
);
};
}
30 changes: 30 additions & 0 deletions apps/web/src/network/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import {
IconPlayerStop,
IconRotate2,
IconSettingsAutomation,
IconToggleLeft,
IconToggleRight,
} from "@tabler/icons-react";
import React from "react";
import { Network } from "vis-network";

export function ActionMenu({
network,
initNetwork,
graphMode,
groupedGraphAvailable,
onGraphModeChange,
}: {
network: Network | undefined;
groupedGraphAvailable: boolean;
graphMode: "full" | "grouped";
onGraphModeChange: (mode: "full" | "grouped") => void;
initNetwork: () => void;
}) {
const [simulation, setSimulation] = React.useState(false);
Expand Down Expand Up @@ -65,6 +73,28 @@ export function ActionMenu({
>
{simulation ? "Stop" : "Start"} simulation
</Menu.Item>
{groupedGraphAvailable && (
<Menu.Item
onClick={() => {
if (graphMode === "full") {
onGraphModeChange("grouped");
} else {
onGraphModeChange("full");
}
}}
icon={
graphMode === "full" ? (
<IconToggleLeft style={{ width: rem(14), height: rem(14) }} />
) : (
<IconToggleRight
style={{ width: rem(14), height: rem(14) }}
/>
)
}
>
Use {graphMode === "full" ? "grouped" : "full"} graph
</Menu.Item>
)}

<Menu.Divider />

Expand Down
32 changes: 27 additions & 5 deletions apps/web/src/network/Network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import {
} from "./dependencies";
import { ProgressLoader } from "@/network/ProgressLoader";
import { AppEffects, callUseCase, notify } from "@/store/store";
import { updateConfiguration } from "@/core/network/update-configuration";
import {
updateConfiguration,
toggleGraph,
} from "@/core/network/update-configuration";
import { storeDefaultValue } from "@/store/state";

export default function GraphNetwork() {
Expand All @@ -46,6 +49,17 @@ export default function GraphNetwork() {
const [graphConfig, setGraphConfig] = React.useState<NetworkLayout>(
appStore.getState().ui.network.layout
);
const [graphMode, setGraphMode] = React.useState<"full" | "grouped">(
appStore.getState().ui.network.graph
);

React.useEffect(() => {
const subscription = appStore.store$
.pipe(map(({ ui }) => ui.network.graph))
.subscribe(setGraphMode);

return subscription.unsubscribe;
});

function focusOnNetworkNode(nodeId: string) {
network?.selectNodes([nodeId], true);
Expand Down Expand Up @@ -236,12 +250,17 @@ export default function GraphNetwork() {
if (networkContainerRef.current) {
subscription = appStore.store$
.pipe(
map(({ data }) => data),
distinctUntilChanged(isEqual)
distinctUntilChanged(({ data: prevData }, { data: currentData }) =>
isEqual(prevData, currentData)
)
)
.subscribe((data) => {
.subscribe(({ data, ui }) => {
const { graphNodes, graphEdges } = makeNodesAndEdges(
Object.values(data.graph),
Object.values(
ui.network.graph === "grouped" && data.groupedGraph
? data.groupedGraph
: data.graph
),
{ entrypoint: data.entrypoint }
);

Expand Down Expand Up @@ -296,6 +315,9 @@ export default function GraphNetwork() {
<ActionMenu
network={network}
initNetwork={() => initNetwork(graphConfig)}
groupedGraphAvailable={!!appStore.getState().data.groupedGraph}
graphMode={graphMode}
onGraphModeChange={(graph) => toggleGraph(appStore)({ graph })}
/>
<ProgressLoader />
<div
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/store/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface UiState {
glob: string;
};
network: {
graph: "full" | "grouped";
dependencies: {
circular: {
active: boolean;
Expand Down Expand Up @@ -51,6 +52,7 @@ export const storeDefaultValue = {
glob: "",
},
network: {
graph: "full",
dependencies: {
circular: {
active: false,
Expand Down
60 changes: 59 additions & 1 deletion packages/skott/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function raiseIllegalConfigException(configuration: string): never {

function checkIllegalConfigs<T>(config: O.Option<InputConfig<T>>): void {
if (O.isSome(config)) {
const { entrypoint, includeBaseDir, cwd } = config.value;
const { entrypoint, includeBaseDir, cwd, groups } = config.value;

if (!entrypoint && includeBaseDir) {
raiseIllegalConfigException(
Expand All @@ -33,7 +33,65 @@ function checkIllegalConfigs<T>(config: O.Option<InputConfig<T>>): void {
"`cwd` can't be customized when providing an entrypoint"
);
}

if (groups) {
const list = Object.entries(groups);

for (const [groupName, group] of list) {
for (const [otherGroupName, otherGroup] of list) {
if (groupName === otherGroupName) {
continue;
}

const resolvedPath = resolveGroupPath(group);
const otherResolvedPath = resolveGroupPath(otherGroup);

if (resolvedPath && otherResolvedPath) {
if (
resolvedPath === otherResolvedPath ||
resolvedPath.includes(otherResolvedPath) ||
otherResolvedPath.includes(resolvedPath)
) {
raiseIllegalConfigException(
`Overlapping groups: ${groupName}, ${otherGroupName}`
);
}
}
}
}
}
}
}

function resolveGroupPath(
group: Exclude<SkottConfig<any>["groups"], undefined>[string]
): string {
let resolvedPath: string = "";

if (typeof group === "string") {
resolvedPath = group;
} else if ("basePath" in group) {
resolvedPath = group.basePath;
}

/* trim stuff */
if (resolvedPath.startsWith(".")) {
resolvedPath = resolvedPath.slice(1);
}

if (resolvedPath.startsWith("/")) {
resolvedPath = resolvedPath.slice(1);
}

if (resolvedPath.endsWith("*")) {
resolvedPath = resolvedPath.slice(0, -1);
}

if (resolvedPath.endsWith("/")) {
resolvedPath = resolvedPath.slice(0, -1);
}

return resolvedPath;
}

export default async function skott<T>(
Expand Down
6 changes: 6 additions & 0 deletions packages/skott/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const config = D.struct({
typeOnly: D.boolean
})
),
groups: withDefaultValue(defaultConfig.groups)({
/**
* Temporary placeholder, will be replaced by a proper decoder
*/
decode: (v) => v as any
}),
fileExtensions: withDefaultValue(defaultConfig.fileExtensions)(
D.array(D.literal(".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"))
),
Expand Down