diff --git a/.yarnrc.yml b/.yarnrc.yml index 9c65c37f..c7ebb9ad 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -10,6 +10,10 @@ packageExtensions: dependencies: supports-color: "*" + '@next/react-dev-overlay@*': + peerDependencies: + react: '*' + pnpFallbackMode: all pnpMode: loose diff --git a/package.json b/package.json index cbf0cb5b..8244fe3f 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@blueprintjs/table": "^5.1.4", "@lagunovsky/redux-react-router": "^3.2.0", "@loadable/component": "^5.14.1", + "@macrostrat-web/column-builder": "workspace:*", "@macrostrat-web/globe": "workspace:*", "@macrostrat-web/lithology-hierarchy": "workspace:*", "@macrostrat-web/map-utils": "workspace:*", diff --git a/packages/column-builder/.eslintrc.json b/packages/column-builder/.eslintrc.json new file mode 100644 index 00000000..bffb357a --- /dev/null +++ b/packages/column-builder/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/column-builder/.gitignore b/packages/column-builder/.gitignore new file mode 100644 index 00000000..bb6aaf6b --- /dev/null +++ b/packages/column-builder/.gitignore @@ -0,0 +1,44 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +_node_modules +# dependencies +/node_modules +/.pnp +.pnp.js + +.yarn/cache +.yarn/install-state.gz +.yarn/unplugged + +.pnp* + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local +.env.production +.env.development +# vercel +.vercel + +# typescript +*.tsbuildinfo diff --git a/packages/column-builder/README.md b/packages/column-builder/README.md new file mode 100644 index 00000000..24be3143 --- /dev/null +++ b/packages/column-builder/README.md @@ -0,0 +1,33 @@ +This web application is based on [Next.js](https://nextjs.org/) and installed using Yarn PnP. + + +## Getting Started + +**You should use Yarn (>v2), not NPM, to install this application. +Everything is set up for "Plug'n'Play" modules.** + +1. Install modules: `yarn` +2. Run the development server: `yarn run dev`. + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/packages/column-builder/__archive/.dockerignore b/packages/column-builder/__archive/.dockerignore new file mode 100644 index 00000000..4c7db2f3 --- /dev/null +++ b/packages/column-builder/__archive/.dockerignore @@ -0,0 +1,8 @@ +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +docker +.git \ No newline at end of file diff --git a/packages/column-builder/__archive/Dockerfile b/packages/column-builder/__archive/Dockerfile new file mode 100644 index 00000000..9a54540c --- /dev/null +++ b/packages/column-builder/__archive/Dockerfile @@ -0,0 +1,14 @@ +FROM node:14.19 + +WORKDIR /app/dacite + +COPY package*.json ./ +COPY deps/web-components/packages/ui-components/package.json ./deps/web-components/packages/ui-components/ +COPY deps/web-components/packages/form-components/package.json ./deps/web-components/packages/form-components/ +COPY deps/web-components/packages/data-components/package.json ./deps/web-components/packages/data-components/ + +RUN npm install + +EXPOSE 1234 + +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/packages/column-builder/__archive/prod.Dockerfile b/packages/column-builder/__archive/prod.Dockerfile new file mode 100644 index 00000000..1e4d4d78 --- /dev/null +++ b/packages/column-builder/__archive/prod.Dockerfile @@ -0,0 +1,30 @@ +FROM node:16-alpine AS builder + +WORKDIR /app/ +COPY . . + +RUN npm install + +# This will do the trick, use the corresponding env file for each environment. +COPY ./.env.production . + +RUN npm run build + +# 3. Production image, copy all the files and run next +FROM node:16-alpine AS runner +WORKDIR /app/ + +ENV NODE_ENV=production + +# # You only need to copy next.config.js if you are NOT using the default configuration +COPY --from=builder /app/.env.production ./ +COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/.next ./.next + +EXPOSE 1234 + +ENV PORT 1234 + +CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/packages/column-builder/env.example b/packages/column-builder/env.example new file mode 100644 index 00000000..5e371937 --- /dev/null +++ b/packages/column-builder/env.example @@ -0,0 +1,11 @@ +## place this into an .env.development file in this directory!! +NEXT_PUBLIC_SERVER_URL=http://localhost:3001 +NEXT_PUBLIC_CLIENT_URL=http://localhost:3001 +NEXT_PUBLIC_TOPOLOGY_URL=http://localhost:1235 +NEXT_PUBLIC_BASE_URL= + +## .env.production +NEXT_PUBLIC_SERVER_URL=http://postgrest:3001 +NEXT_PUBLIC_CLIENT_URL=http://localhost:3001 +NEXT_PUBLIC_TOPOLOGY_URL=http://localhost:1235 +NEXT_PUBLIC_BASE_URL=/dacite diff --git a/packages/column-builder/next-env.d.ts b/packages/column-builder/next-env.d.ts new file mode 100644 index 00000000..4f11a03d --- /dev/null +++ b/packages/column-builder/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/column-builder/next.config.js b/packages/column-builder/next.config.js new file mode 100644 index 00000000..6a8bf86d --- /dev/null +++ b/packages/column-builder/next.config.js @@ -0,0 +1,48 @@ +/** @type {import('next').NextConfig} */ +const path = require("path"); + +const packageSrc = (name) => + path.resolve( + __dirname, + "..", + "..", + "deps", + "web-components", + "packages", + name, + "src" + ); + +const nextConfig = { + assetPrefix: process.env.NEXT_PUBLIC_BASE_URL, + basePath: process.env.NEXT_PUBLIC_BASE_URL, + typescript: { + ignoreBuildErrors: true, + }, + reactStrictMode: true, + transpilePackages: [ + "@macrostrat/form-components", + "@macrostrat/data-components", + "@macrostrat/ui-components", + "@macrostrat/column-components", + ], + webpack: (config, options) => { + return { + ...config, + + resolve: { + ...config.resolve, + alias: { + ...config.resolve.alias, + "~": path.resolve(__dirname, "src"), + "@macrostrat/form-components": packageSrc("form-components"), + "@macrostrat/data-components": packageSrc("data-components"), + "@macrostrat/ui-components": packageSrc("ui-components"), + "@macrostrat/column-components": packageSrc("column-components"), + }, + }, + }; + }, +}; + +module.exports = nextConfig; diff --git a/packages/column-builder/package.json b/packages/column-builder/package.json new file mode 100644 index 00000000..60a296bf --- /dev/null +++ b/packages/column-builder/package.json @@ -0,0 +1,39 @@ +{ + "name": "@macrostrat-web/column-builder", + "scripts": { + "dev": "next dev -p 1234", + "build": "next build", + "start": "next start -p 1234", + "lint": "next lint" + }, + "module": "src/index.ts", + "dependencies": { + "@blueprintjs/core": "^5.10.5", + "@blueprintjs/icons": "^5.10.0", + "@blueprintjs/popover2": "^1.2.1", + "@blueprintjs/select": "^5.2.1", + "@macrostrat-web/settings": "workspace:*", + "@macrostrat/column-components": "workspace:*", + "@macrostrat/data-components": "workspace:*", + "@macrostrat/form-components": "workspace:*", + "@macrostrat/hyper": "^2.0.1", + "@macrostrat/ui-components": "workspace:*", + "@mapbox/mapbox-gl-draw": "^1.3.0", + "@supabase/postgrest-js": "^0.36.0", + "@types/mapbox__mapbox-gl-draw": "^1.2.3", + "axios": "^0.27.2", + "cross-fetch": "^3.1.5", + "mapbox-gl": "^2.8.2", + "react": "^18", + "react-beautiful-dnd": "^13.1.0", + "react-color": "^2.19.3" + }, + "devDependencies": { + "@types/node": "^17.0.10", + "@types/react": "^17", + "@types/react-beautiful-dnd": "^13.1.2", + "eslint": "^8.7.0", + "eslint-config-next": "^12.0.8", + "typescript": "^4.5.4" + } +} diff --git a/packages/column-builder/src/components/buttons/btns.module.scss b/packages/column-builder/src/components/buttons/btns.module.scss new file mode 100644 index 00000000..998ed479 --- /dev/null +++ b/packages/column-builder/src/components/buttons/btns.module.scss @@ -0,0 +1,10 @@ +.flat-btn { + padding: 0 10px; + min-height: 0; +} + +.position-increment-container { + display: flex; + justify-content: center; + align-items: center; +} diff --git a/packages/column-builder/src/components/buttons/index.ts b/packages/column-builder/src/components/buttons/index.ts new file mode 100644 index 00000000..b42bfb34 --- /dev/null +++ b/packages/column-builder/src/components/buttons/index.ts @@ -0,0 +1,128 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { useModelEditor } from "@macrostrat/ui-components"; +import { Link } from "../routing/routing-helpers"; +import { Button, ButtonGroup } from "@blueprintjs/core"; +import styles from "./btns.module.scss"; +import { ReactChild } from "react"; + +const h = hyperStyled(styles); + +function EditButton({ + href, + small = false, +}: { + href: string; + small?: boolean; +}) { + return h(Link, { href }, [ + h(Button, { + small, + minimal: true, + intent: "success", + icon: "edit", + onClick: (e) => { + e.stopPropagation(); + }, + }), + ]); +} + +interface CreateButtonI { + text: string; + href: string; + minimal?: boolean; +} + +function CreateButton(props: CreateButtonI) { + const { text, href, minimal = true } = props; + return h(Link, { href }, [ + h( + Button, + { + minimal, + intent: "success", + }, + [text] + ), + ]); +} + +interface CancelButtonI { + href: string; +} + +function CancelButton(props: CancelButtonI) { + const { href } = props; + return h(Link, { href }, [ + h( + Button, + { + intent: "danger", + }, + ["Cancel"] + ), + ]); +} + +function SubmitButton(props: { disabled?: boolean }) { + const { hasChanges, actions } = useModelEditor(); + + return h( + Button, + { + disabled: props.disabled || !hasChanges(), + intent: "success", + onClick: () => actions.persistChanges(), + }, + ["Submit"] + ); +} + +function AddButton(props: { + onClick: () => void; + minimal?: boolean; + children: ReactChild; +}) { + const { onClick, minimal = true, children } = props; + + return h( + Button, + { minimal: minimal, onClick, fill: true, intent: "success" }, + ["+ ", children] + ); +} + +function PositionIncrementBtns(props: { + onClickUp: () => void; + onClickDown: () => void; + position_bottom: number; + isFirst?: boolean; + isLast?: boolean; +}) { + return h("div.position-increment-container", [ + props.position_bottom, + h(ButtonGroup, { vertical: true, minimal: true }, [ + h(Button, { + icon: "chevron-up", + className: styles["flat-btn"], + disabled: props.isFirst, + onClick: props.onClickUp, + }), + h(Button, { + icon: "chevron-down", + className: styles["flat-btn"], + disabled: props.isLast, + onClick: props.onClickDown, + }), + ]), + ]); +} + +export { + CreateButton, + EditButton, + SubmitButton, + CancelButton, + AddButton, + PositionIncrementBtns, +}; diff --git a/packages/column-builder/src/components/column-group/column-group-editor.ts b/packages/column-builder/src/components/column-group/column-group-editor.ts new file mode 100644 index 00000000..f618ea47 --- /dev/null +++ b/packages/column-builder/src/components/column-group/column-group-editor.ts @@ -0,0 +1,99 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { ColumnGroupI } from "../../types"; +import { FormGroup, InputGroup } from "@blueprintjs/core"; +import { + ModelEditor, + useModelEditor, + ModelEditButton, + //@ts-ignore +} from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import { SubmitButton } from ".."; + +const h = hyperStyled(styles); + +function ColumnGroupEdit() { + const { + model, + actions, + isEditing, + hasChanges, + }: { + model: ColumnGroupI; + actions: any; + isEditing: boolean; + hasChanges: () => boolean; + } = useModelEditor(); + + // two text editors, name and description + // could have a suggest for the timescale + + const defaultColGroupShort = + model.col_group.length > 0 ? model.col_group : undefined; + const defaultColGroupLong = + model.col_group_long.length > 2 ? model.col_group_long : undefined; + + const updateProject = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + + return h("div", [ + h( + FormGroup, + { + helperText: "Add a short column group name", + label: "Column Group Name (short)", + labelFor: "project-input", + labelInfo: "(required)", + }, + [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: defaultColGroupShort, + onChange: (e) => updateProject("col_group", e.target.value), + }), + ] + ), + h( + FormGroup, + { + helperText: "Add a long column group name", + label: "Column Group Name (long)", + labelFor: "descrip-input", + labelInfo: "(recommended)", + }, + [ + h(InputGroup, { + style: { width: "300px" }, + defaultValue: defaultColGroupLong, + onChange: (e) => updateProject("col_group_long", e.target.value), + }), + ] + ), + h(SubmitButton), + ]); +} + +interface ColumnGroupEditorProps { + model: ColumnGroupI | {}; + persistChanges: ( + e: Partial, + c: Partial + ) => ColumnGroupI; +} + +function ColumnGroupEditor(props: ColumnGroupEditorProps) { + return h( + ModelEditor, + { + model: props.model, + //@ts-ignore + persistChanges: props.persistChanges, + canEdit: true, + isEditing: true, + }, + [h(ColumnGroupEdit)] + ); +} + +export { ColumnGroupEditor }; diff --git a/packages/column-builder/src/components/column/column-editor.ts b/packages/column-builder/src/components/column/column-editor.ts new file mode 100644 index 00000000..5c16855f --- /dev/null +++ b/packages/column-builder/src/components/column/column-editor.ts @@ -0,0 +1,193 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { + NumericInput, + InputGroup, + TextArea, + FormGroup, + Card, + Callout, +} from "@blueprintjs/core"; +import styles from "../comp.module.sass"; +import { ColumnForm, ColumnGroupI } from "~/types"; + +import { SubmitButton } from ".."; +import { LngLatMap } from "./map"; +import { Point } from "@macrostrat/form-components"; +import { ModelEditor, useModelEditor } from "@macrostrat/ui-components"; +import { ColumnRef } from "./column-ref"; + +const h = hyperStyled(styles); + +export interface Model { + model: ColumnForm; + actions: any; + hasChanges: () => boolean; +} + +interface ModelComponentBaseProps { + updateColumn: (field: string, e: any) => void; +} +function ColumnNotes(props: ModelComponentBaseProps) { + const { model, actions, hasChanges }: Model = useModelEditor(); + + return h("div", [ + h("h4", { style: { marginBottom: 0 } }, ["Notes"]), + h(TextArea, { + style: { width: "400px", height: "150px" }, + value: model.notes ?? "", + onChange: (e) => props.updateColumn("notes", e.target.value), + }), + ]); +} + +function ColumnName(props: ModelComponentBaseProps) { + const { model, actions, hasChanges }: Model = useModelEditor(); + + return h("div", [ + h( + FormGroup, + { label: h("h4", { style: { margin: 0 } }, ["Column Name"]) }, + [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: model.col_name || undefined, + onChange: (e) => props.updateColumn("col_name", e.target.value), + }), + ] + ), + ]); +} + +function ColumnNumber(props: ModelComponentBaseProps) { + const { model, actions, hasChanges }: Model = useModelEditor(); + return h( + FormGroup, + { label: h("h4", { style: { margin: 0 } }, ["Column Number"]) }, + [ + h(NumericInput, { + style: { width: "200px" }, + defaultValue: model.col || undefined, + onValueChange: (e: number) => props.updateColumn("col", e), + }), + ] + ); +} + +function ColumnRef_() { + const { model, actions, hasChanges }: Model = useModelEditor(); + + return h( + Callout, + { + style: { + width: "400px", + marginTop: "10px", + marginBottom: "10px", + }, + title: "Reference", + }, + [ + h("div.ref", [model.refs[0]?.ref]), + h("div.ref-author", [ + model.refs[0]?.author, + "(", + model.refs[0]?.pub_year, + ")", + ]), + h("div.doi", [ + h("a", { href: model.refs[0]?.url, target: "_blank" }, [ + model.refs[0]?.doi, + ]), + ]), + h(ColumnRef), + ] + ); +} + +function isDisabled(model: ColumnForm) { + if (typeof model.col_name == "undefined") return true; + if (typeof model.lat == "undefined" || typeof model.lng == "undefined") + return true; + if (typeof model.refs == "undefined") return true; + return false; +} + +function ColumnEdit({ curColGroup }: { curColGroup: Partial }) { + const { model, actions, hasChanges }: Model = useModelEditor(); + + const updateColumn = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + + const setCoords = (p: Point) => { + const [long, lat] = p.geometry.coordinates; + actions.updateState({ + model: { lng: { $set: long }, lat: { $set: lat } }, + }); + }; + + return h(Card, [ + h("div.col-editor-container", [ + h("div.left", [ + h("h4", ["Column Group: ", curColGroup.col_group]), + h(ColumnName, { updateColumn }), + h(ColumnNumber, { updateColumn }), + h("div", [h("td", [h(ColumnNotes, { updateColumn }), h(ColumnRef_)])]), + ]), + h("div.right", [ + h(Card, { style: { minWidth: "600px" }, elevation: 1 }, [ + h(LngLatMap, { + disabled: model.poly_geom != null, + longitude: model.lng ?? 0, + latitude: model.lat ?? 0, + onChange: (p: Point) => setCoords(p), + }), + h.if(model.poly_geom != null)( + Callout, + { + intent: "warning", + title: "No editing location", + style: { width: "600px" }, + }, + [ + "This column has an associated footprint geometry.", + " All editing must take place in ", + h( + "a", + { + href: process.env.NEXT_PUBLIC_TOPOLOGY_URL, + target: "_blank", + }, + ["Column-Topology-Editor."] + ), + ] + ), + ]), + ]), + ]), + h(SubmitButton, { disabled: isDisabled(model) }), + ]); +} + +interface ColumnEditorProps { + model: ColumnForm | {}; + curColGroup: Partial; + persistChanges: (e: ColumnForm, c: Partial) => ColumnForm; +} + +export function ColumnEditor(props: ColumnEditorProps) { + return h( + ModelEditor, + { + model: props.model, + persistChanges: props.persistChanges, + isEditing: true, + canEdit: true, + }, + [ + h(ColumnEdit, { + curColGroup: props.curColGroup, + }), + ] + ); +} diff --git a/packages/column-builder/src/components/column/column-ref.ts b/packages/column-builder/src/components/column/column-ref.ts new file mode 100644 index 00000000..9f8eecde --- /dev/null +++ b/packages/column-builder/src/components/column/column-ref.ts @@ -0,0 +1,171 @@ +import { Ref, useState } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + Button, + NumericInput, + InputGroup, + TextArea, + Spinner, + FormGroup, + Card, + MenuItem, + Drawer, + Collapse, + Callout, +} from "@blueprintjs/core"; +import styles from "../comp.module.sass"; +import { ColumnForm, ColumnGroupI } from "~/types"; +import { DataI, ItemSuggest } from "../suggest"; +import { RefI } from "~/types"; +import pg, { usePostgrest } from "../../db"; +import { RefEditor } from "../ref/ref-editor"; +import { SubmitButton } from ".."; +import { LngLatMap } from "./map"; +import { Point, Pub, PublicationFinder } from "@macrostrat/form-components"; +import { ModelEditor, useModelEditor } from "@macrostrat/ui-components"; +import { ItemRenderer } from "@blueprintjs/select"; +import { Model } from "./column-editor"; + +const h = hyperStyled(styles); + +interface RefDataI { + value: string; + data: RefI; +} + +function formPubSelectText(ref: RefI) { + const title = ref.ref?.length > 50 ? ref.ref.slice(0, 50) + "..." : ref.ref; + + return `${ref?.author}(${ref?.pub_year})-${title}`; +} + +const ColumnRefItemRenderer: ItemRenderer> = ( + item: DataI, + { handleClick, modifiers, index } +) => { + const { value, data } = item; + return h(MenuItem, { + key: index, + text: h(Callout, [ + h("div", [data.ref]), + h("div.ref-author", [data.author, "(", data.pub_year, ")"]), + h("div.doi", [data.doi]), + ]), + onClick: handleClick, + active: modifiers.active, + }); +}; + +function ColumnRef() { + const [open, setOpen] = useState(false); + const [newRefOpen, setNewRefOpen] = useState(false); + + const { model, actions }: Model = useModelEditor(); + const refs: RefI[] = usePostgrest( + pg.from("refs").select("id, pub_year, author, ref, doi, url") + ); + + if (!refs) return h(Spinner); + + const onClick = () => { + setOpen(!open); + }; + + const onChange = (item: RefDataI) => { + actions.updateState({ model: { refs: { $set: [item.data] } } }); + }; + + const onPubFinderClick = async (e: Pub) => { + const newRef: RefI = { + ref: e.title, + pub_year: e.year, + author: e.author, + doi: e.doi, + url: e.link, + }; + const { data, error } = await pg.from("refs").insert([newRef]); + if (!error) { + actions.updateState({ model: { refs: { $set: [data[0]] } } }); + } + setOpen(false); + setNewRefOpen(false); + }; + + // have the ref suggest as well as option to create new ref. + return h("div", [ + h(ItemSuggest, { + items: refs.map((ref) => { + return { value: formPubSelectText(ref), data: ref }; + }), + itemRenderer: ColumnRefItemRenderer, + initialSelected: { + value: model.refs[0] ? formPubSelectText(model.refs[0]) : "", + data: model.refs[0] || {}, + }, + onChange, + }), + h(Button, { onClick, icon: "plus" }), + h( + Drawer, + { + usePortal: true, + isOpen: open, + onClose: () => setOpen(false), + title: "Add a new reference", + }, + [ + h( + "div", + { + style: { + padding: "5px", + display: "flex", + flexDirection: "column", + }, + }, + [ + h("div", [ + h("h3", { style: { marginBottom: 0 } }, [ + "Search for a Publication", + ]), + h(PublicationFinder, { + onClick: onPubFinderClick, + }), + ]), + h("div", [ + h( + Button, + { + minimal: true, + fill: true, + onClick: () => setNewRefOpen(!newRefOpen), + }, + ["+ Can't find the paper, add it's details +"] + ), + h(Collapse, { isOpen: newRefOpen }, [h(NewRef)]), + ]), + ] + ), + ] + ), + ]); +} + +function NewRef() { + const { model, actions, hasChanges }: Model = useModelEditor(); + + const persistChanges = async (e: RefI, c: Partial) => { + console.log("persistChanges!!"); + // would need to post ref to back as new ref first + const { data, error } = await pg.from("refs").insert([e]); + if (!error) { + actions.updateState({ model: { refs: { $set: [data[0]] } } }); + } + + return e; + }; + //@ts-ignore + return h(RefEditor, { model: model.ref || {}, persistChanges }); +} + +export { ColumnRef }; diff --git a/packages/column-builder/src/components/column/index.ts b/packages/column-builder/src/components/column/index.ts new file mode 100644 index 00000000..98032cbc --- /dev/null +++ b/packages/column-builder/src/components/column/index.ts @@ -0,0 +1,2 @@ +export * from "../unit-section-table/reducer"; +export * from "./map"; diff --git a/packages/column-builder/src/components/column/map.ts b/packages/column-builder/src/components/column/map.ts new file mode 100644 index 00000000..976f1df6 --- /dev/null +++ b/packages/column-builder/src/components/column/map.ts @@ -0,0 +1,61 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import mapboxgl from "mapbox-gl"; +import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css"; +import { useEffect, useState } from "react"; +import "mapbox-gl/dist/mapbox-gl.css"; +import styles from "../comp.module.sass"; +import { + LngLatMap as LngLatMap_, + LngLatInputs, + Point, +} from "@macrostrat/form-components"; +const h = hyperStyled(styles); + +mapboxgl.accessToken = + "pk.eyJ1IjoidGhlZmFsbGluZ2R1Y2siLCJhIjoiY2tsOHAzeDZ6MWtsaTJ2cXhpMDAxc3k5biJ9.FUMK57K0UP7PSrTUi3DiFQ"; + +function roundCoordinates(p: Point) { + let [long, lat] = p.geometry.coordinates; + p.geometry.coordinates = [ + parseFloat(long.toPrecision(7)), + parseFloat(lat.toPrecision(7)), + ]; +} + +/* break state out and share it */ +function LngLatMap(props: { + longitude: number; + latitude: number; + onChange: (p: Point) => void; + disabled: boolean; +}) { + const [point, setPoint] = useState({ + geometry: { coordinates: [props.longitude, props.latitude], type: "Point" }, + id: "", + properties: {}, + type: "Feature", + }); + + const setPoint_ = (p: Point) => { + roundCoordinates(p); + setPoint(p); + }; + + useEffect(() => { + props.onChange(point); + }, [point]); + + return h("div", [ + h("h4", { style: { marginTop: 0 } }, ["Set a location"]), + h(LngLatMap_, { point, setPoint: setPoint_, disabled: props.disabled }), + h("div.lng-lat-inputs", [ + h(LngLatInputs, { + point, + setPoint: setPoint_, + disabled: props.disabled, + }), + ]), + ]); +} + +export { LngLatMap }; diff --git a/packages/column-builder/src/components/comp.module.sass b/packages/column-builder/src/components/comp.module.sass new file mode 100644 index 00000000..c8aaf326 --- /dev/null +++ b/packages/column-builder/src/components/comp.module.sass @@ -0,0 +1,183 @@ +.column-builder :global + a + color: inherit + text-decoration: none + * + box-sizing: border-box + .table-container + margin: 10px + height: 100% + padding: 0px + border-radius: 3px + .col-group-name + display: flex + justify-content: center + align-items: center + .bp5-menu + max-height: 400px + max-width: 400px + overflow: auto +.mySuggest + overflow: scroll + max-height: 200px +.itemSelectPopover + padding: 5px + background-color: var(--panel-background-color) + -ms-overflow-style: none + /* Internet Explorer 10+ */ + scrollbar-width: none + /* Firefox */ + ::-webkit-scrollbar + display: none + /* Safari and Chrome */ +.interval-row + display: flex + justify-content: space-between +.row + display: flex + align-items: baseline +.color-block + border-style: solid + //border-color: black + border-width: 0.5px + height: 30px + width: 30px +.page + margin: 10px +.bread-crumbs + display: flex +.strat-name-con + display: flex + align-items: baseline +.strat-name + margin: 0 +.tag-container, +.tag-popover + flex-wrap: wrap + display: flex + align-items: baseline +.tag-popover + padding: 10px + height: 300px + width: 900px + overflow-y: scroll +.tag + margin: 1px +.interval-cell-min-editor + display: flex + flex-direction: column + height: 8em + justify-content: space-between +.nomargin + margin-bottom: 0px +.margin-bottom-spacing + margin-bottom: 20px +.map-container + height: 200px + width: 100% +.header, +.header-drag + min-height: 15px + padding: 5px + padding-left: 10px + text-transform: capitalize + font-size: 15px + //background: rgb(211, 216, 222) + font-weight: bold +.header-drag + cursor: grab + &:active + cursor: grabbing +.lng-lat-inputs + margin: 5px + display: flex + justify-content: space-around +.col-editor-container + display: flex + justify-content: space-between +.unit-section-tables + width: 75% +.unit-section-container + display: flex + justify-content: center + margin-top: 10px +.unit-moved + box-shadow: -6px 0px 0px 0px #10b146 +.btwn-row-btn + height: 10px + width: 100% + margin: -10px 0% + padding-top: 5px + &:hover + cursor: pointer +.base-table + border: solid 1px + //border-color: #c5cbd3 + margin: 10px + //background-color: white + border-radius: 5px +.full-width + width: 100% + table-layout: fixed +.ellipse + text-overflow: ellipsis + overflow: hidden + max-width: 80px + white-space: nowrap +.strat-name-tooltip + h4 + margin: 0 + .underline + text-decoration: underline + i + color: rgb(166, 170, 174) + a + text-decoration: underline + color: rgb(108, 138, 175) + +.no-strat-meta + h4::before + color: rgb(243, 170, 33) + content: "Warning: " +///////////////////////// strat modal /////////////// +.concept-card + h4 + margin: 0 + font-size: 15px + .geologic-age + font-style: italic + //color: #7b7b7b + p + margin: 0 + a + text-decoration: underline + +.strat-title + h3 + margin: 0 + display: flex + justify-content: space-between +.related-strat-card + h3 + margin: 0 +.strat-name-dialog-container + display: flex + padding: 10px + .left, + .right + margin: 5px +.strat-dialog-bottom + margin: 10px +/////////////// ref list item ////////////////// +.ref-author + font-style: italic + &::before + content: "-" +.doi + &::before + content: "DOI: " + color: rgb(86, 109, 138) + +.table-header-row + background-color: var(--panel-secondary-background-color) + border-bottom: 1px solid var(--panel-rule-color) diff --git a/packages/column-builder/src/components/draggable-overlay.ts b/packages/column-builder/src/components/draggable-overlay.ts new file mode 100644 index 00000000..256a84b8 --- /dev/null +++ b/packages/column-builder/src/components/draggable-overlay.ts @@ -0,0 +1,92 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { useState, useEffect, ReactChild } from "react"; +import { Overlay, Card } from "@blueprintjs/core"; +import styles from "./comp.module.sass"; + +const h = hyperStyled(styles); +interface DraggableOverlayPropsI { + open: boolean; + children: ReactChild; + title?: string; + cardStyles?: object; + top?: number; + left?: number; +} + +function DraggableOverlay(props: DraggableOverlayPropsI) { + const { open, children, cardStyles = {}, top = 0, left = 0 } = props; + + const [state, setState] = useState({ top, left }); + const [offset, setOffset] = useState({ rel_x: 0, rel_y: 0 }); + const [dragging, setDragging] = useState(false); + + useEffect(() => { + //setup event listeners + if (!dragging) return; + document.addEventListener("mousemove", onMouseMove, true); + document.addEventListener("mouseup", onMouseUp, true); + return () => { + document.removeEventListener("mouseup", onMouseUp, true); + document.removeEventListener("mousemove", onMouseMove, true); + }; + }, [dragging]); + + const onMouseDown = (e: any) => { + if (e.button !== 0) return; + setDragging(true); + setOffset(getOffest(e)); + }; + + const onMouseUp = (e: any) => { + setDragging(false); + e.stopPropagation(); + e.preventDefault(); + }; + + const getOffest = (e: any) => { + const rel_x = e.pageX - state.left; + const rel_y = e.pageY - state.top; + return { rel_x, rel_y }; + }; + + const onMouseMove = (e: any) => { + if (dragging) { + const { rel_x, rel_y } = offset; + const left_ = e.pageX - rel_x; + const top_ = e.pageY - rel_y; + setState({ top: top_, left: left_ }); + const portals = Array.from( + document.getElementsByClassName( + "bp5-portal" + ) as HTMLCollectionOf + ); + portals.map((portal) => { + portal.style.top = `${top_}px`; + portal.style.left = `${left_}px`; + }); + } + e.stopPropagation(); + e.preventDefault(); + }; + + const overlayProperties = { + autoFocus: true, + canEscapeKeyClose: true, + canOutsideClickClose: true, + enforceFocus: false, + hasBackdrop: false, + usePortal: true, + useTallContent: false, + }; + + return h(Overlay, { isOpen: open, ...overlayProperties }, [ + h("div", {}, [ + h(Card, { style: { padding: 0, paddingBottom: "5px" }, elevation: 4 }, [ + h("div.header-drag", { onMouseDown: onMouseDown }, [props.title]), + children, + ]), + ]), + ]); +} + +export { DraggableOverlay }; diff --git a/packages/column-builder/src/components/helpers.ts b/packages/column-builder/src/components/helpers.ts new file mode 100644 index 00000000..45e21c04 --- /dev/null +++ b/packages/column-builder/src/components/helpers.ts @@ -0,0 +1,95 @@ +import { QueryI } from "."; +import { EnvironUnit, LithUnit, StratNameI } from ".."; + +/* +returns a list of number ids for the envs or liths to be deleted or +added from a unit. + +Otherwise it's impossible to detect if a user has removed envs or liths +*/ +const detectDeletionsAndAdditions = ( + og: EnvironUnit[] | LithUnit[] | StratNameI[], + changes: EnvironUnit[] | LithUnit[] | StratNameI[] +) => { + let deletions = new Set(); + let additions = new Set(); + + const present_og: any = {}; + + og.map((o) => { + Object.assign(present_og, { [o.id]: true }); + }); + + changes.map((c) => { + let key = c.id; + if (present_og[key]) { + delete present_og[key]; + } else { + additions.add(c.id); + } + }); + Object.keys(present_og).map((i) => deletions.add(parseInt(i))); + return { deletions, additions }; +}; + +/* +Returns a string url for a next link that creates the query string from +the passed 'query' object +*/ +const createLink = (base: string, query: QueryI) => { + let queryString = "?"; + Object.entries(query).map(([key, value], i) => { + if (queryString == "?") { + queryString = queryString + `${key}=${value}`; + } else { + queryString = queryString + `&${key}=${value}`; + } + }); + return base + queryString; +}; + +const filterOrAddIds = (id: number, mergeIds: number[]): [] | number[] => { + if (mergeIds.length == 0) { + return [id]; + } else if (mergeIds.includes(id)) { + return mergeIds.filter((i) => i != id); + } + return [id, ...mergeIds]; +}; + +/* mapping color names from macrostrat to hex codes */ +const colorMap: { [name: string]: string } = { + blue: "#0000FF", + [`blue dark`]: "#00008B", + [`blue green`]: "#008B8B", + black: "#000000", + yellow: "#FFFF00", + orange: "#FFA500", + [`brown dark`]: "#A52A2A", + [`brown light`]: "#DEB887", + tan: "#D2B48C", + [`green dark`]: "#006400", + [`green light`]: "#90EE90", + [`gray dark`]: "#A9A9A9", + [`gray light`]: "#D3D3D3", + pink: "#FFC0CB", + purple: "#800080", + red: "#FF0000", + gray: "#808080", + green: "#008000", + brown: "#D2691E", + [`steel blue`]: "#B0C4DE", + white: "#FFFFFF", +}; + +const convertColorNameToHex = (name: string): string => { + if (name[0] === "#") return name; + return colorMap[name] ?? "#ffffff"; +}; + +export { + detectDeletionsAndAdditions, + createLink, + filterOrAddIds, + convertColorNameToHex, +}; diff --git a/packages/column-builder/src/components/index.ts b/packages/column-builder/src/components/index.ts new file mode 100644 index 00000000..6b5e7103 --- /dev/null +++ b/packages/column-builder/src/components/index.ts @@ -0,0 +1,18 @@ +export * from "./table"; +export * from "./unit/interval-suggest"; +export * from "./routing/base-page"; +export * from "./routing/routing-helpers"; +export * from "./tag"; +export * from "./unit/color"; +export * from "./unit/unit-editor"; +export * from "./project/project-editor"; +export * from "./column-group/column-group-editor"; +export * from "./column/column-editor"; +export * from "./column"; +export * from "./strat-name"; +export * from "./buttons"; +export * from "./helpers"; +export * from "./unit"; +export * from "./section"; +export * from "./ssr-data-helpers"; +export * from "./unit-section-table"; diff --git a/packages/column-builder/src/components/lith/dialog.ts b/packages/column-builder/src/components/lith/dialog.ts new file mode 100644 index 00000000..bee0397c --- /dev/null +++ b/packages/column-builder/src/components/lith/dialog.ts @@ -0,0 +1,134 @@ +import { useState } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { Lith, LithUnit } from "~/types"; +import { LithSegment, LithSegmentContainer } from "./renderer"; +import { Dialog, Switch, Button, Intent, Divider } from "@blueprintjs/core"; +import styles from "./lith.module.scss"; +import { LithSelect } from "./query-list"; + +const h = hyperStyled(styles); + +interface LithDialogProps { + liths?: LithUnit[]; + onRemove?: (l: LithUnit) => void; + onClose: () => void; + onSwitchChange: (i: number) => void; + onAdd: (l: Lith) => void; + isOpen: boolean; +} +function LithDialog(props: LithDialogProps) { + const { + liths = [], + isOpen, + onRemove = (l) => console.log(l), + onClose, + onSwitchChange, + onAdd, + } = props; + + const title = "Manage lithologies"; + + const onChange = (id: number) => { + onSwitchChange(id); + }; + + return h( + Dialog, + { + isOpen, + onClose, + title, + isCloseButtonShown: true, + }, + [ + h("div.lith-dialog", [ + h(LithSegmentContainer, { liths, onClick: () => {}, large: false }), + h("div.lith-switches", [ + liths.map((lith, i) => { + return h(LithSwitch, { + key: lith.id, + lith, + index: i, + onChange, + onRemove, + }); + }), + ]), + h(Divider), + h("div.lith-select", [h(LithSelect, { onItemSelect: onAdd })]), + ]), + ] + ); +} + +interface LithSwitchProps { + lith: LithUnit; + onChange: (id: number) => void; + onRemove?: (l: LithUnit) => void; +} +function LithSwitch(props: LithSwitchProps) { + const { lith, onRemove } = props; + return h("div.row", [ + h("div.lith-item", [ + h(LithSegment, { + lith, + large: true, + onRemove, + widthInherit: false, + }), + ]), + // switch between dom and sub + h("div.switch", [ + h(Switch, { + innerLabel: "Sub", + innerLabelChecked: "Dom", + checked: lith.dom == "dom", + onChange: () => props.onChange(lith.id), + }), + ]), + ]); +} + +interface LithContainerProps { + liths: LithUnit[]; + large: boolean; + onRemove?: (l: LithUnit) => void; + onAdd: (l: Lith) => void; + onSwitchProp: (id: number, prop: "dom" | "sub") => void; + isEditing: boolean; +} +function LithContainer(props: LithContainerProps) { + const [isOpen, setIsOpen] = useState(false); + + const onClickOpen = () => { + if (!props.isEditing) return; + setIsOpen(true); + }; + + return h("div", [ + h(LithDialog, { + isOpen, + onClose: () => setIsOpen(false), + onAdd: props.onAdd, + onSwitchChange: props.onSwitchProp, + liths: props.liths, + onRemove: props.onRemove, + }), + h("div.row", [ + h(LithSegmentContainer, { + liths: props.liths, + onRemove: props.onRemove, + onClick: onClickOpen, + large: props.large, + }), + h.if(props.isEditing)(Button, { + intent: Intent.SUCCESS, + icon: "plus", + onClick: onClickOpen, + minimal: true, + }), + ]), + ]); +} + +export { LithDialog, LithContainer }; diff --git a/packages/column-builder/src/components/lith/index.ts b/packages/column-builder/src/components/lith/index.ts new file mode 100644 index 00000000..d5b729b3 --- /dev/null +++ b/packages/column-builder/src/components/lith/index.ts @@ -0,0 +1,2 @@ +export * from "./renderer"; +export * from "./dialog"; diff --git a/packages/column-builder/src/components/lith/lith.module.scss b/packages/column-builder/src/components/lith/lith.module.scss new file mode 100644 index 00000000..8474192b --- /dev/null +++ b/packages/column-builder/src/components/lith/lith.module.scss @@ -0,0 +1,65 @@ +.lith-segment-container { + display: flex; + width: 100%; + min-width: 100px; + max-width: 600px; + &:hover { + cursor: pointer; + } +} + +.segment-tooltip { + display: flex; + flex-direction: column; + b { + text-transform: capitalize; + } + i { + color: #d3d8de; + } +} + +.lith-segment { + margin-right: 1px; + text-transform: uppercase; + display: flex; + justify-content: center; + align-items: center; + + p { + text-overflow: ellipsis; + overflow: hidden; + margin-bottom: 0; + color: black; + font-size: 13px; + } + + .small { + font-size: 11px !important; + } +} + +.lith-dialog { + padding: 10px; + min-height: 400px; +} +.row { + display: flex; +} +.lith-switches { + list-style: none; + .row { + display: flex; + justify-content: space-around; + align-items: baseline; + margin: 10px; + .lith-item { + flex-grow: 1; + max-width: 100px; + } + .switch { + flex-grow: 0; + display: flex; + } + } +} diff --git a/packages/column-builder/src/components/lith/query-list.ts b/packages/column-builder/src/components/lith/query-list.ts new file mode 100644 index 00000000..cb78469c --- /dev/null +++ b/packages/column-builder/src/components/lith/query-list.ts @@ -0,0 +1,74 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { Lith } from "../../types"; +import { LithMenuItem } from "../tag"; +import styles from "./lith.module.scss"; +import { + QueryList, + ItemRenderer, + IQueryListRendererProps, + ItemPredicate, +} from "@blueprintjs/select"; +import pg, { usePostgrest } from "../../db"; +import { InputGroup } from "@blueprintjs/core"; + +const h = hyperStyled(styles); + +const LithQueryList = QueryList.ofType(); + +const itemPredicate: ItemPredicate = (query, item, index) => { + const { lith } = item; + + return lith?.toLowerCase().indexOf(query.toLowerCase()) >= 0; +}; + +const LithItemRenderer: ItemRenderer = ( + item: Lith, + { handleClick, index, modifiers } +) => { + return h(LithMenuItem, { + key: index, + data: { name: item.lith, color: item.lith_color }, + modifiers, + handleClick, + }); +}; + +const LithQueryListRenderer = (props: IQueryListRendererProps) => { + const { itemList, handleKeyDown, handleKeyUp, ...listProps } = props; + + return h( + "div.lith-query-list-renderer", + { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, + [ + h(InputGroup, { + ["aria-autocomplete"]: "list", + leftIcon: "search", + placeholder: "Add a lithology...", + onChange: listProps.handleQueryChange, + value: listProps.query, + }), + itemList, + ] + ); +}; + +interface LithSelectProps { + onItemSelect: (l: Lith) => void; +} + +function LithSelect(props: LithSelectProps) { + const liths: Lith[] = usePostgrest(pg.from("liths")); + if (!liths) return null; + + return h(LithQueryList, { + itemRenderer: LithItemRenderer, + itemPredicate, + onItemSelect: props.onItemSelect, + items: liths, + renderer: LithQueryListRenderer, + resetOnSelect: true, + menuProps: { style: { maxWidth: "100%", margin: "0 10px" } }, + }); +} + +export { LithSelect }; diff --git a/packages/column-builder/src/components/lith/renderer.ts b/packages/column-builder/src/components/lith/renderer.ts new file mode 100644 index 00000000..44d67953 --- /dev/null +++ b/packages/column-builder/src/components/lith/renderer.ts @@ -0,0 +1,109 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { Lith, LithUnit } from "~/types"; +import { mergeRefs, Tag, Dialog } from "@blueprintjs/core"; +import { Tooltip2, Popover2 } from "@blueprintjs/popover2"; +import styles from "./lith.module.scss"; + +const h = hyperStyled(styles); + +const getLithProportions = (liths: LithUnit[]) => { + if (liths.length == 0) return [1, 1]; + + let dom_count = 0; + let sub_count = 0; + + liths.map((lith) => { + if (lith.dom == "dom") dom_count++; + else sub_count++; + }); + + const dom_prop = 5 / (sub_count + dom_count * 5); + const sub_prop = 1 / (sub_count + dom_count * 5); + + return [dom_prop, sub_prop]; +}; + +interface LithContainerProps { + liths?: LithUnit[]; + onRemove?: (l: LithUnit) => void; + large: boolean; + onClick: () => void; +} + +function LithSegmentContainer(props: LithContainerProps) { + const { liths = [] } = props; + + const [dom_prop, sub_prop] = getLithProportions(liths); + return h("div.lith-segment-container", { onClick: props.onClick }, [ + liths.map((lith, i) => { + return h(LithSegment, { + key: i, + lith, + onRemove: props.onRemove, + large: props.large, + width: lith.dom == "dom" ? dom_prop * 100 : sub_prop * 100, + }); + }), + ]); +} + +function LithSegmentToolTipContent(props: { lith: LithUnit | Lith }) { + return h("div.segment-tooltip", [ + h("span", [ + h("b", [props.lith.lith]), + " ", + h("i", ["(", props.lith.dom, ")"]), + ]), + h.if( + typeof props.lith.lith_group !== "undefined" && + props.lith.lith_group != null + )("span", ["Group: ", h("i", [props.lith.lith_group])]), + h("span", [props.lith.lith_class, ", ", props.lith.lith_type]), + ]); +} + +function LithSegment(props: { + lith: LithUnit | Lith; + onRemove?: (l: LithUnit) => void; + large: boolean; + width: number; +}) { + const { width = 0 } = props; + const style = { + backgroundColor: props.lith.lith_color + "70", + width: width > 0 ? `${width}%` : "100%", + }; + + return h(Tooltip2, { + position: "top", + content: h(LithSegmentToolTipContent, { lith: props.lith }), + renderTarget: ({ isOpen, ref, ...tooltipProps }) => + h( + Tag, + { + style: { + ...style, + padding: props.large ? "5px 7px" : "2px 6px", + display: "flex", + justifyContent: "space-around", + marginRight: "1px", + borderRadius: 0, + }, + onRemove: + typeof props.onRemove !== "undefined" + ? //@ts-ignore + () => props.onRemove(props.lith) + : undefined, + elementRef: mergeRefs(ref), + ...tooltipProps, + }, + [ + h(`div.lith-segment ${!props.large ? ".small" : ""}`, [ + h("p", [props.lith.lith]), + ]), + ] + ), + }); +} + +export { LithSegmentContainer, LithSegment }; diff --git a/packages/column-builder/src/components/project/project-editor.ts b/packages/column-builder/src/components/project/project-editor.ts new file mode 100644 index 00000000..e4f38bf8 --- /dev/null +++ b/packages/column-builder/src/components/project/project-editor.ts @@ -0,0 +1,135 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { Project, TimeScale } from "../../types"; +import { FormGroup, InputGroup, Spinner, TextArea } from "@blueprintjs/core"; +import { + ModelEditor, + useModelEditor, + ModelEditButton, + ModelEditorContext, +} from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import pg, { usePostgrest } from "../.."; +import { CancelButton, SubmitButton } from ".."; +import { ItemSuggest } from "../suggest"; + +const h = hyperStyled(styles); + +interface TimeScaleSuggest { + value: string; + data: TimeScale; +} + +interface TimeScaleSuggestProps { + onChange: (e: TimeScaleSuggest) => void; + initialSelected?: number; + onQueryChange?: (e: string) => void; + timescales: TimeScale[]; +} + +function TimeScaleSuggest(props: TimeScaleSuggestProps) { + const timescales_ = props.timescales.map((t) => { + return { value: t.timescale, data: t }; + }); + + const init = timescales_.filter((t) => t.data.id == props.initialSelected)[0]; + + return h(ItemSuggest, { + items: timescales_, + initialSelected: init, + onChange: props.onChange, + onQueryChange: props.onQueryChange, + }); +} + +function ProjectEdit() { + const { + model, + actions, + }: { + model: Project; + actions: any; + } = useModelEditor(); + + const timescales: TimeScale[] = usePostgrest(pg.from("timescales")); + + const defaultProjectName = + model.project.length > 2 ? model.project : undefined; + const defaultProjectDescrip = + model.descrip.length > 2 ? model.descrip : undefined; + + const updateProject = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + + return h("div", [ + h( + FormGroup, + { + helperText: "Add a name to your project", + label: "Project Name", + labelInfo: "(required)", + }, + [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: defaultProjectName, + onChange: (e) => updateProject("project", e.target.value), + }), + ] + ), + h( + FormGroup, + { + helperText: "Add a description to your project", + label: "Project Description", + labelInfo: "(recommended)", + }, + [ + h(TextArea, { + style: { minHeight: "170px", minWidth: "500px" }, + defaultValue: defaultProjectDescrip, + onChange: (e) => updateProject("descrip", e.target.value), + }), + ] + ), + h( + FormGroup, + { + helperText: "Most projects use International Ages", + label: "Project Timescale", + labelInfo: "(required)", + }, + [ + h.if(timescales == undefined)(Spinner), + h.if(timescales != undefined)(TimeScaleSuggest, { + initialSelected: model.timescale_id, + timescales, + onChange: (e: TimeScaleSuggest) => + updateProject("timescale_id", e.data.id), + }), + ] + ), + h(SubmitButton), + ]); +} + +interface ProjectEditorProps { + project: Project | {}; + persistChanges: (e: Partial, c: Partial) => Project; +} + +function ProjectEditor(props: ProjectEditorProps) { + return h( + ModelEditor, + { + model: props.project, + //@ts-ignore + persistChanges: props.persistChanges, + canEdit: true, + isEditing: true, + }, + [h(ProjectEdit)] + ); +} + +export { ProjectEditor }; diff --git a/packages/column-builder/src/components/ref/ref-editor.ts b/packages/column-builder/src/components/ref/ref-editor.ts new file mode 100644 index 00000000..5b2af809 --- /dev/null +++ b/packages/column-builder/src/components/ref/ref-editor.ts @@ -0,0 +1,90 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { Table } from "../../index"; +import { NumericInput, TextArea, InputGroup } from "@blueprintjs/core"; +import { + ModelEditor, + useModelEditor, + ModelEditButton, +} from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import { RefI } from "~/types"; +import { SubmitButton } from ".."; +import { FeatureCell } from "../table"; + +const h = hyperStyled(styles); + +interface Model { + model: RefI; + actions: any; + hasChanges: () => boolean; +} + +function RefEdit() { + const { model, actions, hasChanges }: Model = useModelEditor(); + //author: text, year: numeric (validation), ref:textArea, doi:text, url:text + + const updateRef = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + + return h("div", [ + h(Table, { interactive: false }, [ + h("tr", [ + h(FeatureCell, { text: "Author" }, [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: model.author || undefined, + onChange: (e) => updateRef("author", e.target.value), + }), + ]), + h(FeatureCell, { text: "Pub Year" }, [ + h(NumericInput, { + style: { width: "200px" }, + defaultValue: model.pub_year || undefined, + onValueChange: (e) => updateRef("pub_year", e), + }), + ]), + ]), + h("tr", [ + h(FeatureCell, { text: "Ref", colSpan: 3 }, [ + h(TextArea, { onChange: (e) => updateRef("ref", e.target.value) }), + ]), + ]), + h("tr", [ + h(FeatureCell, { text: "DOI" }, [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: model.doi || undefined, + onChange: (e) => updateRef("doi", e.target.value), + }), + ]), + h(FeatureCell, { text: "URL" }, [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: model.url || undefined, + onChange: (e) => updateRef("url", e.target.value), + }), + ]), + ]), + ]), + h(SubmitButton), + ]); +} + +interface RefEditorProps { + model: RefI | {}; + persistChanges: (e: RefI, c: Partial) => RefI; +} + +export function RefEditor(props: RefEditorProps) { + return h( + ModelEditor, + { + model: props.model, + persistChanges: props.persistChanges, + isEditing: true, + canEdit: true, + }, + [h(RefEdit)] + ); +} diff --git a/packages/column-builder/src/components/routing/base-page.ts b/packages/column-builder/src/components/routing/base-page.ts new file mode 100644 index 00000000..2e83ad03 --- /dev/null +++ b/packages/column-builder/src/components/routing/base-page.ts @@ -0,0 +1,45 @@ +import React from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import styles from "../comp.module.sass"; +import { BreadcrumbProps } from "@blueprintjs/core"; +import { ReactChild } from "react"; +import { ErrorDialog } from "./error-boundary"; +import { PostgrestError } from "@supabase/postgrest-js"; +const h = hyperStyled(styles); + +export interface QueryI { + project_id?: number; + col_id?: number; + section_id?: number; + unit_id?: number; + col_group_id?: number; + strat_name_id?: number; + name?: string; +} + +export interface BasePageProps { + query: QueryI; + errors: PostgrestError[]; + children: ReactChild; +} + +export interface CrumbsI extends BreadcrumbProps { + predicate: string; +} + +/* +Creates the breadcrumbs at the top of each page based on the router query +*/ +export function BasePage(props: BasePageProps) { + //const router = useRouter(); + const { query, errors = [] } = props; + + // This should really be moved out to a hook. + //const breadCrumbs: CrumbsI[] = useBreadCrumbs({ router, query }); + + return h("div.page", [ + //h("div.bread-crumbs", [h(Breadcrumbs, { items: breadCrumbs })]), + h.if(errors.length > 0)(ErrorDialog, { errors }), + h.if(errors.length == 0)(React.Fragment, [props.children]), + ]); +} diff --git a/packages/column-builder/src/components/routing/error-boundary.ts b/packages/column-builder/src/components/routing/error-boundary.ts new file mode 100644 index 00000000..239138a2 --- /dev/null +++ b/packages/column-builder/src/components/routing/error-boundary.ts @@ -0,0 +1,30 @@ +import React from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { Intent, Dialog, Callout, Divider } from "@blueprintjs/core"; + +import styles from "../comp.module.sass"; +import { PostgrestError } from "@supabase/postgrest-js"; +const h = hyperStyled(styles); + +function ErrorDialog(props: { errors: PostgrestError[] }) { + return h(Dialog, { isOpen: true, style: { paddingBottom: "0" } }, [ + h( + Callout, + { intent: Intent.DANGER, title: "Error has occured", icon: "error" }, + [ + props.errors.map((error, i) => { + return h(React.Fragment, [ + h.if(error.message != null)("p", { key: i }, [ + "Message: ", + error.message, + ]), + h.if(error.hint != null)("p", { key: i }, ["Hint: ", error.hint]), + h(Divider), + ]); + }), + ] + ), + ]); +} + +export { ErrorDialog }; diff --git a/packages/column-builder/src/components/routing/routing-helpers.ts b/packages/column-builder/src/components/routing/routing-helpers.ts new file mode 100644 index 00000000..8772f850 --- /dev/null +++ b/packages/column-builder/src/components/routing/routing-helpers.ts @@ -0,0 +1,200 @@ +import pg from "../../db"; +import { ReactChild, useCallback } from "react"; +import h from "@macrostrat/hyper"; +import { usePageContext } from "vike-react/usePageContext"; +import { navigate as vikeNavigate } from "vike/client/router"; + +const routePrefix = "/dev/column-editor"; + +export function Link(props: { href: string; children: ReactChild }) { + const ctx = usePageContext(); + return h("a", { href: buildHref(props.href, ctx.urlPathname) }, [ + props.children, + ]); +} + +export function useNavigate() { + const ctx = usePageContext(); + return useCallback( + (href: string, opts: any) => { + vikeNavigate(buildHref(href, ctx.urlPathname), opts); + }, + [ctx.urlPathname] + ); +} + +function buildHref(href: string, currentRoute: string) { + console.log(currentRoute, href); + if (href.startsWith("/")) { + return joinPaths(routePrefix, href.slice(1)); + } else { + // A relative path + return joinPaths(currentRoute, href); + } +} + +function joinPaths(...paths: string[]) { + let parts: string[] = []; + for (const path of paths) { + // If we're not the first path but are an absolute path, throw an error + console.log(parts, path); + if (path.startsWith("/") && parts.length > 0) { + throw new Error("Cannot join an absolute path with a relative path"); + } + + const newParts = path.split("/"); + for (const part of newParts) { + if (part == ".") continue; + if (part == "..") { + parts.pop(); + } + parts.push(part); + } + } + return parts.join("/"); +} + +export interface IdsFromColGroup { + project_id?: number; +} + +async function fetchIdsFromColGroup( + col_group_id: number +): Promise { + const { data, error } = await pg + .from("col_groups") + .select("project_id") + .match({ id: col_group_id }); + if (data) { + return data[0]; + } + return {}; +} + +export interface IdsFromUnit { + unit_id?: number; + section_id?: number; + col_id?: number; + project_id?: number; +} + +async function fetchIdsFromUnitId(unit_id: number): Promise { + const { data, error } = await pg + .from("units") + .select("section_id, cols!units_col_id_fkey(id, project_id)") + .match({ id: unit_id }); + + if (data) { + const { section_id, cols } = data[0]; + const { id: col_id, project_id } = cols; + return { section_id, col_id, project_id, unit_id }; + } + return {}; +} + +export interface IdsFromSection { + col_id?: number; + section_id?: number; + project_id?: number; +} + +async function fetchIdsFromSectionId( + section_id: number +): Promise { + const { data, error } = await pg + .from("sections") + .select("cols!sections_col_id_fkey(id, project_id)") + .match({ id: section_id }); + + if (data) { + const { id: col_id, project_id } = data[0]["cols"]; + return { col_id, project_id, section_id }; + } + return {}; +} + +export interface IdsFromCol { + col_id?: number; + project_id?: number; +} + +async function fetchIdsFromColId(col_id: number): Promise { + const { data, error } = await pg + .from("cols") + .select("project_id") + .match({ id: col_id }); + + if (data) { + return { ...data[0], col_id }; + } + return {}; +} + +/** + * + * Legacy NextJS implementation of breadcrumbs + interface BreadCrumbsHookI { + query: QueryI; + router: NextRouter; + } + function useBreadCrumbs(props: BreadCrumbsHookI) { + const { query, router } = props; + + const filterCrumbs = (obj: CrumbsI): boolean => { + if (obj.text == "Projects") { + return true; + } + + if (!(obj.predicate in query)) return false; + return true; + }; + + const breadCrumbs: CrumbsI[] = [ + { + text: "Projects", + onClick: async () => { + router.push("/"); + }, + predicate: "", + }, + { + text: "Column Groups", + onClick: async () => { + router.push(`/column-groups/${query.project_id}`); + }, + predicate: "project_id", + }, + { + text: "Column", + onClick: async () => { + router.push(`/column/${query.col_id}`); + }, + predicate: "col_id", + }, + { + text: "Section", + onClick: async () => { + router.push(`/section/${query.section_id}`); + }, + predicate: "section_id", + }, + { + text: "Unit", + onClick: async () => { + router.push(`/unit/${query.unit_id}/edit`); + }, + predicate: "unit_id", + }, + ].filter(filterCrumbs); + + return breadCrumbs; + } + */ + +export { + fetchIdsFromColId, + fetchIdsFromSectionId, + fetchIdsFromUnitId, + fetchIdsFromColGroup, + //useBreadCrumbs, +}; diff --git a/packages/column-builder/src/components/section/index.ts b/packages/column-builder/src/components/section/index.ts new file mode 100644 index 00000000..dbe2dc79 --- /dev/null +++ b/packages/column-builder/src/components/section/index.ts @@ -0,0 +1,2 @@ +export * from "./new-helpers"; +export * from "./table"; diff --git a/packages/column-builder/src/components/section/new-helpers.ts b/packages/column-builder/src/components/section/new-helpers.ts new file mode 100644 index 00000000..e809e205 --- /dev/null +++ b/packages/column-builder/src/components/section/new-helpers.ts @@ -0,0 +1,86 @@ +import pg, { tableInsert, UnitsView } from "../.."; + +const keys = [ + "strat_name", + "color", + "outcrop", + "fo", + "lo", + "position_bottom", + "position_top", + "max_thick", + "min_thick", + "section_id", + "col_id", + "notes", +]; + +async function createNewSection(col_id: number) { + const { data, error } = await pg + .from("sections") + .insert([{ col_id: col_id }]); + + const s_id = data ? data[0].id : null; + return s_id; +} + +export const persistNewUnitAbove = async ( + updatedModel: UnitsView, + changeSet: Partial, + section_id: number, + col_id: number +) => { + /* + we need to get the smalled position_bottom in section, that will be the position_bottom for the new unit. + Every unit below that in the column needs to have the position_bottom incremented by 1. + 1. get smallest position bottom in section + 2. for all units where position_bottom >= this.position_bottom: position_bottom++ + + but maybe ask shanan + */ +}; + +export const persistNewUnitChanges = async ( + updatedModel: UnitsView, + changeSet: Partial, + section_id: any, + col_id: number +) => { + let s_id = section_id; + if (s_id == null) { + // we're making a new section with this unit + s_id = await createNewSection(col_id); + } + console.log(updatedModel, changeSet); + let unit_id: number; + + const unit: Partial = {}; + keys.map((k) => { + if (k == "strat_name") { + unit.strat_name_id = changeSet.strat_names.id; + } else { + //@ts-ignore + unit[k] = changeSet.unit[k]; + } + }); + unit.section_id = s_id; + const { data, error } = await tableInsert("units", { + col_id: col_id, + ...unit, + }); + + unit_id = data ? data[0].id : null; + + if (changeSet.envs) { + const inserts = changeSet.envs.map((e) => { + return { unit_id: unit_id, environ_id: e.id }; + }); + const { data, error } = await pg.from("unit_environs").insert(inserts); + } + if (changeSet.liths) { + const inserts = changeSet.liths.map((e) => { + return { unit_id: unit_id, lith_id: e.id }; + }); + const { data, error } = await pg.from("unit_liths").insert(inserts); + } +}; diff --git a/packages/column-builder/src/components/section/table.ts b/packages/column-builder/src/components/section/table.ts new file mode 100644 index 00000000..04878402 --- /dev/null +++ b/packages/column-builder/src/components/section/table.ts @@ -0,0 +1,59 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import styles from "../comp.module.sass"; +import { ColSectionI } from "~/types"; +import { Table, Row } from "../table"; +import { SectionUnitCheckBox } from "../unit-section-table/helpers"; + +const h = hyperStyled(styles); + +function ColSectionsTable(props: { + colSections: ColSectionI[]; + onChange: (id: number) => void; +}) { + const { colSections, onChange } = props; + const headers = [ + "", + "Section number", + "Top interval", + "Bottom interval", + "# of units", + ]; + return h(Table, { interactive: true, headers }, [ + colSections.map((section, i) => { + return h( + Row, + { + href: `/section/${section.id}`, + key: i, + }, + [ + h( + "td", + { + onClick: (e: React.MouseEvent) => + e.stopPropagation(), + }, + [ + h(SectionUnitCheckBox, { + data: section.id, + onChange: onChange, + }), + ] + ), + h("td", [section.id]), + h("td", [section.top]), + h("td", [section.bottom]), + h("td", [ + h( + "a", + { href: `/section/${section.id}` }, + `view ${section.unit_count} units` + ), + ]), + ] + ); + }), + ]); +} + +export { ColSectionsTable }; diff --git a/packages/column-builder/src/components/ssr-data-helpers.ts b/packages/column-builder/src/components/ssr-data-helpers.ts new file mode 100644 index 00000000..402c79c6 --- /dev/null +++ b/packages/column-builder/src/components/ssr-data-helpers.ts @@ -0,0 +1,34 @@ +import { UnitsView } from "@macrostrat-web/column-builder/src"; + +/* A collection of data processing functions to run server side before page is rendered */ + +/** + * Creates a list of objects where the key is a section_id and the values are the + * corresponding units belonging to that section + * @param units UnitsView[] + * @returns unitsBySection - {[section_id: number]: UnitsView[]}[] + */ +function createUnitBySections( + units: UnitsView[] | null +): { [section_id: string | number]: UnitsView[] }[] { + if (!units) return []; + const seen: { [section_id: number | string]: number } = {}; // store section_ids and their index in array + const unitsBySections: { [section_id: string | number]: UnitsView[] }[] = []; + + units.map((unit, i) => { + if (unit.section_id in seen) { + const index = seen[unit.section_id]; + unitsBySections[index][unit.section_id].push(unit); + } else { + let index = 0; + if (unitsBySections.length > 0) { + index = unitsBySections.length; + } + seen[unit.section_id] = index; + unitsBySections.push({ [unit.section_id]: [unit] }); + } + }); + return unitsBySections; +} + +export { createUnitBySections }; diff --git a/packages/column-builder/src/components/strat-name/hierarchy/fetch.ts b/packages/column-builder/src/components/strat-name/hierarchy/fetch.ts new file mode 100644 index 00000000..23b74d17 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/hierarchy/fetch.ts @@ -0,0 +1,95 @@ +import axios from "axios"; +import { IHierarchy } from "@macrostrat/ui-components/lib/types"; + +const url = "https://macrostrat.org/api/v2/defs/strat_names"; + +// most of this is copied from the sift codebase +var rankMap = { + SGp: null, + Gp: "sgp", + SubGp: "gp", + Fm: "subgp", + Mbr: "fm", + Bed: "mbr", + 1: null, + 2: "sgp", + 3: "gp", + 4: "subgp", + 5: "fm", + 6: "mbr", +}; +var rankMapOrder = { SGp: 1, Gp: 2, SubGp: 3, Fm: 4, Mbr: 5, Bed: 6 }; + +const fetchStratNames = async (id: number | undefined) => { + // function to fetch stratnames and orgnize hierarchy + if (id === undefined) { + return null; + } + const res = await axios.get(url, { + params: { rule: "all", strat_name_id: id }, + }); + + if (res.status != 200) { + return res.data; + } + + const data = res.data.success.data; + + data.forEach((d) => { + // Figure out if this is the target name or not + d.active = false; + if (d.strat_name_id == id) { + d.active = true; + } + + d.children = []; + d.totalChildren = + data.filter((j) => { + if (j[d.rank.toLowerCase() + "_id"] == d.strat_name_id) { + return j; + } + }).length - 1; + d.total = d.totalChildren; + }); + + data.forEach((d) => { + var belongsTo = d[rankMap[d.rank] + "_id"]; + + // Need to make sure belongsTo doesn't = 0 when it shouldn't (ex: strat_name_id=9574) + var previousRank = 1; + while (belongsTo === 0) { + belongsTo = d[rankMap[rankMapOrder[d.rank] - previousRank] + "_id"]; + previousRank--; + } + + // Find the one it belongs to and add it + data.forEach((j) => { + if (j.strat_name_id == belongsTo && j.strat_name_id != d.strat_name_id) { + j.children.push(d); + } + }); + }); + + const hierarchy = data.sort((a, b) => { + return b.totalChildren - a.totalChildren; + })[0]; + const mappedData = mapToHier(hierarchy); + // Find the top of the hierarchy and return it + return mappedData; +}; + +const mapToHier = (data) => { + const Hier: Partial = {}; + Hier.name = data.strat_name_long; + Hier.units = data.t_units; + Hier.active = data.active; + Hier.onClick = (e) => { + e.preventDefault(); + const url = `https://macrostrat.org/sift/#/strat_name/${data.strat_name_id}`; + window.open(url, "_blank"); + }; + Hier.subhierarchy = data.children.map((c) => mapToHier(c)); + return Hier; +}; + +export { fetchStratNames }; diff --git a/packages/column-builder/src/components/strat-name/hierarchy/hierarchy.module.scss b/packages/column-builder/src/components/strat-name/hierarchy/hierarchy.module.scss new file mode 100644 index 00000000..3830b958 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/hierarchy/hierarchy.module.scss @@ -0,0 +1,3 @@ +.strat-name-hierarchy { + margin-bottom: 20px; +} diff --git a/packages/column-builder/src/components/strat-name/hierarchy/index.ts b/packages/column-builder/src/components/strat-name/hierarchy/index.ts new file mode 100644 index 00000000..491e0738 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/hierarchy/index.ts @@ -0,0 +1,39 @@ +import { useState, useEffect } from "react"; +import { fetchStratNames } from "./fetch"; +import { Spinner } from "@blueprintjs/core"; +import { Hierarchy, IHierarchy } from "@macrostrat/data-components"; +import { hyperStyled } from "@macrostrat/hyper"; +import styles from "./hierarchy.module.scss"; + +const h = hyperStyled(styles); + +export { Hierarchy }; + +export function StratNameHierarchy({ + strat_name_id, +}: { + strat_name_id?: number; +}) { + const [state, setState] = useState>({}); + + useEffect(() => { + async function fetch() { + const res = await fetchStratNames(strat_name_id); + setState(res); + } + if (typeof strat_name_id !== "undefined") { + fetch(); + } + }, [strat_name_id]); + + if (typeof strat_name_id === "undefined") return null; + + if (!state) { + return h("h3", "No results"); + } + + return h("div.strat-name-hierarchy", [ + h.if(state.name)(Hierarchy, { ...state }), + h.if(!state.name)(Spinner), + ]); +} diff --git a/packages/column-builder/src/components/strat-name/index.ts b/packages/column-builder/src/components/strat-name/index.ts new file mode 100644 index 00000000..1c6447cd --- /dev/null +++ b/packages/column-builder/src/components/strat-name/index.ts @@ -0,0 +1,3 @@ +export * from "./strat-name-editor"; +export * from "./strat-name"; +export * from "./modal-editor"; diff --git a/packages/column-builder/src/components/strat-name/modal-editor.ts b/packages/column-builder/src/components/strat-name/modal-editor.ts new file mode 100644 index 00000000..90a299c2 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/modal-editor.ts @@ -0,0 +1,131 @@ +import React, { useState } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { useModelEditor } from "@macrostrat/ui-components"; +import { Button, Callout, Dialog, Tag } from "@blueprintjs/core"; +import styles from "../comp.module.sass"; +import pg, { usePostgrest } from "../../db"; +import { RANK, StratNameConceptLongI, StratNameI } from "~/types"; +import { StratNameStack } from "./panel-stack"; +import { AuthorTag } from "./query-list"; + +const h = hyperStyled(styles); + +const rankLong = { + Fm: "Formation", + Gp: "Group", + Mbr: "Member", + SGp: "SuperGroup", + Bed: "Bed", +}; + +export function StratNameConceptCard(props: { + concept_id?: number; + strat_name: StratNameI; +}) { + const data: StratNameConceptLongI[] = usePostgrest( + pg + .from("strat_names_meta") + .select("*,intervals(*),refs(*)") + .match({ concept_id: props.concept_id }) + ); + + if (!data || typeof data == "undefined") return null; + + const concept: StratNameConceptLongI = data[0]; + + return h( + Callout, + { + className: "concept-card", + }, + [ + h("div.strat-title", [ + h("h3", [ + `${props.strat_name.strat_name} ${rankLong[props.strat_name.rank]}`, + ]), + h(Tag, { intent: "success", minimal: true, icon: "tick" }, [ + concept.refs.author, + ]), + ]), + h("p.geologic-age", [concept.province]), + h("div", { style: { display: "flex", alignItems: "center" } }, [ + h("p.geologic-age", [ + concept.geologic_age, + " (", + concept.intervals.age_bottom, + "ma", + " - ", + concept.intervals.age_top, + "ma)", + ]), + h("div.color-block", { + style: { + background: concept.intervals.interval_color, + width: "15px", + height: "15px", + marginLeft: "3px", + }, + }), + ]), + h("p.source", [ + "reference: ", + h("a", { href: concept.url, target: "_blank" }, [concept.refs.author]), + ]), + ] + ); +} + +function UnitStratNameModalEditor() { + const [open, setOpen] = useState(false); + const { model: unit, actions } = useModelEditor(); + + const onSubmitStratName = (e: StratNameI | null) => { + if (!e) return; + actions.updateState({ + model: { + strat_names: { $push: [e] }, + }, + }); + }; + + const onDelete = (id: number) => { + const newNames = [...unit.strat_names].filter((sn) => sn.id != id); + actions.updateState({ + model: { + strat_names: { $set: newNames }, + }, + }); + }; + + const onStratNameSelect = (e: StratNameI | null) => { + onSubmitStratName(e); + }; + + return h(React.Fragment, [ + h(Button, { + minimal: true, + onClick: () => setOpen(true), + icon: "edit", + style: { padding: 0 }, + }), + h( + Dialog, + { + style: { width: "600px" }, + isOpen: open, + title: `Modify Unit #${unit.id} strat_name`, + onClose: () => setOpen(false), + }, + [ + h(StratNameStack, { + col_id: unit.col_id, + stratNames: unit.strat_names, + onStratNameSelect, + onDelete, + }), + ] + ), + ]); +} + +export { UnitStratNameModalEditor }; diff --git a/packages/column-builder/src/components/strat-name/panel-stack/index.ts b/packages/column-builder/src/components/strat-name/panel-stack/index.ts new file mode 100644 index 00000000..2175d7a7 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/panel-stack/index.ts @@ -0,0 +1 @@ +export * from "./panel-stack"; diff --git a/packages/column-builder/src/components/strat-name/panel-stack/panel-stack.ts b/packages/column-builder/src/components/strat-name/panel-stack/panel-stack.ts new file mode 100644 index 00000000..9a14e29b --- /dev/null +++ b/packages/column-builder/src/components/strat-name/panel-stack/panel-stack.ts @@ -0,0 +1,307 @@ +import React, { useContext, useState } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + PanelProps, + PanelStack2, + Panel, + Button, + Intent, + Callout, + Menu, + MenuItem, + FormGroup, + InputGroup, + ControlGroup, +} from "@blueprintjs/core"; +import styles from "./strat-name-panel.module.scss"; +import { StratNameI } from "~/types"; +import { StratNameListItem, StratNameSelect } from "../query-list"; +import { StratNameHierarchy } from "../hierarchy"; +import { StratNameConceptCard } from "../modal-editor"; +import { RankSelect } from "../strat-name-editor"; +import pg, { usePostgrest } from "@macrostrat-web/column-builder/src/db"; + +const h = hyperStyled(styles); + +interface SearchPanelProps { + col_id: number; + onSubmitStratName: (l: StratNameI) => void; + onDelete: (id: number) => void; + stratNames: StratNameI[]; +} + +interface CurrentStratNamesProps { + stratName: StratNameI; + onDelete: (id: number) => void; + onClick: (stratName: StratNameI, canAdd: boolean) => void; +} + +function CurrentStratName(props: CurrentStratNamesProps) { + const { stratName } = props; + + const data: StratNameI[] = usePostgrest( + pg.rpc("get_strat_name_info", { strat_name_id: stratName.id }) + ); + + if (!data) return null; + return h(MenuItem, { + text: h(StratNameListItem, { ...data[0] }), + onClick: () => props.onClick(stratName, false), + style: { marginBottom: "3px", background: "#EDEFF2" }, + labelElement: h(Button, { + intent: "danger", + minimal: true, + icon: "trash", + onClick: (e: any) => { + e.stopPropagation(); + props.onDelete(stratName.id); + }, + }), + }); +} + +const SearchPanel: React.FC> = (props) => { + const { col_id, onDelete, onStratNameSelect, stratNames } = + useContext(StratStackContext); + + const onItemSelect = (stratName: StratNameI, canAdd?: boolean) => { + props.openPanel({ + props: { stratName, onStratNameSelect, col_id, canAdd }, + renderPanel: MetaDataPanel, + }); + }; + + const onClickCreateNew = () => { + props.openPanel({ + props: { onStratNameSelect }, + renderPanel: NewStratNamePanel, + }); + }; + + return h("div.strat-name-select", [ + h.if(stratNames.length > 0)("h3", ["Current stratigraphic names"]), + h.if(stratNames.length > 0)(Menu, { style: { maxWidth: "100%" } }, [ + stratNames.map((stratName) => { + return h(CurrentStratName, { + key: stratName.id, + stratName, + onClick: onItemSelect, + onDelete, + }); + }), + ]), + h("h3", ["Choose a stratigraphic name"]), + h(StratNameSelect, { col_id, onItemSelect, onClickCreateNew }), + ]); +}; + +interface NewStratNamePanelProps { + onStratNameSelect: (l: StratNameI) => void; +} + +const NewStratNamePanel: React.FC> = ( + props +) => { + const [state, setState] = useState({ strat_name: "", rank: "Fm" }); + + const updateStratName = (field: string, value: string) => { + setState((prevState) => { + return { ...prevState, [field]: value }; + }); + }; + + const onBackClick = () => { + props.closePanel(); + }; + + return h("div", [ + h("div.action-btns", [ + h( + Button, + { + intent: Intent.WARNING, + onClick: onBackClick, + minimal: true, + icon: "arrow-left", + }, + ["Return to search"] + ), + ]), + h("div.bottom", [ + h( + FormGroup, + { + helperText: "Create a stratigraphic name", + label: "Stratigraphic Name", + }, + [ + h(ControlGroup, { fill: true }, [ + h(InputGroup, { + fill: true, + defaultValue: state.strat_name, + onChange: (e) => updateStratName("strat_name", e.target.value), + }), + h(RankSelect, { updateStratName, rank: state.rank }), + h( + Button, + { intent: "warning", disabled: state.strat_name.length < 1 }, + ["Submit"] + ), + ]), + ] + ), + h( + Callout, + { + title: "Unlinked", + style: { borderRadius: "5px" }, + }, + [ + h("div", [ + "This name will be unlinked to external resources. ", + "The most valuable stratigraphic name is one linked to an official lexicon or reference.", + ]), + h( + Button, + { + intent: "success", + rightIcon: "arrow-right", + style: { marginTop: "3px" }, + }, + ["Link stratigraphic name"] + ), + ] + ), + ]), + ]); +}; + +interface MetaDataPanelProps { + stratName: StratNameI | null; + onStratNameSelect: (l: StratNameI) => void; + col_id: number; + canAdd?: boolean; +} + +const MetaDataPanel: React.FC> = (props) => { + const { canAdd = true, stratName, onStratNameSelect, col_id } = props; + + const onBackClick = () => { + props.closePanel(); + }; + + const onSaveClick = (stratName: StratNameI | null) => { + if (stratName == null) { + return; + } + onStratNameSelect(stratName); + props.closePanel(); + }; + + if (!props.stratName) return h("div"); + const concept_id = props.stratName.concept_id; + + return h("div", [ + h("div.action-btns", [ + h( + Button, + { + intent: Intent.WARNING, + onClick: onBackClick, + minimal: true, + icon: "arrow-left", + }, + ["Search for another"] + ), + h.if(canAdd)( + Button, + { + minimal: true, + intent: Intent.SUCCESS, + onClick: () => onSaveClick(stratName), + icon: "plus", + }, + ["add name"] + ), + ]), + h("div.strat-name-select", [ + h.if(concept_id != null)(StratNameConceptCard, { + strat_name: props.stratName, + concept_id, + }), + h.if(concept_id == null)( + Callout, + { intent: "warning", title: "No official lexicon" }, + [ + "This stratigraphic name is not linked to an official lexicon reference. You can use this but it may be better to find a strat_name that is linked.", + ] + ), + h("h3", ["Stratigraphic name hierarchy"]), + h("div.strat-hierarchy-constainer", [ + h(StratNameHierarchy, { + strat_name_id: props.stratName?.id, + }), + ]), + ]), + ]); +}; + +interface StratNameStackProps { + onStratNameSelect: (i: StratNameI | null) => void; + col_id: number; + stratNames: StratNameI[]; + onDelete: (id: number) => void; +} + +type StratPanelTypes = SearchPanelProps | MetaDataPanelProps; +type StratPanels = Panel; + +/* PanelStack has the limitation of NOT re-rendering on props change. +https://github.com/palantir/blueprint/issues/3173 +So I use a react context to get around the issue. +Not perfect but better than nothing. +*/ + +const StratStackContext = React.createContext({}); + +function StratNameStack(props: StratNameStackProps) { + const { col_id, onStratNameSelect, stratNames } = props; + + const initialPanel: Panel = { + renderPanel: SearchPanel, + title: "Search for a strat name", + }; + + const [currentPanelStack, setCurrentPanelStack] = useState< + Array + >([initialPanel]); + + const addToPanelStack = React.useCallback( + (newPanel: StratPanels) => + setCurrentPanelStack((stack) => [...stack, newPanel]), + [stratNames] + ); + const removeFromPanelStack = React.useCallback( + () => + setCurrentPanelStack([ + { + renderPanel: SearchPanel, + title: "Search for a strat name", + }, + ]), + [stratNames] + ); + + return h(StratStackContext.Provider, { value: props }, [ + h(PanelStack2, { + className: "strat-name-stack", + stack: currentPanelStack, + onOpen: addToPanelStack, + onClose: removeFromPanelStack, + renderActivePanelOnly: false, + showPanelHeader: false, + }), + ]); +} + +export { StratNameStack }; diff --git a/packages/column-builder/src/components/strat-name/panel-stack/strat-name-panel.module.scss b/packages/column-builder/src/components/strat-name/panel-stack/strat-name-panel.module.scss new file mode 100644 index 00000000..0fdd1357 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/panel-stack/strat-name-panel.module.scss @@ -0,0 +1,26 @@ +.strat-name-stack { + height: 500px; +} +.action-btns { + display: flex; + justify-content: space-between; + position: sticky; + padding: 3px 0; + top: 0; + background: white; + margin-bottom: 5px; +} +.strat-name-select { + padding: 1px 10px 0 10px; +} +.bottom { + .row { + display: flex; + } + .new-strat-name { + display: flex; + justify-content: space-between; + margin: 10px; + } + margin: 10px; +} diff --git a/packages/column-builder/src/components/strat-name/query-list/index.ts b/packages/column-builder/src/components/strat-name/query-list/index.ts new file mode 100644 index 00000000..1bed1dde --- /dev/null +++ b/packages/column-builder/src/components/strat-name/query-list/index.ts @@ -0,0 +1 @@ +export * from "./query-list"; diff --git a/packages/column-builder/src/components/strat-name/query-list/query-list.ts b/packages/column-builder/src/components/strat-name/query-list/query-list.ts new file mode 100644 index 00000000..bd4e1700 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/query-list/query-list.ts @@ -0,0 +1,201 @@ +import React, { useState, useEffect } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + QueryList, + ItemRenderer, + IQueryListRendererProps, + ItemPredicate, +} from "@blueprintjs/select"; +import pg from "../../../db"; +import { + Button, + Callout, + InputGroup, + MenuItem, + NonIdealState, + Tag, +} from "@blueprintjs/core"; +import styles from "./strat-name.module.scss"; +import { + PostgrestFilterBuilder, + PostgrestQueryBuilder, +} from "@supabase/postgrest-js"; +import { StratNameI } from "~/types"; +import { Tooltip2 } from "@blueprintjs/popover2"; + +const h = hyperStyled(styles); + +const StrtaNameQueryList = QueryList.ofType(); + +const itemPredicate: ItemPredicate = (query, item, index) => { + const { strat_name } = item; + + return strat_name?.toLowerCase().indexOf(query.toLowerCase()) >= 0; +}; + +const SourceTag = ({ source }: { source: string | undefined }) => { + return h.if(typeof source !== "undefined")( + Tooltip2, + { + content: source ?? "", + className: "source-text", + placement: "top", + minimal: true, + }, + [ + h.if(source === "unrelated")(Tag, { minimal: true, intent: "danger" }, [ + h("b", ["may be unrelated"]), + ]), + h.if(source !== "unrelated")(Tag, { minimal: true }, [h("i", [source])]), + ] + ); +}; + +const AuthorTag = ({ + author, + concept_id, +}: { + author: string | null; + concept_id: number | null; +}) => { + return h(React.Fragment, [ + h("div.author-tag", [ + h.if(author != null)(Tag, { intent: "success", minimal: true }, [author]), + ]), + h("div.author-tag", [ + h.if(!concept_id)(Tag, { intent: "warning", minimal: true }, "Unlinked"), + ]), + ]); +}; + +const StratNameListItem = (props: StratNameI) => { + const { strat_name, author, rank, parent, source, concept_id } = props; + + const parentText = parent ? `${parent}` : ""; + + return h("div", [ + h("div.flex-between", [ + h("div.name-text", [ + h("b", [`${strat_name} ${rank}`]), + h("i.parent-name", [`${parentText}`]), + ]), + h("div.author-source-tag", [ + h(SourceTag, { source }), + h(AuthorTag, { author, concept_id }), + ]), + ]), + ]); +}; + +const StratNameItemRenderer: ItemRenderer = ( + item: StratNameI, + { handleClick, index, modifiers } +) => { + return h(MenuItem, { + key: index, + text: h(StratNameListItem, { ...item }), + onClick: handleClick, + active: modifiers.active, + }); +}; + +const StratNameNewRenderer = (props: { onClickCreateNew: () => void }) => { + return h(NonIdealState, { + icon: "warning-sign", + title: "No results in Macrostrat lexicon", + description: h("div", [ + h("li", ["Ensure column location is correct"]), + h("li", ["Try an alternative spelling"]), + ]), + action: h( + Button, + { + intent: "warning", + onClick: props.onClickCreateNew, + }, + ["Create new"] + ), + }); +}; + +const StratNameQueryListRenderer = ( + props: IQueryListRendererProps +) => { + const { itemList, handleKeyDown, handleKeyUp, ...listProps } = props; + return h( + "div.lith-query-list-renderer", + { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, + [ + h(InputGroup, { + ["aria-autocomplete"]: "list", + leftIcon: "search", + placeholder: "Search for a stratigraphic name", + onChange: listProps.handleQueryChange, + value: listProps.query, + }), + itemList, + ] + ); +}; + +const getStratNames = async ( + query: string, + setNames: (e: StratNameI[]) => void, + col_id?: number +) => { + let baseQuery: + | PostgrestFilterBuilder + | PostgrestQueryBuilder = pg.from("strat_names"); + if (typeof col_id !== "undefined") { + baseQuery = pg.rpc("get_strat_names_col_priority", { _col_id: col_id }); + } + if (query.length > 2) { + const { data, error } = await baseQuery + .select() + .ilike("strat_name", `%${query}%`) + .limit(50); + setNames(data ?? []); + } else { + const { data, error } = await baseQuery.select().limit(50); + setNames(data ?? []); + } +}; + +interface StratNameSelectProps { + onItemSelect: (l: StratNameI) => void; + onClickCreateNew: () => void; + col_id: number; +} + +function StratNameSelect(props: StratNameSelectProps) { + const [names, setNames] = useState([]); + const onQueryChange = (i: string) => { + getStratNames(i, (e: StratNameI[]) => setNames(e), props.col_id); + }; + + useEffect(() => { + onQueryChange(""); + }, []); + + return h(StrtaNameQueryList, { + itemRenderer: StratNameItemRenderer, + itemPredicate, + onItemSelect: props.onItemSelect, + items: names, + renderer: StratNameQueryListRenderer, + resetOnSelect: false, + noResults: h(StratNameNewRenderer, { + onClickCreateNew: props.onClickCreateNew, + }), + menuProps: { + style: { + maxWidth: "100%", + margin: "0 10px", + maxHeight: "400px !important", + }, + }, + onQueryChange, + }); +} + +export { StratNameSelect, StratNameListItem, AuthorTag }; diff --git a/packages/column-builder/src/components/strat-name/query-list/strat-name.module.scss b/packages/column-builder/src/components/strat-name/query-list/strat-name.module.scss new file mode 100644 index 00000000..79565315 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/query-list/strat-name.module.scss @@ -0,0 +1,29 @@ +.flex-between { + display: flex; + justify-content: space-between; + min-height: 2.5rem; +} +.source-text { + overflow: hidden; + text-overflow: ellipsis; + max-width: 150px; + text-align: right; +} +.name-text { + display: flex; + flex-direction: column; +} +.author-source-tag { + display: flex; +} +.author-tag { + margin-left: 3px; + overflow: hidden; + text-overflow: ellipsis; +} +.short { + max-width: 150px; +} +.parent-name { + color: #5f6b7c; +} diff --git a/packages/column-builder/src/components/strat-name/strat-name-editor.ts b/packages/column-builder/src/components/strat-name/strat-name-editor.ts new file mode 100644 index 00000000..22902b78 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/strat-name-editor.ts @@ -0,0 +1,210 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { StratNameSuggest } from "@macrostrat-web/column-builder/src"; +import { Select, ItemRenderer } from "@blueprintjs/select"; +import { + Button, + MenuItem, + FormGroup, + InputGroup, + Icon, + Card, + Callout, +} from "@blueprintjs/core"; +import { ModelEditor, useModelEditor } from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import { RANK, StratNameI } from "../../types"; +import { SubmitButton } from ".."; +import { StratNameDataI } from "."; +import { StratNameHierarchy } from "./hierarchy"; +import { DataI, ItemSelect } from "../suggest"; +import { Checkbox } from "@blueprintjs/core"; + +const h = hyperStyled(styles); + +interface Model { + model: StratNameI; + actions: any; + hasChanges: () => boolean; +} + +export function RankSelect({ + updateStratName, + rank, +}: { + updateStratName: (field: string, i: any) => void; + rank?: string; +}) { + const possibleRanks = [ + { value: "SGp", data: "SGp" }, + { value: "Gp", data: "Gp" }, + { value: "SubGp", data: "SubGp" }, + { value: "Fm", data: "Fm" }, + { value: "Mbr", data: "Mbr" }, + { value: "Bed", data: "Bed" }, + ]; + + const itemRenderer: ItemRenderer> = ( + item: DataI, + { handleClick, index } + ) => { + const active = rank == item.value; + return h(MenuItem, { + key: index, + labelElement: active ? h(Icon, { icon: "tick" }) : null, + text: item.value, + onClick: handleClick, + active: active, + }); + }; + + return h( + ItemSelect, + { + items: possibleRanks, + itemRenderer, + // selectedItem: model.rank, + onItemSelect: (item) => updateStratName("rank", item.value), + }, + [h(Button, { rightIcon: "double-caret-vertical" }, [rank ?? "Fm"])] + ); +} + +/* +Edit the name and rank of strat_name - text input and select +Assign other strat_name as parent +Add to a concept +*/ +function StratNameEdit(props: { new_name?: boolean }) { + const { new_name = false } = props; + const { model, actions, hasChanges }: Model = useModelEditor(); + + const updateStratName = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + + const title = new_name ? "Create new strat name" : "Edit strat name"; + const helperText = new_name + ? "Create new strat name" + : "Edit existing strat name"; + + return h("div", [ + h(StratNameHierarchy, { strat_name_id: model.id }), + h("div", [ + h(Card, [ + h("h3", { style: { marginTop: 0 } }, [title]), + h("div.row", [ + h( + FormGroup, + { + helperText: helperText, + label: "Stratigraphic Name", + }, + [ + h(InputGroup, { + style: { width: "200px" }, + defaultValue: model.strat_name, + onChange: (e) => updateStratName("strat_name", e.target.value), + }), + ] + ), + h(FormGroup, { label: "Rank" }, [ + h(RankSelect, { updateStratName, rank: model.rank }), + ]), + ]), + h.if(!new_name)("div.row", [ + h(Checkbox, { label: "Apply globally", style: { margin: "5px" } }), + h(Checkbox, { + label: "Apply to this unit only (create new strat name)", + style: { margin: "5px" }, + }), + ]), + h( + Callout, + { + intent: "warning", + title: "Unlinked", + style: { width: "265px", borderRadius: "5px" }, + }, + ["This name will be unlinked to external resources"] + ), + ]), + h(Card, [ + h("h3", { style: { marginTop: 0 } }, ["Edit Hierarcy"]), + h( + FormGroup, + { + helperText: `This will assign the parent of ${model.strat_name} ${model.rank}`, + label: "(re)-Assign Parent", + labelFor: "descrip-input", + }, + [ + h(StratNameSuggest, { + onChange: (item: StratNameDataI) => { + updateStratName("parent", item.data); + }, + }), + ] + ), + h( + FormGroup, + { + helperText: `This will assign a child to ${model.strat_name} ${model.rank}`, + label: `Add Name to ${model.strat_name} ${model.rank}`, + }, + [ + h(StratNameSuggest, { + onChange: (item: StratNameDataI) => { + updateStratName("child", item.data); + }, + }), + ] + ), + h("h3", { style: { marginTop: 0 } }, ["Create new strat name"]), + h("div.row", [ + h( + FormGroup, + { + label: "Create new name", + labelInfo: "(optional)", + }, + [ + h(InputGroup, { + style: { width: "200px" }, + onChange: (e) => console.log(e.target.value), + }), + ] + ), + h(FormGroup, { label: "Rank" }, [h(RankSelect, { updateStratName })]), + ]), + h("div.row", [ + h(Checkbox, { label: "Assign as Child", style: { margin: "5px" } }), + h(Checkbox, { + label: "Assign as Parent", + style: { margin: "5px" }, + }), + ]), + ]), + h(SubmitButton), + ]), + ]); +} + +interface StratNameEditorProps { + model: StratNameI | {}; + persistChanges: (e: StratNameI, c: Partial) => StratNameI; + new_name?: boolean; +} + +export function StratNameEditor(props: StratNameEditorProps) { + const { new_name } = props; + return h( + ModelEditor, + { + model: props.model, + persistChanges: props.persistChanges, + isEditing: true, + canEdit: true, + }, + [h(StratNameEdit, { new_name })] + ); +} diff --git a/packages/column-builder/src/components/strat-name/strat-name.ts b/packages/column-builder/src/components/strat-name/strat-name.ts new file mode 100644 index 00000000..f1f98642 --- /dev/null +++ b/packages/column-builder/src/components/strat-name/strat-name.ts @@ -0,0 +1,92 @@ +import { useState, useEffect } from "react"; +import pg from "../../db"; +import { hyperStyled } from "@macrostrat/hyper"; +import styles from "../comp.module.sass"; +import { StratNameI } from "../.."; +import { MenuItem } from "@blueprintjs/core"; +import { ItemRenderer } from "@blueprintjs/select"; +import { ItemSuggest } from "../suggest"; +import { + PostgrestFilterBuilder, + PostgrestQueryBuilder, +} from "@supabase/postgrest-js"; + +const h = hyperStyled(styles); + +export interface StratNameDataI { + value: string; + data: StratNameI; +} + +const itemRenderer: ItemRenderer = ( + item: StratNameDataI, + { handleClick, modifiers, index } +) => { + const { value, data } = item; + + return h(MenuItem, { + key: index, + intent: data.concept_id ? "primary" : "warning", + text: value, + onClick: handleClick, + active: modifiers.active, + }); +}; + +const getStratNames = async ( + query: string, + setNames: (e: StratNameDataI[]) => void, + col_id?: number +) => { + let baseQuery: PostgrestFilterBuilder | PostgrestQueryBuilder = + pg.from("strat_names"); + if (typeof col_id !== "undefined") { + baseQuery = pg.rpc("get_strat_names_col_priority", { _col_id: col_id }); + } + if (query.length > 2) { + const { data, error } = await baseQuery + .select() + .like("strat_name", `%${query}%`) + .limit(50); + const d: StratNameDataI[] = data?.map((d: StratNameI) => { + return { value: `${d.strat_name} ${d.rank}`, data: d }; + }); + setNames(d); + } else { + const { data, error } = await baseQuery.select().limit(50); + const d: StratNameDataI[] = data?.map((d: StratNameI) => { + return { value: `${d.strat_name} ${d.rank}`, data: d }; + }); + setNames(d); + } +}; + +interface StratCellProps { + initialSelected?: StratNameDataI | undefined; + onChange: (item: StratNameDataI) => void; + placeholder?: string; + col_id?: number; +} + +function StratNameSuggest(props: StratCellProps) { + const [names, setNames] = useState([]); + + const onQueryChange = (i: string) => { + getStratNames(i, (e: StratNameDataI[]) => setNames(e), props.col_id); + }; + + useEffect(() => { + onQueryChange(""); + }, []); + + return h(ItemSuggest, { + items: names, + onQueryChange: onQueryChange, + onChange: props.onChange, + initialSelected: props.initialSelected, + itemRenderer, + placeholder: props.placeholder, + }); +} + +export { StratNameSuggest }; diff --git a/packages/column-builder/src/components/suggest.ts b/packages/column-builder/src/components/suggest.ts new file mode 100644 index 00000000..c3fe0320 --- /dev/null +++ b/packages/column-builder/src/components/suggest.ts @@ -0,0 +1,150 @@ +import { ReactChild, useEffect, useState } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + Suggest, + ItemRenderer, + ItemPredicate, + Select, + ItemListRenderer, +} from "@blueprintjs/select"; +import { MenuItem, Icon, PopoverPosition } from "@blueprintjs/core"; +import styles from "./comp.module.sass"; + +const h = hyperStyled(styles); + +export interface DataI { + value: string; + data: T; +} + +interface SuggestI { + onChange: (e: DataI) => void; + initialSelected?: DataI; + items: DataI[]; + itemRenderer?: ItemRenderer>; + onQueryChange?: (e: string) => void; + placeholder?: string; +} +const ItemSuggestComponent = Suggest.ofType(); + +function ItemSuggest(props: SuggestI) { + let itemz = [...props.items]; + //sees if initialSelected is in list, and moves to front + if ( + props.initialSelected && + props.items.map((s) => s.value).includes(props.initialSelected.value) + ) { + const spot = itemz.map((s) => s.value).indexOf(props.initialSelected.value); + itemz.splice(spot, 1); + } + + itemz = props.initialSelected ? [props.initialSelected, ...itemz] : itemz; + + const [selected, setSelected] = useState(props.initialSelected); + + useEffect(() => { + setSelected(props.initialSelected); + }, [props.initialSelected]); + + const itemRenderer: ItemRenderer> = ( + item: DataI, + { handleClick, modifiers } + ) => { + const { value, data } = item; + const active = selected?.value == value; + return h(MenuItem, { + key: value, + labelElement: active ? h(Icon, { icon: "tick" }) : null, + text: value, + onClick: handleClick, + active: modifiers.active, + }); + }; + + const itemPredicate: ItemPredicate> = ( + query: string, + item: DataI + ) => { + const { value } = item; + + return value?.toLowerCase().indexOf(query.toLowerCase()) >= 0; + }; + + const onItemSelect = (item: DataI) => { + setSelected(item); + props.onChange(item); + }; + + return h(ItemSuggestComponent, { + inputValueRenderer: (item: DataI) => item.value, + items: itemz, + popoverProps: { + minimal: true, + }, + inputProps: { + placeholder: props.placeholder, + }, + selectedItem: selected, + onItemSelect: onItemSelect, + itemRenderer: props.itemRenderer ?? itemRenderer, + itemPredicate: itemPredicate, + onQueryChange: props.onQueryChange, + resetOnQuery: true, + noResults: h(MenuItem, { disabled: true, text: "No Results" }), + }); +} + +interface ItemSelectI { + items: DataI[]; + onItemSelect: (e: DataI) => void; + children: ReactChild; + itemRenderer?: ItemRenderer>; + itemPredicate?: ItemPredicate>; + itemListRenderer?: ItemListRenderer>; + filterable?: boolean; + position?: PopoverPosition; +} + +const ItemSelectComponent = Select.ofType>(); + +const itemRenderer: ItemRenderer> = ( + item: DataI, + { handleClick, index, modifiers } +) => { + const { value, data } = item; + return h(MenuItem, { + key: index, + text: value, + onClick: handleClick, + active: modifiers.active, + }); +}; + +const itemPredicate: ItemPredicate> = (query, item, _index) => { + const { value } = item; + + return value?.toLowerCase().indexOf(query.toLowerCase()) >= 0; +}; + +function ItemSelect(props: ItemSelectI) { + return h( + ItemSelectComponent, + { + filterable: props.filterable || false, + items: props.items, + popoverProps: { + minimal: true, + position: props.position, + popoverClassName: styles.itemSelectPopover, + }, + itemListRenderer: props.itemListRenderer, + itemRenderer: props.itemRenderer || itemRenderer, + onItemSelect: props.onItemSelect, + itemPredicate: props.itemPredicate || itemPredicate, + noResults: h(MenuItem, { disabled: true, text: "No Results" }), + }, + [props.children] + ); +} + +export { ItemSuggest, ItemSelect }; diff --git a/packages/column-builder/src/components/table.ts b/packages/column-builder/src/components/table.ts new file mode 100644 index 00000000..e172035d --- /dev/null +++ b/packages/column-builder/src/components/table.ts @@ -0,0 +1,283 @@ +import React, { useCallback } from "react"; +import { ReactChild } from "react"; +import h from "./comp.module.sass"; +import { + Draggable, + DraggableProvided, + DraggableStateSnapshot, + Droppable, + DroppableProvided, +} from "react-beautiful-dnd"; +import { Card, Icon } from "@blueprintjs/core"; +import { useNavigate } from "../components"; + +interface RowProps { + children: ReactChild; + draggableId?: string; + index: number; + href?: string; + drag?: boolean; + isMoved?: boolean; + onDoubleClick?: () => void; +} + +const routePrefix = "/dev/column-editor"; + +const getItemStyle = (isDragging: boolean, draggableStyle: any) => ({ + display: isDragging ? "table" : "", + ...draggableStyle, +}); + +const RowWrapper = (props: { + link: string | undefined; + children: ReactChild; +}) => { + const { link, children } = props; + + return children; + // TODO: row links break things, now that links are not ephemeral items + /* + if (typeof link === "undefined") { + return h(React.Fragment, [children]); + } else { + return h(Link, { href: link }, [children]); + } + */ +}; + +function Row(props: { href: string; children: ReactChild }) { + const { href, children } = props; + const navigate = useNavigate(); + + const onClick = useCallback( + (evt) => { + navigate(href); + }, + [href] + ); + + return h("tr", { onClick }, children); +} + +function DraggableRow(props: RowProps) { + const { + draggableId = "", + drag = false, + onDoubleClick = () => console.log("double clicked!"), + href, + isMoved, + } = props; + + const className = isMoved ? "unit-moved" : null; + + return h( + Draggable, + { + key: props.index, + index: props.index, + isDragDisabled: !drag, + draggableId, + }, + [ + (provided: DraggableProvided, snapshot: DraggableStateSnapshot) => { + return h( + `tr`, + { + onClick: (e: MouseEvent) => { + e.stopPropagation(); + if (e.detail == 2) { + onDoubleClick(); + if (href != null) { + navigate(href); + } + } + }, + className, + ref: provided.innerRef, + ...provided.draggableProps, + style: getItemStyle( + snapshot.isDragging, + provided.draggableProps.style + ), + }, + [ + h.if(drag)( + "td", + { + ...provided.dragHandleProps, + width: "2%", + style: { verticalAlign: "middle" }, + }, + [h(Icon, { icon: "drag-handle-vertical" })] + ), + props.children, + ] + ); + }, + ] + ); +} + +interface FeatureCellI { + text: string; + children: ReactChild; + colSpan?: number; +} + +function FeatureCell(props: FeatureCellI) { + return h(React.Fragment, [ + h("td", [h("h4.strat-name", [props.text])]), + h("td", { colSpan: props.colSpan }, [props.children]), + ]); +} + +interface TableProps { + interactive: boolean; + children: ReactChild; + headers?: any[]; + title?: string; +} + +function Table(props: TableProps) { + const { headers = [] } = props; + const baseClass = + "bp5-html-table .bp5-html-table-condensed .bp5-html-table-bordered"; + let className = props.interactive + ? `${baseClass} .bp5-interactive` + : baseClass; + + return h(Card, { className: "table-container" }, [ + h(`table`, { style: { width: "100%", tableLayout: "auto" }, className }, [ + h.if(headers.length > 0)(TableHeader, { + headers: headers, + title: props.title, + }), + h("tbody", [props.children]), + ]), + ]); +} + +interface DnDTableProps { + interactive: boolean; + children: ReactChild; + drag?: boolean; + headers?: any[]; + droppableId?: string; + draggableId?: string; + title?: string; + index: number; + widths?: number[]; +} + +function DnDTable(props: DnDTableProps) { + const { + drag = false, + headers = [], + widths, + droppableId = "table-drop-zone", + } = props; + const baseClass = + "bp5-html-table .bp5-html-table-condensed .bp5-html-table-bordered .base-table .full-width"; + let tableClassName = props.interactive + ? `${baseClass} .bp5-interactive` + : baseClass; + + return h( + Draggable, + { + key: props.index, + index: props.index, + isDragDisabled: !drag, + draggableId: props.draggableId ?? "", + }, + [ + (provided: DraggableProvided, snapshot: DraggableStateSnapshot) => { + return h( + `table.${tableClassName}`, + { + ref: provided.innerRef, + ...provided.draggableProps, + }, + [ + h.if(headers.length > 0)(TableHeader, { + dragProps: provided.dragHandleProps, + headers: headers, + widths, + title: props.title, + }), + h(TableBody, { drag, droppableId }, [props.children]), + ] + ); + }, + ] + ); +} + +function TableBody(props: { + children: ReactChild; + drag?: boolean; + droppableId?: string; +}) { + const { drag = false, droppableId = "table-drop" } = props; + return h( + Droppable, + { droppableId: droppableId, isDropDisabled: !drag, type: "table-body" }, + [ + (provided: DroppableProvided) => { + return h( + "tbody", + { + ...provided.droppableProps, + ref: provided.innerRef, + }, + [props.children, provided.placeholder] + ); + }, + ] + ); +} + +function TableHeader(props: { + headers: any[]; + widths?: number[]; + title?: string; + dragProps?: any; +}) { + return h(React.Fragment, [ + h.if(props.title != null)("thead", [ + h("tr.table-header-row", [ + h( + "th.table-header", + { + ...props.dragProps, + colSpan: props.headers.length, + }, + [props.title] + ), + ]), + ]), + h("colgroup", [ + props.widths?.map((width, i) => { + return h("col", { + key: i, + width: `${typeof width != "undefined" ? width : ""}%`, + }); + }), + ]), + h("thead", [ + h("tr", [ + props.headers.map((head, i) => { + return h( + "th", + { + key: i, + }, + [head] + ); + }), + ]), + ]), + ]); +} + +export { Row, Table, DnDTable, FeatureCell, TableHeader, DraggableRow }; diff --git a/packages/column-builder/src/components/tag.ts b/packages/column-builder/src/components/tag.ts new file mode 100644 index 00000000..4b8aa81b --- /dev/null +++ b/packages/column-builder/src/components/tag.ts @@ -0,0 +1,252 @@ +import { MouseEventHandler } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { Tooltip2 as Tooltip } from "@blueprintjs/popover2"; +import { Button, MenuItem, Spinner, Tag } from "@blueprintjs/core"; +import { + IItemModifiers, + ItemPredicate, + ItemRenderer, +} from "@blueprintjs/select"; +import pg, { usePostgrest } from "../db"; +import styles from "./comp.module.sass"; +import { EnvironUnit, LithUnit } from ".."; +import { ItemSelect } from "./suggest"; + +const h = hyperStyled(styles); + +interface SelectDataI { + data: any; + value: any; +} + +interface tagInfo { + name: string; + description: string; + color: string; + id?: number; +} + +interface tagBody extends tagInfo { + onClickDelete?: (e: number) => void; + onClick?: (e: tagInfo) => void | null; + disabled?: boolean; + isEditing?: boolean; + large?: boolean; +} + +export function isTooDark(hexcolor: string) { + var r = parseInt(hexcolor.substr(1, 2), 16); + var g = parseInt(hexcolor.substr(3, 2), 16); + var b = parseInt(hexcolor.substr(4, 2), 16); + var yiq = (r * 299 + g * 587 + b * 114) / 1000; + + return yiq < 90; +} + +/** + * + * @param props: tagBody + * @returns + */ +function TagBody(props: tagBody) { + const { + name, + description, + color, + onClick = (e: tagInfo) => {}, + onClickDelete = () => {}, + isEditing = false, + id = 10000, + disabled = false, + large = true, + } = props; + + const showName = name.length > 0 ? name : "Tag Preview"; + const darkTag = isTooDark(color); + + const textColor = darkTag ? "white" : "black"; + + let tag: tagInfo = { id, name, description, color }; + + const onRemove = () => { + onClickDelete(tag.id || 0); + }; + + return h(Tooltip, { content: description, disabled }, [ + h( + Tag, + { + key: id, + large, + round: true, + className: styles.tag, + onRemove: isEditing ? onRemove : undefined, + onClick: (e) => onClick(tag), + interactive: props.onClick != null, + style: { backgroundColor: color, color: textColor }, + }, + [showName] + ), + ]); +} + +interface LithMenuItemProps { + data: { name: string; color: string }; + modifiers?: IItemModifiers; + handleClick: MouseEventHandler; +} + +export function LithMenuItem(props: LithMenuItemProps) { + const { modifiers, handleClick, data } = props; + + const style = + modifiers?.active ?? false + ? { + marginBottom: "2px", + } + : { + backgroundColor: data.color + "40", // add opaquness + marginBottom: "2px", + }; + + return h(MenuItem, { + active: modifiers?.active, + text: data.name, + onClick: handleClick, + style, + }); +} + +const lithItemRenderer: ItemRenderer = ( + item: SelectDataI, + { handleClick, index, modifiers } +) => { + const { value, data } = item; + + return h(LithMenuItem, { key: index, data, modifiers, handleClick }); +}; + +const itemPredicate: ItemPredicate = (query, item, index) => { + const { + data: { name }, + } = item; + + return name?.toLowerCase().indexOf(query.toLowerCase()) >= 0; +}; + +function LithTagsAdd(props: { onClick: (e: Partial) => void }) { + const liths: Partial[] = usePostgrest(pg.from("liths")); + if (!liths) return null; + + const data: { data: any; value: any }[] = liths.map((lith, i) => { + return { + data: { + id: lith.id || 0, + color: lith.lith_color || "fffff", + name: lith.lith || "None", + description: lith.lith_class || "None", + }, + value: { ...lith, type: "dom" }, + }; + }); + + return h( + ItemSelect, + { + filterable: true, + items: data, + itemRenderer: lithItemRenderer, + itemPredicate, + position: "bottom-left", + onItemSelect: (item: SelectDataI) => { + props.onClick(item.value); + }, + }, + [h(Button, { icon: "plus", minimal: true, intent: "success" })] + ); +} + +const envItemRenderer: ItemRenderer = ( + item: SelectDataI, + { handleClick, index, modifiers } +) => { + const { value, data } = item; + + const style = modifiers.active + ? { + marginBottom: "2px", + } + : { + backgroundColor: data.color + "40", // add opaquness + marginBottom: "2px", + }; + + return h(MenuItem, { + active: modifiers.active, + key: index, + text: data.name, + onClick: handleClick, + style, + }); +}; + +function EnvTagsAdd(props: { onClick: (e: Partial) => void }) { + const envs: Partial[] = usePostgrest(pg.from("environs")); + if (!envs) return null; + + const data: SelectDataI[] = envs.map((env, i) => { + return { + data: { + id: env.id || 0, + color: env.environ_color || "fffff", + name: env.environ || "None", + description: env.environ_class || "None", + }, + value: env, + }; + }); + + return h( + ItemSelect, + { + filterable: true, + items: data, + itemRenderer: envItemRenderer, + itemPredicate, + position: "bottom-left", + onItemSelect: (item: SelectDataI) => props.onClick(item.value), + }, + [h(Button, { icon: "plus", minimal: true, intent: "success" })] + ); +} + +interface TagCellProps { + data: tagInfo[]; + disabled?: boolean; + isEditing?: boolean; + onClick?: (e: tagInfo) => void | null; + onClickDelete?: (e: number) => void; + large?: boolean; +} + +function TagContainerCell(props: TagCellProps) { + const { large = true } = props; + return h("div", [ + props.data.map((tag, i) => { + const { id, name, color, description } = tag; + return h(TagBody, { + key: i, + id, + color, + onClick: props.onClick, + isEditing: props.isEditing, + onClickDelete: props.onClickDelete, + name, + description, + large, + }); + }), + ]); +} + +export { TagBody, TagContainerCell, EnvTagsAdd, LithTagsAdd }; diff --git a/packages/column-builder/src/components/unit-section-table/async-helpers.ts b/packages/column-builder/src/components/unit-section-table/async-helpers.ts new file mode 100644 index 00000000..8b14c774 --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/async-helpers.ts @@ -0,0 +1,39 @@ +import { PostgrestError, PostgrestResponse } from "@supabase/postgrest-js"; +import pg from "../../db"; +import { UnitsView } from "~/types"; +import { SectionUnits } from "./reducer"; + +const createNewSection = async (col_id: number) => { + const { data, error }: PostgrestResponse<{ id: number; col_id: number }> = + await pg.from("sections").insert([{ col_id }]); + return data; +}; + +const saveColumnReorder = async (sections: SectionUnits) => { + // multiple things need to be done here. + // We need to go through and set the section_id for each unit as well as the p_bottom + // this will run in o(n) time, where n is the number of units + let position_bottom = 1; + const updates: { id: number; section_id: number; position_bottom: number }[] = + []; + sections.map((section) => { + const section_id = parseInt(Object.keys(section)[0]); + const units = section[section_id]; + console.log("p_b", position_bottom); + units.map((unit) => { + console.log("p_b", position_bottom); + updates.push({ id: unit.id, section_id, position_bottom }); + //increment p_bottom for next unit + position_bottom = position_bottom + 1; + }); + }); + updates.map(async (update) => { + const { id, position_bottom, section_id } = update; + const { data, error }: PostgrestResponse = await pg + .from("units") + .update({ position_bottom, section_id }) + .match({ id }); + }); +}; + +export { createNewSection, saveColumnReorder }; diff --git a/packages/column-builder/src/components/unit-section-table/helpers.ts b/packages/column-builder/src/components/unit-section-table/helpers.ts new file mode 100644 index 00000000..0656dd3d --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/helpers.ts @@ -0,0 +1,264 @@ +import React, { useState } from "react"; +import { + Button, + ButtonGroup, + Checkbox, + Menu, + MenuItem, + MenuDivider, +} from "@blueprintjs/core"; +import { Popover2, Tooltip2 } from "@blueprintjs/popover2"; +import { UnitsView } from "@macrostrat-web/column-builder/src"; +import styles from "../comp.module.sass"; +import { hyperStyled } from "@macrostrat/hyper"; +import { useUnitSectionContext } from "./index"; + +const h = hyperStyled(styles); + +function SectionUnitCheckBox(props: { + data: any; + onChange: (e: number) => void; +}) { + const onChange = (e: any) => { + e.stopPropagation(); + props.onChange(props.data); + }; + return h(Checkbox, { onChange }); +} + +function MergeDivideBtn(props: { + onClick: () => void; + disabled: boolean; + text: string; +}) { + return h( + Button, + { + disabled: props.disabled, + onClick: props.onClick, + }, + [props.text] + ); +} + +export interface ColBtnMenuI { + toggleUnitsView?: () => void; + toggleDrag: () => void; + onSave: () => void; + onCancel: () => void; + state: { + unitsView: boolean; + drag: boolean; + mergeIds: number[]; + moved: { [unit_id: number]: boolean }; + }; + mergeSections: () => void; + noSectionView: boolean; +} + +function ColumnPageBtnMenu(props: ColBtnMenuI) { + const { + state: { unitsView, drag, mergeIds, moved }, + } = props; + return h("div", [ + //@ts-ignore + h(ButtonGroup, [ + h( + Button, + { + onClick: props.toggleUnitsView, + intent: unitsView ? "primary" : "none", + disabled: unitsView, + }, + ["Unit view"] + ), + h.if(!props.noSectionView)( + Button, + { + onClick: props.toggleUnitsView, + intent: !unitsView ? "primary" : "none", + disabled: !unitsView, + }, + ["Section View"] + ), + h.if(!unitsView)(MergeDivideBtn, { + onClick: props.mergeSections, + disabled: mergeIds.length < 2, + text: "Merge Sections", + }), + ]), + h.if(unitsView)(ReorderUnitsBtnGrp, { + drag, + onClick: props.toggleDrag, + moved, + onSave: props.onSave, + onCancel: props.onCancel, + }), + ]); +} + +function ReorderUnitsBtnGrp(props: { + drag: boolean; + onClick: () => void; + onCancel: () => void; + onSave: () => void; + moved: { [unit_id: number]: boolean }; +}) { + //@ts-ignore + return h(ButtonGroup, { style: { marginLeft: "20px" } }, [ + h.if(!props.drag)(Button, { onClick: props.onClick }, ["Reorder Units"]), + h.if(props.drag)( + Button, + { + intent: "success", + onClick: props.onSave, + disabled: Object.keys(props.moved).length == 0, + }, + ["Save"] + ), + h.if(props.drag)(Button, { intent: "danger", onClick: props.onCancel }, [ + "Cancel", + ]), + ]); +} + +enum UNIT_ADD_POISITON { + ABOVE = "above", + BELOW = "below", + EDIT = "edit", +} + +export interface UnitRowContextMenuI { + // either we are adding a new unit above, below or editing the current unit + // or we are editing the unit inRow + unit: UnitsView; + unit_index: number; + section_index: number; + copyUnitUp: () => void; + copyUnitDown: () => void; + addEmptyUnit: (unit_index: number) => void; + editUnitAt: (unit_index: number) => void; +} +function UnitRowContextMenu(props: UnitRowContextMenuI) { + const { state, runAction } = useUnitSectionContext(); + const SubMenu = ({ pos }: { pos: UNIT_ADD_POISITON }) => { + return h(React.Fragment, [ + h(MenuItem, { + text: `Copy unit #${props.unit.id}`, + icon: "duplicate", + onClick: () => { + if (pos == UNIT_ADD_POISITON.ABOVE) { + props.copyUnitUp(); + } else props.copyUnitDown(); + }, + }), + h(MenuItem, { + text: `With empty unit`, + icon: "new-object", + onClick: () => { + if (pos == UNIT_ADD_POISITON.ABOVE) { + props.addEmptyUnit(props.unit_index); + } else props.addEmptyUnit(props.unit_index + 1); + }, + }), + ]); + }; + + const ContextMenu = () => + h(Menu, [ + h( + MenuItem, + { + text: "Add Unit Above", + icon: "circle-arrow-up", + }, + [h(SubMenu, { pos: UNIT_ADD_POISITON.ABOVE })] + ), + h( + MenuItem, + { + text: "Add Unit Below", + icon: "circle-arrow-down", + }, + [h(SubMenu, { pos: UNIT_ADD_POISITON.BELOW })] + ), + h(MenuItem, { + text: `Edit unit #${props.unit.id}`, + icon: "annotation", + onClick: () => props.editUnitAt(props.unit_index), + }), + h(MenuDivider), + h(MenuItem, { + text: `Delete unit #${props.unit.id}`, + icon: "trash", + intent: "danger", + onClick: () => + runAction({ + type: "remove-unit", + section_index: props.section_index, + unit_index: props.unit_index, + }), + }), + ]); + + return h( + Popover2, + { + content: h(ContextMenu), + minimal: true, + position: "left-top", + }, + [h(Button, { minimal: true, icon: "more" })] + ); +} + +function AddBtnBetweenRows(props: { + onClick: (e: any) => void; + colSpan: number; +}) { + const [style, setStyle] = useState({ display: "none" }); + return h("tr", [ + h("td", { colSpan: props.colSpan, style: { padding: 0 } }, [ + h( + Tooltip2, + { + content: "add unit", + fill: true, + position: "right", + intent: "success", + }, + [ + h( + "div.btwn-row-btn", + { + onMouseEnter: (e: React.MouseEvent) => { + setStyle({ display: "flex" }); + }, + onMouseLeave: (e: React.MouseEvent) => { + setStyle({ display: "none" }); + }, + onClick: props.onClick, + }, + [ + h(Button, { + intent: "success", + fill: true, + onClick: props.onClick, + style: { ...style, minHeight: "5px" }, + }), + ] + ), + ] + ), + ]), + ]); +} + +export { + SectionUnitCheckBox, + MergeDivideBtn, + ColumnPageBtnMenu, + AddBtnBetweenRows, + UNIT_ADD_POISITON, + UnitRowContextMenu, +}; diff --git a/packages/column-builder/src/components/unit-section-table/index.ts b/packages/column-builder/src/components/unit-section-table/index.ts new file mode 100644 index 00000000..2e79e7d7 --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/index.ts @@ -0,0 +1,2 @@ +export * from "./table"; +export * from "./reducer"; diff --git a/packages/column-builder/src/components/unit-section-table/reducer.ts b/packages/column-builder/src/components/unit-section-table/reducer.ts new file mode 100644 index 00000000..c193ea77 --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/reducer.ts @@ -0,0 +1,414 @@ +import { Dispatch } from "react"; +import { DropResult } from "react-beautiful-dnd"; +import { + filterOrAddIds, + UnitsView, +} from "@macrostrat-web/column-builder/src/index"; +import { persistNewUnit, persistUnitChanges } from "../unit/edit-helpers"; +import { createNewSection, saveColumnReorder } from "./async-helpers"; + +///////////////// helper functions ////////////// +/* + This would be async and would persist to db + then would return db representation which we + would add to units in a Sync Action +*/ +function persistUnit(unit: UnitsView) { + let color = "#FFFFF"; + if (typeof unit.lith_unit !== "undefined" && unit.lith_unit.length > 0) { + color = unit?.lith_unit[0].lith_color; + } + const newUnit = { + ...unit, + id: unit.id ?? 666, + color: unit.color ?? color, + }; + return newUnit; +} + +// a little function to help us with reordering the result +const reorder = (list: any[], startIndex: number, endIndex: number): void => { + const [removed] = list.splice(startIndex, 1); + list.splice(endIndex, 0, removed); +}; + +const addElementToList = (list: any[], index: number, element: any): void => { + list.splice(index, 0, element); +}; + +/////////////// Data Types ////////////////// + +export type SectionUnits = { [section_id: string | number]: UnitsView[] }[]; +type UnitSection = { unit_id: number; section_id: number }; + +/////////////// async actions ////////////////// + +type CreateNewSection = { + type: "create-new-section"; + index: number; + col_id: number; +}; + +type SaveReorder = { type: "save-reorder"; sections: SectionUnits }; +type SaveUnitAt = { + type: "save-unit-at"; + section_index: number; + unit_index: number; + unit: UnitsView; + changeSet: Partial; + og_unit: UnitsView; + sections: SectionUnits; +}; +/////////////// Action Types /////////////// + +type CancelReorder = { type: "cancel-reorder" }; +type SetMergeIds = { type: "set-merge-ids"; id: number }; +type MergeIds = { type: "merge-ids" }; +type DroppedUnit = { + type: "dropped-unit"; + result: DropResult; +}; +type DroppedSection = { + type: "dropped-section"; + result: DropResult; +}; +type ToggleDrag = { type: "toggle-drag" }; +type ToggleUnitsView = { type: "toggle-units-view" }; +type AddSectionAt = { + type: "add-section-at"; + index: number; + section_id: number; +}; + +type AddUnitAt = { + type: "add-unit-at"; + section_index: number; + unit_index: number; + unit: UnitsView; +}; + +type PersistEditsAt = { + type: "persist-edits-at"; + section_index: number; + unit_index: number; + unit: UnitsView; +}; + +type EditUnitAt = { + type: "edit-unit-at"; + section_index: number; + unit_index: number; +}; + +type CancelEditing = { + type: "cancel-editing"; + section_index: number; + unit_index: number; +}; + +type RemoveUnit = { + type: "remove-unit"; + section_index: number; + unit_index: number; +}; + +type PersistReorder = { type: "persist-reorder" }; + +export type SyncActions = + | PersistReorder + | CancelReorder + | RemoveUnit + | CancelEditing + | AddSectionAt + | EditUnitAt + | SetMergeIds + | DroppedUnit + | DroppedSection + | MergeIds + | ToggleDrag + | AddUnitAt + | PersistEditsAt + | ToggleUnitsView; + +export type AsyncActions = CreateNewSection | SaveReorder | SaveUnitAt; + +export type Actions = SyncActions | AsyncActions; + +export interface EditorState { + open: boolean; + section_index: number; + unit_index: number; +} + +export interface ColumnStateI { + col_id: number; + sections: SectionUnits; + originalSections: SectionUnits; + mergeIds: number[]; + moved: { [unit_id: number]: boolean }; + drag: boolean; + unitsView: boolean; + edit: EditorState; + unitsMovedToNewSections: UnitSection[]; +} + +export interface UnitSectionTableCtx { + state: ColumnStateI; + runAction(action: Actions): Promise; +} + +/// we can filter async actions through here first +export const useUnitSectionTableActions = (dispatch: Dispatch) => { + return async (action: Actions) => { + switch (action.type) { + case "create-new-section": + const data = await createNewSection(action.col_id); + if (!data) { + throw Error("Section was not created"); + } + return dispatch({ + type: "add-section-at", + index: action.index, + section_id: data[0].id, + }); + case "save-reorder": + await saveColumnReorder(action.sections); + return dispatch({ type: "persist-reorder" }); + case "save-unit-at": + let persistedUnit: UnitsView; + if (action.unit.id === "new") { + persistedUnit = await persistNewUnit( + action.og_unit, + action.unit, + action.changeSet + ); + } else { + persistedUnit = await persistUnitChanges( + action.og_unit, + action.unit, + action.changeSet + ); + } + await saveColumnReorder(action.sections); + return dispatch({ + type: "persist-edits-at", + unit: persistedUnit, + section_index: action.section_index, + unit_index: action.unit_index, + }); + default: + return dispatch(action); + } + }; +}; + +const columnReducer = (state: ColumnStateI, action: Actions): ColumnStateI => { + const currSections: SectionUnits = JSON.parse(JSON.stringify(state.sections)); + switch (action.type) { + case "cancel-editing": + const _section_id_ = Object.keys(currSections[action.section_index])[0]; + const _unit_ = + currSections[action.section_index][_section_id_][action.unit_index]; + if (_unit_.id == "new") { + state = { ...state, edit: { ...state.edit, open: false } }; + return columnReducer(state, { + type: "remove-unit", + section_index: action.section_index, + unit_index: action.unit_index, + }); + } + + return { + ...state, + edit: { + ...state.edit, + open: false, + }, + }; + case "edit-unit-at": + return { + ...state, + edit: { + open: true, + section_index: action.section_index, + unit_index: action.unit_index, + }, + }; + case "set-merge-ids": + const currentIds = [...state.mergeIds]; + const id = action.id; + const newIds = filterOrAddIds(id, currentIds); + return { + ...state, + mergeIds: newIds, + }; + case "cancel-reorder": + return { + ...state, + sections: state.originalSections, + drag: false, + moved: {}, + }; + case "persist-reorder": + return { + ...state, + drag: false, + moved: {}, + }; + case "toggle-drag": + return { + ...state, + drag: !state.drag, + }; + case "toggle-units-view": + return { + ...state, + unitsView: !state.unitsView, + }; + case "merge-ids": + console.log("Merging sections ", state.mergeIds); + return state; + case "add-section-at": + const sectionIndex = action.index; + //@ts-ignore + const newSection: SectionUnits = { [action.section_id]: [] }; + addElementToList(currSections, sectionIndex, newSection); + return { ...state, sections: currSections }; + case "remove-unit": + const _section_id = Object.keys(currSections[action.section_index])[0]; + currSections[action.section_index][_section_id].splice( + action.unit_index, + 1 + ); + return { + ...state, + sections: currSections, + }; + case "add-unit-at": + // this will encapsulate the add top and bottom + // mutate a the sections list in place + const section_id = Object.keys(currSections[action.section_index])[0]; + + currSections[action.section_index][section_id].splice( + action.unit_index, + 0, + persistUnit(action.unit) + ); + return { + ...state, + sections: currSections, + edit: { + open: true, + section_index: action.section_index, + unit_index: action.unit_index, + }, + }; + case "persist-edits-at": + const section_id_ = Object.keys(currSections[action.section_index])[0]; + + currSections[action.section_index][section_id_].splice( + action.unit_index, + 1, + persistUnit(action.unit) + ); + return { + ...state, + sections: currSections, + edit: { + ...state.edit, + open: false, + }, + }; + case "dropped-section": + if (!action.result.combine) return state; + /* Merge sections!! What data should you expect? + draggableId : `${section_id} ${index}` of section that was dropped. + combine.draggableID: `${section_id} ${index}` of section that was dropped on. + + Get the indexes, whichever is greater, take those units and append to smaller one + */ + const [s_id, s_index] = action.result.draggableId.split(" "); + const [d_id, d_index] = action.result.combine.draggableId.split(" "); + + let big = { id: s_id, index: parseInt(s_index) }; + let small = { id: d_id, index: parseInt(d_index) }; + if (parseInt(s_index) < parseInt(d_index)) { + big = { id: d_id, index: parseInt(d_index) }; + small = { id: s_id, index: parseInt(s_index) }; + } + + const newMoved: { [x: number]: boolean } = {}; + currSections[big.index][big.id].forEach((unit) => { + newMoved[unit.id] = true; + }); + currSections[small.index][small.id].push( + ...currSections[big.index][big.id] + ); + currSections.splice(big.index, 1); + + // set all units in moved section to be moved + + return { + ...state, + sections: currSections, + moved: { ...state.moved, ...newMoved }, + }; + case "dropped-unit": + if ( + typeof action.result.destination === "undefined" || + action.result.destination == null + ) + return state; + let source_index = action.result.source.index; + let destination_index = action.result.destination.index; + + /// our drop result source and destination provide + // The index of where to find the section in our list as well + // as the section_id itself. + const [sourceSectionIndex, sourceSection] = + action.result.source.droppableId.split(" "); + const [destSectionIndex, destSection] = + action.result.destination.droppableId.split(" "); + + const movedUnitId = + currSections[parseInt(sourceSectionIndex)][sourceSection][source_index][ + "id" + ]; + if (sourceSection == destSection && source_index == destination_index) { + //we haven't moved + return state; + } + + if (sourceSection === destSection) { + // same section + reorder( + currSections[parseInt(sourceSectionIndex)][sourceSection], + source_index, + destination_index + ); + } else { + // we changed sections! + const [movedUnit] = currSections[parseInt(sourceSectionIndex)][ + sourceSection + ].splice(source_index, 1); + + movedUnit["section_id"] = parseInt(destSection); + + currSections[parseInt(destSectionIndex)][destSection].splice( + destination_index, + 0, + movedUnit + ); + } + + return { + ...state, + moved: { ...state.moved, [movedUnitId]: true }, + sections: currSections, + }; + default: + return state; + } +}; + +export { columnReducer }; diff --git a/packages/column-builder/src/components/unit-section-table/section.ts b/packages/column-builder/src/components/unit-section-table/section.ts new file mode 100644 index 00000000..5a740095 --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/section.ts @@ -0,0 +1,119 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { UnitsView } from "@macrostrat-web/column-builder/src"; +import { DnDTable } from "../table"; +import { UnitRow } from "./unit"; +import { useUnitSectionContext } from "./table"; +import { AddBtnBetweenRows } from "./helpers"; +import styles from "../comp.module.sass"; + +const h = hyperStyled(styles); + +const getEmptyUnit = (col_id: number) => { + let emptyUnit: UnitsView = { + id: "new", + strat_name: "unnamed", + strat_names: [], + lith_unit: [], + environ_unit: [], + color: "#fffff", + col_id, + name_fo: "", + age_bottom: 0, + name_lo: "", + age_top: 0, + }; + return emptyUnit; +}; + +interface SectionTableProps { + index: number; + section: { [section_id: number | string]: UnitsView[] }; + drag: boolean; + moved: { [unit_id: number]: boolean }; + addUnitAt: (unit: UnitsView, unit_index: number) => void; + editUnitAt: (unit_index: number) => void; +} + +function SectionTable(props: SectionTableProps) { + const { index, drag } = props; + const { state, runAction } = useUnitSectionContext(); + const { edit } = state; + + let headers = [ + "ID", + "Unit Name", + "Strat Name", + "Liths", + "Envs", + "Interval", + "Thickness", + "notes", + "", + ]; + let widths = [7, 15, 15, 15, 15, 10, 8, 10, 5]; + if (drag) { + headers = ["", ...headers]; + widths = [5, 5, 15, 15, 15, 10, 10, 10, 10, 5]; + } + + const units: UnitsView[] = Object.values(props.section)[0]; + const id = Object.keys(props.section)[0]; + + return h( + DnDTable, + { + index, + interactive: false, + headers, + widths, + title: `Section ${id}`, + draggableId: `${id} ${index}`, + drag, + droppableId: index.toString() + " " + id.toString(), + }, + + [ + h.if(units.length == 0)(AddBtnBetweenRows, { + colSpan: headers.length, + onClick: (e) => { + e.stopPropagation(); + props.addUnitAt(getEmptyUnit(state.col_id), 0); + }, + }), + units.map((unit, j) => { + const isEditing = + edit.unit_index == j && edit.section_index == index && edit.open; + + // these ids here are meaningless... this action needs to be persisted + const copyUnitDown = () => { + props.addUnitAt({ ...unit, id: "new" }, j + 1); + }; + const copyUnitUp = () => { + props.addUnitAt({ ...unit, id: "new" }, j); + }; + const addEmptyUnit = (unit_index: number) => { + props.addUnitAt(getEmptyUnit(unit.col_id), unit_index); + }; + const editUnitAt = (unit_index: number) => { + props.editUnitAt(unit_index); + }; + return h(UnitRow, { + key: unit.id, + unit, + drag, + unit_index: j, + section_index: index, + colSpan: headers.length, + isMoved: unit.id in props.moved, + inRowEditing: isEditing, + copyUnitDown, + copyUnitUp, + addEmptyUnit, + editUnitAt, + }); + }), + ] + ); +} + +export { SectionTable }; diff --git a/packages/column-builder/src/components/unit-section-table/table.ts b/packages/column-builder/src/components/unit-section-table/table.ts new file mode 100644 index 00000000..67a76ee0 --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/table.ts @@ -0,0 +1,208 @@ +import React, { createContext, useContext, useReducer } from "react"; +import { + ColumnStateI, + UnitsView, + ColSectionsTable, + ColSectionI, + UnitSectionTableCtx, +} from "@macrostrat-web/column-builder/src"; +import { + AsyncActions, + Actions, + columnReducer, + SyncActions, + useUnitSectionTableActions, +} from "../column"; +import { DragDropContext, Droppable } from "react-beautiful-dnd"; +import { DropResult, DroppableProvided } from "react-beautiful-dnd"; +import { ColumnPageBtnMenu } from "./helpers"; +import { SectionTable } from "./section"; +import { NewSectionBtn } from "../unit/minimal-unit-editor"; + +import h from "../comp.module.sass"; + +interface SectionUnitTableProps { + onDragEnd: (r: DropResult) => void; +} + +function SectionsDropContainer(props: SectionUnitTableProps) { + const { onDragEnd } = props; + const { state, runAction } = useUnitSectionContext(); + const { sections, moved } = state; + + return h("div", [ + h(DragDropContext, { onDragEnd }, [ + h( + Droppable, + { + droppableId: "unit-section-tables", + type: "SECTIONS", + isCombineEnabled: true, + }, + [ + (provided: DroppableProvided) => { + return h( + "div", + { ...provided.droppableProps, ref: provided.innerRef }, + [ + h(NewSectionBtn, { + index: 0, + addNewSection: (i: number) => + runAction({ + type: "create-new-section", + index: i, + col_id: state.col_id, + }), + }), + sections.map((section, i) => { + const addUnitAt = (e: UnitsView, n: number) => { + runAction({ + type: "add-unit-at", + unit_index: n, + unit: e, + section_index: i, + }); + }; + + const editUnitAt = (unit_index: number) => { + runAction({ + type: "edit-unit-at", + section_index: i, + unit_index, + }); + }; + + return h(React.Fragment, { key: i }, [ + h(SectionTable, { + addUnitAt, + section, + index: i, + drag: state.drag, + editUnitAt, + moved, + }), + h(NewSectionBtn, { + index: i + 1, + addNewSection: (i: number) => + runAction({ + type: "create-new-section", + index: i, + col_id: state.col_id, + }), + }), + ]); + }), + provided.placeholder, + ] + ); + }, + ] + ), + ]), + ]); +} + +const UnitSectionTableContext = createContext({ + state: { + col_id: 0, + sections: [], + originalSections: [], + mergeIds: [], + moved: {}, + drag: false, + unitsView: true, + edit: { + open: false, + section_index: 0, + unit_index: 0, + }, + unitsMovedToNewSections: [], + }, + runAction: async (action: SyncActions | AsyncActions) => {}, +}); + +const useUnitSectionContext = () => useContext(UnitSectionTableContext); + +interface UnitSectionTableParams { + col_id: number; + colSections: ColSectionI[]; + sections: { [section_id: number | string]: UnitsView[] }[]; +} + +function UnitSectionTable(props: UnitSectionTableParams) { + const { colSections, sections, col_id } = props; + + const initialState: ColumnStateI = { + col_id, + sections, + originalSections: sections, + mergeIds: [], + moved: {}, + drag: false, + unitsView: true, + edit: { + open: false, + section_index: 0, + unit_index: 0, + }, + unitsMovedToNewSections: [], + }; + + const [state, dispatch] = useReducer(columnReducer, initialState); + const runAction = useUnitSectionTableActions(dispatch); + + const onChange = (id: number) => { + dispatch({ type: "set-merge-ids", id }); + }; + + const onDragEnd = (r: DropResult) => { + if (r.type == "SECTIONS") { + dispatch({ type: "dropped-section", result: r }); + } else { + dispatch({ type: "dropped-unit", result: r }); + } + }; + + const onReorderCancel = () => { + runAction({ type: "cancel-reorder" }); + }; + + const onReorderSave = () => { + runAction({ type: "save-reorder", sections: state.sections }); + }; + + return h(UnitSectionTableContext.Provider, { value: { state, runAction } }, [ + h("div", [ + h(ColumnPageBtnMenu, { + state: { + unitsView: state.unitsView, + drag: state.drag, + mergeIds: state.mergeIds, + moved: state.moved, + }, + toggleUnitsView: () => runAction({ type: "toggle-units-view" }), + toggleDrag: () => { + runAction({ type: "toggle-drag" }); + }, + divideSection: () => {}, + mergeSections: () => {}, + onCancel: onReorderCancel, + onSave: onReorderSave, + noSectionView: colSections.length == 0, + }), + h.if(colSections.length > 0 && !state.unitsView)(ColSectionsTable, { + colSections, + onChange, + }), + h.if(state.unitsView)("div.unit-section-container", [ + h("div.unit-section-tables", [ + h(SectionsDropContainer, { + onDragEnd, + }), + ]), + ]), + ]), + ]); +} + +export { UnitSectionTable, useUnitSectionContext, UnitSectionTableContext }; diff --git a/packages/column-builder/src/components/unit-section-table/unit.ts b/packages/column-builder/src/components/unit-section-table/unit.ts new file mode 100644 index 00000000..8be4d57a --- /dev/null +++ b/packages/column-builder/src/components/unit-section-table/unit.ts @@ -0,0 +1,266 @@ +import React from "react"; +import { Link } from "~/components"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + UnitsView, + convertColorNameToHex, + IntervalDataI, + IntervalSuggest, +} from "@macrostrat-web/column-builder/src"; +import { + EnvTags, + LithTags, + UnitEditorProps, + UnitRowThicknessEditor, + UnitRowStratNameEditor, + InformalUnitName, +} from "../unit/common-editing"; +import { MinEditorCard } from "../unit/minimal-unit-editor"; +import { DraggableRow } from "../table"; +import { UnitRowContextMenu, AddBtnBetweenRows } from "./helpers"; +import { useUnitSectionContext } from "@macrostrat-web/column-builder/src"; +import styles from "../comp.module.sass"; +import { Button, Dialog, TextArea } from "@blueprintjs/core"; +import { ModelEditor, useModelEditor } from "@macrostrat/ui-components"; +import { SubmitButton } from "../buttons"; + +const h = hyperStyled(styles); + +function UnitRowIntervalEditor() { + const { + model: unit, + actions, + isEditing, + }: { model: UnitsView; actions: any; isEditing: boolean } = useModelEditor(); + + const changeInterval = (interval: IntervalDataI, lo: boolean) => { + const { id, interval_name } = interval.data; + + let intervalField = "fo"; + let intervalName = "name_fo"; + if (lo) { + intervalField = "lo"; + intervalName = "name_lo"; + } + actions.updateState({ + model: { + [intervalField]: { $set: id }, + [intervalName]: { $set: interval_name }, + }, + }); + }; + + const onChangeLo = (interval: IntervalDataI) => { + changeInterval(interval, true); + }; + + const onChangeFo = (interval: IntervalDataI) => { + changeInterval(interval, false); + }; + + return h(React.Fragment, [ + h.if(!isEditing)("div", [ + unit.name_fo !== unit.name_lo + ? `${unit.name_fo} - ${unit.name_lo}` + : unit.name_lo, + ]), + h.if(isEditing)("div", { style: { display: "flex" } }, [ + h(IntervalSuggest, { + placeholder: "Bottom interval", + initialSelected: { + value: unit?.name_fo, + data: { id: unit?.fo || 0, interval_name: unit?.name_fo }, + }, + onChange: onChangeFo, + }), + "-", + h(IntervalSuggest, { + placeholder: "Top Interval", + initialSelected: { + value: unit?.name_lo, + data: { + id: unit?.lo || 0, + interval_name: unit?.name_lo, + }, + }, + onChange: onChangeLo, + }), + ]), + ]); +} + +function UnitRowNotes() { + const { + model: unit, + actions, + isEditing, + }: { model: UnitsView; actions: any; isEditing: boolean } = useModelEditor(); + + const updateUnit = (field: string, value: string) => { + actions.updateState({ + model: { + [field]: { $set: value }, + }, + }); + }; + + return h(React.Fragment, [ + h.if(isEditing)(TextArea, { + style: { maxWidth: "100px" }, + value: unit.notes ?? "", + onChange: (e) => updateUnit("notes", e.target.value), + }), + h.if(!isEditing)("p.ellipse", [unit.notes]), + ]); +} + +function UnitCellGroup(props: { unit: UnitsView; onCancel: () => void }) { + const { unit } = props; + const { + isEditing, + actions, + }: { isEditing: boolean; actions: { persistChanges: any } } = + useModelEditor(); + + const backgroundColor = convertColorNameToHex(unit.color) + "80"; + return h(React.Fragment, [ + h("td", [ + h(Link, { href: `../unit/${unit.id}/edit` }, [h("a", [unit.id])]), + ]), + h("td", { style: { background: backgroundColor } }, [h(InformalUnitName)]), + h("td", { style: { background: backgroundColor } }, [ + h(UnitRowStratNameEditor), + ]), + h("td", [h(LithTags, { large: false })]), + h("td", [h(EnvTags, { large: false })]), + h("td", [h(UnitRowIntervalEditor)]), + h("td", [h(UnitRowThicknessEditor)]), + h("td", [h(UnitRowNotes)]), + h.if(isEditing)("td", [ + h(Button, { intent: "danger", onClick: props.onCancel }, ["Cancel"]), + h(SubmitButton), + ]), + ]); +} + +interface UnitRowProps { + unit: UnitsView; + drag: boolean; + unit_index: number; + section_index: number; + colSpan: number; + isMoved: boolean; + inRowEditing: boolean; + copyUnitUp: () => void; + copyUnitDown: () => void; + addEmptyUnit: (unit_index: number) => void; + editUnitAt: (unit_index: number) => void; +} + +function UnitRow(props: UnitRowProps) { + const { state, runAction } = useUnitSectionContext(); + + const persistChanges = (e: UnitsView, c: Partial) => { + runAction({ + type: "save-unit-at", + unit: e, + changeSet: c, + og_unit: props.unit, + unit_index: props.unit_index, + section_index: props.section_index, + sections: state.sections, + }); + }; + + const onCancel = () => { + runAction({ + type: "cancel-editing", + section_index: props.section_index, + unit_index: props.unit_index, + }); + }; + + return h(React.Fragment, { key: props.unit.id }, [ + h.if(props.unit_index == 0)(AddBtnBetweenRows, { + colSpan: props.colSpan, + onClick: (e) => { + e.stopPropagation(); + props.addEmptyUnit(props.unit_index); + }, + }), + h( + DraggableRow, + { + key: props.unit.id, + index: props.unit_index, + drag: props.drag, + draggableId: props.unit.strat_name + props.unit.id.toString(), + href: undefined, + isMoved: props.isMoved, + onDoubleClick: () => { + runAction({ + type: "edit-unit-at", + unit_index: props.unit_index, + section_index: props.section_index, + }); + }, + }, + [ + h( + ModelEditor, + { + isEditing: props.inRowEditing, + //@ts-ignore + persistChanges, + model: { ...props.unit }, + }, + [ + h(UnitCellGroup, { + unit: props.unit, + key: props.unit_index, + onCancel, + }), + ] + ), + h.if(!props.inRowEditing)("td", { width: "0%" }, [ + h(UnitRowContextMenu, { + unit: props.unit, + unit_index: props.unit_index, + section_index: props.section_index, + copyUnitUp: props.copyUnitUp, + copyUnitDown: props.copyUnitDown, + addEmptyUnit: props.addEmptyUnit, + editUnitAt: props.editUnitAt, + }), + ]), + ] + ), + + h(AddBtnBetweenRows, { + colSpan: props.colSpan, + onClick: (e) => { + e.stopPropagation(); + props.addEmptyUnit(props.unit_index + 1); + }, + }), + ]); +} + +function UnitRowEditorModal( + props: UnitEditorProps & { + onCancel: () => void; + title: string; + open: boolean; + } +) { + return h(Dialog, { isOpen: props.open, style: { width: "50%" } }, [ + h(MinEditorCard, { + title: props.title, + persistChanges: props.persistChanges, + model: props.model, + onCancel: props.onCancel, + }), + ]); +} + +export { UnitRow, UnitRowEditorModal }; diff --git a/packages/column-builder/src/components/unit/color.ts b/packages/column-builder/src/components/unit/color.ts new file mode 100644 index 00000000..5a9f31cd --- /dev/null +++ b/packages/column-builder/src/components/unit/color.ts @@ -0,0 +1,33 @@ +import pkg from "react-color"; +import { Popover2 } from "@blueprintjs/popover2"; +import h from "../comp.module.sass"; + +interface ColorProps { + color: string; + onChange: (hex: string) => void; +} + +function ColorBlock(props: ColorProps) { + const onChange = (color: { hex: string }) => { + const { hex } = color; + props.onChange(hex); + }; + const color = props.color ?? "black"; + + console.log(pkg); + + return h("div", [ + h( + Popover2, + { + content: h(pkg, { + onChange: onChange, + color, + }), + }, + h("div.color-block", { style: { backgroundColor: color } }) + ), + ]); +} + +export { ColorBlock }; diff --git a/packages/column-builder/src/components/unit/common-editing.ts b/packages/column-builder/src/components/unit/common-editing.ts new file mode 100644 index 00000000..e94dcc15 --- /dev/null +++ b/packages/column-builder/src/components/unit/common-editing.ts @@ -0,0 +1,226 @@ +import React from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + UnitsView, + LithUnit, + Lith, + EnvironUnit, + TagContainerCell, +} from "../../index"; +import { InputGroup, NumericInput, FormGroup } from "@blueprintjs/core"; +import { + useModelEditor, + //@ts-ignore +} from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import { EnvTagsAdd } from ".."; +import { UnitStratNameModalEditor } from ".."; +import { LithContainer } from "../lith"; +const h = hyperStyled(styles); + +export interface UnitEditorProps { + persistChanges: (e: UnitsView, c: Partial) => void; + model: UnitsView | {}; +} + +export function EnvTags(props: { large: boolean }) { + const { large = true } = props; + const { + model: unit, + isEditing, + actions, + }: { + model: UnitsView; + isEditing: boolean; + actions: any; + } = useModelEditor(); + const { environ_unit: envs } = unit; + + const tagData = + envs?.map((env) => { + return { + id: env.id, + color: env.environ_color, + name: env.environ, + description: env.environ_class, + }; + }) ?? []; + + const onClickDelete = (id: number) => { + const filteredEnvs = [...(envs ?? [])].filter((l) => l.id != id); + actions.updateState({ + model: { environ_unit: { $set: filteredEnvs } }, + }); + }; + + const onClick = (env: Partial) => { + actions.updateState({ + model: { environ_unit: { $push: [env] } }, + }); + }; + + return h("div.tag-container", [ + h.if(tagData.length == 0 && isEditing)("div", ["Add environments"]), + h(TagContainerCell, { data: tagData, onClickDelete, isEditing, large }), + h.if(isEditing)(EnvTagsAdd, { onClick }), + ]); +} + +export function LithTags(props: { large?: boolean }) { + const { large = true } = props; + const { + model: unit, + isEditing, + actions, + }: { + model: UnitsView; + isEditing: boolean; + actions: any; + } = useModelEditor(); + const { lith_unit: liths = [] } = unit; + + const onClickDelete = (lith: LithUnit) => { + const filteredLiths = [...(liths ?? [])].filter((l) => l.id != lith.id); + actions.updateState({ + model: { lith_unit: { $set: filteredLiths } }, + }); + }; + + const onAdd = (lith: Lith) => { + lith.dom = "sub"; + actions.updateState({ model: { lith_unit: { $push: [lith] } } }); + }; + + const onSwitchProp = (id: number) => { + const liths_ = JSON.parse(JSON.stringify(liths)); + const index = liths_.findIndex((lith: LithUnit) => lith.id == id); + const arrayOfDeleted = liths_.splice(index, 1); + const lith: LithUnit = arrayOfDeleted[0]; + if (lith.dom == "dom") { + lith.dom = "sub"; + } else lith.dom = "dom"; + liths_.splice(index, 0, lith); + actions.updateState({ model: { lith_unit: { $set: liths_ } } }); + }; + + return h("div", [ + h.if(liths.length == 0 && isEditing)("div", ["Add lithologies"]), + h(LithContainer, { + large, + liths, + onAdd, + onSwitchProp, + onRemove: isEditing ? onClickDelete : undefined, + isEditing, + }), + ]); +} + +export function UnitThickness(props: { + field: string; + defaultValue: number | undefined; + placeholder: string; + small?: boolean; +}) { + const { model: unit, actions }: { model: UnitsView; actions: any } = + useModelEditor(); + + const update = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + const width = props.small ?? false ? "60px" : undefined; + + return h(NumericInput, { + style: { width }, + onValueChange: (e) => update(props.field, e), + defaultValue: props.defaultValue, + placeholder: props.placeholder, + buttonPosition: props.small ?? false ? "none" : undefined, + }); +} + +export function UnitRowThicknessEditor() { + const { + model: unit, + actions, + isEditing, + }: { model: UnitsView; actions: any; isEditing: boolean } = useModelEditor(); + + return h(React.Fragment, [ + h.if(!isEditing)("div", [ + unit.min_thick != unit.max_thick + ? `${unit.min_thick} - ${unit.max_thick}` + : unit.min_thick, + ]), + h.if(isEditing)("div", { style: { display: "flex" } }, [ + h(UnitThickness, { + field: "min_thick", + placeholder: "min thickness", + defaultValue: unit?.min_thick, + small: true, + }), + " - ", + h(UnitThickness, { + field: "max_thick", + placeholder: "max thickness", + defaultValue: unit?.max_thick, + small: true, + }), + ]), + ]); +} + +export function InformalUnitName() { + const { model: unit, actions, isEditing } = useModelEditor(); + + const updateUnitName = (e: string) => { + actions.updateState({ + model: { strat_name: { $set: e } }, + }); + }; + + return h("div", [ + h.if(!isEditing)("p", [unit.strat_name]), + h.if(isEditing)(InputGroup, { + placeholder: "Informal Unit Name", + style: { width: "120px" }, + value: unit.strat_name || undefined, + onChange: (e) => updateUnitName(e.target.value), + }), + ]); +} + +export function UnitRowStratNameEditor() { + const { + model: unit, + actions, + isEditing, + }: { model: UnitsView; actions: any; isEditing: boolean } = useModelEditor(); + + const nameText = unit.strat_names.length + ? unit.strat_names.map((sn) => `${sn.strat_name} ${sn.rank}`).join(", ") + : "No stratigraphic name"; + + return h("div", [nameText, h.if(isEditing)(UnitStratNameModalEditor)]); +} + +export function FormalStratName() { + /** TODO: this is invalid */ + const { model: unit, actions, isEditing } = useModelEditor(); + + const updateUnitName = (e: string) => { + actions.updateState({ + model: { strat_name: { $set: e } }, + }); + }; + + return h("div", [ + h.if(!isEditing)("p", [unit.strat_name]), + h.if(isEditing)(InputGroup, { + placeholder: "Informal Unit Name", + style: { width: "120px" }, + value: unit.strat_name || undefined, + onChange: (e) => updateUnitName(e.target.value), + }), + ]); +} diff --git a/packages/column-builder/src/components/unit/edit-helpers.ts b/packages/column-builder/src/components/unit/edit-helpers.ts new file mode 100644 index 00000000..6b3c3559 --- /dev/null +++ b/packages/column-builder/src/components/unit/edit-helpers.ts @@ -0,0 +1,204 @@ +import { PostgrestResponse } from "@supabase/postgrest-js"; +import pg, { + UnitsView, + tableUpdate, + StratNameI, + EnvironUnit, + LithUnit, +} from "../.."; +import { detectDeletionsAndAdditions } from "../helpers"; + +async function handleLithCollection( + collection: LithUnit[], + changes: LithUnit[], + unit_id: number +) { + // find deletions, and additions + // for rest, set the prop where the id is already set + const { deletions, additions } = detectDeletionsAndAdditions( + collection, + changes + ); + + changes.map(async (change) => { + if (additions.has(change.id)) { + // this is completely new! Insert! + const { data, error } = await pg + .from("unit_liths") + .insert([{ unit_id: unit_id, lith_id: change.id, dom: change.dom }]); + } else { + // already exists! update! + const { data, error } = await pg + .from("unit_liths") + .update({ dom: change.dom }) + .match({ unit_id: unit_id, lith_id: change.id }); + } + }); + deletions.forEach(async (id) => { + // delete from the table where id + const { data, error } = await pg + .from("unit_liths") + .delete() + .match({ unit_id: unit_id, lith_id: id }); + }); +} + +async function handleEnvironCollection( + collection: EnvironUnit[], + changes: EnvironUnit[], + unit_id: number +) { + const { deletions, additions } = detectDeletionsAndAdditions( + collection, + changes + ); + + deletions.forEach(async (id) => { + const { data, error } = await pg + .from("unit_environs") + .delete() + .match({ unit_id: unit_id, environ_id: id }); + }); + const inserts = []; + additions.forEach((id) => { + inserts.push({ unit_id, environ_id: id }); + }); + const { data, error } = await pg.from("unit_environs").insert(inserts); +} + +async function handleStratNameCollection( + collection: StratNameI[], + changes: StratNameI[], + unit_id: number +) { + const { deletions, additions } = detectDeletionsAndAdditions( + collection, + changes + ); + deletions.forEach(async (id) => { + const { data, error } = await pg + .from("unit_strat_names") + .delete() + .match({ unit_id: unit_id, strat_name_id: id }); + }); + const inserts = []; + additions.forEach((id) => { + inserts.push({ unit_id, strat_name_id: id }); + }); + const { data, error } = await pg.from("unit_strat_names").insert(inserts); +} + +const persistable_fields = new Set([ + "strat_name", + "lo", + "fo", + "min_thick", + "max_thick", + "notes", + "color", + "section_id", + "col_id", +]); + +async function updateExistingUnit( + unit: UnitsView, + updatedModel: UnitsView, + changeSet: Partial +) { + if (changeSet) { + const updates = Object.keys(changeSet) + .filter((key) => persistable_fields.has(key)) + .reduce((cur, key) => { + return Object.assign(cur, { [key]: changeSet[key] }); + }, {}); + const { data, error } = await tableUpdate("units", { + changes: updates, + id: unit.id, + }); + } +} + +async function insertNewUnit(unit: UnitsView) { + const inserts = Object.keys(unit) + .filter((key) => persistable_fields.has(key)) + .reduce((cur, key) => { + return Object.assign(cur, { [key]: unit[key] }); + }, {}); + const { data, error }: PostgrestResponse = await pg + .from("units") + .insert([inserts]); + return data[0]; +} + +/* +Function to handle changes to Units! This is a bit complicated because of the +one to many relationship between a unit and environments, lithologies, and strat_names. +*/ +export async function persistUnitChanges( + unit: UnitsView, + updatedModel: UnitsView, + changeSet: Partial +) { + console.log(unit, updatedModel, changeSet); + + await updateExistingUnit(unit, updatedModel, changeSet); + + if (changeSet?.environ_unit) { + await handleEnvironCollection( + unit.environ_unit ?? [], + changeSet.environ_unit, + unit.id + ); + } + if (changeSet?.lith_unit) { + await handleLithCollection( + unit.lith_unit ?? [], + changeSet.lith_unit, + unit.id + ); + } + if (changeSet?.strat_names) { + await handleStratNameCollection( + unit.strat_names ?? [], + changeSet.strat_names, + unit.id + ); + } + return updatedModel; +} + +export async function persistNewUnit( + unit: UnitsView, + updatedModel: UnitsView, + changeSet: Partial +) { + const { id } = await insertNewUnit(updatedModel); + unit.id = id; + updatedModel.id = id; + console.log(updatedModel); + + if (updatedModel?.environ_unit) { + updatedModel.environ_unit.map(async (env) => { + const { data, error } = await pg + .from("unit_environs") + .insert([{ unit_id: updatedModel.id, environ_id: env.id }]); + }); + } + if (updatedModel?.lith_unit) { + updatedModel.lith_unit.map(async (lith) => { + const { data, error } = await pg + .from("unit_liths") + .insert([ + { unit_id: updatedModel.id, lith_id: lith.id, dom: lith.dom }, + ]); + }); + } + if (updatedModel?.strat_names) { + updatedModel.strat_names.map(async (strat_name) => { + const { data, error } = await pg + .from("unit_strat_names") + .insert([{ unit_id: updatedModel.id, strat_name_id: strat_name.id }]); + }); + } + return updatedModel; +} diff --git a/packages/column-builder/src/components/unit/index.ts b/packages/column-builder/src/components/unit/index.ts new file mode 100644 index 00000000..0499ce7e --- /dev/null +++ b/packages/column-builder/src/components/unit/index.ts @@ -0,0 +1,6 @@ +export * from "./common-editing"; +export * from "./minimal-unit-editor"; +export * from "./color"; +export * from "./interval-suggest"; +export * from "./unit-editor"; +export * from "../table"; diff --git a/packages/column-builder/src/components/unit/interval-suggest.ts b/packages/column-builder/src/components/unit/interval-suggest.ts new file mode 100644 index 00000000..bdc07e7c --- /dev/null +++ b/packages/column-builder/src/components/unit/interval-suggest.ts @@ -0,0 +1,135 @@ +import React, { useState, useEffect } from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { Spinner, MenuItem } from "@blueprintjs/core"; +import { IntervalI } from "../../types"; +import pg from "../../db"; +import styles from "../comp.module.sass"; +import { ItemSuggest } from "../suggest"; +import { FeatureCell } from "../table"; +import { ItemRenderer } from "@blueprintjs/select"; + +const h = hyperStyled(styles); + +interface IntervalProps { + onChange: (e: IntervalDataI) => void; + initialSelected?: IntervalDataI; + onQueryChange?: (e: string) => void; + placeholder?: string; +} + +export interface IntervalDataI { + value: string; + data: IntervalI | Partial; +} + +interface IntervalRowProps extends IntervalProps { + age_bottom?: number; + age_top?: number; + bottom: boolean; +} + +const intervalItemRenderer: ItemRenderer = ( + item: IntervalDataI, + { modifiers, handleClick } +) => { + const { + interval_name, + age_bottom, + age_top, + interval_color, + interval_type, + id, + } = item.data; + + const style = + modifiers?.active ?? false + ? { + marginBottom: "2px", + } + : { + backgroundColor: interval_color + "40", // add opaquness + marginBottom: "2px", + }; + + return h(MenuItem, { + key: id, + text: h("div.interval-row", [ + `${interval_name} (${interval_type})`, + h("i", [`${age_bottom}-${age_top} Ma`]), + ]), + onClick: handleClick, + active: modifiers.active, + style, + }); +}; + +function IntervalSuggest(props: IntervalProps) { + const [intervals, setIntervals] = useState([]); + const getIntervals = async (query: string) => { + if (query.length > 2) { + const { data, error } = await pg + .from("intervals") + .select() + .like("interval_name", `%${query}%`) + .order("age_bottom") + .limit(200); + const d = data?.map((d: IntervalI) => { + return { value: d.interval_name, data: d }; + }); + setIntervals(d); + } else { + const { data, error } = await pg + .from("intervals") + .select() + .order("age_bottom") + .limit(50); + const d = data?.map((d: IntervalI) => { + return { value: d.interval_name, data: d }; + }); + setIntervals(d); + } + }; + + useEffect(() => { + const getData = async () => { + const { data, error } = await pg + .from("intervals") + .select() + .order("age_bottom") + .limit(50); + const d = data?.map((d: IntervalI) => { + return { value: d.interval_name, data: d }; + }); + setIntervals(d); + }; + getData(); + }, []); + + return h(React.Fragment, [ + h.if(intervals == undefined)(Spinner), + h.if(intervals != undefined)(ItemSuggest, { + items: intervals, + onChange: props.onChange, + onQueryChange: getIntervals, + initialSelected: props.initialSelected, + itemRenderer: intervalItemRenderer, + placeholder: props.placeholder, + }), + ]); +} + +function IntervalRow(props: IntervalRowProps) { + const label: string = !props.bottom ? "Top (LO): " : "Bottom (FO): "; + + const ageLabel: string = props.bottom ? "Age Bottom: " : "Age Top: "; + + return h(React.Fragment, [ + h(FeatureCell, { text: label }, [h(IntervalSuggest, { ...props })]), + h(FeatureCell, { text: ageLabel }, [ + props.age_bottom || props.age_top, + " ma", + ]), + ]); +} + +export { IntervalRow, IntervalSuggest }; diff --git a/packages/column-builder/src/components/unit/minimal-unit-editor.ts b/packages/column-builder/src/components/unit/minimal-unit-editor.ts new file mode 100644 index 00000000..85d737f7 --- /dev/null +++ b/packages/column-builder/src/components/unit/minimal-unit-editor.ts @@ -0,0 +1,263 @@ +import { hyperStyled } from "@macrostrat/hyper"; +import { + IntervalDataI, + Table, + IntervalSuggest, +} from "@macrostrat-web/column-builder/src"; +import { Button, TextArea, Card, Collapse, Dialog } from "@blueprintjs/core"; +import { ModelEditor, useModelEditor } from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import { SubmitButton } from ".."; +import { + UnitThickness, + UnitEditorProps, + InformalUnitName, + FormalStratName, + EnvTags, + LithTags, +} from "./common-editing"; +import { useState } from "react"; +import { AddButton } from "../buttons"; +import { UnitsView } from "~/types"; +const h = hyperStyled(styles); + +function UnitEditCancelAlert(props: { + onCancel: () => void; + onClose: () => void; + open: boolean; +}) { + return h( + Dialog, + { + isOpen: props.open, + icon: "warning-sign", + title: "Unsaved Changes", + }, + [ + h( + "div", + { + style: { margin: "10px" }, + }, + [ + h("p", [ + "There are unsaved changes, are you sure you want to leave?", + ]), + h("div", [ + h(Button, { intent: "danger", onClick: props.onCancel }, [ + "Don't Save", + ]), + h(Button, { intent: "primary", onClick: props.onClose }, [ + "Back to Editing", + ]), + ]), + ] + ), + ] + ); +} + +function UnitEdit(props: { onCancel: () => void }) { + const [open, setOpen] = useState(false); + const { model, hasChanges, actions, ...rest } = useModelEditor(); + const { unit }: { unit: UnitsView } = model; + + const updateUnit = (field: string, e: any) => { + actions.updateState({ model: { unit: { [field]: { $set: e } } } }); + }; + + const onChangeLo = (interval: IntervalDataI) => { + const { data } = interval; + const { id: lo, interval_name: name_lo } = data; + actions.updateState({ + model: { + unit: { + lo: { $set: lo }, + name_lo: { $set: name_lo }, + }, + }, + }); + }; + + const onChangeFo = (interval: IntervalDataI) => { + const { data } = interval; + const { id: fo, interval_name: name_fo } = data; + actions.updateState({ + model: { + unit: { + fo: { $set: fo }, + name_fo: { $set: name_fo }, + }, + }, + }); + }; + + const onCancelBtnClick = () => { + if (hasChanges()) { + setOpen(true); + } else { + props.onCancel(); + } + }; + + return h("div", [ + h(UnitEditCancelAlert, { + open, + onCancel: props.onCancel, + onClose: () => setOpen(false), + }), + h(Table, { interactive: false }, [ + h("tr", [ + h("td", [ + h("div.margin-bottom-spacing", [h(InformalUnitName)]), + h(FormalStratName), + ]), + h("td", [ + h("div.margin-bottom-spacing", [h(LithTags, { large: false })]), + h(EnvTags, { large: false }), + ]), + + h("td", [ + h("div.margin-bottom-spacing", [ + h(UnitThickness, { + field: "min_thick", + placeholder: "min thickness", + defaultValue: unit?.min_thick || undefined, + }), + ]), + h(UnitThickness, { + field: "max_thick", + placeholder: "max thickness", + defaultValue: unit?.max_thick || undefined, + }), + ]), + ]), + h("tr", [ + h("td.interval-cell-min-editor", [ + h(IntervalSuggest, { + placeholder: "Top interval", + initialSelected: { + value: unit?.name_lo, + data: { id: unit?.lo || 0, interval_name: unit?.name_lo }, + }, + onChange: onChangeLo, + }), + h(IntervalSuggest, { + placeholder: "Bottom Interval", + initialSelected: { + value: unit?.name_fo, + data: { + id: unit?.fo || 0, + interval_name: unit?.name_fo, + }, + }, + onChange: onChangeFo, + }), + ]), + h("td", { colSpan: 2 }, [ + h("div", { style: { display: "flex", flexDirection: "column" } }, [ + h(TextArea, { + value: unit.notes, + onChange: (e) => updateUnit("notes", e.target.value), + }), + h("div", { style: { marginTop: "10px" } }, [ + h(SubmitButton), + h(Button, { intent: "danger", onClick: onCancelBtnClick }, [ + "Cancel", + ]), + ]), + ]), + ]), + ]), + ]), + ]); +} + +interface MinUnitEditorProps extends UnitEditorProps { + onCancel: () => void; +} + +function MinUnitEditor(props: MinUnitEditorProps) { + return h( + ModelEditor, + { + model: props.model, + //@ts-ignore + persistChanges: props.persistChanges, + canEdit: true, + isEditing: true, + }, + [h(UnitEdit, { onCancel: props.onCancel })] + ); +} + +interface ToggleI extends UnitEditorProps { + btnText: string; + btnPosition: "top" | "bottom"; +} + +function MinEditorToggle(props: ToggleI) { + const [add, setAdd] = useState(false); + + const model = { + unit: { new_section: false, lith_unit: [], environ_unit: [] }, + }; + + const persistChanges = (e: UnitsView, c: Partial) => { + props.persistChanges(e, c); + setAdd(false); + }; + + const onCancel = () => { + setAdd(false); + }; + + return h("div", [ + h.if(props.btnPosition == "top")( + AddButton, + { onClick: () => setAdd(!add) }, + [props.btnText] + ), + h(Collapse, { isOpen: add }, [ + h(MinEditorCard, { + persistChanges, + onCancel, + model, + title: props.btnText, + }), + ]), + h.if(props.btnPosition == "bottom")( + AddButton, + { onClick: () => setAdd(!add) }, + [props.btnText] + ), + ]); +} + +function NewSectionBtn(props: { + addNewSection: (i: number) => void; + index: number; +}) { + return h(AddButton, { onClick: () => props.addNewSection(props.index) }, [ + "Add Section", + ]); +} + +function MinEditorCard( + props: UnitEditorProps & { + onCancel: () => void; + title?: string; + } +) { + const { persistChanges, model, onCancel, title } = props; + return h(Card, { style: { padding: 0, paddingBottom: "5px" } }, [ + h("div.header", [title]), + h(MinUnitEditor, { + model, + persistChanges, + onCancel, + }), + ]); +} + +export { MinUnitEditor, MinEditorToggle, MinEditorCard, NewSectionBtn }; diff --git a/packages/column-builder/src/components/unit/unit-editor.ts b/packages/column-builder/src/components/unit/unit-editor.ts new file mode 100644 index 00000000..7fcb6d1f --- /dev/null +++ b/packages/column-builder/src/components/unit/unit-editor.ts @@ -0,0 +1,216 @@ +import React from "react"; +import { hyperStyled } from "@macrostrat/hyper"; +import { + UnitsView, + IntervalRow, + IntervalDataI, + ColorBlock, + Table, + FeatureCell, +} from "../../index"; +import { NumericInput, TextArea } from "@blueprintjs/core"; +import { + ModelEditor, + useModelEditor, + //@ts-ignore +} from "@macrostrat/ui-components"; +import styles from "../comp.module.sass"; +import { SubmitButton } from ".."; +import { + UnitEditorProps, + EnvTags, + LithTags, + UnitRowStratNameEditor, + InformalUnitName, + UnitThickness, +} from "./common-editing"; +const h = hyperStyled(styles); + +function UnitThicknesses() { + const { model: unit, actions }: { model: UnitsView; actions: any } = + useModelEditor(); + + return h(React.Fragment, [ + h(FeatureCell, { text: "Min-Thick" }, [ + h(UnitThickness, { + field: "min_thick", + defaultValue: unit?.min_thick || undefined, + placeholder: "Min thick", + }), + ]), + h(FeatureCell, { text: "Max-Thick: " }, [ + h(UnitThickness, { + field: "max_thick", + defaultValue: unit?.max_thick || undefined, + placeholder: "Max thick", + }), + ]), + ]); +} + +function StratName() { + const { model: unit }: { model: UnitsView } = useModelEditor(); + + if (unit?.strat_names == null) { + return null; + } + + const baseURl = `/unit/${unit.id}`; + // this complexity is born of the confusing strat_name issues in the db + // const href = + unit.strat_names.length > 0 + ? `${baseURl}/strat-name/${unit.strat_names[0].id}/edit` + : `${baseURl}/strat-name/new`; + + const linkText = unit.strat_names.length > 0 ? "(modify)" : "(create)"; + + return h("tr", [ + h(FeatureCell, { text: "Informal Unit Name" }, [h(InformalUnitName)]), + h(FeatureCell, { text: "Formal Stratigraphic Name: " }, [ + h(UnitRowStratNameEditor), + // h(Link, { href }, [h("a", { style: { fontSize: "10px" } }, [linkText])]), + ]), + ]); +} + +interface UnitPositionI { + bottom: boolean; + position_bottom?: number; + position_top?: number; + onPositionChange: (e: number) => void; +} + +function UnitPosition(props: UnitPositionI) { + const positionLabel: string = props.bottom + ? "Position Bottom: " + : "Position Top: "; + + return h(React.Fragment, [ + h(FeatureCell, { text: positionLabel }, [ + h(NumericInput, { + onValueChange: props.onPositionChange, + defaultValue: props.position_bottom || props.position_top, + }), + ]), + ]); +} + +/* +Probably the most complicated component, bc there are so many editable things. +*/ +function UnitEdit() { + const { model: unit, hasChanges, actions, ...rest } = useModelEditor(); + + const updateUnit = (field: string, e: any) => { + actions.updateState({ model: { [field]: { $set: e } } }); + }; + + const onChangeLo = (interval: IntervalDataI) => { + const { data } = interval; + const { id: lo, interval_name: name_lo, age_top } = data; + actions.updateState({ + model: { + lo: { $set: lo }, + name_lo: { $set: name_lo }, + age_top: { $set: age_top }, + }, + }); + }; + + const onChangeFo = (interval: IntervalDataI) => { + const { data } = interval; + const { id: fo, interval_name: name_fo, age_bottom } = data; + actions.updateState({ + model: { + fo: { $set: fo }, + name_fo: { $set: name_fo }, + age_bottom: { $set: age_bottom }, + }, + }); + }; + + return h("div", [ + h(Table, { interactive: false }, [ + h(StratName), + h("tr", [ + h(IntervalRow, { + bottom: false, + age_top: unit?.age_top, + initialSelected: { + value: unit?.name_lo, + data: { id: unit?.lo || 0, interval_name: unit?.name_lo }, + }, + onChange: onChangeLo, + }), + h(UnitPosition, { + bottom: false, + onPositionChange: (e) => updateUnit("position_top", e), + position_top: unit?.position_top || undefined, + }), + ]), + h("tr", [ + h(IntervalRow, { + bottom: true, + age_bottom: unit?.age_bottom, + initialSelected: { + value: unit?.name_fo, + data: { + id: unit?.fo || 0, + interval_name: unit?.name_fo, + }, + }, + onChange: onChangeFo, + }), + h(UnitPosition, { + bottom: true, + onPositionChange: (e) => updateUnit("position_bottom", e), + position_bottom: unit?.position_bottom || undefined, + }), + ]), + h("tr", [ + h(FeatureCell, { text: "Color: " }, [ + h(ColorBlock, { + onChange: (color) => { + actions.updateState({ + model: { color: { $set: color } }, + }); + }, + color: unit?.color, + }), + ]), + h(UnitThicknesses), + ]), + h("tr", [ + h(FeatureCell, { text: "Notes: ", colSpan: 5 }, [ + h(TextArea, { + value: unit.notes, + onChange: (e) => updateUnit("notes", e.target.value), + }), + ]), + ]), + h("tr", [ + h(FeatureCell, { text: "Lithologies: ", colSpan: 5 }, [h(LithTags)]), + ]), + h("tr", [ + h(FeatureCell, { text: "Environments: ", colSpan: 5 }, [h(EnvTags)]), + ]), + ]), + h(SubmitButton), + ]); +} + +function UnitEditor(props: UnitEditorProps) { + return h( + ModelEditor, + { + model: props.model, + //@ts-ignore + persistChanges: props.persistChanges, + canEdit: true, + isEditing: true, + }, + h(UnitEdit) + ); +} + +export { UnitEditor }; diff --git a/packages/column-builder/src/data-fetching/index.ts b/packages/column-builder/src/data-fetching/index.ts new file mode 100644 index 00000000..76fe7251 --- /dev/null +++ b/packages/column-builder/src/data-fetching/index.ts @@ -0,0 +1 @@ +export * from "./units" \ No newline at end of file diff --git a/packages/column-builder/src/data-fetching/units.ts b/packages/column-builder/src/data-fetching/units.ts new file mode 100644 index 00000000..138c59d9 --- /dev/null +++ b/packages/column-builder/src/data-fetching/units.ts @@ -0,0 +1,34 @@ +import { PostgrestError, PostgrestResponse } from "@supabase/postgrest-js"; +import pg, { + createUnitBySections, + UnitsView, +} from "@macrostrat-web/column-builder/src"; + +type ReturnType = { + data: { [section_id: number | string]: UnitsView[] }[]; + error: PostgrestError | null; +}; + +export async function getSectionData( + match: any, + limit: number | null = null +): Promise { + const { data: units, error }: PostgrestResponse = await pg + .from("units") + .select( + /// joins the lith_unit and environ_unit table + "*, unit_strat_name_expanded(*,strat_names(*, strat_names_meta(*))),lith_unit(*),environ_unit(*)" + ) + .order("position_bottom", { ascending: true }) + .match(match) + .limit(limit ?? 100000); + + const u1 = units ?? []; + + const unitsMapped: UnitsView[] = u1.map((d) => { + const { unit_strat_name_expanded = [], ...rest } = d; + return { ...rest, strat_names: unit_strat_name_expanded }; + }); + + return { data: createUnitBySections(unitsMapped), error }; +} diff --git a/packages/column-builder/src/db/index.ts b/packages/column-builder/src/db/index.ts new file mode 100644 index 00000000..3807bbdd --- /dev/null +++ b/packages/column-builder/src/db/index.ts @@ -0,0 +1,148 @@ +import { useEffect, useCallback, useState } from "react"; +import { + PostgrestClient, + PostgrestFilterBuilder, + PostgrestQueryBuilder, + PostgrestResponse, +} from "@supabase/postgrest-js"; +import fetch from "cross-fetch"; +import { postgrestPrefix } from "@macrostrat-web/settings"; + +function isServer() { + return typeof window === "undefined"; +} + +// The address of the postgrest service is different between the client and the server! +const pg = new PostgrestClient( + //@ts-ignore + postgrestPrefix, + { + fetch(input, options) { + console.log(input); + return fetch(input, options); + }, + } +); + +/** + * Fetch data using postgrestclient + * Takes in a postgrestclient QueryBuilder or FilterBuilder + * which are js objects, more at https://github.com/supabase/postgrest-js + * returns data + * @param query : A postgrest-js querybuilder or filterbuilder object + */ +function usePostgrest( + query: PostgrestQueryBuilder | PostgrestFilterBuilder +) { + const [result, setResult] = useState(); // cop-out type for now + + const getData = useCallback(async () => { + const { data, error } = await query; + if (error) { + console.error(error); + } else { + setResult(data); + } + }, [query]); + + useEffect(() => { + getData(); + }, []); + + return result; +} +interface SelectTableI { + match?: number | Record; + limit?: number; + columns?: string | undefined; +} + +/* + * Helper hook to abstract some query building + */ +function useTableSelect(table: string, opts: SelectTableI) { + let query = pg.from(table).select(opts.columns); + if (opts.match) { + if (typeof opts.match !== "number") { + query = query.match(opts.match); + } else { + query = query.match({ id: opts.match }); + } + } + if (opts.limit) { + query = query.limit(opts.limit); + } + return usePostgrest(query); +} + +interface QueryOptsI { + columns?: string; + match?: number | Record; + limit?: number; +} + +async function tableSelect(table: string, opts: QueryOptsI = {}) { + let query = pg.from(table).select(opts.columns); + if (opts.match) { + if (typeof opts.match !== "number") { + query = query.match(opts.match); + } else { + query = query.match({ id: opts.match }); + } + } + if (opts.limit) { + query = query.limit(opts.limit); + } + + return await query; +} + +async function selectFirst(table: string, opts: QueryOptsI) { + const { data, error }: PostgrestResponse = await tableSelect( + table, + opts + ); + + const firstData = data ? data[0] : null; + return { firstData, error }; +} + +interface UpdateTableI { + id: number | Record; + changes: object; +} + +async function tableUpdate(table: string, opts: UpdateTableI) { + let query = pg.from(table).update(opts.changes); + if (typeof opts.id !== "number") { + query = query.match(opts.id); + } else { + query = query.match({ id: opts.id }); + } + return await query; +} + +/* + Hook for easy table inserts +*/ +async function tableInsert(table: string, row: object) { + let query = pg.from(table).insert([row]); + return await query; +} + +async function tableInsertMany(table: string, rows: object[]) { + let query = pg.from(table).insert(rows); + return await query; +} + +export default pg; +export { + usePostgrest, + useTableSelect, + tableUpdate, + tableInsert, + selectFirst, + tableInsertMany, + tableSelect, + isServer, +}; diff --git a/packages/column-builder/src/index.ts b/packages/column-builder/src/index.ts new file mode 100644 index 00000000..07e1897c --- /dev/null +++ b/packages/column-builder/src/index.ts @@ -0,0 +1,6 @@ +import pg from "./db"; +export * from "./db"; +export * from "./components"; +export * from "./types"; + +export default pg; diff --git a/packages/column-builder/src/types.ts b/packages/column-builder/src/types.ts new file mode 100644 index 00000000..4f072832 --- /dev/null +++ b/packages/column-builder/src/types.ts @@ -0,0 +1,181 @@ +/* +The different data types used in the application. Usually matching up with a specific db view in +macrostrat_api schema +*/ + +import { PointGeom } from "@macrostrat/form-components"; + +export interface Project { + descrip: string; + id?: number; + project: string; + timescale_id?: number; +} + +export interface TimeScale { + id: number; + timescale: string; + ref_id: number; +} + +export interface ColumnGroupI { + col_group: string; + col_group_long: string; + cols: ICol[]; + id: number; + project_id: number; +} + +export interface RefI { + id?: number; + pub_year: number; + author: string; + ref: string; + doi?: string; + url?: string; +} + +export interface ColumnForm { + id: number; + col_area: number; + col_type: string; + coordinate: PointGeom; + poly_geom: { type: "Polygon"; coordinates: [][] } | null; + project_id: number; + col_name: string; + col: number; + lng: number; + lat: number; + notes?: string; + wkt: string; + refs: RefI[]; +} + +// interface for col that shows up in column_group +export interface ICol { + col_id: number; + col_number?: number; + col_name: string; + status_code?: string; +} + +export interface IColumnSection extends ICol { + bottom: string; + position_bottom: number; + position_top: number; + top: string; + section_id: number; + units?: number; +} + +export interface UnitsView { + id: number; + strat_name: string; + strat_names: StratNameI[]; + color: string; + outcrop?: string; + fo?: number; + name_fo: string; + age_bottom: number; + lo?: number; + name_lo: string; + age_top: number; + section_id: number; + col_id: number; + notes?: string; + position_bottom: number; + position_top: number; + max_thick: number; + min_thick: number; + lith_unit?: LithUnit[]; + environ_unit?: EnvironUnit[]; +} + +export interface Lith { + id: number; + lith: string; + lith_group?: string; + lith_type: string; + lith_class: string; + lith_color: string; + dom: "dom" | "sub"; + mod_prop: number; + comp_prop: number; +} + +export interface LithUnit extends Lith { + unit_id: number; +} + +export interface Environ { + id: number; + environ: string; + environ_type: string; + environ_class: string; + environ_color: string; +} + +export interface EnvironUnit extends Environ { + unit_id: number; +} + +export interface IntervalI { + id: number; + age_bottom: number; + age_top: number; + interval_name: string; + interval_abbrev?: string; + interval_type?: string; + interval_color: string; + rank: number; +} + +export enum RANK { + SGp = "SGp", + Gp = "Gp", + SubGp = "SubGp", + Fm = "Fm", + Mbr = "Mbr", + Bed = "Bed", +} + +interface StratNameConceptBase { + concept_id: number; + orig_id: number; + name: string; + geologic_age: string; + b_int: number; + t_int: number; + usage_notes: string; + other: string; + province: string; + url: string; +} +export interface StratNameConceptI extends StratNameConceptBase { + interval_id: number; + ref_id: number; +} + +export interface StratNameConceptLongI extends StratNameConceptBase { + intervals: IntervalI; + refs: RefI; +} + +export interface StratNameI { + id: number; + strat_name: string; + rank: RANK; + ref_id: number; + author: string | null; + concept_id: number | null; + parent: string | null; + source?: string; + strat_names_meta?: StratNameConceptBase; +} + +export interface ColSectionI { + id: number; + unit_count: number; + top: string; + bottom: string; +} diff --git a/packages/column-builder/tsconfig.json b/packages/column-builder/tsconfig.json new file mode 100644 index 00000000..abc91aac --- /dev/null +++ b/packages/column-builder/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"], + "@macrostrat/ui-components": [ + "deps/web-components/packages/ui-components/src/" + ], + "@macrostrat/form-components": [ + "deps/web-components/packages/form-components/src/" + ], + "@macrostrat/data-components": [ + "deps/web-components/packages/data-components/src/" + ] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/pages/_error/+Page.ts b/pages/_error/+Page.ts index 49c057cc..0240169b 100644 --- a/pages/_error/+Page.ts +++ b/pages/_error/+Page.ts @@ -2,23 +2,31 @@ import h from "@macrostrat/hyper"; import { CenteredContentPage } from "~/layouts"; import { PageHeader } from "~/components"; import { usePageContext } from "vike-react/usePageContext"; +import { ClientOnly } from "vike-react/ClientOnly"; +import { Spinner, Button } from "@blueprintjs/core"; export function Page() { + return h(CenteredContentPage, [h(PageHeader), h(PageContent)]); +} + +function PageContent() { const ctx = usePageContext(); const is404 = ctx.is404; + const path = ctx.urlPathname; + const statusCode = ctx.abortStatusCode; + const reason = ctx.abortReason; - return h(CenteredContentPage, [ - h(PageHeader, { title: "Macrostrat" }), - h(PageContent, { is404, path: ctx.urlPathname }), - ]); -} - -function PageContent({ is404, path }: { is404: boolean; path: string }) { if (is404) { return h([ h("h1", [h("code.bp5-code", "404"), " Page Not Found"]), h("p", ["Could not find a page at path ", h("code.bp5-code", path), "."]), ]); + } else if (statusCode == 401) { + return h([ + h("h1", [h("code.bp5-code", "401"), " Unauthorized"]), + h("p", [reason]), + h(LoginButton), + ]); } else { return h([ h("h1", "Internal Error"), @@ -27,3 +35,24 @@ function PageContent({ is404, path }: { is404: boolean; path: string }) { ]); } } + +function LoginButton() { + /** For now, the login button only loads on the client side */ + return h(ClientOnly, { + load: async () => { + const res = await import("@macrostrat/auth-components"); + return res.AuthStatus; + }, + fallback: h( + Button, + { + disabled: true, + icon: h(Spinner, { size: 16 }), + minimal: true, + large: true, + }, + "Not logged in" + ), + children: (component) => h(component), + }); +} diff --git a/pages/dev/+Page.mdx b/pages/dev/+Page.mdx index 81e01f99..41741872 100644 --- a/pages/dev/+Page.mdx +++ b/pages/dev/+Page.mdx @@ -10,6 +10,12 @@ import { PageHeader } from "~/components"; - [Paleogeography](/dev/paleo) +## Column editing + +- [Column editor](/dev/column-editor) + +## xDD integration + ## xDD - [Map legend affinity](/dev/map/legend-affinity) diff --git a/pages/dev/+config.ts b/pages/dev/+config.ts index 56c3523b..ff8b4c56 100644 --- a/pages/dev/+config.ts +++ b/pages/dev/+config.ts @@ -1,4 +1 @@ -export default { - // Forces global style separation from other pages by reloading - clientRouting: false, -}; +export default {}; diff --git a/pages/dev/column-editor/+config.ts b/pages/dev/column-editor/+config.ts new file mode 100644 index 00000000..73a0f7f7 --- /dev/null +++ b/pages/dev/column-editor/+config.ts @@ -0,0 +1,11 @@ +export default { + meta: { + Page: { + env: { + client: true, + server: false, + }, + }, + }, + title: "Column editor", +}; diff --git a/pages/dev/column-editor/+guard.ts b/pages/dev/column-editor/+guard.ts new file mode 100644 index 00000000..6bd9cc8e --- /dev/null +++ b/pages/dev/column-editor/+guard.ts @@ -0,0 +1,10 @@ +import { render } from "vike/abort"; + +// This guard() hook protects all pages /pages/admin/**/+Page.js +// https://vike.dev/guard + +export async function guard(pageContext) { + if (pageContext.user?.role != "web_admin") { + throw render(401, "You aren't allowed to access this page."); + } +} diff --git a/pages/dev/column-editor/column-group/@col_group_id/edit/+Page.ts b/pages/dev/column-editor/column-group/@col_group_id/edit/+Page.ts new file mode 100644 index 00000000..37f02967 --- /dev/null +++ b/pages/dev/column-editor/column-group/@col_group_id/edit/+Page.ts @@ -0,0 +1,33 @@ +import h from "@macrostrat/hyper"; +import { + BasePage, + ColumnGroupEditor, + ColumnGroupI, + tableUpdate, +} from "@macrostrat-web/column-builder"; +import { useData } from "vike-react/useData"; +import { EditColumnGroupData } from "./+data"; + +export function Page() { + const { columnGroup, query, errors } = useData(); + const persistChanges = async ( + e: Partial, + changes: Partial + ) => { + const { data, error } = await tableUpdate("col_groups", { + changes, + id: e.id || 0, + }); + if (!error) { + return data[0]; + } else { + console.error(error); + } + }; + + return h(BasePage, { query, errors }, [ + h("h3", ["Edit Column Group: ", columnGroup.col_group_long]), + //@ts-ignore + h(ColumnGroupEditor, { model: columnGroup, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/column-group/@col_group_id/edit/+data.ts b/pages/dev/column-editor/column-group/@col_group_id/edit/+data.ts new file mode 100644 index 00000000..787ab637 --- /dev/null +++ b/pages/dev/column-editor/column-group/@col_group_id/edit/+data.ts @@ -0,0 +1,34 @@ +import { + ColumnGroupI, + fetchIdsFromColGroup, + IdsFromColGroup, + tableSelect, +} from "@macrostrat-web/column-builder"; +import { PostgrestError, PostgrestResponse } from "@supabase/postgrest-js"; +import { PageContext } from "vike/types"; + +export async function data(ctx: PageContext): Promise { + let { col_group_id } = ctx.routeParams; + if (Array.isArray(col_group_id)) { + col_group_id = col_group_id[0]; + } + + const query: IdsFromColGroup = await fetchIdsFromColGroup( + parseInt(col_group_id ?? "0") + ); + + const { data, error }: PostgrestResponse> = + await tableSelect("col_groups", { + match: { id: col_group_id ?? "0" }, + }); + + const columnGroup = data ? data[0] : {}; + const errors = [error].filter((e) => e != null); + return { col_group_id, columnGroup, query, errors }; +} + +export interface EditColumnGroupData { + errors: PostgrestError[]; + columnGroup: Partial; + query: IdsFromColGroup; +} diff --git a/pages/dev/column-editor/column-group/@col_group_id/new-column/+Page.ts b/pages/dev/column-editor/column-group/@col_group_id/new-column/+Page.ts new file mode 100644 index 00000000..0e408a47 --- /dev/null +++ b/pages/dev/column-editor/column-group/@col_group_id/new-column/+Page.ts @@ -0,0 +1,62 @@ +import h from "@macrostrat/hyper"; +import { + BasePage, + ColumnEditor, + ColumnForm, + tableInsert, +} from "@macrostrat-web/column-builder"; +import { useData } from "vike-react/useData"; +import { NewColumnData } from "./+data"; + +export function Page() { + const { colGroup, col_group_id, errors } = useData(); + + const persistChanges = async ( + e: ColumnForm, + changes: Partial + ) => { + //create the correct column object for persistence. + // project_id, col_group_id, col (#), col_name, status_code, col_type + //get the id back and enter that into the ref_col table + const newColumn = { + project_id: colGroup.project_id, + col_group_id, + col: e.col_number, + col_name: e.col_name, + status_code: "in process", + col_type: "column", + lat: e.lat, + lng: e.lng, + }; + + const { data, error } = await tableInsert("cols", newColumn); + + if (!error) { + // create new col_refs from new id + const col_id: number = data[0].id; + const ref_col = { ref_id: e.ref.id, col_id: col_id }; + + const { data: data_, error } = await tableInsert("col_refs", ref_col); + + if (!error) { + return e; + } else { + //catch errror + } + } else { + //catch error + } + }; + + return h(BasePage, { query: props.query, errors }, [ + h("h3", [ + `Add a new column to ${colGroup.col_group_long}(${colGroup.col_group})`, + ]), + //@ts-ignore + h(ColumnEditor, { + model: {}, + persistChanges, + curColGroup: colGroup, + }), + ]); +} diff --git a/pages/dev/column-editor/column-group/@col_group_id/new-column/+data.ts b/pages/dev/column-editor/column-group/@col_group_id/new-column/+data.ts new file mode 100644 index 00000000..02c10fab --- /dev/null +++ b/pages/dev/column-editor/column-group/@col_group_id/new-column/+data.ts @@ -0,0 +1,36 @@ +import pg, { + ColumnGroupI, + fetchIdsFromColGroup, + IdsFromColGroup, +} from "@macrostrat-web/column-builder"; +import { PostgrestError, PostgrestResponse } from "@supabase/postgrest-js"; +import { PageContext } from "vike/types"; + +export async function data(ctx: PageContext) { + let { col_group_id } = ctx.routeParams; + + if (Array.isArray(col_group_id)) { + col_group_id = col_group_id[0]; + } + + const query: IdsFromColGroup = await fetchIdsFromColGroup( + parseInt(col_group_id ?? "0") + ); + + const { data, error }: PostgrestResponse> = await pg + .from("col_groups") + .select() + .match({ id: col_group_id }); + + const colGroup = data ? data[0] : {}; + + const errors = error == null ? [] : [error]; + return { props: { col_group_id, colGroup, query, errors } }; +} + +export interface NewColumnData { + col_group_id: string; + colGroup: Partial; + query: IdsFromColGroup; + errors: PostgrestError[]; +} diff --git a/pages/dev/column-editor/column-groups/@project_id/+Page.ts b/pages/dev/column-editor/column-groups/@project_id/+Page.ts new file mode 100644 index 00000000..3481e08a --- /dev/null +++ b/pages/dev/column-editor/column-groups/@project_id/+Page.ts @@ -0,0 +1,68 @@ +import h from "@macrostrat/hyper"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { useData } from "vike-react/useData"; +import pg, { + ColumnGroupI, + Row, + BasePage, + Table, + CreateButton, + EditButton, +} from "@macrostrat-web/column-builder"; +import type { ColumnGroupsData } from "./+data"; + +export function Page() { + const props = useData(); + + const { project_id, errors } = props; + + const headers = ["ID", "Name", "Col #", "Status"]; + + return h(BasePage, { query: { project_id }, errors }, [ + h("h3", [ + `Column Groups for Project #${props.project_id}: ${props.projectName}`, + h(CreateButton, { + href: `/column-groups/${project_id}/new`, + text: "Add New Group", + }), + ]), + h("div", { style: { display: "flex", flexWrap: "wrap" } }, [ + props.columnGroups.map((colGroup, i) => { + return h( + "div", + { key: i, style: { textAlign: "center", height: "100%" } }, + [ + h("div.col-group-name", [ + h("h3", { style: { margin: 0 } }, colGroup.col_group_long), + h(EditButton, { + small: true, + href: `../column-group/${colGroup.id}/edit`, + }), + ]), + h(Table, { interactive: true, headers }, [ + colGroup.cols.map((id, i) => { + return h( + Row, + { + key: id.col_id, + href: `../column/${id.col_id}`, + }, + [ + h("td", [id.col_id]), + h("td", [id.col_name]), + h("td", [id.col_number]), + h("td", [id.status_code]), + ] + ); + }), + ]), + h(CreateButton, { + href: `/column-group/${colGroup.id}/new-column`, + text: "Add New Column", + }), + ] + ); + }), + ]), + ]); +} diff --git a/pages/dev/column-editor/column-groups/@project_id/+data.ts b/pages/dev/column-editor/column-groups/@project_id/+data.ts new file mode 100644 index 00000000..3e65cad0 --- /dev/null +++ b/pages/dev/column-editor/column-groups/@project_id/+data.ts @@ -0,0 +1,24 @@ +import pg, { ColumnGroupI } from "@macrostrat-web/column-builder"; +import { PageContext } from "vike/types"; +import { PostgrestError } from "@supabase/postgrest-js"; + +export async function data(ctx: PageContext): Promise { + const { project_id } = ctx.routeParams; + + const { data, error } = await pg + .from("col_group_with_cols") + .select("*") + .match({ project_id }); + + const projectName: string = data && data.length > 0 ? data[0].project : ""; + const errors = [error].filter((e) => e != null); + + return { project_id, projectName, columnGroups: data, errors }; +} + +export interface ColumnGroupsData { + projectName: string; + project_id: number; + columnGroups: ColumnGroupI[]; + errors: PostgrestError[]; +} diff --git a/pages/dev/column-editor/column-groups/@project_id/colgroup.module.scss b/pages/dev/column-editor/column-groups/@project_id/colgroup.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/pages/dev/column-editor/column-groups/@project_id/new/+Page.ts b/pages/dev/column-editor/column-groups/@project_id/new/+Page.ts new file mode 100644 index 00000000..eeac3853 --- /dev/null +++ b/pages/dev/column-editor/column-groups/@project_id/new/+Page.ts @@ -0,0 +1,37 @@ +import pg, { + BasePage, + ColumnGroupEditor, + ColumnGroupI, +} from "@macrostrat-web/column-builder"; +import h from "../colgroup.module.scss"; +import { useData } from "vike-react/useData"; +import type { NewColumnGroupParams } from "./+data"; + +export function Page() { + const { project, project_id, errors } = useData(); + + const newColumnGroup: Partial = { + col_group: "", + col_group_long: "", + }; + + const persistChanges = async ( + columnGroup: Partial, + c: Partial + ) => { + const { data, error } = await pg + .from("col_groups") + .insert([{ ...columnGroup, project_id: project_id }]); + if (!error) { + return data[0]; + } else { + //catch error + } + }; + + return h(BasePage, { query: { project_id }, errors }, [ + h("h3", ["Create a New Column Group for ", project.project]), + //@ts-ignore + h(ColumnGroupEditor, { model: newColumnGroup, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/column-groups/@project_id/new/+data.ts b/pages/dev/column-editor/column-groups/@project_id/new/+data.ts new file mode 100644 index 00000000..1838b85e --- /dev/null +++ b/pages/dev/column-editor/column-groups/@project_id/new/+data.ts @@ -0,0 +1,21 @@ +import { Project, tableSelect } from "@macrostrat-web/column-builder"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { PageContext } from "vike/types"; + +export async function data(ctx: PageContext): Promise { + const { project_id } = ctx.routeParams; + + const { data, error } = await tableSelect("projects", { + match: { id: project_id }, + }); + + const project = data ? data[0] : {}; + const errors = [error].filter((e) => e != null); + return { project_id, project, errors }; +} + +export interface NewColumnGroupParams { + project_id: number; + project: Project; + errors: PostgrestError[]; +} diff --git a/pages/dev/column-editor/column/@col_id/+Page.ts b/pages/dev/column-editor/column/@col_id/+Page.ts new file mode 100644 index 00000000..ada611b3 --- /dev/null +++ b/pages/dev/column-editor/column/@col_id/+Page.ts @@ -0,0 +1,27 @@ +import h from "@macrostrat/hyper"; +import { + BasePage, + EditButton, + UnitSectionTable, +} from "@macrostrat-web/column-builder"; +import { useData } from "vike-react/useData"; +import { ColumnProps } from "./+data"; + +export function Page() { + const props: ColumnProps = useData(); + const { col_id, colSections, column, query, sections, errors } = props; + + const columnName = column ? column[0].col_name : null; + + return h(BasePage, { query, errors }, [ + h("h3", [ + `Sections for column ${columnName}`, + h(EditButton, { + href: `./${col_id}/edit`, + }), + ]), + // there doesn't appear to be a good solution yet, so this is the best we can do. It loses the SSR + // for this component unfortunately + h(UnitSectionTable, { sections, colSections, col_id }), + ]); +} diff --git a/pages/dev/column-editor/column/@col_id/+data.ts b/pages/dev/column-editor/column/@col_id/+data.ts new file mode 100644 index 00000000..cf1de81b --- /dev/null +++ b/pages/dev/column-editor/column/@col_id/+data.ts @@ -0,0 +1,58 @@ +import { PostgrestError, PostgrestResponse } from "@supabase/postgrest-js"; +import { PageContext } from "vike/types"; +import pg, { + ColSectionI, + fetchIdsFromColId, + IdsFromCol, + UnitsView, +} from "@macrostrat-web/column-builder"; + +import { getSectionData } from "@macrostrat-web/column-builder/src/data-fetching"; + +export async function data(ctx: PageContext) { + let { + routeParams: { col_id }, + } = ctx; + if (Array.isArray(col_id)) { + col_id = col_id[0]; + } + + const query: IdsFromCol = await fetchIdsFromColId(parseInt(col_id ?? "0")); + + const { data: colSections, error: e }: PostgrestResponse = + await pg.from("col_section_data").select().match({ col_id }); + + const { + data: column, + error: col_error, + }: PostgrestResponse<{ col_name: string }> = await pg + .from("cols") + .select("col_name") + .match({ id: col_id }); + + const { data: sections, error: unit_error } = await getSectionData({ + col_id, + }); + + const errors = [e, col_error, unit_error].filter((e) => e != null); + if (errors.length > 0) { + console.error(errors); + } + return { + col_id, + colSections, + column, + errors, + query, + sections, + }; +} + +export interface ColumnProps { + col_id: string; + colSections: ColSectionI[]; + column: { col_name: string }[]; + errors: PostgrestError[]; + query: IdsFromCol; + sections: { [section_id: number | string]: UnitsView[] }[]; +} diff --git a/pages/dev/column-editor/column/@col_id/edit/+Page.ts b/pages/dev/column-editor/column/@col_id/edit/+Page.ts new file mode 100644 index 00000000..486055de --- /dev/null +++ b/pages/dev/column-editor/column/@col_id/edit/+Page.ts @@ -0,0 +1,85 @@ +import h from "@macrostrat/hyper"; +import pg, { + BasePage, + ColumnEditor, + ColumnForm, + IdsFromCol, + tableUpdate, +} from "@macrostrat-web/column-builder"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { useData } from "vike-react/useData"; + +interface EditColumnData { + col_id: string; + curColGroup: any; + column: ColumnForm[]; + query: IdsFromCol; + errors: PostgrestError[]; +} + +export function Page() { + const props = useData(); + const { col_id, curColGroup, column, errors }: EditColumnData = props; + console.log(column); + const persistChanges = async ( + e: ColumnForm, + changes: Partial + ) => { + console.log(e, changes); + // port names to match db (only col_numer -> col) + let ref_id: number | undefined = undefined; + if (changes.refs) { + // handle the changing of a ref, either one that exists or was created + ref_id = changes.refs[0].id; + delete changes.refs; + } + // lat,lng only need to be entered to update coordinate and wkt. + // DB triggers, see /api-views/02-functions.sql + const { data, error } = await tableUpdate("cols", { + changes, + id: e.id, + }); + + if (!error) { + if (ref_id) { + const ref_col = { ref_id: ref_id }; + const { data: count, error: _ } = await pg + .from("col_refs") + .select() + .match({ col_id: e.id }); + if (count?.length ?? 0 > 1) { + const { data: data_, error } = await pg + .from("col_refs") + .update({ ref_id }) + .match({ col_id: e.id }); + } else { + const { data: data_, error } = await pg + .from("col_refs") + .insert([{ ref_id, col_id: e.id }]); + } + } + if (error) { + //catch errror + } + return e; + } else { + //catch error + } + + // check if ref has changed + + return e; + }; + + return h(BasePage, { query: props.query, errors }, [ + h("h3", [ + `Edit column ${column[0].col_name}, part of ${curColGroup.col_group_long}(${curColGroup.col_group}) Column Group`, + ]), + //@ts-ignore + h(ColumnEditor, { + model: column[0], + persistChanges, + curColGroup: curColGroup, + }), + ]); +} diff --git a/pages/dev/column-editor/column/@col_id/edit/+data.ts b/pages/dev/column-editor/column/@col_id/edit/+data.ts new file mode 100644 index 00000000..f01429c7 --- /dev/null +++ b/pages/dev/column-editor/column/@col_id/edit/+data.ts @@ -0,0 +1,39 @@ +import pg, { + ColumnForm, + fetchIdsFromColId, + IdsFromCol, + selectFirst, +} from "@macrostrat-web/column-builder"; +import { PostgrestResponse } from "@supabase/postgrest-js"; +import { PageContext } from "vike/types"; + +export async function data(ctx: PageContext) { + let { col_id } = ctx.routeParams; + if (Array.isArray(col_id)) { + col_id = col_id[0]; + } + const query: IdsFromCol = await fetchIdsFromColId(parseInt(col_id ?? "0")); + + const { data, error }: PostgrestResponse = await pg + .from("cols") + .select("*,refs(*)") + .match({ id: parseInt(col_id ?? "0") }); + + const { firstData, error: error_ } = await selectFirst("cols", { + columns: "col_groups!cols_col_group_id_fkey(*)", + match: { id: parseInt(col_id ?? "0") }, + limit: 1, + }); + + const errors = [error, error_].filter((e) => e != null); + + console.log(data); + + return { + col_id, + column: data, + curColGroup: firstData.col_groups, + query, + errors, + }; +} diff --git a/pages/dev/column-editor/column/@col_id/new/+Page.ts b/pages/dev/column-editor/column/@col_id/new/+Page.ts new file mode 100644 index 00000000..37676ad2 --- /dev/null +++ b/pages/dev/column-editor/column/@col_id/new/+Page.ts @@ -0,0 +1,27 @@ +import h from "@macrostrat/hyper"; +import { + BasePage, + UnitEditor, + IdsFromCol, + UnitsView, +} from "@macrostrat-web/column-builder"; +import { persistNewUnitChanges } from "@macrostrat-web/column-builder/src/components/section/new-helpers"; +import { useData } from "vike-react/useData"; +import type { NewUnitData } from "./+data"; + +function Page() { + const { col_id, query }: NewUnitData = useData(); + const model = { unit: { col_id: col_id }, liths: [], envs: [] }; + + const persistChanges = async ( + updatedModel: UnitsView, + changeSet: Partial + ) => { + return await persistNewUnitChanges(updatedModel, changeSet, null, col_id); + }; + + return h(BasePage, { query, errors: [] }, [ + //@ts-ignore + h(UnitEditor, { model, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/column/@col_id/new/+data.ts b/pages/dev/column-editor/column/@col_id/new/+data.ts new file mode 100644 index 00000000..917d0692 --- /dev/null +++ b/pages/dev/column-editor/column/@col_id/new/+data.ts @@ -0,0 +1,19 @@ +import { fetchIdsFromColId, IdsFromCol } from "@macrostrat-web/column-builder"; + +import { PageContext } from "vike/types"; + +export async function data(ctx: PageContext): Promise { + let { col_id } = ctx.routeParams; + + if (Array.isArray(col_id)) { + col_id = col_id[0]; + } + const query: IdsFromCol = await fetchIdsFromColId(parseInt(col_id ?? "0")); + + return { col_id, query }; +} + +export interface NewUnitData { + col_id: number; + query: IdsFromCol; +} diff --git a/pages/dev/column-editor/index/+Page.ts b/pages/dev/column-editor/index/+Page.ts new file mode 100644 index 00000000..5a2cb46d --- /dev/null +++ b/pages/dev/column-editor/index/+Page.ts @@ -0,0 +1,54 @@ +import h from "@macrostrat/hyper"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { + Row, + Project, + BasePage, + Table, + EditButton, + CreateButton, +} from "@macrostrat-web/column-builder"; +import { useData } from "vike-react/useData"; + +export function Page() { + const data: { + projects: Project[]; + errors: PostgrestError[]; + } = useData(); + const { projects, errors } = data; + + const headers = Object.keys(projects[0]); + + return h(BasePage, { query: {}, errors }, [ + h("h3,", [ + "Choose a Project", + h(CreateButton, { + minimal: true, + href: "/project/new", + text: "Create New Project", + }), + ]), + h(Table, { interactive: true, headers }, [ + projects.map((project, i) => { + return h( + Row, + { + key: i, + href: `/column-groups/${project.id}`, + }, + [ + h("td", [project.id]), + h("td", [project.project]), + h("td", [project.descrip]), + h("td", [project.timescale_id]), + h("td", [ + h(EditButton, { + href: `/project/${project.id}/edit`, + }), + ]), + ] + ); + }), + ]), + ]); +} diff --git a/pages/dev/column-editor/index/+config.ts b/pages/dev/column-editor/index/+config.ts new file mode 100644 index 00000000..6fbafe04 --- /dev/null +++ b/pages/dev/column-editor/index/+config.ts @@ -0,0 +1,4 @@ +export default { + title: "Dacite v2", + description: "A modern rewrite of Macrostrat's classic column editing app.", +}; diff --git a/pages/dev/column-editor/index/+data.ts b/pages/dev/column-editor/index/+data.ts new file mode 100644 index 00000000..e12da89f --- /dev/null +++ b/pages/dev/column-editor/index/+data.ts @@ -0,0 +1,10 @@ +import { PageContext } from "vike/types"; +import { Project, tableSelect } from "@macrostrat-web/column-builder"; + +export async function data(ctx: PageContext) { + const { data, error } = await tableSelect("projects"); + const projects: Project[] = data ? data : [{}]; + + const errors = [error].filter((e) => e != null); + return { projects, errors }; +} diff --git a/pages/dev/column-editor/project/@project_id/edit/+Page.ts b/pages/dev/column-editor/project/@project_id/edit/+Page.ts new file mode 100644 index 00000000..23c5acc7 --- /dev/null +++ b/pages/dev/column-editor/project/@project_id/edit/+Page.ts @@ -0,0 +1,31 @@ +import { + BasePage, + Project, + ProjectEditor, + tableUpdate, +} from "@macrostrat-web/column-builder"; +import h from "../../project.module.scss"; +import { useData } from "vike-react/useData"; +import type { ProjectData } from "./+data"; + +export function Page() { + const { project, project_id, errors } = useData(); + + const persistChanges = async (e: Project, changes: Partial) => { + const { data, error } = await tableUpdate("projects", { + id: e.id, + changes, + }); + + if (!error) { + return data[0]; + } else { + // error catching here + } + }; + + return h(BasePage, { query: {}, errors }, [ + h("h3", ["Create a New Project"]), + h(ProjectEditor, { project: project, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/project/@project_id/edit/+data.ts b/pages/dev/column-editor/project/@project_id/edit/+data.ts new file mode 100644 index 00000000..68d00b0a --- /dev/null +++ b/pages/dev/column-editor/project/@project_id/edit/+data.ts @@ -0,0 +1,20 @@ +import { Project, selectFirst } from "@macrostrat-web/column-builder"; +import { PageContext } from "vike/types"; +import { PostgrestError } from "@supabase/postgrest-js"; + +export async function data(ctx: PageContext): Promise { + const { project_id } = ctx.routeParams; + + const { firstData, error } = await selectFirst("projects", { + match: { id: project_id }, + }); + const project = firstData ? firstData : {}; + const errors = [error].filter((e) => e != null); + return { project_id, project, errors }; +} + +export interface ProjectData { + project_id: string; + project: Project; + errors: PostgrestError[]; +} diff --git a/pages/dev/column-editor/project/new/+Page.ts b/pages/dev/column-editor/project/new/+Page.ts new file mode 100644 index 00000000..3100b943 --- /dev/null +++ b/pages/dev/column-editor/project/new/+Page.ts @@ -0,0 +1,26 @@ +import { + tableInsert, + BasePage, + Project, + ProjectEditor, +} from "@macrostrat-web/column-builder"; +import h from "../project.module.scss"; + +export function Page() { + const newProject: Project = { + project: "", + descrip: "", + timescale_id: undefined, + }; + + const persistChanges = async (project: Project, c: Partial) => { + const { data, error } = await tableInsert("projects", project); + return data ? data[0] : {}; + }; + + return h(BasePage, { query: {}, errors: [] }, [ + h("h3", ["Create a New Project"]), + //@ts-ignore + h(ProjectEditor, { project: newProject, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/project/project.module.scss b/pages/dev/column-editor/project/project.module.scss new file mode 100644 index 00000000..bd283f77 --- /dev/null +++ b/pages/dev/column-editor/project/project.module.scss @@ -0,0 +1,4 @@ +.textArea { + min-width: 500px; + min-height: 170px; +} diff --git a/pages/dev/column-editor/section/@section_id/+Page.ts b/pages/dev/column-editor/section/@section_id/+Page.ts new file mode 100644 index 00000000..4a9fa23a --- /dev/null +++ b/pages/dev/column-editor/section/@section_id/+Page.ts @@ -0,0 +1,17 @@ +import h from "@macrostrat/hyper"; +import { BasePage, UnitSectionTable } from "@macrostrat-web/column-builder"; +import { useData } from "vike-react/useData"; +import type { SectionData } from "./+data"; + +export function Page() { + const { section_id, sections, errors } = useData(); + + return h(BasePage, { query: props.query, errors }, [ + h("h3", [`Units in Section #${section_id}`]), + h(UnitSectionTable, { + sections, + colSections: [], + col_id: props.query.col_id, + }), + ]); +} diff --git a/pages/dev/column-editor/section/@section_id/+data.ts b/pages/dev/column-editor/section/@section_id/+data.ts new file mode 100644 index 00000000..284b67c2 --- /dev/null +++ b/pages/dev/column-editor/section/@section_id/+data.ts @@ -0,0 +1,34 @@ +import { + UnitsView, + fetchIdsFromSectionId, + IdsFromSection, +} from "@macrostrat-web/column-builder"; +import { PageContext } from "vike/types"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { getSectionData } from "@macrostrat-web/column-builder/src/data-fetching"; + +export interface SectionData { + section_id: string; + query: IdsFromSection; + sections: { [section_id: number | string]: UnitsView[] }[]; + errors: PostgrestError[]; +} + +export async function data(ctx: PageContext): Promise { + let { section_id } = ctx.routeParams; + + if (Array.isArray(section_id)) { + section_id = section_id[0]; + } else if (typeof section_id == "undefined") { + section_id = "0"; + } + + const query: IdsFromSection = await fetchIdsFromSectionId( + parseInt(section_id) + ); + + const { data: sections, error } = await getSectionData({ section_id }); + + const errors = [error].filter((e) => e != null); + return { section_id, query, sections, errors }; +} diff --git a/pages/dev/column-editor/section/@section_id/new-unit/+Page.ts b/pages/dev/column-editor/section/@section_id/new-unit/+Page.ts new file mode 100644 index 00000000..34d17ef2 --- /dev/null +++ b/pages/dev/column-editor/section/@section_id/new-unit/+Page.ts @@ -0,0 +1,32 @@ +import h from "@macrostrat/hyper"; +import { + BasePage, + UnitEditor, + UnitsView, + persistNewUnitChanges, +} from "@macrostrat-web/column-builder"; +import { useData } from "vike-react/useData"; +import type { NewUnitData } from "./+data"; + +export function Page() { + const { col_id, section_id, query, errors } = useData(); + + const model = { unit: { col_id: col_id }, liths: [], envs: [] }; + + const persistChanges = async ( + updatedModel: UnitsView, + changeSet: Partial + ) => { + return await persistNewUnitChanges( + updatedModel, + changeSet, + section_id, + col_id + ); + }; + + return h(BasePage, { query, errors }, [ + //@ts-ignore + h(UnitEditor, { model, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/section/@section_id/new-unit/+data.ts b/pages/dev/column-editor/section/@section_id/new-unit/+data.ts new file mode 100644 index 00000000..594022a6 --- /dev/null +++ b/pages/dev/column-editor/section/@section_id/new-unit/+data.ts @@ -0,0 +1,36 @@ +import { + IdsFromSection, + fetchIdsFromSectionId, + selectFirst, +} from "@macrostrat-web/column-builder"; +import { PageContext } from "vike/types"; +import { PostgrestError } from "@supabase/postgrest-js"; + +export interface NewUnitData { + col_id: number; + section_id: string; + query: IdsFromSection; + errors: PostgrestError[]; +} + +export async function data(ctx: PageContext): Promise { + let { section_id } = ctx.routeParams; + + if (Array.isArray(section_id)) { + section_id = section_id[0]; + } else if (typeof section_id == "undefined") { + section_id = "0"; + } + + const query: IdsFromSection = await fetchIdsFromSectionId( + parseInt(section_id) + ); + + const { firstData, error } = await selectFirst("sections", { + match: { id: section_id }, + }); + + const { col_id } = firstData; + const errors = error == null ? [] : [error]; + return { section_id: ctx.query.section_id, col_id, query, errors }; +} diff --git a/pages/dev/column-editor/unit/@unit_id/edit/+Page.ts b/pages/dev/column-editor/unit/@unit_id/edit/+Page.ts new file mode 100644 index 00000000..d7b67d7c --- /dev/null +++ b/pages/dev/column-editor/unit/@unit_id/edit/+Page.ts @@ -0,0 +1,35 @@ +import h from "@macrostrat/hyper"; +import { + BasePage, + UnitEditor, + UnitsView, +} from "@macrostrat-web/column-builder"; +import { persistUnitChanges } from "@macrostrat-web/column-builder/src/components/unit/edit-helpers"; +import { useData } from "vike-react/useData"; +import type { UnitEditParams } from "./+data"; + +/* +Needs a strat_name displayer, we'll be stricter with editing that + +Need interval suggest component (2), Need A color picker, Contact suggests. +Tags for liths and environs; adding components for those too. +*/ +export function Page() { + const { unit, errors, unit_id, query } = useData(); + + const model = unit; + console.log("UnitA", model); + + const persistChanges = async ( + updatedModel: UnitsView, + changeSet: Partial + ) => { + return await persistUnitChanges(unit, updatedModel, changeSet); + }; + + return h(BasePage, { query, errors }, [ + h("h3", [`Edit Unit #${unit.id}: `, unit.strat_name]), + //@ts-ignore + h(UnitEditor, { model, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/unit/@unit_id/edit/+data.ts b/pages/dev/column-editor/unit/@unit_id/edit/+data.ts new file mode 100644 index 00000000..a1c0920b --- /dev/null +++ b/pages/dev/column-editor/unit/@unit_id/edit/+data.ts @@ -0,0 +1,35 @@ +import { + fetchIdsFromUnitId, + IdsFromUnit, + UnitsView, +} from "@macrostrat-web/column-builder"; +import { PageContext } from "vike/types"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { getSectionData } from "@macrostrat-web/column-builder/src/data-fetching"; + +export async function data(ctx: PageContext): Promise { + let { unit_id } = ctx.routeParams; + + if (Array.isArray(unit_id)) { + unit_id = unit_id[0]; + } else if (typeof unit_id == "undefined") { + unit_id = "0"; + } + + const query: IdsFromUnit = await fetchIdsFromUnitId(parseInt(unit_id)); + + const { data: units, error: e } = await getSectionData({ id: unit_id }, 1); + + // This is kind of crazy but it seems to work OK + const unit = Object.values(units[0])[0][0]; + + const errors = e == null ? [] : [e]; + return { unit_id, unit, query, errors }; +} + +export interface UnitEditParams { + unit_id: string; + unit: UnitsView; + query: IdsFromUnit; + errors: PostgrestError[]; +} diff --git a/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/edit/+Page.ts b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/edit/+Page.ts new file mode 100644 index 00000000..3bda973d --- /dev/null +++ b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/edit/+Page.ts @@ -0,0 +1,40 @@ +import { + BasePage, + StratNameEditor, + StratNameI, + tableUpdate, +} from "@macrostrat-web/column-builder"; +import h from "../stratname.module.scss"; +import { useData } from "vike-react/useData"; +import type { EditStratigraphicNameData } from "./+data"; + +export default function EditStratigraphicName() { + const { strat_name, errors } = useData(); + + const persistChanges = async ( + e: StratNameI, + changes: Partial + ) => { + const { data, error } = await tableUpdate("strat_names", { + changes, + id: e.id, + }); + + if (!error) { + return data[0]; + } else { + console.error(error); + } + }; + + return h(BasePage, { query: props.query, errors }, [ + h("h3", [ + "Edit Stratigraphic Name and Hierarchy for ", + strat_name.strat_name, + " ", + strat_name.rank, + ]), + //@ts-ignore + h(StratNameEditor, { model: strat_name, persistChanges }), + ]); +} diff --git a/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/edit/+data.ts b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/edit/+data.ts new file mode 100644 index 00000000..842d97e3 --- /dev/null +++ b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/edit/+data.ts @@ -0,0 +1,38 @@ +import { + fetchIdsFromUnitId, + IdsFromUnit, + selectFirst, + StratNameI, +} from "@macrostrat-web/column-builder"; +import { PostgrestError } from "@supabase/postgrest-js"; +import { PageContext } from "vike/types"; + +export async function data( + ctx: PageContext +): Promise { + let { strat_name_id, unit_id } = ctx.routeParams; + + if (Array.isArray(unit_id)) { + unit_id = unit_id[0]; + } else if (typeof unit_id == "undefined") { + unit_id = "0"; + } + const query: IdsFromUnit = await fetchIdsFromUnitId(parseInt(unit_id)); + + const { firstData: strat_name, error } = await selectFirst( + "strat_names_ref", + { + match: { id: strat_name_id }, + } + ); + const errors = error == null ? [] : [error]; + return { strat_name_id, strat_name, errors, unit_id, query }; +} + +export interface EditStratigraphicNameData { + strat_name_id: number; + strat_name: StratNameI; + unit_id: number; + query: IdsFromUnit; + errors: PostgrestError[]; +} diff --git a/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/new/+Page.ts b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/new/+Page.ts new file mode 100644 index 00000000..e2dc1ab8 --- /dev/null +++ b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/new/+Page.ts @@ -0,0 +1,50 @@ +import { + BasePage, + StratNameEditor, + StratNameI, + tableInsert, + tableUpdate, +} from "@macrostrat-web/column-builder"; +import h from "../stratname.module.scss"; +import { useData } from "vike-react/useData"; +import type { StratNameData } from "./+data"; + +export default function Page() { + const { name, unit_id, query } = useData(); + const persistChanges = async (e: StratNameI, c: Partial) => { + const { data, error } = await tableInsert("strat_names", e); + + const strat_name_id: number = data ? data[0].id : null; + + if (!strat_name_id) { + const { data, error } = await tableUpdate("units", { + changes: { strat_name_id: strat_name_id }, + id: unit_id, + }); + + return data[0]; + } else { + console.error(error); + } + }; + + let model: Partial = {}; + if (name != undefined && typeof name == "string") { + model.strat_name = name; + } + + const pageTitle = + name == undefined + ? "Make New Stratigraphic Name " + : "This Stratigraphic name doesn't exist in the database. Make New Stratigraphic Name"; + + return h(BasePage, { query, errors: [] }, [ + h("h3", [pageTitle]), + //@ts-ignore + h(StratNameEditor, { + model, + persistChanges, + new_name: true, + }), + ]); +} diff --git a/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/new/+data.ts b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/new/+data.ts new file mode 100644 index 00000000..203a709c --- /dev/null +++ b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/new/+data.ts @@ -0,0 +1,27 @@ +import { + fetchIdsFromUnitId, + IdsFromUnit, +} from "@macrostrat-web/column-builder"; + +export interface StratNameData { + name: string | undefined; + unit_id: number; + query: IdsFromUnit; +} + +export async function getServerSideProps( + ctx: GetServerSidePropsContext +): Promise { + let { + query: { unit_id }, + } = ctx; + if (Array.isArray(unit_id)) { + unit_id = unit_id[0]; + } else if (typeof unit_id == "undefined") { + unit_id = "0"; + } + + const query: IdsFromUnit = await fetchIdsFromUnitId(parseInt(unit_id)); + + return { props: { unit_id, query } }; +} diff --git a/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/stratname.module.scss b/pages/dev/column-editor/unit/@unit_id/strat-name/@strat_name_id/stratname.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/pages/dev/me/+Page.client.ts b/pages/dev/me/+Page.client.ts new file mode 100644 index 00000000..0f44ef7d --- /dev/null +++ b/pages/dev/me/+Page.client.ts @@ -0,0 +1,7 @@ +import h from "@macrostrat/hyper"; +import { DocumentationPage } from "~/layouts"; +import { AuthStatus } from "@macrostrat/auth-components"; + +export function Page() { + return h(DocumentationPage, { title: "Login" }, [h(AuthStatus)]); +} diff --git a/pages/dev/paleo/map-style.ts b/pages/dev/paleo/map-style.ts index 643ef570..06569f79 100644 --- a/pages/dev/paleo/map-style.ts +++ b/pages/dev/paleo/map-style.ts @@ -117,6 +117,20 @@ const lightStyle = { }, }, ], + fog: { + color: "#ffffff", + "space-color": [ + "interpolate", + ["linear"], + ["zoom"], + 4, + "hsl(215, 28%, 64%)", + 7, + "hsl(209, 92%, 85%)", + ], + "star-intensity": ["interpolate", ["linear"], ["zoom"], 5, 0.35, 6, 0], + range: [5, 15], + }, } as mapboxgl.Style; export function replaceSourcesForTileset( diff --git a/pages/integrations/xdd/types/+Page.client.ts b/pages/integrations/xdd/types/+Page.client.ts index d32ff03b..dd3def67 100644 --- a/pages/integrations/xdd/types/+Page.client.ts +++ b/pages/integrations/xdd/types/+Page.client.ts @@ -9,7 +9,6 @@ import { ColorPicker, } from "@macrostrat/data-sheet2"; import { asChromaColor } from "@macrostrat/color-utils"; -import { LoginButton } from "#/maps/ingestion/components/navbar"; import { AuthStatus } from "@macrostrat/auth-components"; const colorField = { diff --git a/server/vike-handler.ts b/server/vike-handler.ts index bf50f31a..12d71d95 100644 --- a/server/vike-handler.ts +++ b/server/vike-handler.ts @@ -35,17 +35,16 @@ export async function vikeHandler< async function getUserFromCookie(cookies: Record) { // Pull out the authorization cookie and decrypt it - let user: any = undefined; + let user: any = null; try { const authHeader = cookies?.["access_token"]; const secret = new TextEncoder().encode(process.env.SECRET_KEY); const jwt = authHeader.substring(7, authHeader.length); - // We probably don't need to verify the JWT on each request. - // OR we can pass the user obju - user = (await jose.jwtVerify(jwt, secret)).payload; + let res = await jose.jwtVerify(jwt, secret); + user = res.payload; console.log("User", user); } catch (e) { - // I don't care if it fails, it just means the user isn't logged in + // If it fails, the user isn't logged in. Could also have an expired token... console.log("Anonymous user"); } @@ -67,9 +66,9 @@ function getCookies(request: Request) { function synthesizeConfigFromEnvironment() { /** Creates a mapping of environment variables that start with VITE_, * and returns them as an object. This allows us to pass environment - * variables to the client. + * variables to the client at runtime. * - * TODO: Ideally this would be defined in library code. + * TODO: Ideally this would be defined in a library. * */ const env = {}; for (const key of Object.keys(process.env)) { diff --git a/src/vike.d.ts b/src/vike.d.ts index 6dec24e7..1d6c74d3 100644 --- a/src/vike.d.ts +++ b/src/vike.d.ts @@ -51,4 +51,4 @@ declare global { } // Tell TypeScript this file isn't an ambient module: -export { PageContext, PageContextClient, PageContextServer }; +export {}; diff --git a/vite.config.ts b/vite.config.ts index 2ddf4f11..32893bc6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -100,7 +100,12 @@ export default defineConfig({ // If not building for server context }, ssr: { - noExternal: ["labella", "@supabase/postgrest-js"], + // https://vike.dev/broken-npm-package + noExternal: [ + "labella", + "@supabase/postgrest-js", + "@macrostrat/auth-components", + ], }, css: { preprocessorOptions: { diff --git a/yarn.lock b/yarn.lock index 6cdfc2c0..fda60ba0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3115,6 +3115,15 @@ __metadata: languageName: node linkType: hard +"@blueprintjs/colors@npm:^4.2.1": + version: 4.2.1 + resolution: "@blueprintjs/colors@npm:4.2.1" + dependencies: + tslib: "npm:~2.5.0" + checksum: 10/4bac17586b88b975dda443098e1c70ef1ba7c30cedadec9985491c64d9de1e7e1a7205dbc724f5671b80e2036139f5d5288542c310a864f973a9b64b4819fb47 + languageName: node + linkType: hard + "@blueprintjs/colors@npm:^5.1.1": version: 5.1.1 resolution: "@blueprintjs/colors@npm:5.1.1" @@ -3124,12 +3133,41 @@ __metadata: languageName: node linkType: hard -"@blueprintjs/colors@npm:^5.1.2": - version: 5.1.2 - resolution: "@blueprintjs/colors@npm:5.1.2" +"@blueprintjs/colors@npm:^5.1.3": + version: 5.1.3 + resolution: "@blueprintjs/colors@npm:5.1.3" dependencies: tslib: "npm:~2.6.2" - checksum: 10/ef9aa91eaeb78c9738216a0c7b5884d417e4e21684c16d0a7a782939df9e9219d7b570ae1058311ff4bd0cee12ae3a8f7126be9c6fd3f7492df224f3c7b09259 + checksum: 10/50c02828ec7671dcb42b2574d21c9669ab9d4c118d86cbd409207b635d622b97396b5cf25343dfcaf4fdc4fc654fa04eaeeaa68e0db21e4cd2085b56cee5bee0 + languageName: node + linkType: hard + +"@blueprintjs/core@npm:^4.20.2": + version: 4.20.2 + resolution: "@blueprintjs/core@npm:4.20.2" + dependencies: + "@blueprintjs/colors": "npm:^4.2.1" + "@blueprintjs/icons": "npm:^4.16.0" + "@juggle/resize-observer": "npm:^3.4.0" + "@types/dom4": "npm:^2.0.2" + classnames: "npm:^2.3.1" + dom4: "npm:^2.1.5" + normalize.css: "npm:^8.0.1" + popper.js: "npm:^1.16.1" + react-popper: "npm:^1.3.11" + react-transition-group: "npm:^4.4.5" + tslib: "npm:~2.5.0" + peerDependencies: + "@types/react": ^16.14.32 || 17 || 18 + react: ^16.8 || 17 || 18 + react-dom: ^16.8 || 17 || 18 + peerDependenciesMeta: + "@types/react": + optional: true + bin: + upgrade-blueprint-2.0.0-rename: scripts/upgrade-blueprint-2.0.0-rename.sh + upgrade-blueprint-3.0.0-rename: scripts/upgrade-blueprint-3.0.0-rename.sh + checksum: 10/02fad0912fcac1bb97b18e847f68fcf5aa62465f9c002c4f56f21aded92cba7d6fcd1c93c3d2453a427873376dca7b1bab94cd0a3c322429969c1f9101ef7611 languageName: node linkType: hard @@ -3189,12 +3227,40 @@ __metadata: languageName: node linkType: hard -"@blueprintjs/core@npm:^5.13.1": - version: 5.13.1 - resolution: "@blueprintjs/core@npm:5.13.1" +"@blueprintjs/core@npm:^5.10.5": + version: 5.10.5 + resolution: "@blueprintjs/core@npm:5.10.5" + dependencies: + "@blueprintjs/colors": "npm:^5.1.1" + "@blueprintjs/icons": "npm:^5.10.0" + "@popperjs/core": "npm:^2.11.8" + classnames: "npm:^2.3.1" + normalize.css: "npm:^8.0.1" + react-popper: "npm:^2.3.0" + react-transition-group: "npm:^4.4.5" + react-uid: "npm:^2.3.3" + tslib: "npm:~2.6.2" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + "@types/react": ^16.14.41 || 17 || 18 + react: ^16.8 || 17 || 18 + react-dom: ^16.8 || 17 || 18 + peerDependenciesMeta: + "@types/react": + optional: true + bin: + upgrade-blueprint-2.0.0-rename: scripts/upgrade-blueprint-2.0.0-rename.sh + upgrade-blueprint-3.0.0-rename: scripts/upgrade-blueprint-3.0.0-rename.sh + checksum: 10/1f8719dd69a2c306f3b2dff3f611d70c89f50e7085c1b5569fc0e5fa6df384311148f62c8e932c4f543ded6a7bfc47a40d6aad20417baf1c4bd0924797ec3a50 + languageName: node + linkType: hard + +"@blueprintjs/core@npm:^5.14.0": + version: 5.14.0 + resolution: "@blueprintjs/core@npm:5.14.0" dependencies: - "@blueprintjs/colors": "npm:^5.1.2" - "@blueprintjs/icons": "npm:^5.13.0" + "@blueprintjs/colors": "npm:^5.1.3" + "@blueprintjs/icons": "npm:^5.14.0" "@popperjs/core": "npm:^2.11.8" classnames: "npm:^2.3.1" normalize.css: "npm:^8.0.1" @@ -3213,17 +3279,17 @@ __metadata: bin: upgrade-blueprint-2.0.0-rename: scripts/upgrade-blueprint-2.0.0-rename.sh upgrade-blueprint-3.0.0-rename: scripts/upgrade-blueprint-3.0.0-rename.sh - checksum: 10/3e6fe60bd35d6ede91e0525683234e291dd9f351ae288dda046d88aaee9a38fb2c49bfbb764437a967c3067c6822b059005f4cddb6168ef68d7a6c96db71284a + checksum: 10/71f57ad412e858621e3542c99e8ef517f37c07bab75e0954211b926aa03944c229d97d147bb6dedd498c8b6d1ece25dc5accb2cc8439fa3fe2683546546ef0b6 languageName: node linkType: hard "@blueprintjs/datetime2@npm:^2.3.11": - version: 2.3.11 - resolution: "@blueprintjs/datetime2@npm:2.3.11" + version: 2.3.13 + resolution: "@blueprintjs/datetime2@npm:2.3.13" dependencies: - "@blueprintjs/core": "npm:^5.13.1" - "@blueprintjs/datetime": "npm:^5.3.11" - "@blueprintjs/icons": "npm:^5.13.0" + "@blueprintjs/core": "npm:^5.14.0" + "@blueprintjs/datetime": "npm:^5.3.13" + "@blueprintjs/icons": "npm:^5.14.0" classnames: "npm:^2.3.1" date-fns: "npm:^2.28.0" react-day-picker: "npm:^8.10.0" @@ -3236,17 +3302,17 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/7a3e6677deb57c50043f85097560037043963ea219e1720b0e52baef5f6096c415423a20b5e0db655ea65b735145b098d9faa67900a984df567a2045b0577b15 + checksum: 10/293e2f698d5dffa4296c863dd5438120dbada8694d58a5a0aaf868545b829d6b13dfc14e6e7c7c30b6c8de9c16695410b727b5f8be7b46a7724e8cdf6d63c3da languageName: node linkType: hard -"@blueprintjs/datetime@npm:^5.3.11": - version: 5.3.11 - resolution: "@blueprintjs/datetime@npm:5.3.11" +"@blueprintjs/datetime@npm:^5.3.13": + version: 5.3.13 + resolution: "@blueprintjs/datetime@npm:5.3.13" dependencies: - "@blueprintjs/core": "npm:^5.13.1" - "@blueprintjs/icons": "npm:^5.13.0" - "@blueprintjs/select": "npm:^5.2.5" + "@blueprintjs/core": "npm:^5.14.0" + "@blueprintjs/icons": "npm:^5.14.0" + "@blueprintjs/select": "npm:^5.3.1" classnames: "npm:^2.3.1" date-fns: "npm:^2.28.0" date-fns-tz: "npm:^2.0.0" @@ -3259,13 +3325,24 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/c92786cea9e8ee3b09037e5a5557de8c30ef732e96b35896b3a7a00f8d8eb38d074727dcf68e42725c839354907a35e969ed8df5d4b7daf2812eb020dccad888 + checksum: 10/950b8748c685cb797011ef6037f9091a0a90c6ba278b457d4517d573497d86674428df51b1ddaef2a35ebb554aaa0210467188375d4972b78145ea521e9959a9 languageName: node linkType: hard -"@blueprintjs/icons@npm:^5.13.0": - version: 5.13.0 - resolution: "@blueprintjs/icons@npm:5.13.0" +"@blueprintjs/icons@npm:^4.16.0": + version: 4.16.0 + resolution: "@blueprintjs/icons@npm:4.16.0" + dependencies: + change-case: "npm:^4.1.2" + classnames: "npm:^2.3.1" + tslib: "npm:~2.5.0" + checksum: 10/4750fafed488c7eb9f698fb20ccb57b2584a7c617a032782464339d93bcac9eb1435f130f701111ca614a821607d64b9f447f8f9016fc55de3d7252469201e6a + languageName: node + linkType: hard + +"@blueprintjs/icons@npm:^5.10.0": + version: 5.10.0 + resolution: "@blueprintjs/icons@npm:5.10.0" dependencies: change-case: "npm:^4.1.2" classnames: "npm:^2.3.1" @@ -3277,7 +3354,25 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/0e962fe73412c351466dd24f3ffff4f382af8f23de9dcf1d245e71cf0d2ee1e401f767fcf36f9f6df9d604e3b4a2874f6f0ae92e489d40d3b8abddfc0c352c66 + checksum: 10/745bfe03509406ffc449a32812115f6dc03c3945ab23904102e5330e47d57cb953f6e6bbc30c7679b5eec2372c0773be2cf7cd85836c8966774660a95a44caa9 + languageName: node + linkType: hard + +"@blueprintjs/icons@npm:^5.14.0": + version: 5.14.0 + resolution: "@blueprintjs/icons@npm:5.14.0" + dependencies: + change-case: "npm:^4.1.2" + classnames: "npm:^2.3.1" + tslib: "npm:~2.6.2" + peerDependencies: + "@types/react": ^16.14.41 || 17 || 18 + react: ^16.8 || 17 || 18 + react-dom: ^16.8 || 17 || 18 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/7da68febcb07ef63e9ee32c88d7e2325612f13cd43e9feb787d8f062fb3cafc6912370fa1d337d93a53891ddadfa0d09fbc08a69e3499b99ffd7d79f2456e09c languageName: node linkType: hard @@ -3299,6 +3394,28 @@ __metadata: languageName: node linkType: hard +"@blueprintjs/popover2@npm:^1.2.1": + version: 1.14.11 + resolution: "@blueprintjs/popover2@npm:1.14.11" + dependencies: + "@blueprintjs/core": "npm:^4.20.2" + "@juggle/resize-observer": "npm:^3.4.0" + "@popperjs/core": "npm:^2.11.7" + classnames: "npm:^2.3.1" + dom4: "npm:^2.1.5" + react-popper: "npm:^2.3.0" + tslib: "npm:~2.5.0" + peerDependencies: + "@types/react": ^16.14.32 || 17 || 18 + react: ^16.8 || 17 || 18 + react-dom: ^16.8 || 17 || 18 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/0ef9a112ab7756ddbda951290e2f79c34673adc80d7cf878664288024c2c6de3f27eecd3242d3bff75112a39df1b852ae7ad0e0662ab4e91a8e5c202aea378c9 + languageName: node + linkType: hard + "@blueprintjs/select@npm:^5.1.4": version: 5.1.4 resolution: "@blueprintjs/select@npm:5.1.4" @@ -3318,12 +3435,12 @@ __metadata: languageName: node linkType: hard -"@blueprintjs/select@npm:^5.2.5": - version: 5.2.5 - resolution: "@blueprintjs/select@npm:5.2.5" +"@blueprintjs/select@npm:^5.2.1": + version: 5.2.1 + resolution: "@blueprintjs/select@npm:5.2.1" dependencies: - "@blueprintjs/core": "npm:^5.13.1" - "@blueprintjs/icons": "npm:^5.13.0" + "@blueprintjs/core": "npm:^5.10.5" + "@blueprintjs/icons": "npm:^5.10.0" classnames: "npm:^2.3.1" tslib: "npm:~2.6.2" peerDependencies: @@ -3333,7 +3450,26 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/2bf9306e942bd17e97d6a6ecb7fbacd61f042b35a931e2edca7308e05c5466fca7e9e8d02f77c5a7477d53560b4b943f26cde079f487338abf51914752eddbac + checksum: 10/b8a3a98fc4383d1fc1d683efdff0e64b26990af439c398fedb37544c7cf5adac5b91ee9d0c0ef7ee62e7befe5988e7a41f11beaa6545cb5db3e0b8c2932ff575 + languageName: node + linkType: hard + +"@blueprintjs/select@npm:^5.3.1": + version: 5.3.1 + resolution: "@blueprintjs/select@npm:5.3.1" + dependencies: + "@blueprintjs/core": "npm:^5.14.0" + "@blueprintjs/icons": "npm:^5.14.0" + classnames: "npm:^2.3.1" + tslib: "npm:~2.6.2" + peerDependencies: + "@types/react": ^16.14.41 || 17 || 18 + react: ^16.8 || 17 || 18 + react-dom: ^16.8 || 17 || 18 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/9b24393a74e1b657affd7ce2dd2fbb57adca902d429fd7f7b8648208792e0b4a72a14a0602d2c1496e3c6b40f25affa57f2d1e775b905f0df709828dbfe1fca0 languageName: node linkType: hard @@ -3415,9 +3551,9 @@ __metadata: linkType: hard "@bufbuild/protobuf@npm:^2.0.0": - version: 2.1.0 - resolution: "@bufbuild/protobuf@npm:2.1.0" - checksum: 10/de780b67a36c1066c51cc1214eb15facb03619f08ec59f9c9ebff034ed7838069010739e615a2ee8f31b555127c554a1e6421f7d1d3d718dbcdd47c458cefef6 + version: 2.2.2 + resolution: "@bufbuild/protobuf@npm:2.2.2" + checksum: 10/f739b7787f3e978db7ed5eff55fb15ec6fea3d7ab9ae8f1de9e03375e786facd4c8bfd09ea4f9558bf29f8521b524781bfeec6960fc910e7d1309c4a2329317b languageName: node linkType: hard @@ -4849,7 +4985,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.4.0": +"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" dependencies: @@ -4867,6 +5003,37 @@ __metadata: languageName: node linkType: hard +"@eslint-community/regexpp@npm:^4.6.1": + version: 4.11.0 + resolution: "@eslint-community/regexpp@npm:4.11.0" + checksum: 10/f053f371c281ba173fe6ee16dbc4fe544c84870d58035ccca08dba7f6ce1830d895ce3237a0db89ba37616524775dca82f1c502066b58e2d5712d7f87f5ba17c + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^9.6.0" + globals: "npm:^13.19.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.0" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10/7a3b14f4b40fc1a22624c3f84d9f467a3d9ea1ca6e9a372116cb92507e485260359465b58e25bcb6c9981b155416b98c9973ad9b796053fd7b3f776a6946bce8 + languageName: node + linkType: hard + +"@eslint/js@npm:8.57.0": + version: 8.57.0 + resolution: "@eslint/js@npm:8.57.0" + checksum: 10/3c501ce8a997cf6cbbaf4ed358af5492875e3550c19b9621413b82caa9ae5382c584b0efa79835639e6e0ddaa568caf3499318e5bdab68643ef4199dce5eb0a0 + languageName: node + linkType: hard + "@hattip/core@npm:0.0.45, @hattip/core@npm:^0.0.45": version: 0.0.45 resolution: "@hattip/core@npm:0.0.45" @@ -4907,6 +5074,44 @@ __metadata: languageName: node linkType: hard +"@humanwhocodes/config-array@npm:^0.11.14": + version: 0.11.14 + resolution: "@humanwhocodes/config-array@npm:0.11.14" + dependencies: + "@humanwhocodes/object-schema": "npm:^2.0.2" + debug: "npm:^4.3.1" + minimatch: "npm:^3.0.5" + checksum: 10/3ffb24ecdfab64014a230e127118d50a1a04d11080cbb748bc21629393d100850496456bbcb4e8c438957fe0934430d731042f1264d6a167b62d32fc2863580a + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 10/e993950e346331e5a32eefb27948ecdee2a2c4ab3f072b8f566cd213ef485dd50a3ca497050608db91006f5479e43f91a439aef68d2a313bd3ded06909c7c5b3 + languageName: node + linkType: hard + +"@humanwhocodes/object-schema@npm:^2.0.2": + version: 2.0.3 + resolution: "@humanwhocodes/object-schema@npm:2.0.3" + checksum: 10/05bb99ed06c16408a45a833f03a732f59bf6184795d4efadd33238ff8699190a8c871ad1121241bb6501589a9598dc83bf25b99dcbcf41e155cdf36e35e937a3 + languageName: node + linkType: hard + +"@hypnosphi/create-react-context@npm:^0.3.1": + version: 0.3.1 + resolution: "@hypnosphi/create-react-context@npm:0.3.1" + dependencies: + gud: "npm:^1.0.0" + warning: "npm:^4.0.3" + peerDependencies: + prop-types: ^15.0.0 + react: ">=0.14.0" + checksum: 10/79b697d150f9b4aa6cadfb8026f20e023c05fefc4be841b1cdd5567c3fd970ccaae84a0ea6279f579fe2cc9844c201e80713a2691b24e59cc7d6925fa8130c34 + languageName: node + linkType: hard + "@iarna/cli@npm:^2.1.0": version: 2.1.0 resolution: "@iarna/cli@npm:2.1.0" @@ -5273,7 +5478,7 @@ __metadata: languageName: node linkType: hard -"@juggle/resize-observer@npm:^3.3.1": +"@juggle/resize-observer@npm:^3.3.1, @juggle/resize-observer@npm:^3.4.0": version: 3.4.0 resolution: "@juggle/resize-observer@npm:3.4.0" checksum: 10/73d1d00ee9132fb6f0aea0531940a6b93603e935590bd450fc6285a328d906102eeeb95dea77b2edac0e779031a9708aa8c82502bd298ee4dd26e7dff48f397a @@ -5421,17 +5626,35 @@ __metadata: languageName: node linkType: hard -"@macrostrat-web/data-sheet-test@workspace:*, @macrostrat-web/data-sheet-test@workspace:packages/data-sheet-test": +"@macrostrat-web/column-builder@workspace:*, @macrostrat-web/column-builder@workspace:packages/column-builder": version: 0.0.0-use.local - resolution: "@macrostrat-web/data-sheet-test@workspace:packages/data-sheet-test" + resolution: "@macrostrat-web/column-builder@workspace:packages/column-builder" dependencies: - "@blueprintjs/core": "npm:^5.10.2" - "@blueprintjs/table": "npm:^5.1.4" - "@macrostrat/data-sheet": "workspace:*" - "@macrostrat/hyper": "npm:^2.2.1" + "@blueprintjs/core": "npm:^5.10.5" + "@blueprintjs/icons": "npm:^5.10.0" + "@blueprintjs/popover2": "npm:^1.2.1" + "@blueprintjs/select": "npm:^5.2.1" + "@macrostrat-web/settings": "workspace:*" + "@macrostrat/column-components": "workspace:*" + "@macrostrat/data-components": "workspace:*" + "@macrostrat/form-components": "workspace:*" + "@macrostrat/hyper": "npm:^2.0.1" "@macrostrat/ui-components": "workspace:*" - chroma-js: "npm:^2.4.2" - react: "npm:^18.2.0" + "@mapbox/mapbox-gl-draw": "npm:^1.3.0" + "@supabase/postgrest-js": "npm:^0.36.0" + "@types/mapbox__mapbox-gl-draw": "npm:^1.2.3" + "@types/node": "npm:^17.0.10" + "@types/react": "npm:^17" + "@types/react-beautiful-dnd": "npm:^13.1.2" + axios: "npm:^0.27.2" + cross-fetch: "npm:^3.1.5" + eslint: "npm:^8.7.0" + eslint-config-next: "npm:^12.0.8" + mapbox-gl: "npm:^2.8.2" + react: "npm:^18" + react-beautiful-dnd: "npm:^13.1.0" + react-color: "npm:^2.19.3" + typescript: "npm:^4.5.4" languageName: unknown linkType: soft @@ -5742,7 +5965,7 @@ __metadata: languageName: node linkType: hard -"@macrostrat/data-components@workspace:deps/web-components/packages/data-components": +"@macrostrat/data-components@workspace:*, @macrostrat/data-components@workspace:deps/web-components/packages/data-components": version: 0.0.0-use.local resolution: "@macrostrat/data-components@workspace:deps/web-components/packages/data-components" dependencies: @@ -5792,7 +6015,7 @@ __metadata: languageName: unknown linkType: soft -"@macrostrat/data-sheet@workspace:*, @macrostrat/data-sheet@workspace:deps/web-components/packages/data-sheet": +"@macrostrat/data-sheet@workspace:deps/web-components/packages/data-sheet": version: 0.0.0-use.local resolution: "@macrostrat/data-sheet@workspace:deps/web-components/packages/data-sheet" dependencies: @@ -5827,7 +6050,7 @@ __metadata: languageName: unknown linkType: soft -"@macrostrat/form-components@workspace:deps/web-components/packages/form-components": +"@macrostrat/form-components@workspace:*, @macrostrat/form-components@workspace:deps/web-components/packages/form-components": version: 0.0.0-use.local resolution: "@macrostrat/form-components@workspace:deps/web-components/packages/form-components" dependencies: @@ -5962,7 +6185,7 @@ __metadata: dependencies: "@macrostrat/color-utils": "npm:^1.0.0" "@macrostrat/hyper": "npm:^3.0.0" - "@macrostrat/mapbox-react": "npm:^2.2.3" + "@macrostrat/mapbox-react": "npm:^2.4.0" "@macrostrat/mapbox-utils": "npm:^1.3.2" "@macrostrat/ui-components": "npm:^4.0.4" "@mapbox/tilebelt": "npm:^2.0.0" @@ -5984,7 +6207,7 @@ __metadata: languageName: unknown linkType: soft -"@macrostrat/mapbox-react@npm:^2.2.3, @macrostrat/mapbox-react@workspace:*, @macrostrat/mapbox-react@workspace:^2.1.0, @macrostrat/mapbox-react@workspace:deps/web-components/packages/mapbox-react": +"@macrostrat/mapbox-react@npm:^2.4.0, @macrostrat/mapbox-react@workspace:*, @macrostrat/mapbox-react@workspace:^2.1.0, @macrostrat/mapbox-react@workspace:deps/web-components/packages/mapbox-react": version: 0.0.0-use.local resolution: "@macrostrat/mapbox-react@workspace:deps/web-components/packages/mapbox-react" dependencies: @@ -5999,6 +6222,7 @@ __metadata: mapbox-gl: "npm:^2.15.0" mapbox-gl-controls: "npm:^2.3.5" parcel: "npm:^2.6.2" + zustand: "npm:^5.0.1" peerDependencies: "@blueprintjs/core": ^3||^4||^5.10.2 react: ^16||^17||^18 @@ -6131,7 +6355,7 @@ __metadata: "@blueprintjs/table": "npm:^5.1.4" "@lagunovsky/redux-react-router": "npm:^3.2.0" "@loadable/component": "npm:^5.14.1" - "@macrostrat-web/data-sheet-test": "workspace:*" + "@macrostrat-web/column-builder": "workspace:*" "@macrostrat-web/globe": "workspace:*" "@macrostrat-web/lithology-hierarchy": "workspace:*" "@macrostrat-web/map-utils": "workspace:*" @@ -6539,6 +6763,15 @@ __metadata: languageName: node linkType: hard +"@next/eslint-plugin-next@npm:12.3.4": + version: 12.3.4 + resolution: "@next/eslint-plugin-next@npm:12.3.4" + dependencies: + glob: "npm:7.1.7" + checksum: 10/173bc404d7199c127b29feee78876dc4d710e231faf8f0bff9b77b1482268d31bfe4ede866b1824a87e600a14e090a5e14e02ea677913fcac6724e3a50f7b60a + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -6556,7 +6789,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3": +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -8271,7 +8504,7 @@ __metadata: languageName: node linkType: hard -"@popperjs/core@npm:^2.11.8, @popperjs/core@npm:^2.9.3": +"@popperjs/core@npm:^2.11.7, @popperjs/core@npm:^2.11.8, @popperjs/core@npm:^2.9.3": version: 2.11.8 resolution: "@popperjs/core@npm:2.11.8" checksum: 10/ddd16090cde777aaf102940f05d0274602079a95ad9805bd20bc55dcc7c3a2ba1b99dd5c73e5cc2753c3d31250ca52a67d58059459d7d27debb983a9f552936c @@ -9244,6 +9477,13 @@ __metadata: languageName: node linkType: hard +"@rushstack/eslint-patch@npm:^1.1.3": + version: 1.10.3 + resolution: "@rushstack/eslint-patch@npm:1.10.3" + checksum: 10/e1986178618bfb5fb636a54c420a7c359879d7aed6a0e456333a92fdc93e0e7a9a914114284308317cdc75e522c0696f760cd6d0b77409ed8b9633e75f096628 + languageName: node + linkType: hard + "@semantic-release/commit-analyzer@npm:^6.1.0": version: 6.3.3 resolution: "@semantic-release/commit-analyzer@npm:6.3.3" @@ -9395,6 +9635,15 @@ __metadata: languageName: node linkType: hard +"@supabase/postgrest-js@npm:^0.36.0": + version: 0.36.2 + resolution: "@supabase/postgrest-js@npm:0.36.2" + dependencies: + cross-fetch: "npm:^3.0.6" + checksum: 10/7adc59ece6a16741566cb29963f4ed69042ace6fa30666501665ee4b5ecb3b571b964a998324ec614af79b464ca41733e357577050b7437f2767f722896c485e + languageName: node + linkType: hard + "@supabase/postgrest-js@npm:^1.11.0": version: 1.11.0 resolution: "@supabase/postgrest-js@npm:1.11.0" @@ -10679,6 +10928,13 @@ __metadata: languageName: node linkType: hard +"@types/dom4@npm:^2.0.2": + version: 2.0.4 + resolution: "@types/dom4@npm:2.0.4" + checksum: 10/f64228429058eb26d728baa923dd353ce9dd5fd64f391e6619f6d2a3cfbecd0566730278c81d241b936f71961d3f1fd9a7932d7081f1232f1d6a218af89ea658 + languageName: node + linkType: hard + "@types/domhandler@npm:2.4.1": version: 2.4.1 resolution: "@types/domhandler@npm:2.4.1" @@ -10953,6 +11209,13 @@ __metadata: languageName: node linkType: hard +"@types/json5@npm:^0.0.29": + version: 0.0.29 + resolution: "@types/json5@npm:0.0.29" + checksum: 10/4e5aed58cabb2bbf6f725da13421aa50a49abb6bc17bfab6c31b8774b073fa7b50d557c61f961a09a85f6056151190f8ac95f13f5b48136ba5841f7d4484ec56 + languageName: node + linkType: hard + "@types/keyv@npm:^3.1.1, @types/keyv@npm:^3.1.4": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" @@ -11076,6 +11339,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^17.0.10": + version: 17.0.45 + resolution: "@types/node@npm:17.0.45" + checksum: 10/b45fff7270b5e81be19ef91a66b764a8b21473a97a8d211218a52e3426b79ad48f371819ab9153370756b33ba284e5c875463de4d2cf48a472e9098d7f09e8a2 + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.0": version: 2.4.1 resolution: "@types/normalize-package-data@npm:2.4.1" @@ -11141,6 +11411,15 @@ __metadata: languageName: node linkType: hard +"@types/react-beautiful-dnd@npm:^13.1.2": + version: 13.1.8 + resolution: "@types/react-beautiful-dnd@npm:13.1.8" + dependencies: + "@types/react": "npm:*" + checksum: 10/cb8a182ed0ea6f680c7c343fcb4e54237437ca90dc88c2a70f4a203ec8ad0740e389e3ab7772c8cc1479f05e5a046bccbc33d54055b94e0fe5b22be030ab23d2 + languageName: node + linkType: hard + "@types/react-dom@npm:<18.0.0, @types/react-dom@npm:^17.0.16": version: 17.0.20 resolution: "@types/react-dom@npm:17.0.20" @@ -11472,6 +11751,23 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:^5.21.0": + version: 5.62.0 + resolution: "@typescript-eslint/parser@npm:5.62.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:5.62.0" + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/typescript-estree": "npm:5.62.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/b6ca629d8f4e6283ff124501731cc886703eb4ce2c7d38b3e4110322ea21452b9d9392faf25be6bd72f54b89de7ffc72a40d9b159083ac54345a3d04b4fa5394 + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:^6.3.0": version: 6.7.2 resolution: "@typescript-eslint/parser@npm:6.7.2" @@ -11490,6 +11786,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/scope-manager@npm:5.62.0" + dependencies: + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/visitor-keys": "npm:5.62.0" + checksum: 10/e827770baa202223bc0387e2fd24f630690809e460435b7dc9af336c77322290a770d62bd5284260fa881c86074d6a9fd6c97b07382520b115f6786b8ed499da + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:6.7.2": version: 6.7.2 resolution: "@typescript-eslint/scope-manager@npm:6.7.2" @@ -11517,6 +11823,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/types@npm:5.62.0" + checksum: 10/24e8443177be84823242d6729d56af2c4b47bfc664dd411a1d730506abf2150d6c31bdefbbc6d97c8f91043e3a50e0c698239dcb145b79bb6b0c34469aaf6c45 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:6.7.2": version: 6.7.2 resolution: "@typescript-eslint/types@npm:6.7.2" @@ -11524,6 +11837,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" + dependencies: + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/visitor-keys": "npm:5.62.0" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + semver: "npm:^7.3.7" + tsutils: "npm:^3.21.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/06c975eb5f44b43bd19fadc2e1023c50cf87038fe4c0dd989d4331c67b3ff509b17fa60a3251896668ab4d7322bdc56162a9926971218d2e1a1874d2bef9a52e + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:6.7.2": version: 6.7.2 resolution: "@typescript-eslint/typescript-estree@npm:6.7.2" @@ -11559,6 +11890,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" + dependencies: + "@typescript-eslint/types": "npm:5.62.0" + eslint-visitor-keys: "npm:^3.3.0" + checksum: 10/dc613ab7569df9bbe0b2ca677635eb91839dfb2ca2c6fa47870a5da4f160db0b436f7ec0764362e756d4164e9445d49d5eb1ff0b87f4c058946ae9d8c92eb388 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:6.7.2": version: 6.7.2 resolution: "@typescript-eslint/visitor-keys@npm:6.7.2" @@ -11569,6 +11910,13 @@ __metadata: languageName: node linkType: hard +"@ungap/structured-clone@npm:^1.2.0": + version: 1.2.0 + resolution: "@ungap/structured-clone@npm:1.2.0" + checksum: 10/c6fe89a505e513a7592e1438280db1c075764793a2397877ff1351721fe8792a966a5359769e30242b3cd023f2efb9e63ca2ca88019d73b564488cc20e3eab12 + languageName: node + linkType: hard + "@universal-middleware/express@npm:^0.0.2": version: 0.0.2 resolution: "@universal-middleware/express@npm:0.0.2" @@ -12410,7 +12758,7 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.0.0": +"acorn-jsx@npm:^5.0.0, acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" peerDependencies: @@ -12478,6 +12826,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.9.0": + version: 8.12.1 + resolution: "acorn@npm:8.12.1" + bin: + acorn: bin/acorn + checksum: 10/d08c2d122bba32d0861e0aa840b2ee25946c286d5dc5990abca991baf8cdbfbe199b05aacb221b979411a2fea36f83e26b5ac4f6b4e0ce49038c62316c1848f0 + languageName: node + linkType: hard + "affine-hull@npm:^1.0.0": version: 1.0.0 resolution: "affine-hull@npm:1.0.0" @@ -12840,7 +13197,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:5.1.3": +"aria-query@npm:5.1.3, aria-query@npm:~5.1.3": version: 5.1.3 resolution: "aria-query@npm:5.1.3" dependencies: @@ -12859,6 +13216,16 @@ __metadata: languageName: node linkType: hard +"array-buffer-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "array-buffer-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.5" + is-array-buffer: "npm:^3.0.4" + checksum: 10/53524e08f40867f6a9f35318fafe467c32e45e9c682ba67b11943e167344d2febc0f6977a17e699b05699e805c3e8f073d876f8bbf1b559ed494ad2cd0fae09e + languageName: node + linkType: hard + "array-find-index@npm:^1.0.1": version: 1.0.2 resolution: "array-find-index@npm:1.0.2" @@ -12880,6 +13247,20 @@ __metadata: languageName: node linkType: hard +"array-includes@npm:^3.1.6, array-includes@npm:^3.1.7, array-includes@npm:^3.1.8": + version: 3.1.8 + resolution: "array-includes@npm:3.1.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + is-string: "npm:^1.0.7" + checksum: 10/290b206c9451f181fb2b1f79a3bf1c0b66bb259791290ffbada760c79b284eef6f5ae2aeb4bcff450ebc9690edd25732c4c73a3c2b340fcc0f4563aed83bf488 + languageName: node + linkType: hard + "array-union@npm:^2.1.0": version: 2.1.0 resolution: "array-union@npm:2.1.0" @@ -12887,6 +13268,58 @@ __metadata: languageName: node linkType: hard +"array.prototype.findlast@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.findlast@npm:1.2.5" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/7dffcc665aa965718ad6de7e17ac50df0c5e38798c0a5bf9340cf24feb8594df6ec6f3fcbe714c1577728a1b18b5704b15669474b27bceeca91ef06ce2a23c31 + languageName: node + linkType: hard + +"array.prototype.findlastindex@npm:^1.2.3": + version: 1.2.5 + resolution: "array.prototype.findlastindex@npm:1.2.5" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/7c5c821f357cd53ab6cc305de8086430dd8d7a2485db87b13f843e868055e9582b1fd338f02338f67fc3a1603ceaf9610dd2a470b0b506f9d18934780f95b246 + languageName: node + linkType: hard + +"array.prototype.flat@npm:^1.3.1, array.prototype.flat@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flat@npm:1.3.2" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + es-shim-unscopables: "npm:^1.0.0" + checksum: 10/d9d2f6f27584de92ec7995bc931103e6de722cd2498bdbfc4cba814fc3e52f056050a93be883018811f7c0a35875f5056584a0e940603a5e5934f0279896aebe + languageName: node + linkType: hard + +"array.prototype.flatmap@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flatmap@npm:1.3.2" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + es-shim-unscopables: "npm:^1.0.0" + checksum: 10/33f20006686e0cbe844fde7fd290971e8366c6c5e3380681c2df15738b1df766dd02c7784034aeeb3b037f65c496ee54de665388288edb323a2008bb550f77ea + languageName: node + linkType: hard + "array.prototype.reduce@npm:^1.0.6": version: 1.0.6 resolution: "array.prototype.reduce@npm:1.0.6" @@ -12900,6 +13333,31 @@ __metadata: languageName: node linkType: hard +"array.prototype.toreversed@npm:^1.1.2": + version: 1.1.2 + resolution: "array.prototype.toreversed@npm:1.1.2" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + es-shim-unscopables: "npm:^1.0.0" + checksum: 10/b4076d687ddc22c191863ce105d320cc4b0e1435bfda9ffeeff681682fe88fa6fe30e0d2ae94fa4b2d7fad901e1954ea4f75c1cab217db4848da84a2b5889192 + languageName: node + linkType: hard + +"array.prototype.tosorted@npm:^1.1.4": + version: 1.1.4 + resolution: "array.prototype.tosorted@npm:1.1.4" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.3" + es-errors: "npm:^1.3.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/874694e5d50e138894ff5b853e639c29b0aa42bbd355acda8e8e9cd337f1c80565f21edc15e8c727fa4c0877fd9d8783c575809e440cc4d2d19acaa048bf967d + languageName: node + linkType: hard + "arraybuffer.prototype.slice@npm:^1.0.2": version: 1.0.2 resolution: "arraybuffer.prototype.slice@npm:1.0.2" @@ -12915,6 +13373,22 @@ __metadata: languageName: node linkType: hard +"arraybuffer.prototype.slice@npm:^1.0.3": + version: 1.0.3 + resolution: "arraybuffer.prototype.slice@npm:1.0.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.1" + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.22.3" + es-errors: "npm:^1.2.1" + get-intrinsic: "npm:^1.2.3" + is-array-buffer: "npm:^3.0.4" + is-shared-array-buffer: "npm:^1.0.2" + checksum: 10/0221f16c1e3ec7b67da870ee0e1f12b825b5f9189835392b59a22990f715827561a4f4cd5330dc7507de272d8df821be6cd4b0cb569babf5ea4be70e365a2f3d + languageName: node + linkType: hard + "arrify@npm:^1.0.1": version: 1.0.1 resolution: "arrify@npm:1.0.1" @@ -12965,6 +13439,13 @@ __metadata: languageName: node linkType: hard +"ast-types-flow@npm:^0.0.8": + version: 0.0.8 + resolution: "ast-types-flow@npm:0.0.8" + checksum: 10/85a1c24af4707871c27cfe456bd2ff7fcbe678f3d1c878ac968c9557735a171a17bdcc8c8f903ceab3fc3c49d5b3da2194e6ab0a6be7fec0e133fa028f21ba1b + languageName: node + linkType: hard + "astring@npm:^1.8.0": version: 1.8.6 resolution: "astring@npm:1.8.6" @@ -13114,6 +13595,13 @@ __metadata: languageName: node linkType: hard +"axe-core@npm:^4.9.1": + version: 4.9.1 + resolution: "axe-core@npm:4.9.1" + checksum: 10/9d4944f6d3289428e1c6b41a80516f6558a960889f59c3c00f0fb88b955eda81edf9ca377c2cbc2a775f4003596d2aeaa35acca5aad3e1fc6b3d1e26e82b02cc + languageName: node + linkType: hard + "axios@npm:^0.25.0": version: 0.25.0 resolution: "axios@npm:0.25.0" @@ -13123,6 +13611,16 @@ __metadata: languageName: node linkType: hard +"axios@npm:^0.27.2": + version: 0.27.2 + resolution: "axios@npm:0.27.2" + dependencies: + follow-redirects: "npm:^1.14.9" + form-data: "npm:^4.0.0" + checksum: 10/2efaf18dd0805f7bc772882bc86f004abd92d51007b54c5081f74db0d08ce3593e2c010261896d25a14318eeaa2e966fd825e34f810e8a3339dc64b9d177cf70 + languageName: node + linkType: hard + "axios@npm:^1.4.0": version: 1.6.8 resolution: "axios@npm:1.6.8" @@ -13145,6 +13643,15 @@ __metadata: languageName: node linkType: hard +"axobject-query@npm:~3.1.1": + version: 3.1.1 + resolution: "axobject-query@npm:3.1.1" + dependencies: + deep-equal: "npm:^2.0.5" + checksum: 10/3a3931bc419219e78d6438bc457c191e4c972caddae2be7eaa94615269209f1d283aaaece706a69742e5bcf27df99cc75eee97a5e366a06a9f2bdab1a79748c7 + languageName: node + linkType: hard + "babel-jest@npm:^27.4.6, babel-jest@npm:^27.5.1": version: 27.5.1 resolution: "babel-jest@npm:27.5.1" @@ -13906,7 +14413,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.5, call-bind@npm:^1.0.7": +"call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": version: 1.0.7 resolution: "call-bind@npm:1.0.7" dependencies: @@ -14318,20 +14825,13 @@ __metadata: languageName: node linkType: hard -"chroma-js@npm:^2.4.2||^3.0.0": +"chroma-js@npm:^2.4.2||^3.0.0, chroma-js@npm:^3.0.0": version: 3.1.2 resolution: "chroma-js@npm:3.1.2" checksum: 10/ea09b27d04b477a8fdc3314bfa6dc8de05feab47999ff1acdeba6cf3c32c76338baa5b4562d949d33d8adeca57754e5ea591c15b9985d65583c8771e7e07c2ef languageName: node linkType: hard -"chroma-js@npm:^3.0.0": - version: 3.1.1 - resolution: "chroma-js@npm:3.1.1" - checksum: 10/71018790cef594904ee7d765ef22e45c781f2019d3aa878616a49a3c5e0d85179c5ceaab418d75f48e0089b597b57e99264320cdf3ac60deb2a067a9e5067f12 - languageName: node - linkType: hard - "chrome-trace-event@npm:^1.0.2, chrome-trace-event@npm:^1.0.3": version: 1.0.3 resolution: "chrome-trace-event@npm:1.0.3" @@ -15267,6 +15767,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:^3.0.6, cross-fetch@npm:^3.1.5": + version: 3.1.8 + resolution: "cross-fetch@npm:3.1.8" + dependencies: + node-fetch: "npm:^2.6.12" + checksum: 10/ac8c4ca87d2ac0e17a19b6a293a67ee8934881aee5ec9a5a8323c30e9a9a60a0f5291d3c0d633ec2a2f970cbc60978d628804dfaf03add92d7e720b6d37f392c + languageName: node + linkType: hard + "cross-fetch@npm:^4.0.0": version: 4.0.0 resolution: "cross-fetch@npm:4.0.0" @@ -15276,7 +15785,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:7.0.3, cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:7.0.3, cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -15318,6 +15827,15 @@ __metadata: languageName: node linkType: hard +"css-box-model@npm:^1.2.0": + version: 1.2.1 + resolution: "css-box-model@npm:1.2.1" + dependencies: + tiny-invariant: "npm:^1.0.6" + checksum: 10/54778883733e59058b5de983cf442b9db6c1494543d4d84a3defd05b51b991a1865f59e4ae424e733af2aa1fdb6e0bd905cb73db0e7e548fbd89853859fedc81 + languageName: node + linkType: hard + "css-color-names@npm:0.0.4, css-color-names@npm:^0.0.4": version: 0.0.4 resolution: "css-color-names@npm:0.0.4" @@ -16285,6 +16803,13 @@ __metadata: languageName: node linkType: hard +"damerau-levenshtein@npm:^1.0.8": + version: 1.0.8 + resolution: "damerau-levenshtein@npm:1.0.8" + checksum: 10/f4eba1c90170f96be25d95fa3857141b5f81e254f7e4d530da929217b19990ea9a0390fc53d3c1cafac9152fda78e722ea4894f765cf6216be413b5af1fbf821 + languageName: node + linkType: hard + "dargs@npm:^4.0.1": version: 4.1.0 resolution: "dargs@npm:4.1.0" @@ -16321,6 +16846,39 @@ __metadata: languageName: node linkType: hard +"data-view-buffer@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-buffer@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10/5919a39a18ee919573336158fd162fdf8ada1bc23a139f28543fd45fac48e0ea4a3ad3bfde91de124d4106e65c4a7525f6a84c20ba0797ec890a77a96d13a82a + languageName: node + linkType: hard + +"data-view-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10/f33c65e58d8d0432ad79761f2e8a579818d724b5dc6dc4e700489b762d963ab30873c0f1c37d8f2ed12ef51c706d1195f64422856d25f067457aeec50cc40aac + languageName: node + linkType: hard + +"data-view-byte-offset@npm:^1.0.0": + version: 1.0.0 + resolution: "data-view-byte-offset@npm:1.0.0" + dependencies: + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10/96f34f151bf02affb7b9f98762fb7aca1dd5f4553cb57b80bce750ca609c15d33ca659568ef1d422f7e35680736cbccb893a3d4b012760c758c1446bbdc4c6db + languageName: node + linkType: hard + "date-fns-tz@npm:^2.0.0": version: 2.0.1 resolution: "date-fns-tz@npm:2.0.1" @@ -16376,7 +16934,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.0.1, debug@npm:^3.1.0": +"debug@npm:^3.0.1, debug@npm:^3.1.0, debug@npm:^3.2.7": version: 3.2.7 resolution: "debug@npm:3.2.7" dependencies: @@ -16476,6 +17034,20 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^1.1.1": + version: 1.1.2 + resolution: "deep-equal@npm:1.1.2" + dependencies: + is-arguments: "npm:^1.1.1" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + regexp.prototype.flags: "npm:^1.5.1" + checksum: 10/c9d2ed2a0d93a2ee286bdb320cd51c78cd4c310b2161d1ede6476b67ca1d73860e7ff63b10927830aa4b9eca2a48073cfa54c8c4a1b2246397bda618c2138e97 + languageName: node + linkType: hard + "deep-equal@npm:^2.0.5": version: 2.2.2 resolution: "deep-equal@npm:2.2.2" @@ -16535,7 +17107,7 @@ __metadata: languageName: node linkType: hard -"deep-is@npm:~0.1.3": +"deep-is@npm:^0.1.3, deep-is@npm:~0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" checksum: 10/ec12d074aef5ae5e81fa470b9317c313142c9e8e2afe3f8efa124db309720db96d1d222b82b84c834e5f87e7a614b44a4684b6683583118b87c833b3be40d4d8 @@ -16627,7 +17199,7 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": +"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" dependencies: @@ -16821,6 +17393,24 @@ __metadata: languageName: node linkType: hard +"doctrine@npm:^2.1.0": + version: 2.1.0 + resolution: "doctrine@npm:2.1.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10/555684f77e791b17173ea86e2eea45ef26c22219cb64670669c4f4bebd26dbc95cd90ec1f4159e9349a6bb9eb892ce4dde8cd0139e77bedd8bf4518238618474 + languageName: node + linkType: hard + +"doctrine@npm:^3.0.0": + version: 3.0.0 + resolution: "doctrine@npm:3.0.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10/b4b28f1df5c563f7d876e7461254a4597b8cabe915abe94d7c5d1633fed263fcf9a85e8d3836591fc2d040108e822b0d32758e5ec1fe31c590dc7e08086e3e48 + languageName: node + linkType: hard + "dom-accessibility-api@npm:^0.5.9": version: 0.5.16 resolution: "dom-accessibility-api@npm:0.5.16" @@ -16884,6 +17474,13 @@ __metadata: languageName: node linkType: hard +"dom4@npm:^2.1.5": + version: 2.1.6 + resolution: "dom4@npm:2.1.6" + checksum: 10/9f49eb82dc013016d5dae8371d5c77c3a28e213be4d813c1e3f94d9c8af57d6130d3063af7214d594a127cd5c61c16f6f6d15acb0069fae01b5e70266fa958b9 + languageName: node + linkType: hard + "domelementtype@npm:1, domelementtype@npm:^1.3.1": version: 1.3.1 resolution: "domelementtype@npm:1.3.1" @@ -17392,6 +17989,60 @@ __metadata: languageName: node linkType: hard +"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3": + version: 1.23.3 + resolution: "es-abstract@npm:1.23.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.1" + arraybuffer.prototype.slice: "npm:^1.0.3" + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + data-view-buffer: "npm:^1.0.1" + data-view-byte-length: "npm:^1.0.1" + data-view-byte-offset: "npm:^1.0.0" + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-set-tostringtag: "npm:^2.0.3" + es-to-primitive: "npm:^1.2.1" + function.prototype.name: "npm:^1.1.6" + get-intrinsic: "npm:^1.2.4" + get-symbol-description: "npm:^1.0.2" + globalthis: "npm:^1.0.3" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + has-proto: "npm:^1.0.3" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.2" + internal-slot: "npm:^1.0.7" + is-array-buffer: "npm:^3.0.4" + is-callable: "npm:^1.2.7" + is-data-view: "npm:^1.0.1" + is-negative-zero: "npm:^2.0.3" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.3" + is-string: "npm:^1.0.7" + is-typed-array: "npm:^1.1.13" + is-weakref: "npm:^1.0.2" + object-inspect: "npm:^1.13.1" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.5" + regexp.prototype.flags: "npm:^1.5.2" + safe-array-concat: "npm:^1.1.2" + safe-regex-test: "npm:^1.0.3" + string.prototype.trim: "npm:^1.2.9" + string.prototype.trimend: "npm:^1.0.8" + string.prototype.trimstart: "npm:^1.0.8" + typed-array-buffer: "npm:^1.0.2" + typed-array-byte-length: "npm:^1.0.1" + typed-array-byte-offset: "npm:^1.0.2" + typed-array-length: "npm:^1.0.6" + unbox-primitive: "npm:^1.0.2" + which-typed-array: "npm:^1.1.15" + checksum: 10/2da795a6a1ac5fc2c452799a409acc2e3692e06dc6440440b076908617188899caa562154d77263e3053bcd9389a07baa978ab10ac3b46acc399bd0c77be04cb + languageName: node + linkType: hard + "es-array-method-boxes-properly@npm:^1.0.0": version: 1.0.0 resolution: "es-array-method-boxes-properly@npm:1.0.0" @@ -17408,7 +18059,7 @@ __metadata: languageName: node linkType: hard -"es-errors@npm:^1.3.0": +"es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 @@ -17432,6 +18083,28 @@ __metadata: languageName: node linkType: hard +"es-iterator-helpers@npm:^1.0.19": + version: 1.0.19 + resolution: "es-iterator-helpers@npm:1.0.19" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.3" + es-errors: "npm:^1.3.0" + es-set-tostringtag: "npm:^2.0.3" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + globalthis: "npm:^1.0.3" + has-property-descriptors: "npm:^1.0.2" + has-proto: "npm:^1.0.3" + has-symbols: "npm:^1.0.3" + internal-slot: "npm:^1.0.7" + iterator.prototype: "npm:^1.1.2" + safe-array-concat: "npm:^1.1.2" + checksum: 10/980a8081cf6798fe17fcea193b0448d784d72d76aca7240b10813207c67e3dc0d8a23992263870c4fc291da5a946935b0c56dec4fa1a9de8fee0165e4fa1fc58 + languageName: node + linkType: hard + "es-module-lexer@npm:^1.0.0": version: 1.4.1 resolution: "es-module-lexer@npm:1.4.1" @@ -17446,6 +18119,15 @@ __metadata: languageName: node linkType: hard +"es-object-atoms@npm:^1.0.0": + version: 1.0.0 + resolution: "es-object-atoms@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/f8910cf477e53c0615f685c5c96210591841850871b81924fcf256bfbaa68c254457d994a4308c60d15b20805e7f61ce6abc669375e01a5349391a8c1767584f + languageName: node + linkType: hard + "es-set-tostringtag@npm:^2.0.1": version: 2.0.1 resolution: "es-set-tostringtag@npm:2.0.1" @@ -17457,6 +18139,26 @@ __metadata: languageName: node linkType: hard +"es-set-tostringtag@npm:^2.0.3": + version: 2.0.3 + resolution: "es-set-tostringtag@npm:2.0.3" + dependencies: + get-intrinsic: "npm:^1.2.4" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.1" + checksum: 10/7227fa48a41c0ce83e0377b11130d324ac797390688135b8da5c28994c0165be8b252e15cd1de41e1325e5a5412511586960213e88f9ab4a5e7d028895db5129 + languageName: node + linkType: hard + +"es-shim-unscopables@npm:^1.0.0, es-shim-unscopables@npm:^1.0.2": + version: 1.0.2 + resolution: "es-shim-unscopables@npm:1.0.2" + dependencies: + hasown: "npm:^2.0.0" + checksum: 10/6d3bf91f658a27cc7217cd32b407a0d714393a84d125ad576319b9e83a893bea165cf41270c29e9ceaa56d3cf41608945d7e2a2c31fd51c0009b0c31402b91c7 + languageName: node + linkType: hard + "es-to-primitive@npm:^1.2.1": version: 1.2.1 resolution: "es-to-primitive@npm:1.2.1" @@ -18054,6 +18756,159 @@ __metadata: languageName: node linkType: hard +"eslint-config-next@npm:^12.0.8": + version: 12.3.4 + resolution: "eslint-config-next@npm:12.3.4" + dependencies: + "@next/eslint-plugin-next": "npm:12.3.4" + "@rushstack/eslint-patch": "npm:^1.1.3" + "@typescript-eslint/parser": "npm:^5.21.0" + eslint-import-resolver-node: "npm:^0.3.6" + eslint-import-resolver-typescript: "npm:^2.7.1" + eslint-plugin-import: "npm:^2.26.0" + eslint-plugin-jsx-a11y: "npm:^6.5.1" + eslint-plugin-react: "npm:^7.31.7" + eslint-plugin-react-hooks: "npm:^4.5.0" + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 + typescript: ">=3.3.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/53cd24d7b764fe382812a5e76571083fe59e892ac88ac5ccddf171e261f5a3ea36cb1c34283f97569c97a4bae51ece5252d5aa71fd130d31ada94310dc4147ee + languageName: node + linkType: hard + +"eslint-import-resolver-node@npm:^0.3.6, eslint-import-resolver-node@npm:^0.3.9": + version: 0.3.9 + resolution: "eslint-import-resolver-node@npm:0.3.9" + dependencies: + debug: "npm:^3.2.7" + is-core-module: "npm:^2.13.0" + resolve: "npm:^1.22.4" + checksum: 10/d52e08e1d96cf630957272e4f2644dcfb531e49dcfd1edd2e07e43369eb2ec7a7d4423d417beee613201206ff2efa4eb9a582b5825ee28802fc7c71fcd53ca83 + languageName: node + linkType: hard + +"eslint-import-resolver-typescript@npm:^2.7.1": + version: 2.7.1 + resolution: "eslint-import-resolver-typescript@npm:2.7.1" + dependencies: + debug: "npm:^4.3.4" + glob: "npm:^7.2.0" + is-glob: "npm:^4.0.3" + resolve: "npm:^1.22.0" + tsconfig-paths: "npm:^3.14.1" + peerDependencies: + eslint: "*" + eslint-plugin-import: "*" + checksum: 10/4a688440395673492b2e28347ba2173542dcec0bc597065469191be213e30f65b316697a950abf492f4191365626fb13231080c6ca326044df087f57d163e6c6 + languageName: node + linkType: hard + +"eslint-module-utils@npm:^2.8.0": + version: 2.8.1 + resolution: "eslint-module-utils@npm:2.8.1" + dependencies: + debug: "npm:^3.2.7" + peerDependenciesMeta: + eslint: + optional: true + checksum: 10/3e7892c0a984c963632da56b30ccf8254c29b535467138f91086c2ecdb2ebd10e2be61b54e553f30e5abf1d14d47a7baa0dac890e3a658fd3cd07dca63afbe6d + languageName: node + linkType: hard + +"eslint-plugin-import@npm:^2.26.0": + version: 2.29.1 + resolution: "eslint-plugin-import@npm:2.29.1" + dependencies: + array-includes: "npm:^3.1.7" + array.prototype.findlastindex: "npm:^1.2.3" + array.prototype.flat: "npm:^1.3.2" + array.prototype.flatmap: "npm:^1.3.2" + debug: "npm:^3.2.7" + doctrine: "npm:^2.1.0" + eslint-import-resolver-node: "npm:^0.3.9" + eslint-module-utils: "npm:^2.8.0" + hasown: "npm:^2.0.0" + is-core-module: "npm:^2.13.1" + is-glob: "npm:^4.0.3" + minimatch: "npm:^3.1.2" + object.fromentries: "npm:^2.0.7" + object.groupby: "npm:^1.0.1" + object.values: "npm:^1.1.7" + semver: "npm:^6.3.1" + tsconfig-paths: "npm:^3.15.0" + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + checksum: 10/5865f05c38552145423c535326ec9a7113ab2305c7614c8b896ff905cfabc859c8805cac21e979c9f6f742afa333e6f62f812eabf891a7e8f5f0b853a32593c1 + languageName: node + linkType: hard + +"eslint-plugin-jsx-a11y@npm:^6.5.1": + version: 6.9.0 + resolution: "eslint-plugin-jsx-a11y@npm:6.9.0" + dependencies: + aria-query: "npm:~5.1.3" + array-includes: "npm:^3.1.8" + array.prototype.flatmap: "npm:^1.3.2" + ast-types-flow: "npm:^0.0.8" + axe-core: "npm:^4.9.1" + axobject-query: "npm:~3.1.1" + damerau-levenshtein: "npm:^1.0.8" + emoji-regex: "npm:^9.2.2" + es-iterator-helpers: "npm:^1.0.19" + hasown: "npm:^2.0.2" + jsx-ast-utils: "npm:^3.3.5" + language-tags: "npm:^1.0.9" + minimatch: "npm:^3.1.2" + object.fromentries: "npm:^2.0.8" + safe-regex-test: "npm:^1.0.3" + string.prototype.includes: "npm:^2.0.0" + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + checksum: 10/00a854a1a1a7ca52c216e83a574d5a65fc150243afcababfbf1657c5ffff1f076b9bd3d87029bb6432bfaa36d23e16c1e8b59671d0580bbb72e14860ee1bec9a + languageName: node + linkType: hard + +"eslint-plugin-react-hooks@npm:^4.5.0": + version: 4.6.2 + resolution: "eslint-plugin-react-hooks@npm:4.6.2" + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + checksum: 10/5a0680941f34e70cf505bcb6082df31a3e445d193ee95a88ff3483041eb944f4cefdaf7e81b0eb1feb4eeceee8c7c6ddb8a2a6e8c4c0388514a42e16ac7b7a69 + languageName: node + linkType: hard + +"eslint-plugin-react@npm:^7.31.7": + version: 7.34.4 + resolution: "eslint-plugin-react@npm:7.34.4" + dependencies: + array-includes: "npm:^3.1.8" + array.prototype.findlast: "npm:^1.2.5" + array.prototype.flatmap: "npm:^1.3.2" + array.prototype.toreversed: "npm:^1.1.2" + array.prototype.tosorted: "npm:^1.1.4" + doctrine: "npm:^2.1.0" + es-iterator-helpers: "npm:^1.0.19" + estraverse: "npm:^5.3.0" + hasown: "npm:^2.0.2" + jsx-ast-utils: "npm:^2.4.1 || ^3.0.0" + minimatch: "npm:^3.1.2" + object.entries: "npm:^1.1.8" + object.fromentries: "npm:^2.0.8" + object.values: "npm:^1.2.0" + prop-types: "npm:^15.8.1" + resolve: "npm:^2.0.0-next.5" + semver: "npm:^6.3.1" + string.prototype.matchall: "npm:^4.0.11" + string.prototype.repeat: "npm:^1.0.0" + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + checksum: 10/9ad948a109bcb5b1f2f3a8099fa9eab3bee3eb996786f783d1f17208f44c8e3dcbc4f9dab84aa70062c03a053a48a333ca03fee21d406d5732291d204e43d9ca + languageName: node + linkType: hard + "eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -18064,13 +18919,82 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": +"eslint-scope@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10/5c660fb905d5883ad018a6fea2b49f3cb5b1cbf2cd4bd08e98646e9864f9bc2c74c0839bed2d292e90a4a328833accc197c8f0baed89cbe8d605d6f918465491 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 10/3f357c554a9ea794b094a09bd4187e5eacd1bc0d0653c3adeb87962c548e6a1ab8f982b86963ae1337f5d976004146536dcee5d0e2806665b193fbfbf1a9231b languageName: node linkType: hard +"eslint@npm:^8.7.0": + version: 8.57.0 + resolution: "eslint@npm:8.57.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.6.1" + "@eslint/eslintrc": "npm:^2.1.4" + "@eslint/js": "npm:8.57.0" + "@humanwhocodes/config-array": "npm:^0.11.14" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@nodelib/fs.walk": "npm:^1.2.8" + "@ungap/structured-clone": "npm:^1.2.0" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.2" + debug: "npm:^4.3.2" + doctrine: "npm:^3.0.0" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^7.2.2" + eslint-visitor-keys: "npm:^3.4.3" + espree: "npm:^9.6.1" + esquery: "npm:^1.4.2" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^6.0.1" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + globals: "npm:^13.19.0" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + is-path-inside: "npm:^3.0.3" + js-yaml: "npm:^4.1.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + levn: "npm:^0.4.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + strip-ansi: "npm:^6.0.1" + text-table: "npm:^0.2.0" + bin: + eslint: bin/eslint.js + checksum: 10/00496e218b23747a7a9817bf58b522276d0dc1f2e546dceb4eea49f9871574088f72f1f069a6b560ef537efa3a75261b8ef70e51ef19033da1cc4c86a755ef15 + languageName: node + linkType: hard + +"espree@npm:^9.6.0, espree@npm:^9.6.1": + version: 9.6.1 + resolution: "espree@npm:9.6.1" + dependencies: + acorn: "npm:^8.9.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^3.4.1" + checksum: 10/255ab260f0d711a54096bdeda93adff0eadf02a6f9b92f02b323e83a2b7fc258797919437ad331efec3930475feb0142c5ecaaf3cdab4befebd336d47d3f3134 + languageName: node + linkType: hard + "esprima@npm:^3.1.3": version: 3.1.3 resolution: "esprima@npm:3.1.3" @@ -18091,6 +19015,15 @@ __metadata: languageName: node linkType: hard +"esquery@npm:^1.4.2": + version: 1.6.0 + resolution: "esquery@npm:1.6.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10/c587fb8ec9ed83f2b1bc97cf2f6854cc30bf784a79d62ba08c6e358bf22280d69aee12827521cf38e69ae9761d23fb7fde593ce315610f85655c139d99b05e5a + languageName: node + linkType: hard + "esrecurse@npm:^4.3.0": version: 4.3.0 resolution: "esrecurse@npm:4.3.0" @@ -18107,7 +19040,7 @@ __metadata: languageName: node linkType: hard -"estraverse@npm:^5.2.0": +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0, estraverse@npm:^5.3.0": version: 5.3.0 resolution: "estraverse@npm:5.3.0" checksum: 10/37cbe6e9a68014d34dbdc039f90d0baf72436809d02edffcc06ba3c2a12eb298048f877511353b130153e532aac8d68ba78430c0dd2f44806ebc7c014b01585e @@ -18567,7 +19500,7 @@ __metadata: languageName: node linkType: hard -"fast-levenshtein@npm:~2.0.6": +"fast-levenshtein@npm:^2.0.6, fast-levenshtein@npm:~2.0.6": version: 2.0.6 resolution: "fast-levenshtein@npm:2.0.6" checksum: 10/eb7e220ecf2bab5159d157350b81d01f75726a4382f5a9266f42b9150c4523b9795f7f5d9fbbbeaeac09a441b2369f05ee02db48ea938584205530fe5693cfe1 @@ -18699,6 +19632,15 @@ __metadata: languageName: node linkType: hard +"file-entry-cache@npm:^6.0.1": + version: 6.0.1 + resolution: "file-entry-cache@npm:6.0.1" + dependencies: + flat-cache: "npm:^3.0.4" + checksum: 10/099bb9d4ab332cb93c48b14807a6918a1da87c45dce91d4b61fd40e6505d56d0697da060cb901c729c90487067d93c9243f5da3dc9c41f0358483bfdebca736b + languageName: node + linkType: hard + "file-loader@npm:^6.2.0": version: 6.2.0 resolution: "file-loader@npm:6.2.0" @@ -18836,6 +19778,16 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: "npm:^6.0.0" + path-exists: "npm:^4.0.0" + checksum: 10/07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 + languageName: node + linkType: hard + "find-versions@npm:^3.0.0": version: 3.2.0 resolution: "find-versions@npm:3.2.0" @@ -18854,6 +19806,17 @@ __metadata: languageName: node linkType: hard +"flat-cache@npm:^3.0.4": + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.3" + rimraf: "npm:^3.0.2" + checksum: 10/02381c6ece5e9fa5b826c9bbea481d7fd77645d96e4b0b1395238124d581d10e56f17f723d897b6d133970f7a57f0fab9148cbbb67237a0a0ffe794ba60c0c70 + languageName: node + linkType: hard + "flat@npm:^5.0.2": version: 5.0.2 resolution: "flat@npm:5.0.2" @@ -18863,6 +19826,13 @@ __metadata: languageName: node linkType: hard +"flatted@npm:^3.2.9": + version: 3.3.1 + resolution: "flatted@npm:3.3.1" + checksum: 10/7b8376061d5be6e0d3658bbab8bde587647f68797cf6bfeae9dea0e5137d9f27547ab92aaff3512dd9d1299086a6d61be98e9d48a56d17531b634f77faadbc49 + languageName: node + linkType: hard + "flush-write-stream@npm:^1.0.0": version: 1.1.1 resolution: "flush-write-stream@npm:1.1.1" @@ -18892,7 +19862,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.14.7": +"follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.9": version: 1.15.3 resolution: "follow-redirects@npm:1.15.3" peerDependenciesMeta: @@ -19154,7 +20124,7 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.6": +"function.prototype.name@npm:^1.1.5, function.prototype.name@npm:^1.1.6": version: 1.1.6 resolution: "function.prototype.name@npm:1.1.6" dependencies: @@ -19334,7 +20304,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4": +"get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" dependencies: @@ -19457,6 +20427,17 @@ __metadata: languageName: node linkType: hard +"get-symbol-description@npm:^1.0.2": + version: 1.0.2 + resolution: "get-symbol-description@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.5" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + checksum: 10/e1cb53bc211f9dbe9691a4f97a46837a553c4e7caadd0488dc24ac694db8a390b93edd412b48dcdd0b4bbb4c595de1709effc75fc87c0839deedc6968f5bd973 + languageName: node + linkType: hard + "get-tsconfig@npm:^4.7.5": version: 4.7.5 resolution: "get-tsconfig@npm:4.7.5" @@ -19544,7 +20525,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^6.0.1": +"glob-parent@npm:^6.0.1, glob-parent@npm:^6.0.2": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" dependencies: @@ -19560,6 +20541,20 @@ __metadata: languageName: node linkType: hard +"glob@npm:7.1.7": + version: 7.1.7 + resolution: "glob@npm:7.1.7" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.0.4" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10/ff5aab0386e9cace92b0550d42085b71013c5ea382982dd7fdded998a559635f61413b8ba6fb7294eef289c83b52f4e64136f888300ac8afc4f3e5623182d6c8 + languageName: node + linkType: hard + "glob@npm:^10.2.2": version: 10.3.6 resolution: "glob@npm:10.3.6" @@ -19575,7 +20570,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.3": +"glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0, glob@npm:^7.2.3": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -19615,6 +20610,15 @@ __metadata: languageName: node linkType: hard +"globals@npm:^13.19.0": + version: 13.24.0 + resolution: "globals@npm:13.24.0" + dependencies: + type-fest: "npm:^0.20.2" + checksum: 10/62c5b1997d06674fc7191d3e01e324d3eda4d65ac9cc4e78329fa3b5c4fd42a0e1c8722822497a6964eee075255ce21ccf1eec2d83f92ef3f06653af4d0ee28e + languageName: node + linkType: hard + "globals@npm:^13.2.0": version: 13.22.0 resolution: "globals@npm:13.22.0" @@ -19777,6 +20781,13 @@ __metadata: languageName: node linkType: hard +"gud@npm:^1.0.0": + version: 1.0.0 + resolution: "gud@npm:1.0.0" + checksum: 10/3e2eb37cf794364077c18f036d6aa259c821c7fd188f2b7935cb00d589d82a41e0ebb1be809e1a93679417f62f1ad0513e745c3cf5329596e489aef8c5e5feae + languageName: node + linkType: hard + "gzip-size@npm:^3.0.0": version: 3.0.0 resolution: "gzip-size@npm:3.0.0" @@ -19941,6 +20952,13 @@ __metadata: languageName: node linkType: hard +"has-proto@npm:^1.0.3": + version: 1.0.3 + resolution: "has-proto@npm:1.0.3" + checksum: 10/0b67c2c94e3bea37db3e412e3c41f79d59259875e636ba471e94c009cdfb1fa82bf045deeffafc7dbb9c148e36cae6b467055aaa5d9fad4316e11b41e3ba551a + languageName: node + linkType: hard + "has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": version: 1.0.3 resolution: "has-symbols@npm:1.0.3" @@ -19989,7 +21007,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0": +"hasown@npm:^2.0.0, hasown@npm:^2.0.1, hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -20886,6 +21904,17 @@ __metadata: languageName: node linkType: hard +"internal-slot@npm:^1.0.7": + version: 1.0.7 + resolution: "internal-slot@npm:1.0.7" + dependencies: + es-errors: "npm:^1.3.0" + hasown: "npm:^2.0.0" + side-channel: "npm:^1.0.4" + checksum: 10/3e66720508831153ecf37d13def9f6856f9f2960989ec8a0a0476c98f887fca9eff0163127466485cb825c900c2d6fc601aa9117b7783b90ffce23a71ea5d053 + languageName: node + linkType: hard + "internmap@npm:1 - 2": version: 2.0.3 resolution: "internmap@npm:2.0.3" @@ -21037,6 +22066,16 @@ __metadata: languageName: node linkType: hard +"is-array-buffer@npm:^3.0.4": + version: 3.0.4 + resolution: "is-array-buffer@npm:3.0.4" + dependencies: + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.1" + checksum: 10/34a26213d981d58b30724ef37a1e0682f4040d580fa9ff58fdfdd3cefcb2287921718c63971c1c404951e7b747c50fdc7caf6e867e951353fa71b369c04c969b + languageName: node + linkType: hard + "is-arrayish@npm:^0.2.1": version: 0.2.1 resolution: "is-arrayish@npm:0.2.1" @@ -21051,6 +22090,15 @@ __metadata: languageName: node linkType: hard +"is-async-function@npm:^2.0.0": + version: 2.0.0 + resolution: "is-async-function@npm:2.0.0" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10/2cf336fbf8cba3badcf526aa3d10384c30bab32615ac4831b74492eb4e843ccb7d8439a119c27f84bcf217d72024e611b1373f870f433b48f3fa57d3d1b863f1 + languageName: node + linkType: hard + "is-bigint@npm:^1.0.1": version: 1.0.4 resolution: "is-bigint@npm:1.0.4" @@ -21152,6 +22200,24 @@ __metadata: languageName: node linkType: hard +"is-core-module@npm:^2.13.1": + version: 2.15.0 + resolution: "is-core-module@npm:2.15.0" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10/70e962543e5d3a97c07cb29144a86792d545a21f28e67da5401d85878a0193d46fbab8d97bc3ca680e2778705dca66e7b6ca840c493497a27ca0e8c5f3ac3d1d + languageName: node + linkType: hard + +"is-data-view@npm:^1.0.1": + version: 1.0.1 + resolution: "is-data-view@npm:1.0.1" + dependencies: + is-typed-array: "npm:^1.1.13" + checksum: 10/4ba4562ac2b2ec005fefe48269d6bd0152785458cd253c746154ffb8a8ab506a29d0cfb3b74af87513843776a88e4981ae25c89457bf640a33748eab1a7216b5 + languageName: node + linkType: hard + "is-date-object@npm:^1.0.1, is-date-object@npm:^1.0.5": version: 1.0.5 resolution: "is-date-object@npm:1.0.5" @@ -21214,6 +22280,15 @@ __metadata: languageName: node linkType: hard +"is-finalizationregistry@npm:^1.0.2": + version: 1.0.2 + resolution: "is-finalizationregistry@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.2" + checksum: 10/1b8e9e1bf2075e862315ef9d38ce6d39c43ca9d81d46f73b34473506992f4b0fbaadb47ec9b420a5e76afe3f564d9f1f0d9b552ef272cc2395e0f21d743c9c29 + languageName: node + linkType: hard + "is-fullwidth-code-point@npm:^1.0.0": version: 1.0.0 resolution: "is-fullwidth-code-point@npm:1.0.0" @@ -21251,7 +22326,7 @@ __metadata: languageName: node linkType: hard -"is-generator-function@npm:^1.0.7": +"is-generator-function@npm:^1.0.10, is-generator-function@npm:^1.0.7": version: 1.0.10 resolution: "is-generator-function@npm:1.0.10" dependencies: @@ -21260,7 +22335,7 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" dependencies: @@ -21349,6 +22424,13 @@ __metadata: languageName: node linkType: hard +"is-negative-zero@npm:^2.0.3": + version: 2.0.3 + resolution: "is-negative-zero@npm:2.0.3" + checksum: 10/8fe5cffd8d4fb2ec7b49d657e1691889778d037494c6f40f4d1a524cadd658b4b53ad7b6b73a59bcb4b143ae9a3d15829af864b2c0f9d65ac1e678c4c80f17e5 + languageName: node + linkType: hard + "is-npm@npm:^1.0.0": version: 1.0.0 resolution: "is-npm@npm:1.0.0" @@ -21395,6 +22477,13 @@ __metadata: languageName: node linkType: hard +"is-path-inside@npm:^3.0.3": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: 10/abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 + languageName: node + linkType: hard + "is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" @@ -21504,6 +22593,15 @@ __metadata: languageName: node linkType: hard +"is-shared-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "is-shared-array-buffer@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.7" + checksum: 10/bc5402900dc62b96ebb2548bf5b0a0bcfacc2db122236fe3ab3b3e3c884293a0d5eb777e73f059bcbf8dc8563bb65eae972fee0fb97e38a9ae27c8678f62bcfe + languageName: node + linkType: hard + "is-stream@npm:^1.0.0, is-stream@npm:^1.0.1, is-stream@npm:^1.1.0": version: 1.1.0 resolution: "is-stream@npm:1.1.0" @@ -21561,6 +22659,15 @@ __metadata: languageName: node linkType: hard +"is-typed-array@npm:^1.1.13": + version: 1.1.13 + resolution: "is-typed-array@npm:1.1.13" + dependencies: + which-typed-array: "npm:^1.1.14" + checksum: 10/f850ba08286358b9a11aee6d93d371a45e3c59b5953549ee1c1a9a55ba5c1dd1bd9952488ae194ad8f32a9cf5e79c8fa5f0cc4d78c00720aa0bbcf238b38062d + languageName: node + linkType: hard + "is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": version: 1.0.0 resolution: "is-typedarray@npm:1.0.0" @@ -21736,6 +22843,19 @@ __metadata: languageName: node linkType: hard +"iterator.prototype@npm:^1.1.2": + version: 1.1.2 + resolution: "iterator.prototype@npm:1.1.2" + dependencies: + define-properties: "npm:^1.2.1" + get-intrinsic: "npm:^1.2.1" + has-symbols: "npm:^1.0.3" + reflect.getprototypeof: "npm:^1.0.4" + set-function-name: "npm:^2.0.1" + checksum: 10/b5013967ad8f28c9ca1be8e159eb10f591b8e46deae87476fe39d668c04374fe9158c815e8b6d2f45885b0a3fd842a8ba13f497ec762b3a0eff49bec278670b1 + languageName: node + linkType: hard + "jackspeak@npm:^2.0.3": version: 2.3.3 resolution: "jackspeak@npm:2.3.3" @@ -22461,6 +23581,13 @@ __metadata: languageName: node linkType: hard +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: 10/12786c2e2f22c27439e6db0532ba321f1d0617c27ad8cb1c352a0e9249a50182fd1ba8b52a18899291604b0c32eafa8afd09e51203f19109a0537f68db2b652d + languageName: node + linkType: hard + "json-stable-stringify@npm:^1.0.2": version: 1.1.1 resolution: "json-stable-stringify@npm:1.1.1" @@ -22480,7 +23607,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.1": +"json5@npm:^1.0.1, json5@npm:^1.0.2": version: 1.0.2 resolution: "json5@npm:1.0.2" dependencies: @@ -22563,6 +23690,18 @@ __metadata: languageName: node linkType: hard +"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.5": + version: 3.3.5 + resolution: "jsx-ast-utils@npm:3.3.5" + dependencies: + array-includes: "npm:^3.1.6" + array.prototype.flat: "npm:^1.3.1" + object.assign: "npm:^4.1.4" + object.values: "npm:^1.1.6" + checksum: 10/b61d44613687dfe4cc8ad4b4fbf3711bf26c60b8d5ed1f494d723e0808415c59b24a7c0ed8ab10736a40ff84eef38cbbfb68b395e05d31117b44ffc59d31edfc + languageName: node + linkType: hard + "kdbush@npm:^4.0.1, kdbush@npm:^4.0.2": version: 4.0.2 resolution: "kdbush@npm:4.0.2" @@ -22579,6 +23718,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^4.5.3": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: "npm:3.0.1" + checksum: 10/167eb6ef64cc84b6fa0780ee50c9de456b422a1e18802209234f7c2cf7eae648c7741f32e50d7e24ccb22b24c13154070b01563d642755b156c357431a191e75 + languageName: node + linkType: hard + "kind-of@npm:^6.0.0, kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -22673,6 +23821,22 @@ __metadata: languageName: node linkType: hard +"language-subtag-registry@npm:^0.3.20": + version: 0.3.23 + resolution: "language-subtag-registry@npm:0.3.23" + checksum: 10/fe13ed74ab9f862db8e5747b98cc9aa08d52a19f85b5cdb4975cd364c8539bd2da3380e4560d2dbbd728ec33dff8a4b4421fcb2e5b1b1bdaa21d16f91a54d0d4 + languageName: node + linkType: hard + +"language-tags@npm:^1.0.9": + version: 1.0.9 + resolution: "language-tags@npm:1.0.9" + dependencies: + language-subtag-registry: "npm:^0.3.20" + checksum: 10/d3a7c14b694e67f519153d6df6cb200681648d38d623c3bfa9d6a66a5ec5493628acb88e9df5aceef3cf1902ab263a205e7d59ee4cf1d6bb67e707b83538bd6d + languageName: node + linkType: hard + "latest-version@npm:^3.0.0": version: 3.1.0 resolution: "latest-version@npm:3.1.0" @@ -22727,6 +23891,16 @@ __metadata: languageName: node linkType: hard +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: "npm:^1.2.1" + type-check: "npm:~0.4.0" + checksum: 10/2e4720ff79f21ae08d42374b0a5c2f664c5be8b6c8f565bb4e1315c96ed3a8acaa9de788ffed82d7f2378cf36958573de07ef92336cb5255ed74d08b8318c9ee + languageName: node + linkType: hard + "levn@npm:~0.3.0": version: 0.3.0 resolution: "levn@npm:0.3.0" @@ -23281,6 +24455,15 @@ __metadata: languageName: node linkType: hard +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: "npm:^5.0.0" + checksum: 10/72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a + languageName: node + linkType: hard + "lock-verify@npm:^2.0.2, lock-verify@npm:^2.1.0, lock-verify@npm:^2.2.2": version: 2.2.2 resolution: "lock-verify@npm:2.2.2" @@ -23855,7 +25038,7 @@ __metadata: languageName: node linkType: hard -"mapbox-gl@npm:^2.15.0": +"mapbox-gl@npm:^2.15.0, mapbox-gl@npm:^2.8.2": version: 2.15.0 resolution: "mapbox-gl@npm:2.15.0" dependencies: @@ -24192,7 +25375,7 @@ __metadata: languageName: node linkType: hard -"memoize-one@npm:>=3.1.1 <6, memoize-one@npm:^5.0.0": +"memoize-one@npm:>=3.1.1 <6, memoize-one@npm:^5.0.0, memoize-one@npm:^5.1.1": version: 5.2.1 resolution: "memoize-one@npm:5.2.1" checksum: 10/b7141dc148b5c6fdd51e77ecf0421fd2581681eb8756e0b3dfbd4fe765b5e2b5a6bc90214bb6f19a96b6aed44de17eda3407142a7be9e24ccd0774bbd9874d1b @@ -24840,7 +26023,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -26137,6 +27320,41 @@ __metadata: languageName: node linkType: hard +"object.assign@npm:^4.1.5": + version: 4.1.5 + resolution: "object.assign@npm:4.1.5" + dependencies: + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + has-symbols: "npm:^1.0.3" + object-keys: "npm:^1.1.1" + checksum: 10/dbb22da4cda82e1658349ea62b80815f587b47131b3dd7a4ab7f84190ab31d206bbd8fe7e26ae3220c55b65725ac4529825f6142154211220302aa6b1518045d + languageName: node + linkType: hard + +"object.entries@npm:^1.1.8": + version: 1.1.8 + resolution: "object.entries@npm:1.1.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/2301918fbd1ee697cf6ff7cd94f060c738c0a7d92b22fd24c7c250e9b593642c9707ad2c44d339303c1439c5967d8964251cdfc855f7f6ec55db2dd79e8dc2a7 + languageName: node + linkType: hard + +"object.fromentries@npm:^2.0.7, object.fromentries@npm:^2.0.8": + version: 2.0.8 + resolution: "object.fromentries@npm:2.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + checksum: 10/5b2e80f7af1778b885e3d06aeb335dcc86965e39464671adb7167ab06ac3b0f5dd2e637a90d8ebd7426d69c6f135a4753ba3dd7d0fe2a7030cf718dcb910fd92 + languageName: node + linkType: hard + "object.getownpropertydescriptors@npm:^2.0.3, object.getownpropertydescriptors@npm:^2.1.0": version: 2.1.7 resolution: "object.getownpropertydescriptors@npm:2.1.7" @@ -26150,6 +27368,17 @@ __metadata: languageName: node linkType: hard +"object.groupby@npm:^1.0.1": + version: 1.0.3 + resolution: "object.groupby@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + checksum: 10/44cb86dd2c660434be65f7585c54b62f0425b0c96b5c948d2756be253ef06737da7e68d7106e35506ce4a44d16aa85a413d11c5034eb7ce5579ec28752eb42d0 + languageName: node + linkType: hard + "object.values@npm:^1.1.0": version: 1.1.7 resolution: "object.values@npm:1.1.7" @@ -26161,6 +27390,17 @@ __metadata: languageName: node linkType: hard +"object.values@npm:^1.1.6, object.values@npm:^1.1.7, object.values@npm:^1.2.0": + version: 1.2.0 + resolution: "object.values@npm:1.2.0" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/db2e498019c354428c5dd30d02980d920ac365b155fce4dcf63eb9433f98ccf0f72624309e182ce7cc227c95e45d474e1d483418e60de2293dd23fa3ebe34903 + languageName: node + linkType: hard + "obuf@npm:^1.0.0, obuf@npm:^1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" @@ -26290,6 +27530,20 @@ __metadata: languageName: node linkType: hard +"optionator@npm:^0.9.3": + version: 0.9.4 + resolution: "optionator@npm:0.9.4" + dependencies: + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + word-wrap: "npm:^1.2.5" + checksum: 10/a8398559c60aef88d7f353a4f98dcdff6090a4e70f874c827302bf1213d9106a1c4d5fcb68dacb1feb3c30a04c4102f41047aa55d4c576b863d6fc876e001af6 + languageName: node + linkType: hard + "ordered-binary@npm:^1.4.0": version: 1.4.1 resolution: "ordered-binary@npm:1.4.1" @@ -26393,6 +27647,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10/7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 + languageName: node + linkType: hard + "p-limit@npm:^5.0.0": version: 5.0.0 resolution: "p-limit@npm:5.0.0" @@ -26429,6 +27692,15 @@ __metadata: languageName: node linkType: hard +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10/1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 + languageName: node + linkType: hard + "p-map@npm:^2.0.0": version: 2.1.0 resolution: "p-map@npm:2.1.0" @@ -27031,6 +28303,13 @@ __metadata: languageName: node linkType: hard +"popper.js@npm:^1.14.4, popper.js@npm:^1.16.1": + version: 1.16.1 + resolution: "popper.js@npm:1.16.1" + checksum: 10/71338c86faf9b66ce60c3cdd7fb2ed742944e5d2765a188f269239fee2980aa6223b77b41302d1b6eb7d724e611092f9a2576d0048ac2071b605291abc72c0cf + languageName: node + linkType: hard + "possible-typed-array-names@npm:^1.0.0": version: 1.0.0 resolution: "possible-typed-array-names@npm:1.0.0" @@ -28017,6 +29296,13 @@ __metadata: languageName: node linkType: hard +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: 10/0b9d2c76801ca652a7f64892dd37b7e3fab149a37d2424920099bf894acccc62abb4424af2155ab36dea8744843060a2d8ddc983518d0b1e22265a22324b72ed + languageName: node + linkType: hard + "prelude-ls@npm:~1.1.2": version: 1.1.2 resolution: "prelude-ls@npm:1.1.2" @@ -28503,6 +29789,13 @@ __metadata: languageName: node linkType: hard +"raf-schd@npm:^4.0.2": + version: 4.0.3 + resolution: "raf-schd@npm:4.0.3" + checksum: 10/45514041c5ad31fa96aef3bb3c572a843b92da2f2cd1cb4a47c9ad58e48761d3a4126e18daa32b2bfa0bc2551a42d8f324a0e40e536cb656969929602b4e8b58 + languageName: node + linkType: hard + "raf@npm:^3.1.0": version: 3.4.1 resolution: "raf@npm:3.4.1" @@ -28629,6 +29922,24 @@ __metadata: languageName: node linkType: hard +"react-beautiful-dnd@npm:^13.1.0": + version: 13.1.1 + resolution: "react-beautiful-dnd@npm:13.1.1" + dependencies: + "@babel/runtime": "npm:^7.9.2" + css-box-model: "npm:^1.2.0" + memoize-one: "npm:^5.1.1" + raf-schd: "npm:^4.0.2" + react-redux: "npm:^7.2.0" + redux: "npm:^4.0.4" + use-memo-one: "npm:^1.1.1" + peerDependencies: + react: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 + checksum: 10/2de8162a74f7fc78294e5a928b92d3fff02c579d137a25d53b1ab4313abeb108709bb7281512f7f94d18257de3122b8c85cb5a8375113cb8657088b1a9bda65b + languageName: node + linkType: hard + "react-clientside-effect@npm:^1.2.6": version: 1.2.6 resolution: "react-clientside-effect@npm:1.2.6" @@ -29092,6 +30403,23 @@ __metadata: languageName: node linkType: hard +"react-popper@npm:^1.3.11": + version: 1.3.11 + resolution: "react-popper@npm:1.3.11" + dependencies: + "@babel/runtime": "npm:^7.1.2" + "@hypnosphi/create-react-context": "npm:^0.3.1" + deep-equal: "npm:^1.1.1" + popper.js: "npm:^1.14.4" + prop-types: "npm:^15.6.1" + typed-styles: "npm:^0.0.7" + warning: "npm:^4.0.2" + peerDependencies: + react: 0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10/1f7115da1dd0fdca1fc266d2cefa79ed00eca560f72399a9149e9a8a4bc3e9d9fe4e2955bbbe2e3326607ceb3bd4a971e501fb0d6bbf57bf492c0072ae39c2c6 + languageName: node + linkType: hard + "react-popper@npm:^2.2.5, react-popper@npm:^2.3.0": version: 2.3.0 resolution: "react-popper@npm:2.3.0" @@ -29848,7 +31176,7 @@ __metadata: languageName: node linkType: hard -"redux@npm:^4.0.0, redux@npm:^4.0.5, redux@npm:^4.1.1": +"redux@npm:^4.0.0, redux@npm:^4.0.4, redux@npm:^4.0.5, redux@npm:^4.1.1": version: 4.2.1 resolution: "redux@npm:4.2.1" dependencies: @@ -29864,6 +31192,21 @@ __metadata: languageName: node linkType: hard +"reflect.getprototypeof@npm:^1.0.4": + version: 1.0.6 + resolution: "reflect.getprototypeof@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.1" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + globalthis: "npm:^1.0.3" + which-builtin-type: "npm:^1.1.3" + checksum: 10/518f6457e4bb470c9b317d239c62d4b4a05678b7eae4f1c3f4332fad379b3ea6d2d8999bfad448547fdba8fb77e4725cfe8c6440d0168ff387f16b4f19f759ad + languageName: node + linkType: hard + "refractor@npm:^3.6.0": version: 3.6.0 resolution: "refractor@npm:3.6.0" @@ -29939,6 +31282,18 @@ __metadata: languageName: node linkType: hard +"regexp.prototype.flags@npm:^1.5.2": + version: 1.5.2 + resolution: "regexp.prototype.flags@npm:1.5.2" + dependencies: + call-bind: "npm:^1.0.6" + define-properties: "npm:^1.2.1" + es-errors: "npm:^1.3.0" + set-function-name: "npm:^2.0.1" + checksum: 10/9fffc01da9c4e12670ff95bc5204364615fcc12d86fc30642765af908675678ebb0780883c874b2dbd184505fb52fa603d80073ecf69f461ce7f56b15d10be9c + languageName: node + linkType: hard + "regexpu-core@npm:^5.3.1": version: 5.3.2 resolution: "regexpu-core@npm:5.3.2" @@ -30279,7 +31634,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.5": +"resolve@npm:^1.1.5, resolve@npm:^1.22.0, resolve@npm:^1.22.4": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -30305,6 +31660,19 @@ __metadata: languageName: node linkType: hard +"resolve@npm:^2.0.0-next.5": + version: 2.0.0-next.5 + resolution: "resolve@npm:2.0.0-next.5" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/2d6fd28699f901744368e6f2032b4268b4c7b9185fd8beb64f68c93ac6b22e52ae13560ceefc96241a665b985edf9ffd393ae26d2946a7d3a07b7007b7d51e79 + languageName: node + linkType: hard + "resolve@patch:resolve@npm%3A1.17.0#optional!builtin": version: 1.17.0 resolution: "resolve@patch:resolve@npm%3A1.17.0#optional!builtin::version=1.17.0&hash=c3c19d" @@ -30314,7 +31682,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.5#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.5#optional!builtin, resolve@patch:resolve@npm%3A^1.22.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -30340,6 +31708,19 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin": + version: 2.0.0-next.5 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin::version=2.0.0-next.5&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/05fa778de9d0347c8b889eb7a18f1f06bf0f801b0eb4610b4871a4b2f22e220900cf0ad525e94f990bb8d8921c07754ab2122c0c225ab4cdcea98f36e64fa4c2 + languageName: node + linkType: hard + "responselike@npm:^2.0.0": version: 2.0.1 resolution: "responselike@npm:2.0.1" @@ -30908,6 +32289,18 @@ __metadata: languageName: node linkType: hard +"safe-array-concat@npm:^1.1.2": + version: 1.1.2 + resolution: "safe-array-concat@npm:1.1.2" + dependencies: + call-bind: "npm:^1.0.7" + get-intrinsic: "npm:^1.2.4" + has-symbols: "npm:^1.0.3" + isarray: "npm:^2.0.5" + checksum: 10/a54f8040d7cb696a1ee38d19cc71ab3cfb654b9b81bae00c6459618cfad8214ece7e6666592f9c925aafef43d0a20c5e6fbb3413a2b618e1ce9d516a2e6dcfc5 + languageName: node + linkType: hard + "safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" @@ -30940,6 +32333,17 @@ __metadata: languageName: node linkType: hard +"safe-regex-test@npm:^1.0.3": + version: 1.0.3 + resolution: "safe-regex-test@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-regex: "npm:^1.1.4" + checksum: 10/b04de61114b10274d92e25b6de7ccb5de07f11ea15637ff636de4b5190c0f5cd8823fe586dde718504cf78055437d70fd8804976894df502fcf5a210c970afb3 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:^2.1.2, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -30947,175 +32351,175 @@ __metadata: languageName: node linkType: hard -"sass-embedded-android-arm64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-android-arm64@npm:1.79.1" +"sass-embedded-android-arm64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-android-arm64@npm:1.80.6" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"sass-embedded-android-arm@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-android-arm@npm:1.79.1" +"sass-embedded-android-arm@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-android-arm@npm:1.80.6" conditions: os=android & cpu=arm languageName: node linkType: hard -"sass-embedded-android-ia32@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-android-ia32@npm:1.79.1" +"sass-embedded-android-ia32@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-android-ia32@npm:1.80.6" conditions: os=android & cpu=ia32 languageName: node linkType: hard -"sass-embedded-android-riscv64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-android-riscv64@npm:1.79.1" +"sass-embedded-android-riscv64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-android-riscv64@npm:1.80.6" conditions: os=android & cpu=riscv64 languageName: node linkType: hard -"sass-embedded-android-x64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-android-x64@npm:1.79.1" +"sass-embedded-android-x64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-android-x64@npm:1.80.6" conditions: os=android & cpu=x64 languageName: node linkType: hard -"sass-embedded-darwin-arm64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-darwin-arm64@npm:1.79.1" +"sass-embedded-darwin-arm64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-darwin-arm64@npm:1.80.6" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"sass-embedded-darwin-x64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-darwin-x64@npm:1.79.1" +"sass-embedded-darwin-x64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-darwin-x64@npm:1.80.6" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"sass-embedded-linux-arm64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-arm64@npm:1.79.1" +"sass-embedded-linux-arm64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-arm64@npm:1.80.6" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"sass-embedded-linux-arm@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-arm@npm:1.79.1" +"sass-embedded-linux-arm@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-arm@npm:1.80.6" conditions: os=linux & cpu=arm languageName: node linkType: hard -"sass-embedded-linux-ia32@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-ia32@npm:1.79.1" +"sass-embedded-linux-ia32@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-ia32@npm:1.80.6" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"sass-embedded-linux-musl-arm64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-musl-arm64@npm:1.79.1" +"sass-embedded-linux-musl-arm64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-musl-arm64@npm:1.80.6" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"sass-embedded-linux-musl-arm@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-musl-arm@npm:1.79.1" +"sass-embedded-linux-musl-arm@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-musl-arm@npm:1.80.6" conditions: os=linux & cpu=arm languageName: node linkType: hard -"sass-embedded-linux-musl-ia32@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-musl-ia32@npm:1.79.1" +"sass-embedded-linux-musl-ia32@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-musl-ia32@npm:1.80.6" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"sass-embedded-linux-musl-riscv64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-musl-riscv64@npm:1.79.1" +"sass-embedded-linux-musl-riscv64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-musl-riscv64@npm:1.80.6" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"sass-embedded-linux-musl-x64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-musl-x64@npm:1.79.1" +"sass-embedded-linux-musl-x64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-musl-x64@npm:1.80.6" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"sass-embedded-linux-riscv64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-riscv64@npm:1.79.1" +"sass-embedded-linux-riscv64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-riscv64@npm:1.80.6" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"sass-embedded-linux-x64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-linux-x64@npm:1.79.1" +"sass-embedded-linux-x64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-linux-x64@npm:1.80.6" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"sass-embedded-win32-arm64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-win32-arm64@npm:1.79.1" +"sass-embedded-win32-arm64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-win32-arm64@npm:1.80.6" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"sass-embedded-win32-ia32@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-win32-ia32@npm:1.79.1" +"sass-embedded-win32-ia32@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-win32-ia32@npm:1.80.6" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"sass-embedded-win32-x64@npm:1.79.1": - version: 1.79.1 - resolution: "sass-embedded-win32-x64@npm:1.79.1" +"sass-embedded-win32-x64@npm:1.80.6": + version: 1.80.6 + resolution: "sass-embedded-win32-x64@npm:1.80.6" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "sass-embedded@npm:^1.79.1": - version: 1.79.1 - resolution: "sass-embedded@npm:1.79.1" + version: 1.80.6 + resolution: "sass-embedded@npm:1.80.6" dependencies: "@bufbuild/protobuf": "npm:^2.0.0" buffer-builder: "npm:^0.2.0" colorjs.io: "npm:^0.5.0" immutable: "npm:^4.0.0" rxjs: "npm:^7.4.0" - sass-embedded-android-arm: "npm:1.79.1" - sass-embedded-android-arm64: "npm:1.79.1" - sass-embedded-android-ia32: "npm:1.79.1" - sass-embedded-android-riscv64: "npm:1.79.1" - sass-embedded-android-x64: "npm:1.79.1" - sass-embedded-darwin-arm64: "npm:1.79.1" - sass-embedded-darwin-x64: "npm:1.79.1" - sass-embedded-linux-arm: "npm:1.79.1" - sass-embedded-linux-arm64: "npm:1.79.1" - sass-embedded-linux-ia32: "npm:1.79.1" - sass-embedded-linux-musl-arm: "npm:1.79.1" - sass-embedded-linux-musl-arm64: "npm:1.79.1" - sass-embedded-linux-musl-ia32: "npm:1.79.1" - sass-embedded-linux-musl-riscv64: "npm:1.79.1" - sass-embedded-linux-musl-x64: "npm:1.79.1" - sass-embedded-linux-riscv64: "npm:1.79.1" - sass-embedded-linux-x64: "npm:1.79.1" - sass-embedded-win32-arm64: "npm:1.79.1" - sass-embedded-win32-ia32: "npm:1.79.1" - sass-embedded-win32-x64: "npm:1.79.1" + sass-embedded-android-arm: "npm:1.80.6" + sass-embedded-android-arm64: "npm:1.80.6" + sass-embedded-android-ia32: "npm:1.80.6" + sass-embedded-android-riscv64: "npm:1.80.6" + sass-embedded-android-x64: "npm:1.80.6" + sass-embedded-darwin-arm64: "npm:1.80.6" + sass-embedded-darwin-x64: "npm:1.80.6" + sass-embedded-linux-arm: "npm:1.80.6" + sass-embedded-linux-arm64: "npm:1.80.6" + sass-embedded-linux-ia32: "npm:1.80.6" + sass-embedded-linux-musl-arm: "npm:1.80.6" + sass-embedded-linux-musl-arm64: "npm:1.80.6" + sass-embedded-linux-musl-ia32: "npm:1.80.6" + sass-embedded-linux-musl-riscv64: "npm:1.80.6" + sass-embedded-linux-musl-x64: "npm:1.80.6" + sass-embedded-linux-riscv64: "npm:1.80.6" + sass-embedded-linux-x64: "npm:1.80.6" + sass-embedded-win32-arm64: "npm:1.80.6" + sass-embedded-win32-ia32: "npm:1.80.6" + sass-embedded-win32-x64: "npm:1.80.6" supports-color: "npm:^8.1.1" varint: "npm:^6.0.0" dependenciesMeta: @@ -31161,7 +32565,7 @@ __metadata: optional: true bin: sass: dist/bin/sass.js - checksum: 10/e276ef14414dbac30007b46e2ef7ad7b0a7756005b1df9ae9224cd7db39b5ec42c2e6a742daa737d861f0eac898f18d934d45b1d675038fb4218b98432042e5d + checksum: 10/e779d6d7f4b80611af2812bd9dfd08d230b311dc6966ce8727e371b8cc8bd88c38a3c1c93cbf2aa7499ff7b9ebf504190a113d0a1cd029d6fbb44bf10500a89e languageName: node linkType: hard @@ -31371,7 +32775,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4": +"semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -31500,6 +32904,18 @@ __metadata: languageName: node linkType: hard +"set-function-name@npm:^2.0.1, set-function-name@npm:^2.0.2": + version: 2.0.2 + resolution: "set-function-name@npm:2.0.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + functions-have-names: "npm:^1.2.3" + has-property-descriptors: "npm:^1.0.2" + checksum: 10/c7614154a53ebf8c0428a6c40a3b0b47dac30587c1a19703d1b75f003803f73cdfa6a93474a9ba678fa565ef5fbddc2fae79bca03b7d22ab5fd5163dbe571a74 + languageName: node + linkType: hard + "setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" @@ -32351,6 +33767,36 @@ __metadata: languageName: node linkType: hard +"string.prototype.includes@npm:^2.0.0": + version: 2.0.0 + resolution: "string.prototype.includes@npm:2.0.0" + dependencies: + define-properties: "npm:^1.1.3" + es-abstract: "npm:^1.17.5" + checksum: 10/34c1e71ac5cab469bef52a4f3d983d141ca61c43b9fe8859574c8829822aad0a61fce1dddfaf8a48ad7ac5032a1730c19f1fb2d09715f57025cd138b1ad4b0e4 + languageName: node + linkType: hard + +"string.prototype.matchall@npm:^4.0.11": + version: 4.0.11 + resolution: "string.prototype.matchall@npm:4.0.11" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-symbols: "npm:^1.0.3" + internal-slot: "npm:^1.0.7" + regexp.prototype.flags: "npm:^1.5.2" + set-function-name: "npm:^2.0.2" + side-channel: "npm:^1.0.6" + checksum: 10/a902ff4500f909f2a08e55cc5ab1ffbbc905f603b36837674370ee3921058edd0392147e15891910db62a2f31ace2adaf065eaa3bc6e9810bdbc8ca48e05a7b5 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.6": version: 4.0.10 resolution: "string.prototype.matchall@npm:4.0.10" @@ -32379,6 +33825,16 @@ __metadata: languageName: node linkType: hard +"string.prototype.repeat@npm:^1.0.0": + version: 1.0.0 + resolution: "string.prototype.repeat@npm:1.0.0" + dependencies: + define-properties: "npm:^1.1.3" + es-abstract: "npm:^1.17.5" + checksum: 10/4b1bd91b75fa8fdf0541625184ebe80e445a465ce4253c19c3bccd633898005dadae0f74b85ae72662a53aafb8035bf48f8f5c0755aec09bc106a7f13959d05e + languageName: node + linkType: hard + "string.prototype.trim@npm:^1.2.8": version: 1.2.8 resolution: "string.prototype.trim@npm:1.2.8" @@ -32390,6 +33846,18 @@ __metadata: languageName: node linkType: hard +"string.prototype.trim@npm:^1.2.9": + version: 1.2.9 + resolution: "string.prototype.trim@npm:1.2.9" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.0" + es-object-atoms: "npm:^1.0.0" + checksum: 10/b2170903de6a2fb5a49bb8850052144e04b67329d49f1343cdc6a87cb24fb4e4b8ad00d3e273a399b8a3d8c32c89775d93a8f43cb42fbff303f25382079fb58a + languageName: node + linkType: hard + "string.prototype.trimend@npm:^1.0.7": version: 1.0.7 resolution: "string.prototype.trimend@npm:1.0.7" @@ -32401,6 +33869,17 @@ __metadata: languageName: node linkType: hard +"string.prototype.trimend@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimend@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/c2e862ae724f95771da9ea17c27559d4eeced9208b9c20f69bbfcd1b9bc92375adf8af63a103194dba17c4cc4a5cb08842d929f415ff9d89c062d44689c8761b + languageName: node + linkType: hard + "string.prototype.trimstart@npm:^1.0.7": version: 1.0.7 resolution: "string.prototype.trimstart@npm:1.0.7" @@ -32412,6 +33891,17 @@ __metadata: languageName: node linkType: hard +"string.prototype.trimstart@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimstart@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/160167dfbd68e6f7cb9f51a16074eebfce1571656fc31d40c3738ca9e30e35496f2c046fe57b6ad49f65f238a152be8c86fd9a2dd58682b5eba39dad995b3674 + languageName: node + linkType: hard + "string_decoder@npm:^1.1.1": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" @@ -33175,6 +34665,13 @@ __metadata: languageName: node linkType: hard +"tiny-invariant@npm:^1.0.6": + version: 1.3.3 + resolution: "tiny-invariant@npm:1.3.3" + checksum: 10/5e185c8cc2266967984ce3b352a4e57cb89dad5a8abb0dea21468a6ecaa67cd5bb47a3b7a85d08041008644af4f667fb8b6575ba38ba5fb00b3b5068306e59fe + languageName: node + linkType: hard + "tiny-relative-date@npm:^1.3.0": version: 1.3.0 resolution: "tiny-relative-date@npm:1.3.0" @@ -33506,6 +35003,18 @@ __metadata: languageName: node linkType: hard +"tsconfig-paths@npm:^3.14.1, tsconfig-paths@npm:^3.15.0": + version: 3.15.0 + resolution: "tsconfig-paths@npm:3.15.0" + dependencies: + "@types/json5": "npm:^0.0.29" + json5: "npm:^1.0.2" + minimist: "npm:^1.2.6" + strip-bom: "npm:^3.0.0" + checksum: 10/2041beaedc6c271fc3bedd12e0da0cc553e65d030d4ff26044b771fac5752d0460944c0b5e680f670c2868c95c664a256cec960ae528888db6ded83524e33a14 + languageName: node + linkType: hard + "tslib@npm:2.0.1": version: 2.0.1 resolution: "tslib@npm:2.0.1" @@ -33513,6 +35022,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^1.8.1": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: 10/7dbf34e6f55c6492637adb81b555af5e3b4f9cc6b998fb440dac82d3b42bdc91560a35a5fb75e20e24a076c651438234da6743d139e4feabf0783f3cdfe1dddb + languageName: node + linkType: hard + "tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:~2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" @@ -33527,6 +35043,24 @@ __metadata: languageName: node linkType: hard +"tslib@npm:~2.5.0": + version: 2.5.3 + resolution: "tslib@npm:2.5.3" + checksum: 10/d507e60ebe2480af4efc1655dfdb2762bb6ca57d76c4ba680375af801493648c2e97808bbd7e54691eb40e33a7e2e793cdef9c24ce6a8539b03cac8b26e09a61 + languageName: node + linkType: hard + +"tsutils@npm:^3.21.0": + version: 3.21.0 + resolution: "tsutils@npm:3.21.0" + dependencies: + tslib: "npm:^1.8.1" + peerDependencies: + typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + checksum: 10/ea036bec1dd024e309939ffd49fda7a351c0e87a1b8eb049570dd119d447250e2c56e0e6c00554e8205760e7417793fdebff752a46e573fbe07d4f375502a5b2 + languageName: node + linkType: hard + "tsx@npm:^4.11.2": version: 4.11.2 resolution: "tsx@npm:4.11.2" @@ -33659,6 +35193,15 @@ __metadata: languageName: node linkType: hard +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: "npm:^1.2.1" + checksum: 10/14687776479d048e3c1dbfe58a2409e00367810d6960c0f619b33793271ff2a27f81b52461f14a162f1f89a9b1d8da1b237fc7c99b0e1fdcec28ec63a86b1fec + languageName: node + linkType: hard + "type-check@npm:~0.3.2": version: 0.3.2 resolution: "type-check@npm:0.3.2" @@ -33738,6 +35281,17 @@ __metadata: languageName: node linkType: hard +"typed-array-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "typed-array-buffer@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.13" + checksum: 10/02ffc185d29c6df07968272b15d5319a1610817916ec8d4cd670ded5d1efe72901541ff2202fcc622730d8a549c76e198a2f74e312eabbfb712ed907d45cbb0b + languageName: node + linkType: hard + "typed-array-byte-length@npm:^1.0.0": version: 1.0.0 resolution: "typed-array-byte-length@npm:1.0.0" @@ -33750,6 +35304,19 @@ __metadata: languageName: node linkType: hard +"typed-array-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "typed-array-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-proto: "npm:^1.0.3" + is-typed-array: "npm:^1.1.13" + checksum: 10/e4a38329736fe6a73b52a09222d4a9e8de14caaa4ff6ad8e55217f6705b017d9815b7284c85065b3b8a7704e226ccff1372a72b78c2a5b6b71b7bf662308c903 + languageName: node + linkType: hard + "typed-array-byte-offset@npm:^1.0.0": version: 1.0.0 resolution: "typed-array-byte-offset@npm:1.0.0" @@ -33763,6 +35330,20 @@ __metadata: languageName: node linkType: hard +"typed-array-byte-offset@npm:^1.0.2": + version: 1.0.2 + resolution: "typed-array-byte-offset@npm:1.0.2" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-proto: "npm:^1.0.3" + is-typed-array: "npm:^1.1.13" + checksum: 10/ac26d720ebb2aacbc45e231347c359e6649f52e0cfe0e76e62005912f8030d68e4cb7b725b1754e8fdd48e433cb68df5a8620a3e420ad1457d666e8b29bf9150 + languageName: node + linkType: hard + "typed-array-length@npm:^1.0.4": version: 1.0.4 resolution: "typed-array-length@npm:1.0.4" @@ -33774,6 +35355,27 @@ __metadata: languageName: node linkType: hard +"typed-array-length@npm:^1.0.6": + version: 1.0.6 + resolution: "typed-array-length@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-proto: "npm:^1.0.3" + is-typed-array: "npm:^1.1.13" + possible-typed-array-names: "npm:^1.0.0" + checksum: 10/05e96cf4ff836743ebfc593d86133b8c30e83172cb5d16c56814d7bacfed57ce97e87ada9c4b2156d9aaa59f75cdef01c25bd9081c7826e0b869afbefc3e8c39 + languageName: node + linkType: hard + +"typed-styles@npm:^0.0.7": + version: 0.0.7 + resolution: "typed-styles@npm:0.0.7" + checksum: 10/24704459dd5119729a5c20d156f60a1a74489e0a6a57fc6bc93a0d167c805675cc3cadd42ae5d99d7906762e951a44bca9558101353c9d37bedbe8b1e6bf6e51 + languageName: node + linkType: hard + "typedarray-to-buffer@npm:^3.1.5": version: 3.1.5 resolution: "typedarray-to-buffer@npm:3.1.5" @@ -33806,7 +35408,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.1.3": +"typescript@npm:^4.1.3, typescript@npm:^4.5.4": version: 4.9.5 resolution: "typescript@npm:4.9.5" bin: @@ -33836,7 +35438,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^4.1.3#optional!builtin": +"typescript@patch:typescript@npm%3A^4.1.3#optional!builtin, typescript@patch:typescript@npm%3A^4.5.4#optional!builtin": version: 4.9.5 resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin::version=4.9.5&hash=289587" bin: @@ -34396,6 +35998,15 @@ __metadata: languageName: node linkType: hard +"use-memo-one@npm:^1.1.1": + version: 1.1.3 + resolution: "use-memo-one@npm:1.1.3" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10/8f08eba26d69406b61bb4b8dacdd5a92bd6aef5b53d346dfe87954f7330ee10ecabc937cc7854635155d46053828e85c10b5a5aff7a04720e6a97b9f42999bac + languageName: node + linkType: hard + "use-react-router-breadcrumbs@npm:^3.2.1": version: 3.2.1 resolution: "use-react-router-breadcrumbs@npm:3.2.1" @@ -34434,21 +36045,21 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:1.2.2, use-sync-external-store@npm:^1.2.0": - version: 1.2.2 - resolution: "use-sync-external-store@npm:1.2.2" +"use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.0.0": + version: 1.2.0 + resolution: "use-sync-external-store@npm:1.2.0" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/671e9c190aab9a8374a5d468c6ba17f52c38b6fae970110bc196fc1e2b57204149aea9619be49a1bb5207fb6e51d8afd19c3bcb94afe61813fed039821461dc0 + checksum: 10/a676216affc203876bd47981103f201f28c2731361bb186367e12d287a7566763213a8816910c6eb88265eccd4c230426eb783d64c373c4a180905be8820ed8e languageName: node linkType: hard -"use-sync-external-store@npm:^1.0.0": - version: 1.2.0 - resolution: "use-sync-external-store@npm:1.2.0" +"use-sync-external-store@npm:^1.2.0": + version: 1.2.2 + resolution: "use-sync-external-store@npm:1.2.2" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/a676216affc203876bd47981103f201f28c2731361bb186367e12d287a7566763213a8816910c6eb88265eccd4c230426eb783d64c373c4a180905be8820ed8e + checksum: 10/671e9c190aab9a8374a5d468c6ba17f52c38b6fae970110bc196fc1e2b57204149aea9619be49a1bb5207fb6e51d8afd19c3bcb94afe61813fed039821461dc0 languageName: node linkType: hard @@ -34674,8 +36285,8 @@ __metadata: linkType: hard "vike@npm:^0.4.199": - version: 0.4.200 - resolution: "vike@npm:0.4.200" + version: 0.4.201 + resolution: "vike@npm:0.4.201" dependencies: "@brillout/import": "npm:^0.2.3" "@brillout/json-serializer": "npm:^0.5.13" @@ -34697,7 +36308,7 @@ __metadata: optional: true bin: vike: node/cli/bin-entry.js - checksum: 10/169aef59d4e683501414b10390169307c191fd43041782179e77d0633b8de6705161617f2e62c328fd294467d530ecffe2118307acc114cd0d52566eb7385e72 + checksum: 10/c392207bc6fce32f6cc9e96b1afb5d2bef58fc79436d67ee6eeb2215f9dbdb5fd87924887478d439fda3d3c3e6fdfbb09955a6d50e6050e32d47f1186330ea32 languageName: node linkType: hard @@ -35061,7 +36672,7 @@ __metadata: languageName: node linkType: hard -"warning@npm:^4.0.2": +"warning@npm:^4.0.2, warning@npm:^4.0.3": version: 4.0.3 resolution: "warning@npm:4.0.3" dependencies: @@ -35416,6 +37027,26 @@ __metadata: languageName: node linkType: hard +"which-builtin-type@npm:^1.1.3": + version: 1.1.3 + resolution: "which-builtin-type@npm:1.1.3" + dependencies: + function.prototype.name: "npm:^1.1.5" + has-tostringtag: "npm:^1.0.0" + is-async-function: "npm:^2.0.0" + is-date-object: "npm:^1.0.5" + is-finalizationregistry: "npm:^1.0.2" + is-generator-function: "npm:^1.0.10" + is-regex: "npm:^1.1.4" + is-weakref: "npm:^1.0.2" + isarray: "npm:^2.0.5" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.9" + checksum: 10/d7823c4a6aa4fc8183eb572edd9f9ee2751e5f3ba2ccd5b298cc163f720df0f02ee1a5291d18ca8a41d48144ef40007ff6a64e6f5e7c506527086c7513a5f673 + languageName: node + linkType: hard + "which-collection@npm:^1.0.1": version: 1.0.1 resolution: "which-collection@npm:1.0.1" @@ -35448,7 +37079,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.13": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: @@ -35543,7 +37174,7 @@ __metadata: languageName: node linkType: hard -"word-wrap@npm:~1.2.3": +"word-wrap@npm:^1.2.5, word-wrap@npm:~1.2.3": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" checksum: 10/1ec6f6089f205f83037be10d0c4b34c9183b0b63fca0834a5b3cee55dd321429d73d40bb44c8fc8471b5203d6e8f8275717f49a8ff4b2b0ab41d7e1b563e0854 @@ -35915,6 +37546,13 @@ __metadata: languageName: node linkType: hard +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10/f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 + languageName: node + linkType: hard + "yocto-queue@npm:^1.0.0": version: 1.0.0 resolution: "yocto-queue@npm:1.0.0" @@ -35930,10 +37568,10 @@ __metadata: linkType: hard "zustand@npm:^4.5.1": - version: 4.5.5 - resolution: "zustand@npm:4.5.5" + version: 4.5.1 + resolution: "zustand@npm:4.5.1" dependencies: - use-sync-external-store: "npm:1.2.2" + use-sync-external-store: "npm:1.2.0" peerDependencies: "@types/react": ">=16.8" immer: ">=9.0.6" @@ -35945,7 +37583,28 @@ __metadata: optional: true react: optional: true - checksum: 10/481b8210187b69678074a1ca51107654c2379688e90407bfcb7961e0803a259742bfd0d77171c3f07e290896ad55fe9659b3863f30d34cb2572650ead1249f25 + checksum: 10/c5dcd734ddcc393bc1febcf1811d606222c6d40cb4cf0e4d605be6cfa1855c8b09b6725ab6b73a395f1e020f8f617b605c800c1f19301b240c45d5042e1b2a10 + languageName: node + linkType: hard + +"zustand@npm:^5.0.1": + version: 5.0.1 + resolution: "zustand@npm:5.0.1" + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + checksum: 10/9ee5b2483213157c519be2647e873fb21962b4e6e521031916e8969552379dd03348057c9dfd030eb1e7ef98a945a1f99f9aa9badda5d9673eb4004e354dc2dc languageName: node linkType: hard