diff --git a/packages/map-interface/package.json b/packages/map-interface/package.json index 529e2e65..e1246e4e 100644 --- a/packages/map-interface/package.json +++ b/packages/map-interface/package.json @@ -1,6 +1,6 @@ { "name": "@macrostrat/map-interface", - "version": "1.0.0", + "version": "1.1.0", "description": "Map interface for Macrostrat", "main": "dist/index.cjs", "module": "dist/index.js", @@ -11,7 +11,7 @@ "@macrostrat/hyper": "^2.2.1", "@macrostrat/mapbox-react": "workspace:*", "@macrostrat/mapbox-utils": "workspace:*", - "@macrostrat/ui-components": "workspace:*", + "@macrostrat/ui-components": "workspace:^4.1.0", "@mapbox/tilebelt": "^1.0.2", "@turf/bbox": "^6.5.0", "chroma-js": "^2.4.2", diff --git a/packages/map-interface/src/controls.ts b/packages/map-interface/src/controls.ts index 4d814f43..90ab68dd 100644 --- a/packages/map-interface/src/controls.ts +++ b/packages/map-interface/src/controls.ts @@ -10,6 +10,7 @@ import { useMapStatus, } from "@macrostrat/mapbox-react"; import { ScaleControl as BaseScaleControl } from "mapbox-gl"; +import { DevToolsButtonSlot } from "@macrostrat/ui-components"; const h = hyper.styled(styles); @@ -55,6 +56,8 @@ export function MapBottomControls({ children }) { h(CompassControl, { className: "compass-control" }), h(GlobeControl, { className: "globe-control" }), h(GeolocationControl, { className: "geolocation-control" }), + // If we have global development tools enabled, show the button + h(DevToolsButtonSlot, { className: "map-control" }), children, ]); } diff --git a/packages/map-interface/src/main.module.sass b/packages/map-interface/src/main.module.sass index 582a3202..2f437ea6 100644 --- a/packages/map-interface/src/main.module.sass +++ b/packages/map-interface/src/main.module.sass @@ -275,33 +275,40 @@ flex-direction: row justify-content: right margin-bottom: 0 + gap: 0.5em + + :global(.map-control) + &>:global(.bp5-button) + padding: 0 + transform: translate(-3.5px, -3.5px) + width: 22px !important - &>* - margin-left: 0.5em .map-controls :global(.mapbox-control), .map-controls :global(.map-control-wrapper), -.map-controls .map-control - min-width: 22px - min-height: 22px +.map-controls :global(.map-control) + max-height: 22px + height: 22px border-radius: 4px background-color: var(--panel-background-color) box-shadow: 0 0 0 1px var(--card-shadow-color) .map-controls :global(.mapbox-control) button, .map-controls :global(.map-control-wrapper) button, -.map-controls .map-control button - width: 22px +.map-controls :global(.map-control) button + max-height: 22px height: 22px + width: 22px + max-width: 22px background-position: center center padding: 0 - background-color: var(--panel-background-color) - color: var(--text-color) + //background-color: var(--panel-background-color) + //color: var(--text-color) -.map-controls :global(.mapbox-control) button:hover, -.map-controls :global(.map-control-wrapper) button:hover, -.map-controls .map-control button:hover - background-color: var(--panel-background-color) !important +//.map-controls :global(.mapbox-control) button:hover, +//.map-controls :global(.map-control-wrapper) button:hover, +//.map-controls .map-control button:hover +// background-color: var(--panel-background-color) !important .map-controls .map-scale-control background: none diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 9a76bb90..d11e3fcf 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@macrostrat/ui-components", - "version": "4.0.0", + "version": "4.1.0", "description": "UI components for React and blueprint.js", "repository": "https://github.com/UW-Macrostrat/web-components.git", "main": "dist/ui-components.cjs", @@ -46,7 +46,8 @@ "ui-box": "^5.4.1", "underscore": "^1.9.1", "use-async-effect": "^2.2.1", - "use-element-dimensions": "^2.1.3" + "use-element-dimensions": "^2.1.3", + "zustand": "^5.0.0-rc.2" }, "devDependencies": { "@babel/core": "^7.16.12", diff --git a/packages/ui-components/src/dev-panel/_inner.ts b/packages/ui-components/src/dev-panel/_inner.ts new file mode 100644 index 00000000..b75fa0d6 --- /dev/null +++ b/packages/ui-components/src/dev-panel/_inner.ts @@ -0,0 +1,21 @@ +import { Dialog } from "@blueprintjs/core"; +import hyper from "@macrostrat/hyper"; +import styles from "./page-admin.module.sass"; + +const h = hyper.styled(styles); + +export function PageAdminInner({ isOpen, setIsOpen, children }) { + return h([ + h( + Dialog, + { + isOpen, + onClose: () => setIsOpen(false), + title: "Page info", + className: "page-admin", + }, + h("div.dialog-content.bp5-dialog-content", children) + ), + h("span.__render_alarm__"), + ]); +} diff --git a/packages/ui-components/src/dev-panel/index.ts b/packages/ui-components/src/dev-panel/index.ts new file mode 100644 index 00000000..4b5c76a9 --- /dev/null +++ b/packages/ui-components/src/dev-panel/index.ts @@ -0,0 +1,100 @@ +import { Button } from "@blueprintjs/core"; +import { useEffect } from "react"; +import hyper from "@macrostrat/hyper"; +import loadable from "@loadable/component"; +import { create } from "zustand"; +import styles from "./page-admin.module.sass"; +import classNames from "classnames"; + +const h = hyper.styled(styles); + +const useStore: any = create((set) => { + return { + isOpen: false, + isSystemEnabled: false, + setIsOpen(isOpen) { + set({ isOpen }); + }, + toggle() { + set((state) => ({ isOpen: !state.isOpen })); + }, + toggleButton() { + set((state) => { + let isOpen = state.isSystemEnabled ? false : state.isOpen; + return { isSystemEnabled: !state.isSystemEnabled, isOpen }; + }); + }, + buttonRef: null, + setButtonRef(ref) { + console.log("Setting button ref"); + set({ buttonRef: ref }); + }, + }; +}); + +function DevToolsDialog({ isOpen, setIsOpen, children }) { + if (!isOpen) return null; + + const Window = loadable(() => + import("./_inner").then((mod) => mod.PageAdminInner) + ); + + return h(Window, { isOpen, setIsOpen }, children); +} + +export function DevToolsConsole({ className, children }) { + const [isOpen, setIsOpen] = useIsOpen(); + const buttonRef = useStore((state) => state.buttonRef); + useDevToolsKeyBinding(); + + const isSystemEnabled = useStore((state) => state.isSystemEnabled); + + if (!isSystemEnabled) return null; + + return h("div", { className }, [ + h(DevToolsDialog, { isOpen, setIsOpen }, children), + h.if(buttonRef == null)(DevToolsButtonSlot, { setRef: false }), + ]); +} + +export function DevToolsButtonSlot({ setRef = true, className }) { + const onClick = useStore((state) => state.toggle); + const _setRef = useStore((state) => state.setButtonRef); + const isShown = useStore((state) => state.isSystemEnabled); + const ref = (el) => { + if (setRef) _setRef(el); + }; + + if (!isShown) return null; + + return h( + "div.devtools-button", + { className }, + h(Button, { + onClick, + ref, + minimal: true, + icon: "code", + }) + ); +} + +function useDevToolsKeyBinding() { + // Show the page admin console only if the appropriate query parameter is set + // OR if the user presses shift+alt+I + const toggleButton = useStore((s) => s.toggleButton); + + useEffect(() => { + function handleKeyDown(event: KeyboardEvent) { + if (event.key === "I" && event.shiftKey) { + toggleButton(); + } + } + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, []); +} + +function useIsOpen(): [boolean, (isOpen: boolean) => void] { + return useStore((state) => [state.isOpen, state.setIsOpen]); +} diff --git a/packages/ui-components/src/dev-panel/page-admin.module.sass b/packages/ui-components/src/dev-panel/page-admin.module.sass new file mode 100644 index 00000000..fd94522f --- /dev/null +++ b/packages/ui-components/src/dev-panel/page-admin.module.sass @@ -0,0 +1,10 @@ +.dialog-content + padding: 1em + overflow-y: scroll + +.page-admin + max-height: 80vh + +.page-admin-button + width: 16px + height: 16px diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index e62e2a36..d13a0d2b 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -19,3 +19,4 @@ export * from "./patterns"; export * from "./collapse-card"; export * from "./modal-panel"; export * from "./page-layouts"; +export * from "./dev-panel"; diff --git a/packages/ui-components/src/util/json-view.ts b/packages/ui-components/src/util/json-view.ts index 0689cfb5..3bb57fac 100644 --- a/packages/ui-components/src/util/json-view.ts +++ b/packages/ui-components/src/util/json-view.ts @@ -27,17 +27,25 @@ const monokai = { }; export function JSONView(props) { + const { hideRoot, showRoot, className, ...rest } = props; + + let _hideRoot = hideRoot; + if (showRoot === false) { + _hideRoot = true; + } + return h( "div.json-view-container", { - className: classNames(props.className, { - "root-hidden": props.hideRoot, + className: classNames(className, { + "root-hidden": _hideRoot, }), }, h(JSONTree, { theme: monokai, invertTheme: false, - ...props, + hideRoot: _hideRoot, + ...rest, }) ); }