Skip to content

Commit

Permalink
mostly functional version of package picker ui. still needs bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
telamonian committed May 15, 2022
1 parent 39f1c44 commit 6acce7c
Showing 1 changed file with 158 additions and 60 deletions.
218 changes: 158 additions & 60 deletions conda-store-ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,148 @@ import Stack from "@mui/material/Stack";
import SvgIcon from "@mui/material/SvgIcon";
import TextField from "@mui/material/TextField";

interface Spec {
interface PkgRef {
name: string
current?: number
default?: number
options?: number[]
request?: number
version: number
}

const specRef: Spec[] = [
{
name: "dask",
current: 14.37,
default: 15,
options: [1.00, 2.00, 14.37, 15],
},
{
name: "numpy",
current: 1.99,
default: 1.99,
options: [1.00, 1.10, 1.99],
},
{
name: "pandas",
default: 69.420,
options: [13.00, 14.10, 69.420],
},
{
name: "spark",
default: 1024.00,
options: [36.00, 47.10, 1024.00],
},
];

const specMap = new Map<string, Spec>(specRef.map(x => [x.name, x]));

const getNameOptions: () => string[] = () => specRef.map(x => x.name);
const getSpec = (name: string | null) => name === null || !specMap.has(name) ? null : {...specMap.get(name)!};
const getVersionOptions: (name: string) => number[] = name => specMap.get(name)?.options ?? [];

export function PackagePickerList() {
const [specs, setSpecs] = React.useState<(Spec | null)[]>([null]);

const onSpecChange = (newSpec: Spec | null, ix?: number) => {
specs.splice(ix ?? specs.length, 1, newSpec);
setSpecs([...specs]);
interface PkgRecord {
name: string
version?: number
explicit?: boolean
}

const buildCurrents: PkgRecord[] = [
{name: "dask", version: 14.37, explicit: true},
{name: "numpy", version: 1.99},
{name: "fsspec", version: 0.70},
]

// const packagesRef: Package[] = [
// ["dask", [1.00, 2.00, 14.37, 15.00]],
// ["jupyterlab", [1.00, 2.00, 3.00, 3.30]],
// ["jupyter_core", [1.00, 6.00, 7.00]],
// ["jupyter_server", [0.90, 1.00, 1.10]],
// ["numpy", [1.00, 1.10, 1.99]],
// ["pandas", [13.00, 14.10, 69.420]],
// ["spark", [36.00, 47.10, 1024.00]],
// ].map(([name, vers]: [string, number[]]) => vers.map(ver => {return {name, version: ver}})).flat();

const pkgRefs: PkgRef[] = [
{ name: 'dask', version: 1.00 },
{ name: 'dask', version: 2.00 },
{ name: 'dask', version: 14.37 },
{ name: 'dask', version: 15.00 },
{ name: 'fsspec', version: 0.60 },
{ name: 'fsspec', version: 0.70 },
{ name: 'jupyterlab', version: 1.00 },
{ name: 'jupyterlab', version: 2.00 },
{ name: 'jupyterlab', version: 3.00 },
{ name: 'jupyterlab', version: 3.30 },
{ name: 'jupyter_core', version: 1.00 },
{ name: 'jupyter_core', version: 6.00 },
{ name: 'jupyter_core', version: 7.00 },
{ name: 'jupyter_server', version: 0.90 },
{ name: 'jupyter_server', version: 1.00 },
{ name: 'jupyter_server', version: 1.10 },
{ name: 'numpy', version: 1.00 },
{ name: 'numpy', version: 1.10 },
{ name: 'numpy', version: 1.99 },
{ name: 'pandas', version: 13.00 },
{ name: 'pandas', version: 14.10 },
{ name: 'pandas', version: 69.42 },
{ name: 'spark', version: 36.00 },
{ name: 'spark', version: 47.10 },
{ name: 'spark', version: 1024.00 }
]

const getPackageRefNames = () => [...new Set(pkgRefs.map(x => x.name))];
const getPackageBuildNames = (vis?: "explicit" | "implicit") => {
let pkgs = [...buildCurrents];
if (vis === "explicit") {
pkgs = pkgs.filter(x => x.explicit === true);
} else if (vis === "implicit") {
pkgs = pkgs.filter(x => x.explicit !== true);
}
const addSpec = () => {onSpecChange(null);}
return pkgs.map(x => x.name);
};
const getPackageInstallableNames = () => getPackageRefNames().filter(x => !getPackageBuildNames().includes(x));

// this works since Map.set returns the Map object
const getVersions = (pkgs: PkgRef[]) => pkgs.reduce((map, pkg) => map.set(pkg.name, [...map.get(pkg.name) ?? [], pkg.version]), new Map<string, number[]>());
const getModels = (pkgs: PkgRef[]) => new Map(pkgs.map(x => [x.name, x]));

const pkgVersions = getVersions(pkgRefs);
const pkgModels = getModels(pkgRefs);

interface ItemModel {
name: string,
current?: number,
future?: number,
explicit?: boolean,
}

export function PackagePickerList(params: {
currents?: PkgRecord[],
futures?: PkgRecord[],
}) {
const [futures, setFutures] = React.useState<PkgRecord[]>(params.futures ?? [{name: "", explicit: true}]);

const currents = params.currents ?? [];
const currentsMap = new Map<string, PkgRecord>(currents.map(x => [x.name, x]));

const initModels = () => {
const futuresMap = new Map<string, PkgRecord>(futures.map(x => [x.name, x]));
const items: ItemModel[] = [];

for (const c of currents) {
const f = futuresMap.get(c.name);

items.push({
name: c.name,
current: c.version,
future: f?.version,
explicit: f?.explicit || c.explicit,
});
}

for (const f of futures) {
if (!currentsMap.has(f.name)) {
items.push({
name: f.name,
future: f.version,
explicit: f.explicit,
});
}
}

return items;
}

const onFutureChange = (future?: PkgRecord, ix?: number) => {
ix = ix ? ix - currents.length : futures.length;

if (future) {
futures.splice(ix, 1, future);
} else {
futures.splice(ix, 1);
}

setFutures([...futures]);
}

const addSpec = () => {onFutureChange({name: "", explicit: true});}

const items = initModels();

return (
<Stack>
{specs.map((x, i) =>
{items.map((x, i) =>
<PackagePickerItem
ix={i}
key={i}
onSpecChange={onSpecChange}
spec={x}
onFutureChange={onFutureChange}
model={x}
/>
)}
<IconButton
Expand All @@ -81,33 +167,43 @@ export function PackagePickerList() {
}

export function PackagePickerItem(params: {
spec: Spec | null,
model: ItemModel,
ix: number,
onSpecChange: (newSpec: Spec | null, key: number) => void,
onFutureChange: (future?: PkgRecord, ix?: number) => void,
}) {
return (
<Stack direction="row">
<Autocomplete
onChange={(event: any, name: string | null) => {
params.onSpecChange(getSpec(name), params.ix)
disabled={!!params.model.current}
onChange={(event: any, name?: string) => {
if (name !== params.model.name) {
params.onFutureChange({name: name || "", explicit: true}, params.ix);
}
}}
options={getNameOptions()}
renderInput={(params) => <TextField {...params} label="Pkg Name" />}
options={[...getPackageInstallableNames(), ""]}
renderInput={(params) => <TextField {...params} label="name" />}
sx = {{minWidth: 300}}
value={params.spec?.name ?? null}
value={params.model.name ?? null}
/>
<Autocomplete
disabled={true}
options={[]}
renderInput={(params) => <TextField {...params} label="current version" />}
sx = {{minWidth: 150}}
value={params.model.current ?? null}
/>
<Autocomplete
disabled={params.spec === null}
disabled={!params.model.name}
getOptionLabel={option => option?.toString() ?? ""}
onChange={(event: any, version?: number) => {
if (params.spec !== null) {
params.onSpecChange({...params.spec, current: version}, params.ix)
if (version != params.model.future) {
params.onFutureChange({...params.model, version}, params.ix);
}
}}
options={params.spec === null ? [] : getVersionOptions(params.spec.name)}
renderInput={(params) => <TextField {...params} label="Pkg Version" />}
options={pkgVersions.get(params.model.name)?.filter(x => x !== params.model.current) ?? []}
renderInput={(params) => <TextField {...params} label="version request" />}
sx = {{minWidth: 150}}
value={params.spec === null || params.spec.current === null ? null : params.spec.current ?? params.spec.default ?? params.spec.options?.at(-1)}
value={params.model.future ?? null}
/>
</Stack>
);
Expand All @@ -127,4 +223,6 @@ const rootElem = document.createElement("div");
document.body.appendChild(rootElem);
const root = createRoot(rootElem);

root.render(<PackagePickerList/>);
root.render(<PackagePickerList
currents={buildCurrents}
/>);

0 comments on commit 6acce7c

Please sign in to comment.