diff options
author | Erlend <erlendniko@hotmail.com> | 2022-07-01 13:30:34 +0200 |
---|---|---|
committer | Erlend <erlendniko@hotmail.com> | 2022-07-01 13:30:34 +0200 |
commit | 790a5e068de378bb0ce1dbaa118c9cd469825b90 (patch) | |
tree | 8a3e5a9ab75bebba0b1c16fc1871a46717156cbd | |
parent | e4265f852d20675d873489973b3745580d2b3c5b (diff) | |
parent | 57d155a72acfeef2e0f8efb7cf53579334dec045 (diff) |
Merge branch 'enikolaisen/querybuilder-js'
34 files changed, 3014 insertions, 8 deletions
diff --git a/client/js/app/package.json b/client/js/app/package.json index 8ea59b0cc35..1230741e1f2 100644 --- a/client/js/app/package.json +++ b/client/js/app/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "react": "^18", + "react-bootstrap": "^2.4.0", "react-dom": "^18" }, "devDependencies": { diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx new file mode 100644 index 00000000000..47b5a67875b --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx @@ -0,0 +1,46 @@ +import React, { useContext, useState } from 'react'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; +import SimpleButton from './SimpleButton'; + +export default function AddPropertyButton({ id }) { + const { inputs, setInputs, childMap } = useContext(QueryInputContext); + const [childId, setChildId] = useState(1); + + /** + * Add a child to the input that has the provided id + */ + const addChildProperty = () => { + const newInputs = inputs.slice(); + let currentId = id.substring(0, 1); + let index = newInputs.findIndex((element) => element.id === currentId); //get the index of the root parent + let children = newInputs[index].children; + let parentType = newInputs[index].type; + for (let i = 3; i < id.length + 1; i += 2) { + currentId = id.substring(0, i); + index = children.findIndex((element) => element.id === currentId); + parentType = parentType + '_' + children[index].type; + children = children[index].children; + } + let type = childMap[parentType]; + children.push({ + id: id + '.' + childId, + type: type[Object.keys(type)[0]].name, + typeof: type[Object.keys(type)[0]].type, + input: '', + hasChildren: false, + children: [], + }); + setInputs(newInputs); + setChildId((childId) => childId + 1); + }; + + return ( + <SimpleButton + id={`propb${id}`} + className={'addpropsbutton'} + onClick={addChildProperty} + > + + Add property + </SimpleButton> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx new file mode 100644 index 00000000000..8aeaff971bf --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx @@ -0,0 +1,48 @@ +import React, { useContext } from 'react'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; +import Tooltip from 'react-bootstrap/Tooltip'; + +export default function AddQueryInput() { + const { inputs, setInputs, id, setId } = useContext(QueryInputContext); + + /** + * Adds a new element to inputs. + * @param {Event} e Event that happened. + */ + const updateInputs = (e) => { + e.preventDefault(); + setId((id) => id + 1); + setInputs((prevInputs) => [ + ...prevInputs, + { + id: `${id + 1}`, + type: 'yql', + typeof: 'String', + input: '', + hasChildren: false, + children: [], + }, + ]); + }; + + return ( + <OverlayTrigger + placement="right" + delay={{ show: 250, hide: 400 }} + overlay={<Tooltip id="button-tooltip">Add row</Tooltip>} + > + <span> + <button + id="addRow" + className="addRow" + height="0" + width="0" + onClick={updateInputs} + > + + + </button> + </span> + </OverlayTrigger> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx new file mode 100644 index 00000000000..f620146bea5 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx @@ -0,0 +1,25 @@ +import React from 'react'; + +export default function ImageButton({ + onClick, + children, + className, + id, + image, + height = '15', + width = '15', + style, +}) { + return ( + <button id={id} className={className} onClick={onClick}> + <img + src={image} + height={height} + width={width} + style={style} + alt="Missing" + /> + {children} + </button> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx new file mode 100644 index 00000000000..2cb9c7d6e9a --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; +import Tooltip from 'react-bootstrap/Tooltip'; +import ImageButton from './ImageButton'; + +export default function OverlayImageButton({ + onClick, + children, + className, + id, + image, + height = '15', + width = '15', + style, + tooltip, +}) { + return ( + <OverlayTrigger + placement="right" + delay={{ show: 250, hide: 400 }} + overlay={<Tooltip id="button-tooltip">{tooltip}</Tooltip>} + > + <span> + <ImageButton + id={id} + className={className} + image={image} + height={height} + width={width} + style={style} + onClick={onClick} + > + {children} + </ImageButton> + </span> + </OverlayTrigger> + ); +} + +// diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx new file mode 100644 index 00000000000..73dce637500 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx @@ -0,0 +1,75 @@ +import React, { useContext, useState } from 'react'; +import ImageButton from './ImageButton'; +import pasteImage from '../../assets/img/paste.svg'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; + +export default function PasteJSONButton() { + const { inputs, setInputs, id, setId, levelZeroParameters, childMap } = + useContext(QueryInputContext); + const [paste, setPaste] = useState(false); + + const handleClick = (e) => { + alert('Button is non-functional'); + // setPaste(true); + // window.addEventListener('paste', handlePaste) + }; + + const handlePaste = (e) => { + setPaste(false); + const pastedData = e.clipboardData.getData('text'); + alert('Converting JSON: \n\n ' + pastedData); + window.removeEventListener('paste', handlePaste); + convertPastedJSON(pastedData); + }; + + const convertPastedJSON = (pastedData) => { + try { + var json = JSON.parse(pastedData); + setId(1); + const newInputs = buildFromJSON(json, id); + setInputs(newInputs); + } catch (error) { + alert('Could not parse JSON, with error-message: \n\n' + error.message); + } + }; + + const buildFromJSON = (json, id) => { + let newInputs = []; + let keys = Object.keys(json); + for (let i = 0; i < keys.length; i++) { + let childId = 1; + let newInput = { id: id, type: keys[i] }; + if (json[keys[i]] === Object) { + newInput['typeof'] = 'Parent'; + newInput['input'] = ''; + newInput['hasChildren'] = true; + let tempId = id + '.' + childId; + childId += 1; + newInput['children'] = buildFromJSON(json[keys[i]], tempId); + } else { + newInput['typeof'] = levelZeroParameters[keys[i]].type; + newInput['input'] = json[keys[i]]; + newInput['hasChildren'] = false; + newInput['children'] = []; + } + setId(id + 1); + console.log(newInput); + newInputs.push(newInput); + } + return newInputs; + }; + + return ( + <> + <ImageButton + id="pasteJSON" + className="pasteJSON" + image={pasteImage} + style={{ marginTop: '-2px', marginRight: '3px' }} + onClick={handleClick} + > + {paste ? 'Press CMD + V' : 'Paste JSON'} + </ImageButton> + </> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx new file mode 100644 index 00000000000..789fc387b38 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx @@ -0,0 +1,29 @@ +import React, { useContext, useState } from 'react'; +import { QueryContext } from '../Contexts/QueryContext'; +import SimpleButton from './SimpleButton'; + +export default function ShowQueryButton() { + const { query, showQuery, setShowQuery } = useContext(QueryContext); + + const handleClick = () => { + setShowQuery(!showQuery); + }; + + return ( + <> + <SimpleButton className="showJSON" onClick={handleClick}> + Show query JSON + </SimpleButton> + {showQuery && ( + <textarea + id="jsonquery" + className="responsebox" + readOnly + cols="70" + rows="15" + value={query} + ></textarea> + )} + </> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx new file mode 100644 index 00000000000..a153c9577e4 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +export default function SimpleButton({ onClick, children, className, id }) { + return ( + <button id={id} className={className} onClick={onClick}> + {children} + </button> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx new file mode 100644 index 00000000000..00644c21d41 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx @@ -0,0 +1,14 @@ +import React, { createContext, useState } from 'react'; + +export const QueryContext = createContext(); + +export const QueryProvider = (prop) => { + const [query, setQuery] = useState(''); + const [showQuery, setShowQuery] = useState(false); + + return ( + <QueryContext.Provider value={{ query, setQuery, showQuery, setShowQuery }}> + {prop.children} + </QueryContext.Provider> + ); +}; diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx new file mode 100644 index 00000000000..064bf51795b --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx @@ -0,0 +1,159 @@ +import React, { useState, createContext } from 'react'; + +export const QueryInputContext = createContext(); + +export const QueryInputProvider = (prop) => { + // This is the id of the newest QueryInput, gets updated each time a new one is added + const [id, setId] = useState(1); + + // These are the methods that can be chosen in a QueryInput + const levelZeroParameters = { + yql: { name: 'yql', type: 'String', hasChildren: false }, + hits: { name: 'hits', type: 'Integer', hasChildren: false }, + offset: { name: 'offset', type: 'Integer', hasChildren: false }, + queryProfile: { name: 'queryProfile', type: 'String', hasChildren: false }, + noCache: { name: 'noCache', type: 'Boolean', hasChildren: false }, + groupingSessionCache: { + name: 'groupingSessionCache', + type: 'Boolean', + hasChildren: false, + }, + searchChain: { name: 'searchChain', type: 'String', hasChildren: false }, + timeout: { name: 'timeout', type: 'Float', hasChildren: false }, + tracelevel: { name: 'tracelevel', type: 'Parent', hasChildren: true }, + traceLevel: { name: 'traceLevel', type: 'Integer', hasChildren: false }, + explainLevel: { name: 'explainLevel', type: 'Integer', hasChildren: false }, + explainlevel: { name: 'explainlevel', type: 'Integer', hasChildren: false }, + model: { name: 'model', type: 'Parent', hasChildren: true }, + ranking: { name: 'ranking', type: 'Parent', hasChildren: true }, + collapse: { name: 'collapse', type: 'Parent', hasChildren: true }, + collapsesize: { name: 'collapsesize', type: 'Integer', hasChildren: false }, + collapsefield: { + name: 'collapsefield', + type: 'String', + hasChildren: false, + }, + presentation: { name: 'presentation', type: 'Parent', hasChildren: true }, + pos: { name: 'pos', type: 'Parent', hasChildren: true }, + streaming: { name: 'streaming', type: 'Parent', hasChildren: true }, + rules: { name: 'rules', type: 'Parent', hasChildren: true }, + recall: { name: 'recall', type: 'List', hasChildren: false }, + user: { name: 'user', type: 'String', hasChildren: false }, + metrics: { name: 'metrics', type: 'Parent', hasChildren: true }, + }; + + // Children of the levelZeroParameters that have child attributes + const childMap = { + collapse: { + summary: { name: 'summary', type: 'String', hasChildren: false }, + }, + metrics: { + ignore: { name: 'ignore', type: 'Boolean', hasChildren: false }, + }, + model: { + defaultIndex: { + name: 'defaultIndex', + type: 'String', + hasChildren: false, + }, + encoding: { name: 'encoding', type: 'String', hasChildren: false }, + language: { name: 'language', type: 'String', hasChildren: false }, + queryString: { name: 'queryString', type: 'String', hasChildren: false }, + restrict: { name: 'restrict', type: 'List', hasChildren: false }, + searchPath: { name: 'searchPath', type: 'String', hasChildren: false }, + sources: { name: 'sources', type: 'List', hasChildren: false }, + type: { name: 'type', type: 'String', hasChildren: false }, + }, + pos: { + ll: { name: 'll', type: 'String', hasChildren: false }, + radius: { name: 'radius', type: 'String', hasChildren: false }, + bb: { name: 'bb', type: 'List', hasChildren: false }, + attribute: { name: 'attribute', type: 'String', hasChildren: false }, + }, + presentation: { + bolding: { name: 'bolding', type: 'Boolean', hasChildren: false }, + format: { name: 'format', type: 'String', hasChildren: false }, + summary: { name: 'summary', type: 'String', hasChildren: false }, + template: { name: 'template', type: 'String', hasChildren: false }, + timing: { name: 'timing', type: 'Boolean', hasChildren: false }, + }, + ranking: { + location: { name: 'location', type: 'String', hasChildren: false }, + features: { name: 'features', type: 'String', hasChildren: false }, + listFeatures: { + name: 'listFeatures', + type: 'Boolean', + hasChildren: false, + }, + profile: { name: 'profile', type: 'String', hasChildren: false }, + properties: { name: 'properties', type: 'String', hasChildren: false }, + sorting: { name: 'sorting', type: 'String', hasChildren: false }, + freshness: { name: 'freshness', type: 'String', hasChildren: false }, + queryCache: { name: 'queryCache', type: 'Boolean', hasChildren: false }, + matchPhase: { name: 'matchPhase', type: 'Parent', hasChildren: true }, + }, + ranking_matchPhase: { + maxHits: { name: 'maxHits', type: 'Long', hasChildren: false }, + attribute: { name: 'attribute', type: 'String', hasChildren: false }, + ascending: { name: 'ascending', type: 'Boolean', hasChildren: false }, + diversity: { name: 'diversity', type: 'Parent', hasChildren: true }, + }, + ranking_matchPhase_diversity: { + attribute: { name: 'attribute', type: 'String', hasChildren: false }, + minGroups: { name: 'minGroups', type: 'Long', hasChildren: false }, + }, + rules: { + off: { name: 'off', type: 'Boolean', hasChildren: false }, + rulebase: { name: 'rulebase', type: 'String', hasChildren: false }, + }, + streaming: { + userid: { name: 'userid', type: 'Integer', hasChildren: false }, + groupname: { name: 'groupname', type: 'String', hasChildren: false }, + selection: { name: 'selection', type: 'String', hasChildren: false }, + priority: { name: 'priority', type: 'String', hasChildren: false }, + maxbucketspervisitor: { + name: 'maxbucketspervisitor', + type: 'Integer', + hasChildren: false, + }, + }, + trace: { + timestamps: { name: 'timestamps', type: 'Boolean', hasChildren: false }, + }, + tracelevel: { + rules: { name: 'rules', type: 'Integer', hasChildren: false }, + }, + }; + + const firstChoice = levelZeroParameters[Object.keys(levelZeroParameters)[0]]; + + const [inputs, setInputs] = useState([ + { + id: '1', + type: firstChoice.name, + typeof: firstChoice.type, + input: '', + hasChildren: false, + children: [], + }, + ]); + + const [selectedItems, setSelectedItems] = useState([]); + + return ( + <QueryInputContext.Provider + value={{ + inputs, + setInputs, + id, + setId, + levelZeroParameters, + childMap, + selectedItems, + setSelectedItems, + }} + > + {prop.children} + </QueryInputContext.Provider> + ); +}; diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx b/client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx new file mode 100644 index 00000000000..54f5fc955fd --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx @@ -0,0 +1,13 @@ +import React, { createContext, useState } from 'react'; + +export const ResponseContext = createContext(); + +export const ResponseProvider = (prop) => { + const [response, setResponse] = useState(''); + + return ( + <ResponseContext.Provider value={{ response, setResponse }}> + {prop.children} + </ResponseContext.Provider> + ); +}; diff --git a/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx b/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx new file mode 100644 index 00000000000..6ffeae4caed --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Navbar, Container, Nav } from 'react-bootstrap'; + +function CustomNavbar() { + return ( + <Navbar collapseOnSelect variant="default" expand="sm"> + <Container> + <Navbar.Brand href="https://vespa.ai"> + Vespa. Big data. Real time. + </Navbar.Brand> + <Navbar.Toggle aria-controls="responsive-navbar-nav" /> + <Navbar.Collapse id="responsive-navbar-nav"> + <Nav> + <Nav.Link href="https://blog.vespa.ai/">Blog</Nav.Link> + <Nav.Link variant="link" href="https://twitter.com/vespaengine"> + Twitter + </Nav.Link> + <Nav.Link variant="link" href="https://docs.vespa.ai"> + Docs + </Nav.Link> + <Nav.Link variant="link" href="https://github.com/vespa-engine"> + GitHub + </Nav.Link> + <Nav.Link + variant="link" + href="https://docs.vespa.ai/en/getting-started.html" + > + Get Started Now + </Nav.Link> + </Nav> + </Navbar.Collapse> + </Container> + </Navbar> + ); +} + +export default CustomNavbar; diff --git a/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx b/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx new file mode 100644 index 00000000000..b110bce943d --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { Table } from 'react-bootstrap'; + +export default function Footer() { + return ( + <footer> + <Table borderless size="sm"> + <thead className="footer-title"> + <tr> + <th>Resources</th> + <th>Contact</th> + <th>Community</th> + </tr> + </thead> + <tbody> + <tr> + <th> + <a href="https://docs.vespa.ai/en/vespa-quick-start.html"> + Getting Started + </a> + </th> + <th> + <a href="https://twitter.com/vespaengine">Twitter</a> + </th> + <th> + <a href="https://github.com/vespa-engine/vespa/blob/master/CONTRIBUTING.md"> + Contributing + </a> + </th> + </tr> + <tr> + <th> + <a href="https://docs.vespa.ai">Documentation</a> + </th> + <th> + <a href="mailto:info@vespa.ai">info@vespa.ai</a> + </th> + <th> + <a href="https://stackoverflow.com/questions/tagged/vespa"> + Stack Overflow + </a> + </th> + </tr> + <tr> + <th> + <a href="https://github.com/vespa-engine/vespa">Open source</a> + </th> + <th> + <a href="https://github.com/vespa-engine/vespa/issues">Issues</a> + </th> + <th> + <a href="https://gitter.im/vespa-engine/Lobby">Gitter</a> + </th> + </tr> + </tbody> + </Table> + <div className="credits"> + <span>Copyright Yahoo</span> + Licensed under{' '} + <a href="https://github.com/vespa-engine/vespa/blob/master/LICENSE"> + Apache License 2.0 + </a> + , <a href="https://github.com/y7kim/agency-jekyll-theme">Theme</a> by + Rick K. + </div> + </footer> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx new file mode 100644 index 00000000000..d921c53a602 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { OverlayTrigger, Popover } from 'react-bootstrap'; +import image from '../../assets/img/information.svg'; + +export default function Info({ + id, + className = 'tip', + height = 15, + width = 15, +}) { + const popover = ( + <Popover id={`inf${id}`}> + <Popover.Header as="h3">Popover right</Popover.Header> + <Popover.Body>Content</Popover.Body> + </Popover> + ); + + return ( + <> + <OverlayTrigger + placement="right" + delay={{ show: 250, hide: 400 }} + overlay={popover} + > + <span> + <img + src={image} + height={height} + width={width} + className="information" + alt="Missing" + /> + </span> + </OverlayTrigger> + </> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx new file mode 100644 index 00000000000..d9a3417752c --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx @@ -0,0 +1,67 @@ +import { QueryInputContext } from '../Contexts/QueryInputContext'; +import React, { useCallback, useContext, useState } from 'react'; +import SimpleDropDownForm from './SimpleDropDownForm'; + +export default function QueryDropdownForm({ choices, id, child = false }) { + const { + inputs, + setInputs, + levelZeroParameters, + childMap, + selectedItems, + setSelectedItems, + } = useContext(QueryInputContext); + const [choice, setChoice] = useState(); + + /** + * Update the state of inputs to reflect the method chosen from the dropdown. + * If the prevoiusly chosen method had children they are removed. + * @param {Event} e Event containing the new type. + */ + const updateType = (e) => { + e.preventDefault(); + const newType = e.target.value; + const newInputs = inputs.slice(); + let currentId = id.substring(0, 1); + let index = newInputs.findIndex((element) => element.id === currentId); + if (child) { + let parentTypes = newInputs[index].type; + let children = newInputs[index].children; + let childChoices = childMap[parentTypes]; + for (let i = 3; i < id.length; i += 2) { + currentId = id.substring(0, i); + index = children.findIndex((element) => element.id === currentId); + let child = children[index]; + parentTypes = parentTypes + '_' + child.type; + childChoices = childMap[parentTypes]; + children = child.children; + } + index = children.findIndex((element) => element.id === id); + children[index].type = newType; + children[index].hasChildren = childChoices[newType].hasChildren; + children[index].children = []; + children[index].typeof = childChoices[newType].type; + setSelectedItems([...selectedItems, newType]); + } else { + newInputs[index].type = newType; + let hasChildren = levelZeroParameters[newType].hasChildren; + newInputs[index].hasChildren = hasChildren; + newInputs[index].children = []; + newInputs[index].typeof = levelZeroParameters[newType].type; + setSelectedItems([...selectedItems, newType]); + } + setInputs(newInputs); + setChoice(newType); + }; + + //TODO: do not display options that have been chosen + + return ( + <SimpleDropDownForm + id={id} + onChange={updateType} + choices={choices} + value={choice} + ></SimpleDropDownForm> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx new file mode 100644 index 00000000000..c5410ae7c4a --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx @@ -0,0 +1,74 @@ +import React, { useContext } from 'react'; +import SimpleButton from '../Buttons/SimpleButton'; +import SimpleForm from './SimpleForm'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; +import QueryDropdownForm from './QueryDropDownForm'; +import AddPropertyButton from '../Buttons/AddPropertyButton'; +import QueryInputChild from './QueryInputChild'; + +export default function QueryInput() { + const { inputs, setInputs, levelZeroParameters, childMap } = + useContext(QueryInputContext); + + function removeRow(id) { + const newList = inputs.filter((item) => item.id !== id); + setInputs(newList); + } + + const updateInput = (e) => { + e.preventDefault(); + const fid = e.target.id.replace('v', ''); + const newInputs = inputs.slice(); + const index = newInputs.findIndex((element) => element.id === fid); + newInputs[index].input = e.target.value; + setInputs(newInputs); + }; + + const setPlaceholder = (id) => { + try { + const index = inputs.findIndex((element) => element.id === id); + return inputs[index].typeof; + } catch (error) { + console.log(error); + } + }; + + const inputList = inputs.map((value) => { + return ( + <div key={value.id} id={value.id} className="queryinput"> + <QueryDropdownForm choices={levelZeroParameters} id={value.id} /> + {value.hasChildren ? ( + <> + <AddPropertyButton id={value.id} /> + <QueryInputChild id={value.id} /> + </> + ) : ( + <SimpleForm + id={`v${value.id}`} + size="30" + onChange={updateInput} + placeholder={setPlaceholder(value.id)} + /> + )} + <OverlayTrigger + placement="right" + delay={{ show: 250, hide: 400 }} + overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>} + > + <span> + <SimpleButton + id={`b${value.id}`} + className="removeRow" + onClick={() => removeRow(value.id)} + children="-" + ></SimpleButton> + </span> + </OverlayTrigger> + <br /> + </div> + ); + }); + + return <>{inputList}</>; +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx new file mode 100644 index 00000000000..0440d6ef1ba --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx @@ -0,0 +1,194 @@ +import React, { useContext } from 'react'; +import AddPropertyButton from '../Buttons/AddPropertyButton'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; +import QueryDropdownForm from './QueryDropDownForm'; +import SimpleForm from './SimpleForm'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; +import SimpleButton from '../Buttons/SimpleButton'; + +export default function QueryInputChild({ id }) { + const { inputs, setInputs, childMap } = useContext(QueryInputContext); + + let index = inputs.findIndex((element) => element.id === id); + let childArray = inputs[index].children; + let currentTypes = inputs[index].type; + + /** + * Update the state of inputs to reflect what is written into the form. + * @param {Event} e Event containing the new input. + */ + const updateInput = (e) => { + e.preventDefault(); + let newInputs = inputs.slice(); + let iterId = e.target.id.replace('v', ''); + let currentId = iterId.substring(0, 1); + let index = newInputs.findIndex((element) => element.id === currentId); + let children = newInputs[index].children; + const traversedChildren = traverseChildren(iterId, children, ''); + children = traversedChildren.children; + index = children.findIndex((element) => element.id === iterId); + children[index].input = e.target.value; + setInputs(newInputs); + }; + + /** + * Returns a placeholder text for a SimpleForm component. + * @param {String} id The id of the SimpleForm component. + * @returns {String} The placeholder text + */ + const setPlaceHolder = (id) => { + let currentId = id.substring(0, 1); + let index = inputs.findIndex((element) => element.id === currentId); + let combinedType = inputs[index].type; + let children = inputs[index].children; + if (id.length > 3) { + const traversedChildren = traverseChildren(id, children, combinedType); + combinedType = traversedChildren.combinedType; + children = traversedChildren.children; + const currentChoice = childMap[combinedType]; + index = children.findIndex((element) => element.id === id); + combinedType = children[index].type; + return currentChoice[combinedType].type; + } else { + const currentChoice = childMap[combinedType]; + index = children.findIndex((element) => element.id === id); + combinedType = children[index].type; + return currentChoice[combinedType].type; + } + }; + + /** + * Removes the row with the provided id. + * @param {String} id Id of row. + */ + const removeRow = (id) => { + let newInputs = inputs.slice(); + let currentId = id.substring(0, 1); + let index = newInputs.findIndex((element) => element.id === currentId); + let children = newInputs[index].children; + const traversedChildren = traverseChildren(id, children, ''); + index = traversedChildren.children.findIndex((element) => element === id); + traversedChildren.children.splice(index, 1); + setInputs(newInputs); + }; + + /** + * Traverses the children until a child with the provided id is reached. + * @param {String} id Id of the innermost child. + * @param {Array} children Array containing serveral child objects. + * @param {String} combinedType The combined type of all traversed children + * @returns {Object} An object containing the children of the child with the provided id and the combined type. + */ + function traverseChildren(id, children, combinedType) { + let currentId; + let index; + for (let i = 3; i < id.length; i += 2) { + currentId = id.substring(0, i); + index = children.findIndex((element) => element.id === currentId); + combinedType = combinedType + '_' + children[index].type; + children = children[index].children; + } + return { children: children, combinedType: combinedType }; + } + + const inputList = childArray.map((child) => { + return ( + <div key={child.id} id={child.id}> + <QueryDropdownForm + choices={childMap[currentTypes]} + id={child.id} + child={true} + /> + {child.hasChildren ? ( + <> + <AddPropertyButton id={child.id} /> + </> + ) : ( + <SimpleForm + id={`v${child.id}`} + size="30" + onChange={updateInput} + placeholder={setPlaceHolder(child.id)} + /> + )} + <OverlayTrigger + placement="right" + delay={{ show: 250, hide: 400 }} + overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>} + > + <span> + <SimpleButton + id={`b${child.id}`} + className="removeRow" + onClick={() => removeRow(child.id)} + children="-" + ></SimpleButton> + </span> + </OverlayTrigger> + <br /> + <Child + type={currentTypes + '_' + child.type} + child={child} + onChange={updateInput} + placeholder={setPlaceHolder} + removeRow={removeRow} + /> + </div> + ); + }); + + return <>{inputList}</>; +} + +function Child({ child, type, onChange, placeholder, removeRow }) { + const { childMap } = useContext(QueryInputContext); + + const nestedChildren = (child.children || []).map((child) => { + return ( + <div key={child.id}> + <QueryDropdownForm + choices={childMap[type]} + id={child.id} + child={true} + /> + {child.hasChildren ? ( + <> + <AddPropertyButton id={child.id} /> + </> + ) : ( + <SimpleForm + id={`v${child.id}`} + size="30" + onChange={onChange} + placeholder={placeholder(child.id)} + /> + )} + <OverlayTrigger + placement="right" + delay={{ show: 250, hide: 400 }} + overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>} + > + <span> + <SimpleButton + id={`b${child.id}`} + className="removeRow" + onClick={() => removeRow(child.id)} + children="-" + ></SimpleButton> + </span> + </OverlayTrigger> + <br /> + <Child + child={child} + id={child.id} + type={type + '_' + child.type} + onChange={onChange} + placeholder={placeholder} + removeRow={removeRow} + /> + </div> + ); + }); + + return <>{nestedChildren}</>; +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx new file mode 100644 index 00000000000..08c65238434 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx @@ -0,0 +1,17 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { ResponseContext } from '../Contexts/ResponseContext'; + +export default function ResponseBox() { + const { response } = useContext(ResponseContext); + + return ( + <textarea + id="responsetext" + className="responsebox" + readOnly + cols="70" + rows="25" + value={response} + /> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx new file mode 100644 index 00000000000..9ee370de7c9 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx @@ -0,0 +1,108 @@ +import React, { useContext, useEffect, useState } from 'react'; +import SimpleDropDownForm from './SimpleDropDownForm'; +import SimpleButton from '../Buttons/SimpleButton'; +import SimpleForm from './SimpleForm'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; +import { ResponseContext } from '../Contexts/ResponseContext'; +import { QueryContext } from '../Contexts/QueryContext'; + +export default function SendQuery() { + const { inputs } = useContext(QueryInputContext); + const { setResponse } = useContext(ResponseContext); + const { showQuery, setQuery } = useContext(QueryContext); + + const messageMethods = { post: { name: 'POST' }, get: { name: 'GET' } }; + const [method, setMethod] = useState(messageMethods.post.name); + const [url, setUrl] = useState('http://localhost:8080/search/'); + + useEffect(() => { + const query = buildJSON(inputs, {}); + setQuery(JSON.stringify(query, undefined, 4)); + }, [showQuery]); + + const updateMethod = (e) => { + e.preventDefault(); + const newMethod = e.target.value; + setMethod(newMethod); + }; + + function handleClick() { + const json = buildJSON(inputs, {}); + send(json); + } + + async function send(json) { + let responses = await fetch(url, { + method: method, + headers: { 'Content-Type': 'application/json;charset=utf-8' }, + body: JSON.stringify(json), + }); + if (responses.ok) { + let result = await responses.json(); + let resultObject = JSON.stringify(result, undefined, 4); + setResponse(resultObject); + } + } + + function buildJSON(inputs, json) { + let queryJson = json; + for (let i = 0; i < inputs.length; i++) { + let current = inputs[i]; + let key = current.type; + if (current.hasChildren) { + let child = {}; + child = buildJSON(current.children, child); + queryJson[key] = child; + } else { + queryJson[key] = parseInput(current.input, current.typeof); + } + } + return queryJson; + } + + function parseInput(input, type) { + switch (type) { + case 'Integer': + case 'Long': + return parseInt(input); + break; + + case 'Float': + return parseFloat(input); + break; + + case 'Boolean': + return input.toLowerCase() === 'true' ? true : false; + break; + + default: + return input; + } + } + + const updateUrl = (e) => { + const newUrl = e.target.value; + setUrl(newUrl); + }; + + return ( + <> + <SimpleDropDownForm + choices={messageMethods} + id="method" + className="methodselector" + onChange={updateMethod} + /> + <SimpleForm + id="url" + className="textbox" + initial={url} + size="30" + onChange={updateUrl} + /> + <SimpleButton id="send" className="button" onClick={handleClick}> + Send + </SimpleButton> + </> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx new file mode 100644 index 00000000000..01288cea44f --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx @@ -0,0 +1,38 @@ +import React, { useContext, useEffect } from 'react'; +import { QueryInputContext } from '../Contexts/QueryInputContext'; + +export default function SimpleDropDownForm({ + choices, + id, + className = 'input', + onChange, + value, +}) { + const { selectedItems } = useContext(QueryInputContext); + + //FIXME: using the filtered list to render options results in dropdown not changing the displayed selection to what was actually selected. + let filtered = Object.keys(choices).filter( + (choice) => !selectedItems.includes(choice) + ); + useEffect(() => { + filtered = Object.keys(choices).filter( + (choice) => !selectedItems.includes(choice) + ); + }, [selectedItems]); + + const options = Object.keys(choices).map((choice) => { + return ( + <option className="options" key={choice} value={choices[choice].name}> + {choices[choice].name} + </option> + ); + }); + + return ( + <form id={id}> + <select className={className} id={id} value={value} onChange={onChange}> + {options} + </select> + </form> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx new file mode 100644 index 00000000000..bb6aaa13529 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { useState } from 'react'; + +export default function SimpleForm({ + id, + className = 'propvalue', + initial, + size = '20', + onChange, + placeholder, +}) { + SimpleForm.defaultProps = { + onChange: handleChange, + }; + const [input, setValue] = useState(initial); + + function handleChange(e) { + setValue(e.target.value); + } + + return ( + <form className={className} id={id}> + <input + size={size} + type="text" + id={id} + className={className} + defaultValue={initial} + onChange={onChange} + placeholder={placeholder} + /> + </form> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx new file mode 100644 index 00000000000..022b250da7c --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +export default function TextBox({ id, className, children }) { + return ( + <p className={className} id={id}> + {children} + </p> + ); +} diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png b/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png Binary files differnew file mode 100644 index 00000000000..ac87f8e94d0 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png b/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png Binary files differnew file mode 100644 index 00000000000..33063432c20 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg b/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg new file mode 100644 index 00000000000..eada154413a --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3;" xml:space="preserve" width="512px" height="512px" class=""><g><g> + <g> + <path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/> + <path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/> + </g> +</g></g> </svg> diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg b/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg new file mode 100644 index 00000000000..d78d2f9c17f --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg @@ -0,0 +1 @@ +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36"><defs><style>.cls-1{clip-path:url(#clip-path);}.cls-2{clip-path:url(#clip-path-2);}.cls-3{clip-path:url(#clip-path-3);}</style><clipPath id="clip-path"><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/></clipPath><clipPath id="clip-path-2"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/></clipPath><clipPath id="clip-path-3"><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/></clipPath></defs><title>down-arrow</title><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/><g class="cls-1"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="-4.15" y="-4.15" width="44.3" height="44.3" fill="#FFFFFF"/></g></g><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/><g class="cls-3"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="4.35" y="8.21" width="27.3" height="21.12" fill="#FFFFFF"/></g></g></svg> diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png b/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png Binary files differnew file mode 100644 index 00000000000..65702f8b91f --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/information.svg b/client/js/app/src/app/pages/querybuilder/assets/img/information.svg new file mode 100644 index 00000000000..da42cf2caf6 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/information.svg @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px"><g><g> + <g> + <g> + <circle cx="256" cy="378.5" r="25" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/> + <path d="M256,0C114.516,0,0,114.497,0,256c0,141.484,114.497,256,256,256c141.484,0,256-114.497,256-256 C512,114.516,397.503,0,256,0z M256,472c-119.377,0-216-96.607-216-216c0-119.377,96.607-216,216-216 c119.377,0,216,96.607,216,216C472,375.377,375.393,472,256,472z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/> + <path d="M256,128.5c-44.112,0-80,35.888-80,80c0,11.046,8.954,20,20,20s20-8.954,20-20c0-22.056,17.944-40,40-40 c22.056,0,40,17.944,40,40c0,22.056-17.944,40-40,40c-11.046,0-20,8.954-20,20v50c0,11.046,8.954,20,20,20 c11.046,0,20-8.954,20-20v-32.531c34.466-8.903,60-40.26,60-77.469C336,164.388,300.112,128.5,256,128.5z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/> + </g> + </g> +</g></g> </svg> diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg b/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg new file mode 100644 index 00000000000..b2edac680bf --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 561 561" style="enable-background:new 0 0 561 561;" xml:space="preserve" class=""><g><g> + <g id="content-paste"> + <path d="M459,51H351.9c-10.2-30.6-38.25-51-71.4-51c-33.15,0-61.2,20.4-71.4,51H102c-28.05,0-51,22.95-51,51v408 c0,28.05,22.95,51,51,51h357c28.05,0,51-22.95,51-51V102C510,73.95,487.05,51,459,51z M280.5,51c15.3,0,25.5,10.2,25.5,25.5 S295.8,102,280.5,102S255,91.8,255,76.5S265.2,51,280.5,51z M459,510H102V102h51v76.5h255V102h51V510z" data-original="#000000" class="active-path" data-old_color="#F9F6F6" fill="#FBF9F9"/> + </g> +</g></g> </svg> diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg b/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg new file mode 100644 index 00000000000..c5381f9f232 --- /dev/null +++ b/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px" class=""><g><g> + <g> + <path d="M482.195,226.196C482.195,101.471,380.725,0,256.001,0S29.805,101.471,29.805,226.196c0,7.409,6.007,13.416,13.416,13.416 s13.416-6.008,13.416-13.416c0-109.93,89.434-199.363,199.363-199.363s199.363,89.434,199.363,199.363 c0,109.928-89.434,199.362-199.363,199.362h-23.276l33.282-37.255c4.937-5.525,4.458-14.007-1.067-18.944 c-5.525-4.937-14.008-4.457-18.944,1.068l-47.576,53.255c-7.788,8.718-7.788,21.866,0,30.584l47.576,53.255 c2.651,2.968,6.322,4.478,10.01,4.478c3.181,0,6.375-1.126,8.934-3.41c5.526-4.937,6.004-13.419,1.067-18.944l-33.282-37.255 h23.276C380.725,452.39,482.195,350.919,482.195,226.196z" data-original="#000000" class="active-path" data-old_color="#F0EDED" fill="#F3F2F2"/> + </g> +</g></g> </svg> diff --git a/client/js/app/src/app/pages/querybuilder/query-builder.jsx b/client/js/app/src/app/pages/querybuilder/query-builder.jsx index fe158fb5b98..472ec1efe49 100644 --- a/client/js/app/src/app/pages/querybuilder/query-builder.jsx +++ b/client/js/app/src/app/pages/querybuilder/query-builder.jsx @@ -1,6 +1,71 @@ -import React from 'react'; -import { Container } from 'app/components'; +import React, { useContext } from 'react'; +import SimpleButton from './Components/Buttons/SimpleButton'; +import QueryInput from './Components/Text/QueryInput'; +import TextBox from './Components/Text/TextBox'; +import ImageButton from './Components/Buttons/ImageButton'; +import OverlayImageButton from './Components/Buttons/OverlayImageButton'; +import AddQueryInput from './Components/Buttons/AddQueryInputButton'; +import { QueryInputProvider } from './Components/Contexts/QueryInputContext'; +import SendQuery from './Components/Text/SendQuery'; +import { + ResponseContext, + ResponseProvider, +} from './Components/Contexts/ResponseContext'; +import ResponseBox from './Components/Text/ResponseBox'; + +import copyImage from './assets/img/copy.svg'; + +import '../../styles/agency.css'; +import '../../styles/vespa.css'; +import ShowQueryButton from './Components/Buttons/ShowQueryButton'; +import { QueryProvider } from './Components/Contexts/QueryContext'; +import PasteJSONButton from './Components/Buttons/PasteJSONButton'; + +//import 'bootstrap/dist/css/bootstrap.min.css'; //TODO: Find out how to get this css export function QueryBuilder() { - return <Container>query builder</Container>; + return ( + <> + <header> + <div className="intro container"> + <TextBox className={'intro-lead-in'}>Vespa Search Engine</TextBox> + <TextBox className={'intro-long'}> + Select the method for sending a request and construct a query. + </TextBox> + <ResponseProvider> + <QueryProvider> + <QueryInputProvider> + <SendQuery /> + <br /> + <div id="request"> + <QueryInput /> + </div> + <br /> + <AddQueryInput /> + <br /> + <PasteJSONButton /> + </QueryInputProvider> + <ShowQueryButton /> + </QueryProvider> + <TextBox className="response">Response</TextBox> + <ResponseBox /> + </ResponseProvider> + <OverlayImageButton + className="intro-copy" + image={copyImage} + height="30" + width="30" + tooltip="Copy" + onClick={() => { + alert('Button is non-functional'); + }} + > + Copy + </OverlayImageButton> + <br /> + <br /> + </div> + </header> + </> + ); } diff --git a/client/js/app/src/app/styles/agency.css b/client/js/app/src/app/styles/agency.css new file mode 100644 index 00000000000..e17809338c6 --- /dev/null +++ b/client/js/app/src/app/styles/agency.css @@ -0,0 +1,895 @@ +/*! + * Start Bootstrap - Agency Bootstrap Theme (http://startbootstrap.com) + * Code licensed under the Apache License v2.0. + * For details, see http://www.apache.org/licenses/LICENSE-2.0. + */ +:root { + --primary: #188fff; + --secondary: #003abc; + --secondary-dark: #333; + --muted: #777; + + --fontprimary: HelveticaNeue, Helvetica, Arial, sans-serif; + --fontsecondary: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; +} + +body { + overflow-x: hidden; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + color: var(--secondary-dark); +} + +.text-muted { + color: var(--muted); +} + +.text-primary { + color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +p { + font-size: 14px; + line-height: 1.75; +} + +p.large { + font-size: 16px; +} + +a, +a:hover, +a:focus, +a:active, +a.active { + outline: 0; +} + +a { + color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +a:hover, +a:focus, +a:active, +a.active { + color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 700; +} + +.img-centered { + margin: 0 auto; +} + +.bg-light-gray { + background-color: #f7f7f7; +} + +.bg-darkest-gray { + background-color: #222; +} + +.btn-primary { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 700; + color: #fff; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + color: #fff; + background-color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; +} + +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.btn-primary .badge { + color: HelveticaNeue, Helvetica, Arial, sans-serif; + background-color: #fff; +} + +.btn-xl { + padding: 20px 40px; + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + border-radius: 3px; + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-size: 18px; + font-weight: 700; + color: #fff; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.btn-xl:hover, +.btn-xl:focus, +.btn-xl:active, +.btn-xl.active, +.open .dropdown-toggle.btn-xl { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + color: #fff; + background-color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; +} + +.btn-xl:active, +.btn-xl.active, +.open .dropdown-toggle.btn-xl { + background-image: none; +} + +.btn-xl.disabled, +.btn-xl[disabled], +fieldset[disabled] .btn-xl, +.btn-xl.disabled:hover, +.btn-xl[disabled]:hover, +fieldset[disabled] .btn-xl:hover, +.btn-xl.disabled:focus, +.btn-xl[disabled]:focus, +fieldset[disabled] .btn-xl:focus, +.btn-xl.disabled:active, +.btn-xl[disabled]:active, +fieldset[disabled] .btn-xl:active, +.btn-xl.disabled.active, +.btn-xl[disabled].active, +fieldset[disabled] .btn-xl.active { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.btn-xl .badge { + color: HelveticaNeue, Helvetica, Arial, sans-serif; + background-color: #fff; +} + +.navbar-default { + border-color: transparent; + background-color: #222; +} + +.navbar-default .navbar-brand { + font-family: 'Kaushan Script', 'Helvetica Neue', Helvetica, Arial, cursive; + color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus, +.navbar-default .navbar-brand:active, +.navbar-default .navbar-brand.active { + color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; +} + +.navbar-default .navbar-collapse { + border-color: rgba(255, 255, 255, 0.02); +} + +.navbar-default .navbar-toggle { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #fff; +} + +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.navbar-default .nav li a { + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 400; + letter-spacing: 1px; + color: #fff; + -webkit-transition: all ease 0.35s; + -moz-transition: all ease 0.35s; + transition: all ease 0.35s; +} + +.navbar-default .nav li a:hover, +.navbar-default .nav li a:focus { + outline: 0; + color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.navbar-default .navbar-nav > .active > a { + border-radius: 0; + color: #fff; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #fff; + background-color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; +} + +@media (min-width: 768px) { + .navbar-default { + padding: 25px 0; + border: 0; + background-color: transparent; + -webkit-transition: padding 0.3s; + -moz-transition: padding 0.3s; + transition: padding 0.3s; + } + + .navbar-default .navbar-brand { + font-size: 2em; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + } + + .navbar-default .navbar-nav > .active > a { + border-radius: 3px; + } + + .navbar-default.navbar-shrink { + padding: 10px 0; + background-color: #222; + } + + .navbar-default.navbar-shrink .navbar-brand { + font-size: 1.5em; + } +} + +header { + text-align: center; + color: #fff; + background-attachment: scroll; + /*background-image: url('../img/header-bg.png');*/ + background-position: center center; + background-repeat: none; + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + -o-background-size: cover; +} + +header .intro-text { + padding-top: 100px; + padding-bottom: 50px; +} + +header .intro-text .intro-lead-in { + margin-bottom: 25px; + font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; + font-size: 22px; + font-style: italic; + line-height: 22px; +} + +header .intro-text .intro-heading { + margin-bottom: 25px; + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-size: 50px; + font-weight: 700; + line-height: 50px; +} + +@media (min-width: 768px) { + header .intro-text { + padding-top: 300px; + padding-bottom: 200px; + } + + header .intro-text .intro-lead-in { + margin-bottom: 25px; + font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; + font-size: 40px; + font-style: italic; + line-height: 40px; + } + + header .intro-text .intro-heading { + margin-bottom: 50px; + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-size: 75px; + font-weight: 700; + line-height: 75px; + } +} + +section { + padding: 100px 0; +} + +section h2.section-heading { + margin-top: 0; + margin-bottom: 15px; + font-size: 40px; +} + +section h3.section-subheading { + margin-bottom: 75px; + text-transform: none; + font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; + font-size: 16px; + font-style: italic; + font-weight: 400; +} + +@media (min-width: 768px) { + section { + padding: 150px 0; + } +} + +.service-heading { + margin: 15px 0; + text-transform: none; +} + +#portfolio .portfolio-item { + right: 0; + margin: 0 0 15px; +} + +#portfolio .portfolio-item .portfolio-link { + display: block; + position: relative; + margin: 0 auto; + max-width: 400px; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + -webkit-transition: all ease 0.5s; + -moz-transition: all ease 0.5s; + transition: all ease 0.5s; + background: rgba(254, 209, 54, 0.9); /* Fallback when no plugin support */ + background: rgba(var(--primary) | hex_to_rgb | join: ','}}, 0.9); +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover { + opacity: 1; +} + +#portfolio + .portfolio-item + .portfolio-link + .portfolio-hover + .portfolio-hover-content { + position: absolute; + top: 50%; + width: 100%; + height: 20px; + margin-top: -12px; + text-align: center; + font-size: 20px; + color: #fff; +} + +#portfolio + .portfolio-item + .portfolio-link + .portfolio-hover + .portfolio-hover-content + i { + margin-top: -12px; +} + +#portfolio + .portfolio-item + .portfolio-link + .portfolio-hover + .portfolio-hover-content + h3, +#portfolio + .portfolio-item + .portfolio-link + .portfolio-hover + .portfolio-hover-content + h4 { + margin: 0; +} + +#portfolio .portfolio-item .portfolio-caption { + margin: 0 auto; + padding: 25px; + max-width: 400px; + text-align: center; + background-color: #fff; +} + +#portfolio .portfolio-item .portfolio-caption h4 { + margin: 0; + text-transform: none; +} + +#portfolio .portfolio-item .portfolio-caption p { + margin: 0; + font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; + font-size: 16px; + font-style: italic; +} + +#portfolio * { + z-index: 2; +} + +@media (min-width: 767px) { + #portfolio .portfolio-item { + margin: 0 0 30px; + } +} + +.timeline { + position: relative; + padding: 0; + list-style: none; +} + +.timeline:before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 40px; + width: 2px; + margin-left: -1.5px; + background-color: #f1f1f1; +} + +.timeline > li { + position: relative; + margin-bottom: 50px; + min-height: 50px; +} + +.timeline > li:before, +.timeline > li:after { + content: ' '; + display: table; +} + +.timeline > li:after { + clear: both; +} + +.timeline > li .timeline-panel { + float: right; + position: relative; + width: 100%; + padding: 0 20px 0 100px; + text-align: left; +} + +.timeline > li .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline > li .timeline-image { + z-index: 100; + position: absolute; + left: 0; + width: 80px; + height: 80px; + margin-left: 0; + border: 7px solid #f1f1f1; + border-radius: 100%; + text-align: center; + color: #fff; + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.timeline > li .timeline-image h4 { + margin-top: 12px; + font-size: 10px; + line-height: 14px; +} + +.timeline > li.timeline-inverted > .timeline-panel { + float: right; + padding: 0 20px 0 100px; + text-align: left; +} + +.timeline > li.timeline-inverted > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li.timeline-inverted > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline > li:last-child { + margin-bottom: 0; +} + +.timeline .timeline-heading h4 { + margin-top: 0; + color: inherit; +} + +.timeline .timeline-heading h4.subheading { + text-transform: none; +} + +.timeline .timeline-body > p, +.timeline .timeline-body > ul { + margin-bottom: 0; +} + +@media (min-width: 768px) { + .timeline:before { + left: 50%; + } + + .timeline > li { + margin-bottom: 100px; + min-height: 100px; + } + + .timeline > li .timeline-panel { + float: left; + width: 41%; + padding: 0 20px 20px 30px; + text-align: right; + } + + .timeline > li .timeline-image { + left: 50%; + width: 100px; + height: 100px; + margin-left: -50px; + } + + .timeline > li .timeline-image h4 { + margin-top: 16px; + font-size: 13px; + line-height: 18px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + float: right; + padding: 0 30px 20px 20px; + text-align: left; + } +} + +@media (min-width: 992px) { + .timeline > li { + min-height: 150px; + } + + .timeline > li .timeline-panel { + padding: 0 20px 20px; + } + + .timeline > li .timeline-image { + width: 150px; + height: 150px; + margin-left: -75px; + } + + .timeline > li .timeline-image h4 { + margin-top: 30px; + font-size: 18px; + line-height: 26px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + padding: 0 20px 20px; + } +} + +@media (min-width: 1200px) { + .timeline > li { + min-height: 170px; + } + + .timeline > li .timeline-panel { + padding: 0 20px 20px 100px; + } + + .timeline > li .timeline-image { + width: 170px; + height: 170px; + margin-left: -85px; + } + + .timeline > li .timeline-image h4 { + margin-top: 40px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + padding: 0 100px 20px 20px; + } +} + +.team-member { + margin-bottom: 50px; + text-align: center; +} + +.team-member img { + margin: 0 auto; + border: 7px solid #fff; +} + +.team-member h4 { + margin-top: 25px; + margin-bottom: 0; + text-transform: none; +} + +.team-member p { + margin-top: 0; +} + +aside.clients img { + margin: 50px auto; +} + +section#contact { + background-color: #222; + /* background-image: url('../img/map-image.png'); */ + background-position: center; + background-repeat: no-repeat; +} + +section#contact .section-heading { + color: #fff; +} + +section#contact .form-group { + margin-bottom: 25px; +} + +section#contact .form-group input, +section#contact .form-group textarea { + padding: 20px; +} + +section#contact .form-group input.form-control { + height: auto; +} + +section#contact .form-group textarea.form-control { + height: 236px; +} + +section#contact .form-control:focus { + border-color: HelveticaNeue, Helvetica, Arial, sans-serif; + box-shadow: none; +} + +section#contact::-webkit-input-placeholder { + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 700; + color: #bbb; +} + +section#contact:-moz-placeholder { + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 700; + color: #bbb; +} + +section#contact::-moz-placeholder { + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 700; + color: #bbb; +} + +section#contact:-ms-input-placeholder { + text-transform: uppercase; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + font-weight: 700; + color: #bbb; +} + +section#contact .text-danger { + color: #e74c3c; +} + +footer { + padding: 25px 0; + text-align: center; +} + +footer span.copyright { + text-transform: uppercase; + text-transform: none; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + line-height: 40px; +} + +footer ul.quicklinks { + margin-bottom: 0; + text-transform: uppercase; + text-transform: none; + font-family: HelveticaNeue, Helvetica, Arial, sans-serif; + line-height: 40px; +} + +ul.social-buttons { + margin-bottom: 0; +} + +ul.social-buttons li a { + display: block; + width: 40px; + height: 40px; + border-radius: 100%; + font-size: 20px; + line-height: 40px; + outline: 0; + color: #fff; + background-color: #222; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +ul.social-buttons li a:hover, +ul.social-buttons li a:focus, +ul.social-buttons li a:active { + background-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +.btn:focus, +.btn:active, +.btn.active, +.btn:active:focus { + outline: 0; +} + +.portfolio-modal .modal-content { + padding: 100px 0; + min-height: 100%; + border: 0; + border-radius: 0; + text-align: center; + background-clip: border-box; + -webkit-box-shadow: none; + box-shadow: none; +} + +.portfolio-modal .modal-content h2 { + margin-bottom: 15px; + font-size: 3em; +} + +.portfolio-modal .modal-content p { + margin-bottom: 30px; +} + +.portfolio-modal .modal-content p.item-intro { + margin: 20px 0 30px; + font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif; + font-size: 16px; + font-style: italic; +} + +.portfolio-modal .modal-content ul.list-inline { + margin-top: 0; + margin-bottom: 30px; +} + +.portfolio-modal .modal-content img { + margin-bottom: 30px; +} + +.portfolio-modal .close-modal { + position: absolute; + top: 25px; + right: 25px; + width: 75px; + height: 75px; + background-color: transparent; + cursor: pointer; +} + +.portfolio-modal .close-modal:hover { + opacity: 0.3; +} + +.portfolio-modal .close-modal .lr { + z-index: 1051; + width: 1px; + height: 75px; + margin-left: 35px; + background-color: #222; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} + +.portfolio-modal .close-modal .lr .rl { + z-index: 1052; + width: 1px; + height: 75px; + background-color: #222; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +::-moz-selection { + text-shadow: none; + background: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +::selection { + text-shadow: none; + background: HelveticaNeue, Helvetica, Arial, sans-serif; +} + +img::selection { + background: 0 0; +} + +img::-moz-selection { + background: 0 0; +} + +body { + webkit-tap-highlight-color: HelveticaNeue, Helvetica, Arial, sans-serif; +} diff --git a/client/js/app/src/app/styles/vespa.css b/client/js/app/src/app/styles/vespa.css new file mode 100644 index 00000000000..4017f0275c3 --- /dev/null +++ b/client/js/app/src/app/styles/vespa.css @@ -0,0 +1,752 @@ +/** +* Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +* Based on vespa.css from https://github.com/vespa-engine/frontpage for header- and footer-elements +*/ +:root { + --primary: #188fff; + --secondary: #003abc; + --secondary-dark: #333; + --muted: #777; + + --fontprimary: 'Hind Madurai', Helvetica, Arial, sans-serif; + --fontsecondary: 'Hind Madurai', Helvetica, Arial, sans-serif; +} + +.bg-light-blue { + background-image: linear-gradient( + -1deg, + rgba(63, 157, 216, 0.08) 0%, + rgba(163, 195, 215, 0.08) 97% + ); +} + +.yql { +} + +.yql:focus { + outline: none; +} + +.information { + margin-left: -40px; + margin-right: 28px; + margin-top: -2.5px; + opacity: 0.6; + transition: transform 0.1s; /* Animation */ +} + +.information:hover { + transform: scale(1.05); +} + +/*** Tooltips! ***/ +.tip { + visibility: visible; + border-bottom: 1px dotted; + position: relative; + cursor: help; + text-decoration: none; + color: #fff !important; +} +.tip span { + display: none; + z-index: 100; + position: absolute; + padding: 0.6em; + padding-left: 1em; + top: 1.5em; + left: 2.4em; + width: 15em; + background-color: #4da2d6; + border: 1px solid #ffffff; + border-radius: 0.5em; +} + +.tip span td { + text-align: left; + vertical-align: top; + padding-left: 5px; +} + +.tip span a { + text-decoration: none; + color: white; +} + +.tip:hover span { + display: inline-block; +} +.sg-question-set, +.sg-type-radio ul.sg-list-vertical li, +.sg-type-checkbox ul.sg-list-vertical li, +.sg-question-options, +.sg-type-radio-likert .sg-question-options, +.sg-type-table .sg-question-options, +.sg-instructions { + overflow: visible; +} + +section h2.section-heading, +section h2.section-subheading, +section h3.section-heading, +section h3.section-subheading { + text-transform: none; + font-family: 'Hind Madurai', Helvetica, Arial, sans-serif; + font-weight: normal; + font-style: normal; +} + +section h2.section-heading, +section h3.section-heading { + font-size: 50px; + color: #3f9dd8; + margin-bottom: 5px; +} + +section h2.section-subheading, +section h3.section-subheading { + line-height: 26px; + margin-top: 0; +} + +header .help-title { + color: #ffc43c; + text-transform: uppercase; + font-weight: bold; +} + +.text-muted { + color: #303030; +} + +#banner { + background-color: #336699; + color: #ffffff; + font-weight: bold; + padding-bottom: 2px; + padding-top: 2px; + width: 100%; +} + +#banner a { + color: #ffffff; + text-decoration: underline; +} + +.navbar-default { + background-color: #005a8e; + padding: 0; + border-bottom: 2px solid rgba(255, 255, 255, 0.2); +} + +.navbar-default .navbar-header { + margin-left: 13px; +} + +.navbar-default .navbar-brand { + background: transparent url('https://vespa.ai/assets/vespa-logo.png') + no-repeat; + background-size: contain; + direction: ltr; + text-indent: -9000px; + height: 28px; + width: 100px; + display: inline-block; + font-family: 'Hind Madurai', Helvetica, Arial, sans-serif; + font-weight: bold; + color: var(--primary); + margin-top: 16px; + margin-left: 10px; +} + +.navbar-nav .nav-link { + font-weight: bold; + font-size: 14px; + letter-spacing: 1px; + color: #ffffff; + text-transform: none; + font-family: 'Hind Madurai', Helvetica, Arial, sans-serif; +} + +.navbar-default .navbar-nav > a:hover, +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover { + color: #ffc43c; +} + +.navbar-default .navbar-toggler-icon { + background-image: url('data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 30 30%27%3e%3cpath stroke=%27rgba%280, 0, 0, 0.55%29%27 stroke-linecap=%27round%27 stroke-miterlimit=%2710%27 stroke-width=%272%27 d=%27M4 7h22M4 15h22M4 23h22%27/%3e%3c/svg%3e'); +} + +.navbar-default .navbar-toggler { + margin-right: 45px; + border: 1px solid; + border-color: #1a242f; +} +.navbar-toggler:hover, +.navbar-default .navbar-toggler:focus { + background-color: #1a242f; + box-shadow: 0 0 0 0; +} + +.navbar-toggle { + margin-right: 45px; +} + +/* Trick to prevent anchored links to hide heading behind navbar */ +section[id]:before { + display: block; + content: ' '; + margin-top: -80px; + height: 100px; + visibility: hidden; +} + +header { + /*background-image: linear-gradient(0deg, #98C1DB 7%, #3F9DD8 100%); */ + background-color: #005a8e; + min-height: 1150px; +} + +@media (max-height: 1150px) { + header { + min-height: 100vh; + } +} + +header .intro { + margin-top: -80px; + max-width: 350px; + padding-top: 10px; + padding-bottom: 50px; +} + +header .intro-logo { + max-width: 120px; + padding-bottom: 30px; +} + +header .intro-button { + box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5); + padding-left: 50px; + padding-right: 50px; +} + +header .intro-long { + line-height: 27px; + font-size: 16px; + padding-bottom: 20px; +} + +header .intro-param { + line-height: 27px; + font-size: 16px; + padding-bottom: 0px; + margin-bottom: 5px; +} +header .response { + line-height: 27px; + font-size: 20px; + padding-top: 10px; + padding-bottom: 8px; +} + +header .methodselector { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 70px; + height: 25px; + border-width: 0px; + -o-transition: 0.3s; + -ms-transition: 0.3s; + -moz-transition: 0.3s; + -webkit-transition: 0.3s; + transition: 0.3s; +} +header .intro-help:hover { + background-color: #4ea2d6 !important; +} +header .methodselector:hover { + background-color: #79b4d8; +} +header .button:hover { + background-color: #79b4d8; +} +header .removeRow:hover { + background-color: #79b4d8; +} +header .addRow:hover { + background-color: #79b4d8; +} +header .showJSON:hover { + background-color: #79b4d8; +} +header .copyURL:hover { + background-color: #79b4d8; +} +header .copyJSON:hover { + background-color: #79b4d8; +} +header .methodselector:focus { + outline: none; +} +header .textbox:focus { + outline: none; +} +header .input:focus { + outline: none; +} +header .responsebox::selection { + background-color: grey; +} +header .textbox::selection { + background-color: grey; +} +header .input::selection { + background-color: grey; +} +header .propvalue::selection { + background-color: grey; +} +header .responsebox:focus { + outline: none; +} +header .responseinfo:focus { + outline: none; +} +header .propvalue:focus { + outline: none; +} +header .addpropsbutton:focus { + outline: none; +} + +header .input { + color: #000; + width: 190px; + border-width: 0px; + margin-right: 2px; +} + +header .input2 { + color: #000; + width: 175px; + border-width: 0px; + margin-right: 2px; +} + +header .textbox { + color: #000; + padding-left: 2px; +} + +header .responsebox { + color: #000; + background-color: #fff; +} + +header .responseinfo { + color: #d6e6f0; + text-align: center; + border-width: 0px; + background-color: transparent; +} + +header .propvalue { + color: #000; + background-color: #fff; + width: 175px; + border-width: 0px; + margin-bottom: 3px; +} + +header .propvalue::-webkit-input-placeholder { + /* Safari, Chrome(, Opera?) */ + color: gray; + font-style: italic; +} +header .propvalue:-moz-placeholder { + /* Firefox 18- */ + color: gray; + font-style: italic; +} +header .propvalue:-moz-placeholder { + /* Firefox 19+ */ + color: gray; + font-style: italic; +} +header .propvalue-ms-input-placeholder { + /* IE (10+?) */ + color: gray; + font-style: italic; +} + +header .button { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 70px; + height: 25px; + border-width: 0px; + border-radius: 5px; + padding: 0px; +} +header .button:focus { + outline: none; +} + +header .addpropsbutton { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 175px; + height: 23px; + border-width: 0px; + border-radius: 5px; + padding: 0px; + margin-left: 2px; + margin-bottom: 3px; +} +header .addRow { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 70px; + height: 25px; + border-width: 0px; + border-radius: 5px; + padding: 0px; +} +header .addRow:focus { + outline: none; +} + +header .removeRow { + font-size: 16px; + color: #fff; + background-color: #99c1da; + width: 35px; + height: 23px; + border-width: 0px; + border-radius: 5px; + padding: 0px; + padding-bottom: 2px; + margin-left: 3px; + text-align: center; + line-height: normal; +} +header .removeRow:focus { + outline: none; +} + +header .json:focus { + outline: none; +} +header .copyJSON:focus { + outline: none; +} +header .showJSON:focus { + outline: none; +} +header .copyURL:focus { + outline: none; +} +header .pasteJSON:focus { + outline: none; +} + +header .copyJSON { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 130px; + height: 25px; + border-width: 0px; + border-radius: 5px; + padding-left: 1px; + margin-top: 10px; + margin-bottom: 20px; + margin-right: 2px; + display: none; +} + +header .showJSON { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 140px; + height: 25px; + border-width: 0px; + border-radius: 5px; + padding: 0px; + margin-top: 5px; + margin-bottom: 10px; +} + +header .pasteJSON { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 140px; + height: 25px; + border-width: 0px; + border-radius: 5px; + padding-left: 0px; + padding-bottom: 2px; + margin-top: 35px; + margin-bottom: 10px; +} + +header .copyURL { + font-size: 15px; + color: #fff; + background-color: #99c1da; + width: 130px; + height: 25px; + border-width: 0px; + border-radius: 5px; + padding-left: 1px; + margin-top: 25px; + margin-bottom: 10px; + margin-left: 2px; + display: none; +} + +header .intro .intro-lead-in { + font-style: normal; + font-size: 22px; + line-height: 50px; + margin-bottom: 25px; + font-family: 'Hind Madurai', Helvetica, Arial, sans-serif; +} + +header .intro .intro-heading { + font-style: normal; +} + +header .intro-button { + box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5); +} + +header .intro .btn-xl { + background: #ffffff; + border: 1px solid #3f9dd8; + border-radius: 2px; + font-size: 20px; + color: #3f9dd8; + font-weight: normal; + text-transform: none; + font-family: 'Hind Madurai', Helvetica, Arial, sans-serif; +} + +header span { + display: inline-block; + position: inherit; + margin-top: 10px; + /*padding-left: 50px;*/ + transform: translateX(-50%); + background: transparent; + border: none !important; + font-size: 0; +} + +header .intro-copy { + background: transparent; + border: none !important; + font-size: 0; +} + +header .intro-help { + display: inline-block; + margin-top: 15px; + width: 134px; + height: 45px; + background-color: transparent; + border: none !important; + font-size: 0; + + border-top-left-radius: 30%; + border-top-right-radius: 29%; + border-bottom-right-radius: 24%; + border-bottom-left-radius: 26%; +} + +header .intro-refresh { + display: inline-block; + margin-top: 15px; + /* transform: translateX(-50%); */ + background: transparent; + border: none !important; + font-size: 0; +} + +@media (min-height: 1150px) { + header .intro-down-arrow { + /* Hard code top: 1150 (height of top section) - 20 (bottom) - 36 (image height) = 1094 */ + top: 1094px; + } +} + +@media (max-height: 825px) { + header .intro-down-arrow { + display: none; + } +} + +@media (min-width: 768px) { + header .intro { + padding-top: 200px; + padding-bottom: 200px; + max-width: 620px; + } + + header .intro-logo { + max-width: 238px; + padding-bottom: 70px; + } + + header .intro .intro-lead-in { + font-size: 51px; + font-style: normal; + line-height: 50px; + } + + header .intro .intro-long { + margin-left: 80px; + margin-right: 80px; + } + + header .intro .intro-heading { + margin-bottom: 25px; + font-size: 90px; + font-weight: 600; + } +} + +section { + padding: 50px 0; +} + +section h3.section-subheading { + margin-bottom: 50px; + font-weight: normal; +} + +@media (min-width: 768px) { + section { + padding: 50px 0; + } +} + +footer .row { + font-size: 12px; + text-align: left; +} + +footer .row h4 { + font-size: 14px; + padding-left: 40px; +} + +footer .row .quicklinks { + line-height: 25px; + list-style: none; + text-align: left; +} + +footer .credits { + font-size: 10px; + text-align: center; +} + +#description p { + font-weight: 300; + font-size: 25px; +} + +footer { + font-size: 14px; + color: #ffffff; + background-color: #3f9dd8; + line-height: 22px; + letter-spacing: 0; + text-align: left; + display: flex; + flex-wrap: wrap; +} + +footer .footer-row { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + flex-wrap: wrap; +} + +footer quicklinks { + font-size: 14px; + color: #ffffff; +} + +footer ul.quicklinks { + margin: 0; + padding: 0; +} + +footer .footer-title { + color: #ffc43c; + text-transform: uppercase; + font-weight: bold; +} + +footer ul.quicklinks a { + font-size: 14px; + color: #ffffff; +} + +footer .quicklink-section { + padding: 0; + min-width: 120px; +} + +footer .credits { + float: left; + font-size: 12px; + font-weight: normal; + text-align: right; + font-family: 'Hind Madurai', Helvetica, Arial, sans-serif; +} + +footer .credits a { + color: #ffffff; +} + +footer .credits span { + color: #ffc43c; +} + +footer a { + text-decoration: none; +} + +footer .table { + width: fit-content; + margin-right: auto; + margin-left: auto; + float: left; + margin-bottom: 0em; +} + +footer .table tbody th { + font-size: 13px; + padding-right: 2em; +} + +footer table thead { + color: #ffc43c; +} + +footer table tbody a { + color: #ffffff; +} diff --git a/client/js/app/yarn.lock b/client/js/app/yarn.lock index f1cee14e50e..04399707958 100644 --- a/client/js/app/yarn.lock +++ b/client/js/app/yarn.lock @@ -463,7 +463,7 @@ clsx "^1.1.1" csstype "3.0.9" -"@popperjs/core@^2.9.3": +"@popperjs/core@^2.10.1", "@popperjs/core@^2.9.3": version "2.11.5" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== @@ -558,6 +558,45 @@ dependencies: "@babel/runtime" "^7.13.10" +"@reach/router@^1": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.4.tgz#d2574b19370a70c80480ed91f3da840136d10f8c" + integrity sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA== + dependencies: + create-react-context "0.3.0" + invariant "^2.2.3" + prop-types "^15.6.1" + react-lifecycles-compat "^3.0.4" + +"@react-aria/ssr@^3.0.1": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.2.0.tgz#88460384b43204f91c972d5b0de24ee44d6a2984" + integrity sha512-wwJFdkl+Q8NU5yJ4NvdAOqx5LM3QtUVoSjuK7Ey8jZ4WS4bB0EqT3Kr3IInBs257HzZ5nXCiKXKE4NGXXuIRWA== + dependencies: + "@babel/runtime" "^7.6.2" + +"@restart/hooks@^0.4.0", "@restart/hooks@^0.4.6": + version "0.4.7" + resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.7.tgz#d79ca6472c01ce04389fc73d4a79af1b5e33cd39" + integrity sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A== + dependencies: + dequal "^2.0.2" + +"@restart/ui@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@restart/ui/-/ui-1.2.0.tgz#fb90251aa25f99b41ccedc78a91d2a15f3c5e0fb" + integrity sha512-oIh2t3tG8drZtZ9SlaV5CY6wGsUViHk8ZajjhcI+74IQHyWy+AnxDv8rJR5wVgsgcgrPBUvGNkC1AEdcGNPaLQ== + dependencies: + "@babel/runtime" "^7.13.16" + "@popperjs/core" "^2.10.1" + "@react-aria/ssr" "^3.0.1" + "@restart/hooks" "^0.4.0" + "@types/warning" "^3.0.0" + dequal "^2.0.2" + dom-helpers "^5.2.0" + uncontrollable "^7.2.1" + warning "^4.0.3" + "@rollup/pluginutils@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" @@ -588,6 +627,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e" + integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug== + dependencies: + "@types/react" "*" + "@types/react@*": version "18.0.13" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.13.tgz#0f5bd24a5f26593e04e450fe85ff43f51c1524ff" @@ -597,7 +643,7 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@^18": +"@types/react@>=16.9.11", "@types/react@^18": version "18.0.14" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.14.tgz#e016616ffff51dba01b04945610fe3671fdbe06d" integrity sha512-x4gGuASSiWmo0xjDLpm5mPb52syZHJx02VKbqUKdLmKtAwIh63XClGsiTI1K6DO5q7ox4xAsQrU+Gl3+gGXF9Q== @@ -611,6 +657,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/warning@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" + integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA== + "@vitejs/plugin-react@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz#2fcf0b6ce9bcdcd4cec5c760c199779d5657ece1" @@ -782,6 +833,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +classnames@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + clsx@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" @@ -876,6 +932,11 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +dequal@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" + integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -890,6 +951,14 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + electron-to-chromium@^1.4.147: version "1.4.158" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.158.tgz#abbdaaf64676bfa4bc0307522125db34424a0ada" @@ -1546,6 +1615,13 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +invariant@^2.2.3, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -2011,7 +2087,15 @@ pretty-quick@^3: mri "^1.1.5" multimatch "^4.0.0" -prop-types@^15.8.1: +prop-types-extra@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b" + integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew== + dependencies: + react-is "^16.3.2" + warning "^4.0.0" + +prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -2033,6 +2117,24 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +react-bootstrap@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-2.4.0.tgz#99bf9656e2e7a23ae1ae135d18fd5ad7c344b416" + integrity sha512-dn599jNK1Fg5GGjJH+lQQDwELVzigh/MdusKpB/0el+sCjsO5MZDH5gRMmBjRhC+vb7VlCDr6OXffPIDSkNMLw== + dependencies: + "@babel/runtime" "^7.17.2" + "@restart/hooks" "^0.4.6" + "@restart/ui" "^1.2.0" + "@types/react-transition-group" "^4.4.4" + classnames "^2.3.1" + dom-helpers "^5.2.1" + invariant "^2.2.4" + prop-types "^15.8.1" + prop-types-extra "^1.1.0" + react-transition-group "^4.4.2" + uncontrollable "^7.2.1" + warning "^4.0.3" + react-dom@^18: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -2046,7 +2148,7 @@ react-fast-compare@^3.0.1: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== -react-is@^16.13.1, react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -2088,6 +2190,16 @@ react-textarea-autosize@^8.3.2: use-composed-ref "^1.3.0" use-latest "^1.2.1" +react-transition-group@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" + integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -2328,6 +2440,16 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +uncontrollable@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2357,7 +2479,7 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -vite@^2: +vite@^2.9.12: version "2.9.12" resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.12.tgz#b1d636b0a8ac636afe9d83e3792d4895509a941b" integrity sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew== |