mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-03 16:35:29 +03:00
search reformatting
This commit is contained in:
parent
58939bbf5c
commit
492600f9f9
@ -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} /> },
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -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} />
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user