diff --git a/package.json b/package.json index 03da6d9..5ebe941 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "gl-matrix": "^3.4.3", "idb-keyval": "^6.2.1", "lensing": "https://github.com/jessupjs/lensing/tarball/f800a9d67155db599f8054922cb6d45b673fe5c9", - "minerva-author-ui": "2.0.0-beta.27", + "minerva-author-ui": "2.0.0-beta.28", "openseadragon": "^4.1.1", "prop-types": "^15.8.1", "react": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47d76f8..6809fb9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,8 +93,8 @@ importers: specifier: https://github.com/jessupjs/lensing/tarball/f800a9d67155db599f8054922cb6d45b673fe5c9 version: https://github.com/jessupjs/lensing/tarball/f800a9d67155db599f8054922cb6d45b673fe5c9 minerva-author-ui: - specifier: 2.0.0-beta.27 - version: 2.0.0-beta.27(lit@3.2.0)(rollup@4.19.0)(vite@5.3.5(@types/node@20.14.12)) + specifier: 2.0.0-beta.28 + version: 2.0.0-beta.28(lit@3.2.0)(rollup@4.19.0)(vite@5.3.5(@types/node@20.14.12)) openseadragon: specifier: ^4.1.1 version: 4.1.1 @@ -2383,8 +2383,8 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - minerva-author-ui@2.0.0-beta.27: - resolution: {integrity: sha512-G2mHPY7Efkk3knFdGnv4oZfH9OweddBMErnwT4ze/tcb12vKsvwCgm8VhtB/8cTb7GoyycP6wpSdbJKQsdGX0w==} + minerva-author-ui@2.0.0-beta.28: + resolution: {integrity: sha512-QjAHgR73o91l8h78wXkW0b4kMg0fhErFJ0bX2qFUCP2Ov92cxhdIF+AF0/lfZTfc72q3dMBe4Z0DlkYIKT8WFA==} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -6058,7 +6058,7 @@ snapshots: dependencies: mime-db: 1.52.0 - minerva-author-ui@2.0.0-beta.27(lit@3.2.0)(rollup@4.19.0)(vite@5.3.5(@types/node@20.14.12)): + minerva-author-ui@2.0.0-beta.28(lit@3.2.0)(rollup@4.19.0)(vite@5.3.5(@types/node@20.14.12)): dependencies: '@arrow-js/core': 1.0.0-alpha.10 '@haxtheweb/a11y-collapse': 9.0.6 diff --git a/src/components/channel/index.tsx b/src/components/channel/index.tsx index 4653679..4c97802 100644 --- a/src/components/channel/index.tsx +++ b/src/components/channel/index.tsx @@ -3,7 +3,6 @@ import { useState } from "react"; import { Legend } from "./legend"; import { Content } from "./content"; import { Toolbar } from "./toolbar"; -import { author } from "minerva-author-ui"; import { getStyler } from "../../lib/util"; import { theme } from "../../theme.module.css"; import styles from "./index.module.css"; @@ -22,6 +21,7 @@ export type Props = HashContext & ImageProps & { children: any, config: ConfigProps; hiddenChannel: boolean; + controlPanelElement: string; setHiddenChannel: (v: boolean) => void; }; @@ -54,10 +54,8 @@ const Channel = (props: Props) => { ); - const config = props.config; - const suffix = `minerva-${config.UpdateTimestamp}`; const minerva_author_ui = React.createElement( - author(suffix, { config }), { + props.controlPanelElement, { class: theme, children: props.children, } ); diff --git a/src/components/index.tsx b/src/components/index.tsx index fabd411..79140a6 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -18,6 +18,7 @@ type Props = HashContext & { handle: Handle.Dir; config: ConfigProps; marker_names: string[]; + controlPanelElement: string; setExhibit: (e: Exhibit) => void; }; @@ -216,7 +217,7 @@ const Index = (props: Props) => { const { in_f, handle, loader, hash, setHash, - marker_names + marker_names, controlPanelElement } = props; ; const channelProps = { @@ -224,6 +225,7 @@ const Index = (props: Props) => { setHash, groups, stories, + controlPanelElement, config: props.config, editable, hiddenChannel, diff --git a/src/lib/config.ts b/src/lib/config.ts index a5e9e77..df4c360 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -34,13 +34,21 @@ type GroupChannelAssociations = Record< UUID >; -export type ConfigProps = { - UpdateTimestamp: number; +export type MutableFields = (keyof ItemRegistryProps)[] +export type ItemRegistryProps = { Name: string; Groups: ConfigGroup[]; Stories: ConfigWaypoint[]; GroupChannels: ConfigGroupChannel[]; SourceChannels: ConfigSourceChannel[]; +} +interface SetItems { + (user: Partial): void; +} + +export type ConfigProps = { + ItemRegistry: ItemRegistryProps; + ID: string; }; export type ConfigSourceChannel = UUID & { @@ -182,4 +190,18 @@ const mutableConfigArray = ( }); } -export { extractChannels, mutableConfigArray } +const mutableItemRegistry = ( + ItemRegistry: ItemRegistryProps, setItems: SetItems, + fields: MutableFields +) => { + // Transform certain fields into mutable arrays + return fields.reduce((registry, field) => ({ + ...registry, [field]: mutableConfigArray( + ItemRegistry[field], updated => { + setItems({ [field]: updated }) + } + ) + }), ItemRegistry); +} + +export { extractChannels, mutableItemRegistry } diff --git a/src/main.tsx b/src/main.tsx index 20a9089..ba437d8 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,9 +1,10 @@ import * as React from "react"; import { get, set } from 'idb-keyval'; import styled from 'styled-components'; -import { useState, useCallback, useRef, useEffect } from "react"; +import { author } from "minerva-author-ui"; +import { useState, useMemo, useEffect } from "react"; import { useHash } from "./lib/hashUtil"; -import { mutableConfigArray } from './lib/config'; +import { mutableItemRegistry } from './lib/config'; import { hasFileSystemAccess, toDir, toLoader } from "./lib/filesystem"; import { isOpts, validate } from './lib/validate'; import { extractChannels } from './lib/config'; @@ -15,11 +16,12 @@ import type { ValidObj } from './components/upload'; import type { ImageProps } from "./components/channel" import type { FormEventHandler } from "react"; import type { ObjAny, KV } from './lib/validate'; -import type { ConfigProps } from "./lib/config" +import type { ConfigWaypoint } from "./lib/config"; +import type { MutableFields } from "./lib/config"; import type { ExhibitConfig } from "./lib/exhibit"; type Props = ImageProps & { - configWaypoints: ConfigProps['Stories'] + configWaypoints: ConfigWaypoint[]; exhibit_config: ExhibitConfig; marker_names: string[]; handleKeys: string[]; @@ -62,26 +64,28 @@ const Content = (props: Props) => { const hashContext = useHash(url, exhibit.stories); const [handle, setHandle] = useState(null); const [config, setConfig] = useState({ - Name: '', - Groups: [], - SourceChannels: [], - UpdateTimestamp: Date.now(), - Stories: props.configWaypoints, - GroupChannels: [] - }); - const setGroupChannels = useCallback( - (GroupChannels) => { - setConfig((config) => { - return { - ...config, GroupChannels - }; - }) + ItemRegistry: { + Name: '', Groups: [], + GroupChannels: [], SourceChannels: [], + Stories: props.configWaypoints, }, - [setConfig] - ); - useEffect(() => { - setGroupChannels(config.GroupChannels); - }, [config.GroupChannels, setGroupChannels]); + ID: crypto.randomUUID() + }); + const resetItems = ItemRegistry => { + setConfig(config => ({ + ...config, ItemRegistry: { + ...config.ItemRegistry, ...ItemRegistry + }, + ID: crypto.randomUUID() + })); + }; + const setItems = ItemRegistry => { + setConfig(config => ({ + ...config, ItemRegistry: { + ...config.ItemRegistry, ...ItemRegistry + }, + })); + } const [loader, setLoader] = useState(null); const [fileName, setFileName] = useState(''); @@ -111,12 +115,8 @@ const Content = (props: Props) => { const { SourceChannels, GroupChannels, Groups } = extractChannels(loader); - setConfig((config) => { - return { - ...config, Groups, - UpdateTimestamp: Date.now(), - SourceChannels, GroupChannels - } + resetItems({ + Groups, SourceChannels, GroupChannels }); setLoader(loader); setFileName(in_f); @@ -129,18 +129,22 @@ const Content = (props: Props) => { }); }, []) const { marker_names } = props; - const mutable_config = { - ...config, - GroupChannels: mutableConfigArray( - config.GroupChannels, setGroupChannels + const mutableFields: MutableFields = [ + 'GroupChannels' + ] + // Define a WebComponent for the item panel + const controlPanelElement = useMemo(() => author({ + ...config, ItemRegistry: mutableItemRegistry( + config.ItemRegistry, setItems, mutableFields ) - } + }), [config.ID]) // Actual image viewer const imager = loader === null ? '' : (