Loading package.json +0 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "fuse.js": "^7.0.0", "kmenu": "^1.4.32", "lucide-react": "^0.441.0", "qrcodejs": "github:danielgjackson/qrcodejs", "react": "^18.3.1", Loading src/components/Controls.tsx +37 −225 Original line number Diff line number Diff line import 'kmenu/dist/index.css'; import * as Spinners from 'react-loader-spinner'; import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; import { CommandDialog, CommandEmpty, Loading @@ -14,45 +5,20 @@ import { CommandInput, CommandItem, CommandList, CommandSeparator, } from '@/components/ui/command'; import { CommandMenu, CommandWrapper, MenuProvider, useCommands, useKmenu } from 'kmenu'; import { useEffect, useState } from 'react'; import { BsQrCode } from 'react-icons/bs'; import { Button } from '@/components/ui/button'; import { CgSearchFound } from 'react-icons/cg'; import { Command } from 'lucide-react'; import Fuse from 'fuse.js'; import { GiOakLeaf } from 'react-icons/gi'; import { Command, Eye } from 'lucide-react'; import { Input } from '@/components/ui/input'; import QrCode from './QrCode.jsx'; const fuseOptions = { includeScore: true, isCaseSensitive: true, useExtendedSearch: false, keys: [ 'title', 'subtitle', 'sections.introduction', 'sections.challenge', 'sections.approach', 'sections.outcomes', 'sections.research.focus', 'sections.research.areas', 'contact.first', 'contact.last', ], }; const formatScoreText = value => { return value ? `Relevancy = ${(1.0 - value).toFixed(4)}` : ''; }; interface MenuItem { /* eslint-disable-next-line */ children : React.ReactNode shortcut ?: string onSelect ?: (value : string) => void } const getParameters = key => { const search = window.location.search; const params = new URLSearchParams(search); Loading @@ -78,10 +44,6 @@ const updateLocation = key => { window.location.href = location; }; }; const LoadingSpinner = () => { const Spinner = Spinners.LineWave; return <Spinner />; }; const ToggleButton = ({ onClick }) => { return ( <Button id="catalog-toggle-palette" onClick={onClick}> Loading Loading @@ -123,166 +85,21 @@ const SearchInput = () => { </div> ); }; const Palette = ({ items }) => { const [code, setCode] = useState('https://research.ornl.gov'); const [visible, setVisible] = useState(false); const { input, open, setOpen, toggle } = useKmenu(); const search = (items, value) => { const fuse = new Fuse(items, fuseOptions); const results = fuse.search(value).map(({ item, score }) => ({ ...item, score })).map(({ meta: { id, keywords }, score, title }) => ({ text: `${title} (${formatScoreText(score)})`, icon: <CgSearchFound />, href: `/${id}`, keywords: keywords.join(' '), newTab: false })); const commands = { category: 'Results', commands: results }; const resultsCommands = [ { icon: <BsQrCode />, text: 'Generate QR Code', perform: () => { setCode(`${window.origin}/?search=${value}`); setVisible(true); } } ]; return results.length > 0 ? [ { category: '', commands: resultsCommands }, commands ] : [commands]; }; const main = [ { category: 'Actions', commands: [ { icon: <BsQrCode />, text: 'Generate QR Code', perform: () => { setCode(window.location.href); setVisible(true); } } ], subCommands: [] }, { category: 'Divisions', commands: [] }, { category: 'Groups', commands: [] }, { category: 'Projects', commands: items.map(({ meta: { id, keywords }, title: text }) => ({ text, icon: <GiOakLeaf />, href: `/${id}`, keywords: keywords.join(' '), newTab: false })) }, ]; const loading = []; const [mainCommands] = useCommands(main); const [loadingCommands, setLoadingCommands] = useCommands(loading); const [awaiting, setAwaiting] = useState(true); useEffect(() => { if (open !== 2) { return; } setAwaiting(true); setLoadingCommands(search(items, input)); setTimeout(() => setAwaiting(false), 1000); }, [input, open, setOpen]); return ( <> <div id="control-container"> <ToggleButton onClick={toggle} /> <SearchInput /> </div> <CommandWrapper> <CommandMenu commands={mainCommands} crumbs={['Home']} index={1} placeholder="Type something to filter..." /> <CommandMenu commands={loadingCommands} crumbs={['Home', 'Search']} index={2} loadingState={awaiting} loadingPlaceholder={<LoadingSpinner />} placeholder="What are you looking for?" preventSearch={true} /> </CommandWrapper> <QrCode data={code} toggle={setVisible} visible={visible} /> </> ); }; interface MenuItem { /* eslint-disable-next-line */ children : React.ReactNode shortcut ?: string onSelect ?: (value : string) => void } function Item({ children, shortcut, onSelect = () => undefined } : MenuItem) { const style = { cursor: 'pointer', color: '#CCCCCC', }; return ( <CommandItem style={style} onSelect={onSelect}> <CommandItem onSelect={onSelect}> {children} {shortcut && <div cmdk-vercel-shortcuts="">{shortcut.split(' ').map(key => <kbd key={key}>{key}</kbd>)}</div>} </CommandItem> ); } const Crumbs = ({ pages }) => { const style = { margin: '15px', }; return ( <Breadcrumb style={style}> <BreadcrumbList> {pages.map((page, index) => { return ( <> {index > 0 && <BreadcrumbSeparator />} <BreadcrumbItem> <BreadcrumbLink>{page}</BreadcrumbLink> </BreadcrumbItem> </> ); })} </BreadcrumbList> </Breadcrumb> ); }; const Projects = ({ items }) => { const action = { cursor: 'pointer', paddingLeft: '20px', }; return ( <CommandGroup heading="Projects"> <CommandGroup heading="Research Activity Data"> {items.map(item => ( <Item key={item.title}> <GiOakLeaf /> <span style={action}><a href={`/${item.meta.id}`}>{item.title}</a></span> <Eye /> <span> <a href={`/${item.meta.identifier}`}>{item.title}</a></span> </Item> ))} </CommandGroup> Loading @@ -292,7 +109,6 @@ export default ({ items }) => { const [open, setOpen] = useState(false); const [code, setCode] = useState('https://research.ornl.gov'); const [visible, setVisible] = useState(false); const [pages] = useState<string[]>(['Home']); const [, setInputValue] = useState(''); useEffect(() => { const down = e => { Loading @@ -304,16 +120,13 @@ export default ({ items }) => { document.addEventListener('keydown', down); return () => document.removeEventListener('keydown', down); }, []); const action = { cursor: 'pointer', paddingLeft: '10px', }; return ( <MenuProvider> <Palette items={items} /> return <div> <div id="control-container"> <ToggleButton onClick={() => setOpen(true)} /> <SearchInput /> </div> <QrCode data={code} toggle={setVisible} visible={visible} /> <CommandDialog open={open} onOpenChange={setOpen}> <Crumbs pages={pages} /> <CommandInput placeholder="Type something to filter..." onValueChange={value => setInputValue(value)} /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> Loading @@ -325,13 +138,12 @@ export default ({ items }) => { }} > <BsQrCode /> <span style={action}>Generate QR Code</span> <span> Generate QR Code</span> </CommandItem> </CommandGroup> <CommandSeparator /> <div style={{ height: '10px' }}></div> <Projects items={items} /> </CommandList> </CommandDialog> </MenuProvider> ); </div>; }; src/components/Listing.tsx +2 −6 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ const search = items => { keys: [ 'meta.identifier', 'meta.media.caption', 'meta.technology', 'title', 'subtitle', 'sections.mission', Loading Loading @@ -72,12 +73,7 @@ const Details = ({ items }) => { return ( <div> <ChartNoAxesCombined style={{ display: 'inline-block', marginTop: '-2px', marginRight: '8px' }} /> {visibleItems} {' '} of {count} {' '} items { [visibleItems, 'of', count, 'items'].join(' ') } </div> ); }; Loading src/components/QrCode.jsx +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ const QrCode = ({ data, label = 'qr-code', options = {}, size = 600, toggle, vis top: 0, visibility: visible ? 'visible' : 'hidden', width: '100%', zIndex: 9999, }, caption: { color: '#CCCCCC', Loading src/components/ui/command.tsx +1 −3 Original line number Diff line number Diff line import * as React from "react" import { type DialogProps } from "@radix-ui/react-dialog" import { Command as CommandPrimitive } from "cmdk" import { Search } from "lucide-react" import { cn } from "@/lib/utils" import { Dialog, DialogContent } from "@/components/ui/dialog" Loading @@ -26,7 +25,7 @@ interface CommandDialogProps extends DialogProps {} const CommandDialog = ({ children, ...props }: CommandDialogProps) => { return ( <Dialog {...props}> <DialogContent className="overflow-hidden p-0 shadow-lg"> <DialogContent className="overflow-hidden p-0 shadow-lg research-enablement"> <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> Loading @@ -40,7 +39,6 @@ const CommandInput = React.forwardRef< React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> >(({ className, ...props }, ref) => ( <div className="flex items-center border-b px-3" cmdk-input-wrapper=""> <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> <CommandPrimitive.Input ref={ref} className={cn( Loading Loading
package.json +0 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "fuse.js": "^7.0.0", "kmenu": "^1.4.32", "lucide-react": "^0.441.0", "qrcodejs": "github:danielgjackson/qrcodejs", "react": "^18.3.1", Loading
src/components/Controls.tsx +37 −225 Original line number Diff line number Diff line import 'kmenu/dist/index.css'; import * as Spinners from 'react-loader-spinner'; import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; import { CommandDialog, CommandEmpty, Loading @@ -14,45 +5,20 @@ import { CommandInput, CommandItem, CommandList, CommandSeparator, } from '@/components/ui/command'; import { CommandMenu, CommandWrapper, MenuProvider, useCommands, useKmenu } from 'kmenu'; import { useEffect, useState } from 'react'; import { BsQrCode } from 'react-icons/bs'; import { Button } from '@/components/ui/button'; import { CgSearchFound } from 'react-icons/cg'; import { Command } from 'lucide-react'; import Fuse from 'fuse.js'; import { GiOakLeaf } from 'react-icons/gi'; import { Command, Eye } from 'lucide-react'; import { Input } from '@/components/ui/input'; import QrCode from './QrCode.jsx'; const fuseOptions = { includeScore: true, isCaseSensitive: true, useExtendedSearch: false, keys: [ 'title', 'subtitle', 'sections.introduction', 'sections.challenge', 'sections.approach', 'sections.outcomes', 'sections.research.focus', 'sections.research.areas', 'contact.first', 'contact.last', ], }; const formatScoreText = value => { return value ? `Relevancy = ${(1.0 - value).toFixed(4)}` : ''; }; interface MenuItem { /* eslint-disable-next-line */ children : React.ReactNode shortcut ?: string onSelect ?: (value : string) => void } const getParameters = key => { const search = window.location.search; const params = new URLSearchParams(search); Loading @@ -78,10 +44,6 @@ const updateLocation = key => { window.location.href = location; }; }; const LoadingSpinner = () => { const Spinner = Spinners.LineWave; return <Spinner />; }; const ToggleButton = ({ onClick }) => { return ( <Button id="catalog-toggle-palette" onClick={onClick}> Loading Loading @@ -123,166 +85,21 @@ const SearchInput = () => { </div> ); }; const Palette = ({ items }) => { const [code, setCode] = useState('https://research.ornl.gov'); const [visible, setVisible] = useState(false); const { input, open, setOpen, toggle } = useKmenu(); const search = (items, value) => { const fuse = new Fuse(items, fuseOptions); const results = fuse.search(value).map(({ item, score }) => ({ ...item, score })).map(({ meta: { id, keywords }, score, title }) => ({ text: `${title} (${formatScoreText(score)})`, icon: <CgSearchFound />, href: `/${id}`, keywords: keywords.join(' '), newTab: false })); const commands = { category: 'Results', commands: results }; const resultsCommands = [ { icon: <BsQrCode />, text: 'Generate QR Code', perform: () => { setCode(`${window.origin}/?search=${value}`); setVisible(true); } } ]; return results.length > 0 ? [ { category: '', commands: resultsCommands }, commands ] : [commands]; }; const main = [ { category: 'Actions', commands: [ { icon: <BsQrCode />, text: 'Generate QR Code', perform: () => { setCode(window.location.href); setVisible(true); } } ], subCommands: [] }, { category: 'Divisions', commands: [] }, { category: 'Groups', commands: [] }, { category: 'Projects', commands: items.map(({ meta: { id, keywords }, title: text }) => ({ text, icon: <GiOakLeaf />, href: `/${id}`, keywords: keywords.join(' '), newTab: false })) }, ]; const loading = []; const [mainCommands] = useCommands(main); const [loadingCommands, setLoadingCommands] = useCommands(loading); const [awaiting, setAwaiting] = useState(true); useEffect(() => { if (open !== 2) { return; } setAwaiting(true); setLoadingCommands(search(items, input)); setTimeout(() => setAwaiting(false), 1000); }, [input, open, setOpen]); return ( <> <div id="control-container"> <ToggleButton onClick={toggle} /> <SearchInput /> </div> <CommandWrapper> <CommandMenu commands={mainCommands} crumbs={['Home']} index={1} placeholder="Type something to filter..." /> <CommandMenu commands={loadingCommands} crumbs={['Home', 'Search']} index={2} loadingState={awaiting} loadingPlaceholder={<LoadingSpinner />} placeholder="What are you looking for?" preventSearch={true} /> </CommandWrapper> <QrCode data={code} toggle={setVisible} visible={visible} /> </> ); }; interface MenuItem { /* eslint-disable-next-line */ children : React.ReactNode shortcut ?: string onSelect ?: (value : string) => void } function Item({ children, shortcut, onSelect = () => undefined } : MenuItem) { const style = { cursor: 'pointer', color: '#CCCCCC', }; return ( <CommandItem style={style} onSelect={onSelect}> <CommandItem onSelect={onSelect}> {children} {shortcut && <div cmdk-vercel-shortcuts="">{shortcut.split(' ').map(key => <kbd key={key}>{key}</kbd>)}</div>} </CommandItem> ); } const Crumbs = ({ pages }) => { const style = { margin: '15px', }; return ( <Breadcrumb style={style}> <BreadcrumbList> {pages.map((page, index) => { return ( <> {index > 0 && <BreadcrumbSeparator />} <BreadcrumbItem> <BreadcrumbLink>{page}</BreadcrumbLink> </BreadcrumbItem> </> ); })} </BreadcrumbList> </Breadcrumb> ); }; const Projects = ({ items }) => { const action = { cursor: 'pointer', paddingLeft: '20px', }; return ( <CommandGroup heading="Projects"> <CommandGroup heading="Research Activity Data"> {items.map(item => ( <Item key={item.title}> <GiOakLeaf /> <span style={action}><a href={`/${item.meta.id}`}>{item.title}</a></span> <Eye /> <span> <a href={`/${item.meta.identifier}`}>{item.title}</a></span> </Item> ))} </CommandGroup> Loading @@ -292,7 +109,6 @@ export default ({ items }) => { const [open, setOpen] = useState(false); const [code, setCode] = useState('https://research.ornl.gov'); const [visible, setVisible] = useState(false); const [pages] = useState<string[]>(['Home']); const [, setInputValue] = useState(''); useEffect(() => { const down = e => { Loading @@ -304,16 +120,13 @@ export default ({ items }) => { document.addEventListener('keydown', down); return () => document.removeEventListener('keydown', down); }, []); const action = { cursor: 'pointer', paddingLeft: '10px', }; return ( <MenuProvider> <Palette items={items} /> return <div> <div id="control-container"> <ToggleButton onClick={() => setOpen(true)} /> <SearchInput /> </div> <QrCode data={code} toggle={setVisible} visible={visible} /> <CommandDialog open={open} onOpenChange={setOpen}> <Crumbs pages={pages} /> <CommandInput placeholder="Type something to filter..." onValueChange={value => setInputValue(value)} /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> Loading @@ -325,13 +138,12 @@ export default ({ items }) => { }} > <BsQrCode /> <span style={action}>Generate QR Code</span> <span> Generate QR Code</span> </CommandItem> </CommandGroup> <CommandSeparator /> <div style={{ height: '10px' }}></div> <Projects items={items} /> </CommandList> </CommandDialog> </MenuProvider> ); </div>; };
src/components/Listing.tsx +2 −6 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ const search = items => { keys: [ 'meta.identifier', 'meta.media.caption', 'meta.technology', 'title', 'subtitle', 'sections.mission', Loading Loading @@ -72,12 +73,7 @@ const Details = ({ items }) => { return ( <div> <ChartNoAxesCombined style={{ display: 'inline-block', marginTop: '-2px', marginRight: '8px' }} /> {visibleItems} {' '} of {count} {' '} items { [visibleItems, 'of', count, 'items'].join(' ') } </div> ); }; Loading
src/components/QrCode.jsx +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ const QrCode = ({ data, label = 'qr-code', options = {}, size = 600, toggle, vis top: 0, visibility: visible ? 'visible' : 'hidden', width: '100%', zIndex: 9999, }, caption: { color: '#CCCCCC', Loading
src/components/ui/command.tsx +1 −3 Original line number Diff line number Diff line import * as React from "react" import { type DialogProps } from "@radix-ui/react-dialog" import { Command as CommandPrimitive } from "cmdk" import { Search } from "lucide-react" import { cn } from "@/lib/utils" import { Dialog, DialogContent } from "@/components/ui/dialog" Loading @@ -26,7 +25,7 @@ interface CommandDialogProps extends DialogProps {} const CommandDialog = ({ children, ...props }: CommandDialogProps) => { return ( <Dialog {...props}> <DialogContent className="overflow-hidden p-0 shadow-lg"> <DialogContent className="overflow-hidden p-0 shadow-lg research-enablement"> <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> Loading @@ -40,7 +39,6 @@ const CommandInput = React.forwardRef< React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> >(({ className, ...props }, ref) => ( <div className="flex items-center border-b px-3" cmdk-input-wrapper=""> <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> <CommandPrimitive.Input ref={ref} className={cn( Loading