search reformatting

This commit is contained in:
Martina 2020-08-27 21:52:47 -07:00
parent 58939bbf5c
commit 492600f9f9
5 changed files with 3 additions and 544 deletions

View File

@ -3,7 +3,7 @@ import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import { css } from "@emotion/react";
import { SpotlightSearch } from "~/components/system/modules/SpotlightSearch";
import { SearchModal } from "~/components/core/SearchModal";
import { dispatchCustomEvent } from "~/common/custom-events";
const STYLES_ICON_ELEMENT = css`
@ -72,7 +72,7 @@ export default class ApplicationHeader extends React.Component {
_handleCreateSearch = (e) => {
dispatchCustomEvent({
name: "create-modal",
detail: { modal: <SpotlightSearch onAction={this.props.onAction} /> },
detail: { modal: <SearchModal onAction={this.props.onAction} /> },
});
};

View File

@ -35,7 +35,7 @@ export function SlatePreviewRow(props) {
? props.slate.data.objects.slice(0, numItems)
: props.slate.data.objects;
return (
<div css={STYLES_IMAGE_ROW}>
<div css={STYLES_IMAGE_ROW} style={props.containerStyle}>
{objects.map((each) => (
<div key={each.url} css={STYLES_ITEM_BOX} style={props.style}>
<SlateMediaObjectPreview type={each.type} url={each.url} />

View File

@ -1,208 +0,0 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import { css } from "@emotion/react";
const STYLES_DROPDOWN_CONTAINER = css`
box-sizing: border-box;
z-index: ${Constants.zindex.modal};
`;
const STYLES_DROPDOWN = css`
box-sizing: border-box;
position: absolute;
display: flex;
flex-direction: column;
background-color: ${Constants.system.white};
overflow: hidden;
width: 100%;
scrollbar-width: none;
-ms-overflow-style: -ms-autohiding-scrollbar;
::-webkit-scrollbar {
display: none;
}
`;
const STYLES_DROPDOWN_ITEM = css`
box-sizing: border-box;
padding: 8px;
font-size: 0.8em;
border-radius: 16px;
border: 1px solid ${Constants.system.white};
:hover {
border-color: ${Constants.system.border};
}
`;
const STYLES_INPUT = css`
font-family: ${Constants.font.text};
-webkit-appearance: none;
width: 100%;
height: 40px;
background: ${Constants.system.foreground};
color: ${Constants.system.black};
display: flex;
font-size: 14px;
align-items: center;
justify-content: flex-start;
outline: 0;
border: 0;
box-sizing: border-box;
transition: 200ms ease all;
padding: 0 24px 0 48px;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: 12px;
margin-bottom: 16px;
::placeholder {
/* Chrome, Firefox, Opera, Safari 10.1+ */
color: ${Constants.system.black};
opacity: 1; /* Firefox */
}
:-ms-input-placeholder {
/* Internet Explorer 10-11 */
color: ${Constants.system.black};
}
::-ms-input-placeholder {
/* Microsoft Edge */
color: ${Constants.system.black};
}
`;
export class InputMenu extends React.Component {
_input;
_optionRoot;
state = {
selectedIndex: -1,
};
componentDidMount = () => {
window.addEventListener("keydown", this._handleDocumentKeydown);
this._input.focus();
};
componentWillUnmount = () => {
window.removeEventListener("keydown", this._handleDocumentKeydown);
};
_handleInputChange = (e) => {
if (this.state.selectedIndex !== -1) {
this.setState({ selectedIndex: -1 });
}
this.props.onChange({
target: {
value: null,
name: this.props.name,
},
});
this.props.onInputChange(e);
};
_handleSelect = (index) => {
let e = {
target: {
value: this.props.options[index].value,
name: this.props.name,
},
};
this.props.onChange(e);
};
_handleDocumentKeydown = (e) => {
if (e.keyCode === 27) {
this._handleDelete();
e.preventDefault();
} else if (e.keyCode === 9) {
this._handleDelete();
} else if (e.keyCode === 40) {
if (this.state.selectedIndex < this.props.options.length - 1) {
let listElem = this._optionRoot.children[this.state.selectedIndex + 1];
let elemRect = listElem.getBoundingClientRect();
let rootRect = this._optionRoot.getBoundingClientRect();
if (elemRect.bottom > rootRect.bottom) {
this._optionRoot.scrollTop =
listElem.offsetTop +
listElem.offsetHeight -
this._optionRoot.offsetHeight;
}
this.setState({ selectedIndex: this.state.selectedIndex + 1 });
}
e.preventDefault();
} else if (e.keyCode === 38) {
if (this.state.selectedIndex > 0) {
let listElem = this._optionRoot.children[this.state.selectedIndex - 1];
let elemRect = listElem.getBoundingClientRect();
let rootRect = this._optionRoot.getBoundingClientRect();
if (elemRect.top < rootRect.top) {
this._optionRoot.scrollTop = listElem.offsetTop;
}
this.setState({ selectedIndex: this.state.selectedIndex - 1 });
}
e.preventDefault();
} else if (e.keyCode === 13) {
if (
this.props.options.length > this.state.selectedIndex &&
this.state.selectedIndex !== -1
) {
this._handleSelect(this.state.selectedIndex);
}
e.preventDefault();
}
};
render() {
return (
<div css={STYLES_DROPDOWN_CONTAINER} style={this.props.containerStyle}>
<div style={{ position: "relative" }}>
<input
css={STYLES_INPUT}
value={this.props.inputValue}
placeholder={this.props.placeholder}
style={this.props.inputStyle}
onChange={this._handleInputChange}
ref={(c) => {
this._input = c;
}}
/>
<SVG.Search
height="20px"
style={{ position: "absolute", left: "12px", top: "10px" }}
/>
</div>
{
<div
data-menu
ref={(c) => {
this._optionRoot = c;
}}
css={STYLES_DROPDOWN}
style={this.props.style}
>
{(this.props.options && this.props.options.length
? this.props.options
: this.props.defaultOptions
).map((each, i) => (
<div
key={each.value}
css={STYLES_DROPDOWN_ITEM}
style={{
borderColor:
this.state.selectedIndex === i
? Constants.system.border
: "auto",
...this.props.itemStyle,
}}
>
{each.name}
</div>
))}
</div>
}
</div>
);
}
}

View File

@ -16,7 +16,6 @@ import {
FilecoinRetrievalDealsList,
} from "~/components/system/modules/FilecoinDealsList";
import { FilecoinSettings } from "~/components/system/modules/FilecoinSettings";
import { SpotlightSearch } from "~/components/system/modules/SpotlightSearch";
// NOTE(jim): Global components
import { GlobalModal } from "~/components/system/components/GlobalModal";
@ -37,7 +36,6 @@ import { CheckBox } from "~/components/system/components/CheckBox";
import { CodeTextarea } from "~/components/system/components/CodeTextarea";
import { DatePicker } from "~/components/system/components/DatePicker";
import { Input } from "~/components/system/components/Input";
import { InputMenu } from "~/components/system/components/InputMenu";
import { ListEditor } from "~/components/system/components/ListEditor";
import { HoverTile } from "~/components/system/components/HoverTile";
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
@ -121,7 +119,6 @@ export {
GlobalCarousel,
GlobalNotification,
Input,
InputMenu,
HoverTile,
ListEditor,
PopoverNavigation,
@ -129,7 +126,6 @@ export {
SelectCountryMenu,
SelectMenu,
Slider,
SpotlightSearch,
StatUpload,
StatDownload,
TabGroup,

View File

@ -1,329 +0,0 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import * as Strings from "~/common/strings";
import * as Actions from "~/common/actions";
import MiniSearch from "minisearch";
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
import { css } from "@emotion/react";
import { InputMenu } from "~/components/system/components/InputMenu";
import { dispatchCustomEvent } from "~/common/custom-events";
const STYLES_ICON_CIRCLE = css`
height: 24px;
width: 24px;
border-radius: 50%;
background-color: ${Constants.system.foreground};
display: flex;
align-items: center;
justify-content: center;
`;
const STYLES_MODAL = css`
width: 95vw;
max-width: 600px;
height: 60vh;
max-height: 500px;
padding: 24px;
`;
const STYLES_INPUT_MENU = {
height: "calc(100% - 80px)",
width: "calc(100% - 48px)",
overflowY: "scroll",
};
const STYLES_USER_ENTRY_CONTAINER = css`
display: grid;
grid-template-columns: repeat(3, auto) 1fr;
grid-column-gap: 16px;
align-items: center;
`;
const STYLES_PROFILE_IMAGE = css`
background-size: cover;
background-position: 50% 50%;
height: 24px;
width: 24px;
border-radius: 50%;
`;
const UserEntry = ({ item }) => {
return (
<a css={STYLES_LINK} href={`/${item.username}`}>
<div css={STYLES_ENTRY}>
<div css={STYLES_USER_ENTRY_CONTAINER}>
<div
style={{ backgroundImage: `url(${item.data.photo})` }}
css={STYLES_PROFILE_IMAGE}
/>
{item.data.name ? <strong>{item.data.name}</strong> : null}
<a css={STYLES_LINK_HOVER} href={`/${item.username}`}>
@{item.username}
</a>
</div>
</div>
</a>
);
};
const STYLES_ENTRY = css`
padding: 8px 0px;
`;
const STYLES_SLATE_ENTRY_CONTAINER = css`
display: grid;
grid-template-columns: repeat(3, auto) 1fr;
grid-column-gap: 16px;
align-items: center;
`;
const STYLES_SLATE_IMAGES_CONTAINER = css`
display: grid;
grid-template-columns: repeat(3, auto) 1fr;
grid-column-gap: 16px;
margin: 8px 0px;
margin-left: 40px;
`;
const STYLES_SLATE_IMAGE = css`
display: flex;
align-items: center;
justify-content: center;
height: 72px;
width: 72px;
`;
const STYLES_LINK = css`
color: ${Constants.system.black};
text-decoration: none;
`;
const STYLES_LINK_HOVER = css`
color: ${Constants.system.black};
text-decoration: none;
:hover {
color: ${Constants.system.brand};
}
`;
const SlateEntry = ({ item, onAction }) => {
//TODO: utilize auto suggest feature of minisearch
return (
<div
// onClick={() => {
// onAction({ type: "NAVIGATE", value: 17, data: item });
// }}
>
<div css={STYLES_ENTRY}>
<div css={STYLES_SLATE_ENTRY_CONTAINER}>
<div css={STYLES_ICON_CIRCLE}>
<SVG.Slate2 height="16px" />
</div>
<strong>{item.data.name}</strong>
{/* <div>
<a css={STYLES_LINK_HOVER} href={`/${item.username}`}>
@{item.data.username} TODO: add the owner to the slate entries
</a>
</div> */}
</div>
{item.data.objects.length ? (
<div css={STYLES_SLATE_IMAGES_CONTAINER}>
{item.data.objects.slice(0, 4).map((each) => (
<div css={STYLES_SLATE_IMAGE}>
<SlateMediaObjectPreview type={each.type} url={each.url} />
</div>
))}
</div>
) : null}
</div>
</div>
);
};
const FileEntry = ({ item, onAction }) => {
return (
<div
css={STYLES_LINK}
// onClick={() => {
// onAction({ type: "NAVIGATE", value: 15, data: { url: item.url } });
// }}
>
<div css={STYLES_ENTRY}>
<div css={STYLES_USER_ENTRY_CONTAINER}>
<div css={STYLES_ICON_CIRCLE}>
<SVG.Folder2 height="16px" />
</div>
<strong>{item.name}</strong>
<a href={`/${item.username}`} css={STYLES_LINK_HOVER}>
@{item.username}
</a>
</div>
<div
style={{
backgroundImage: `url(${
item.type === "image" ? item.url : fileImg
})`,
margin: "8px 0px 8px 40px",
}}
css={STYLES_SLATE_IMAGE}
/>
</div>
</div>
);
};
const STYLES_DROPDOWN_ITEM = css`
display: grid;
grid-template-columns: 56px 1fr;
align-items: center;
cursor: pointer;
`;
const options = [
{
name: "Send money",
link: null,
icon: <SVG.Wallet2 height="16px" />,
action: { type: "NAVIGATE", value: 2 },
},
{
name: "New slate",
link: null,
icon: <SVG.Slate2 height="16px" />,
action: { type: "NAVIGATE", value: 3 },
},
{
name: "Upload file",
link: null,
icon: <SVG.Folder2 height="16px" />,
action: { type: "NAVIGATE", value: "data" },
},
{
name: "Account settings",
link: null,
icon: <SVG.Tool2 height="16px" />,
action: { type: "NAVIGATE", value: 13 },
},
{
name: "Filecoin settings",
link: null,
icon: <SVG.Tool2 height="16px" />,
action: { type: "NAVIGATE", value: 14 },
},
];
export class SpotlightSearch extends React.Component {
state = {
options: [],
value: null,
inputValue: "",
};
componentDidMount = async () => {
const response = await Actions.getNetworkDirectory();
console.log(response.data);
this.miniSearch = new MiniSearch({
fields: ["slatename", "data.name", "username"], // fields to index for full-text search
storeFields: ["type", "slatename", "username", "data", "id"], // fields to return with search results
extractField: (entry, fieldName) => {
return fieldName
.split(".")
.reduce((doc, key) => doc && doc[key], entry); // Access nested fields
},
searchOptions: {
// boost: { "data.name": 2 },
fuzzy: 0.2,
},
});
this.miniSearch.addAll(response.data.users);
this.miniSearch.addAll(response.data.slates);
//TODO: unpack slates => slate object files and add those too
};
_handleChange = (e) => {
// if (e.target.value !== null) {
// if (e.target.value.substring(0, 1) === "/") {
// window.location.pathname = e.target.value;
// } else {
// window.location.href = e.target.value;
// }
// }
};
_handleInputChange = (e) => {
this.setState({ inputValue: e.target.value }, () => {
let results = this.miniSearch.search(this.state.inputValue);
let options = [];
for (let item of results) {
if (item.type === "USER") {
options.push({
value: `/${item.username}`,
name: <UserEntry item={item} onAction={this.props.onAction} />,
});
} else if (item.type === "SLATE") {
options.push({
value: `/${item.slatename}`, //change this format for input menu to something more appropriate
name: <SlateEntry item={item} onAction={this.props.onAction} />,
});
}
// else if (item.type === "image" || item.type == "file") {
// options.push({
// value: `${item.url}`,
// name: <FileEntry item={item} onAction={this.props.onAction} />,
// });
// }
}
this.setState({ options });
});
};
_handleAction = (action) => {
this.props.onAction(action);
dispatchCustomEvent({
name: "delete-modal",
detail: {},
});
};
render() {
return (
<div css={STYLES_MODAL}>
<InputMenu
show
search
name="exampleThree"
placeholder="Search..."
options={this.state.options}
onChange={this._handleChange}
value={this.state.value}
onInputChange={this._handleInputChange}
inputValue={this.state.inputValue}
style={STYLES_INPUT_MENU}
defaultOptions={[]}
// defaultOptions={options.map((option) => {
// return {
// name: (
// <div
// css={STYLES_DROPDOWN_ITEM}
// onClick={() => this._handleAction(option.action)}
// >
// <div
// css={STYLES_ICON_CIRCLE}
// style={{ height: "40px", width: "40px" }}
// >
// {option.icon}
// </div>
// <div>{option.name}</div>
// </div>
// ),
// value: option.name,
// };
// })}
/>
</div>
);
}
}