hds-web 1.16.0 → 1.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +2 -1
- package/dist/index.css +4 -4
- package/dist/index.es.css +4 -4
- package/dist/index.es.js +5 -5
- package/dist/index.js +5 -5
- package/package.json +1 -2
- package/src/HDS/assets/icons/search-sm.svg +1 -1
- package/src/HDS/components/Headers/v3Header.js +61 -57
- package/src/HDS/helpers/AlgoliaSearch/constants.js +48 -0
- package/src/HDS/helpers/AlgoliaSearch/index.js +1 -0
- package/src/HDS/helpers/AlgoliaSearch/search.js +40 -0
- package/src/HDS/helpers/AlgoliaSearch/searchbox.js +88 -0
- package/src/HDS/helpers/AlgoliaSearch/searchfooter.js +30 -0
- package/src/HDS/helpers/AlgoliaSearch/searchoverlay.js +36 -0
- package/src/HDS/helpers/AlgoliaSearch/searchresults.js +98 -0
- package/src/HDS/helpers/AlgoliaSearch/searchwrapper.js +126 -0
- package/src/HDS/helpers/index.js +1 -0
- package/src/index.css +52 -0
- package/src/styles/tailwind.css +173 -588
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "hds-web",
|
3
|
-
"version": "1.16.
|
3
|
+
"version": "1.16.2",
|
4
4
|
"private": false,
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"module": "dist/index.es.js",
|
@@ -20,7 +20,6 @@
|
|
20
20
|
"react-instantsearch-dom": "^6.40.1",
|
21
21
|
"react-markdown": "^8.0.7",
|
22
22
|
"react-scripts": "5.0.1",
|
23
|
-
"uuid": "^9.0.0",
|
24
23
|
"web-vitals": "^2.1.4",
|
25
24
|
"yarn": "^1.22.19"
|
26
25
|
},
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
-
<path d="M21 21L15.0001 15M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z"
|
2
|
+
<path d="M21 21L15.0001 15M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z" stroke-linecap="round" stroke-linejoin="round"/>
|
3
3
|
</svg>
|
@@ -1,6 +1,4 @@
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
2
|
-
import { Fragment } from 'react';
|
3
|
-
import { GithubWidget } from "./githubButton";
|
4
2
|
import { Icon } from '../common-components/Icon';
|
5
3
|
import { Typography } from '../../foundation/Typography';
|
6
4
|
import { HDSButton } from '../Buttons';
|
@@ -8,6 +6,7 @@ import { V3Dropdown } from '../Cards/Dropdown'
|
|
8
6
|
import { HDSColor } from '../../foundation/ColorPalette';
|
9
7
|
import { motion } from "framer-motion"
|
10
8
|
import { AnimatePresence } from 'framer-motion';
|
9
|
+
import { AlgoliaSearch } from '../../helpers/AlgoliaSearch';
|
11
10
|
|
12
11
|
const solutions = [
|
13
12
|
{
|
@@ -45,7 +44,7 @@ export default function V3Header(props) {
|
|
45
44
|
const dropdownA = (solutions) => (
|
46
45
|
<div>
|
47
46
|
<div>
|
48
|
-
<
|
47
|
+
<hr className='w-full h-9 border-0 ' />
|
49
48
|
</div>
|
50
49
|
|
51
50
|
<div className=" p-2 bg-neutral-150 rounded-3xl ">
|
@@ -134,7 +133,9 @@ export default function V3Header(props) {
|
|
134
133
|
)
|
135
134
|
const dropdownB = (solutions) => (
|
136
135
|
<div>
|
137
|
-
<div
|
136
|
+
<div>
|
137
|
+
<hr className='w-full h-9 border-0 ' />
|
138
|
+
</div>
|
138
139
|
|
139
140
|
|
140
141
|
<div className=" p-2 bg-neutral-150 rounded-3xl ">
|
@@ -301,7 +302,7 @@ export default function V3Header(props) {
|
|
301
302
|
const dropdownC = (solutions) => (
|
302
303
|
<div>
|
303
304
|
<div>
|
304
|
-
<
|
305
|
+
<hr className='w-full h-9 border-0 ' />
|
305
306
|
</div>
|
306
307
|
<div className=" p-2 bg-neutral-150 rounded-3xl ">
|
307
308
|
<V3Dropdown
|
@@ -425,7 +426,7 @@ export default function V3Header(props) {
|
|
425
426
|
initial={{ opacity: 0 }}
|
426
427
|
animate={{ opacity: 1 }}
|
427
428
|
transition={{ ease: "easeInOut", duration: 0.1 }}
|
428
|
-
className='absolute
|
429
|
+
className='absolute -left-[137px] z-[2] transform'>
|
429
430
|
{dropdownA(HEADER_LIST[0]['titleDropdown'])}
|
430
431
|
</motion.div>
|
431
432
|
</div>
|
@@ -465,7 +466,7 @@ export default function V3Header(props) {
|
|
465
466
|
initial={{ opacity: 0 }}
|
466
467
|
animate={{ opacity: 1 }}
|
467
468
|
transition={{ ease: "easeInOut", duration: 0.1 }}
|
468
|
-
className='absolute
|
469
|
+
className='absolute -left-[218px] z-[2]'>
|
469
470
|
{dropdownB(HEADER_LIST[1]['titleDropdown'])}
|
470
471
|
</motion.div>
|
471
472
|
</div>
|
@@ -503,7 +504,7 @@ export default function V3Header(props) {
|
|
503
504
|
initial={{ opacity: 0 }}
|
504
505
|
animate={{ opacity: 1 }}
|
505
506
|
transition={{ ease: "easeInOut", duration: 0.1 }}
|
506
|
-
className='absolute
|
507
|
+
className='absolute -left-[50px] z-[2] transform'>
|
507
508
|
{dropdownC(HEADER_LIST[3]['titleDropdown'])}
|
508
509
|
</motion.div>
|
509
510
|
</div>
|
@@ -931,14 +932,19 @@ export default function V3Header(props) {
|
|
931
932
|
<Icon height='w-[103px] h-[33px]' variant={'hasuraPrimary'} strokeColor={''} />
|
932
933
|
</a>
|
933
934
|
</div>
|
934
|
-
<div className=
|
935
|
-
<div
|
936
|
-
|
935
|
+
<div className='hds-hidden-tbl flex z-[10] mr-[30px] items-center gap-6'>
|
936
|
+
<div className={'h-full ' + ((mobileNavOpen) ? " hds-hidden" : "")}>
|
937
|
+
<AlgoliaSearch {...props} />
|
938
|
+
</div>
|
939
|
+
<div className=' h-5 mt-3 cursor-pointer' onClick={() => handleMbDropdownClose()}>
|
940
|
+
<div aria-hidden="true" className={`${class1} block absolute h-[2px] w-[15px] bg-neutral-800 transform transition duration-500 ease-in-out`}></div>
|
941
|
+
<div aria-hidden="true" className={`${class3} block absolute h-0.5 w-[15px] bg-neutral-800 transform transition duration-500 ease-in-out`}></div>
|
942
|
+
</div>
|
937
943
|
</div>
|
938
944
|
</div>
|
939
|
-
<div className='hidden tb-l:flex flex-row w-full justify-between items-center'>
|
945
|
+
<div className='hds-hidden tb-l:flex flex-row w-full justify-between items-center'>
|
940
946
|
|
941
|
-
<nav className="hidden items-center relative tb-l:pl-8 tb-l:flex tb-l:space-x-6"
|
947
|
+
<nav className="hds-hidden items-center relative tb-l:pl-8 tb-l:flex tb-l:space-x-6"
|
942
948
|
onMouseEnter={() => setIsArrowActive(true)}
|
943
949
|
onMouseLeave={() => setIsArrowActive(false)}
|
944
950
|
|
@@ -963,61 +969,59 @@ export default function V3Header(props) {
|
|
963
969
|
|
964
970
|
</nav>
|
965
971
|
|
966
|
-
<div className='hidden tb-l:flex flex-row items-center gap-x-4 '>
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
<
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
972
|
+
<div className='hds-hidden tb-l:flex flex-row items-center gap-x-4 '>
|
973
|
+
|
974
|
+
<AlgoliaSearch {...props} />
|
975
|
+
|
976
|
+
<a href='' className='hds-hidden db:flex'>
|
977
|
+
<Typography textStyle='body3c-medium' className='text-neutral-800'>
|
978
|
+
Contact Sales
|
979
|
+
</Typography>
|
980
|
+
</a>
|
981
|
+
<HDSButton
|
982
|
+
label="Log In"
|
983
|
+
type='tonal'
|
984
|
+
leftIconVariant='none'
|
985
|
+
rightIconVariant='none'
|
986
|
+
state='default'
|
987
|
+
size='sm'
|
988
|
+
rightAnimatedArrow='true'
|
989
|
+
rightAnimatedArrowColor='#3970FD'
|
990
|
+
animatedHoverStroke='group-hover:stroke-neutral-0'
|
991
|
+
className='hds-hidden tb-l:flex'
|
992
|
+
/>
|
993
|
+
<HDSButton
|
994
|
+
label="Get Started"
|
995
|
+
type='primary'
|
996
|
+
leftIconVariant='none'
|
997
|
+
rightIconVariant='none'
|
998
|
+
state='default'
|
999
|
+
size='sm'
|
1000
|
+
rightAnimatedArrow='true'
|
1001
|
+
rightAnimatedArrowColor='#ffffff'
|
1002
|
+
animatedHoverStroke='group-hover:stroke-neutral-0'
|
1003
|
+
className='hds-hidden tb:flex'
|
1004
|
+
/>
|
998
1005
|
</div>
|
999
1006
|
|
1000
1007
|
</div>
|
1001
|
-
|
1008
|
+
|
1009
|
+
<div className="hds-hidden-tbl overflow-auto flex ">
|
1002
1010
|
{/* <Popover.Button className="inline-flex items-center justify-center rounded-md bg-white pl-6 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
|
1003
1011
|
<span className="sr-only">Open menu</span> */}
|
1004
1012
|
|
1005
|
-
<div className='bg-neutral-100 tb:hidden flex flex-row '>
|
1006
1013
|
|
1007
|
-
|
1008
|
-
|
1009
|
-
</div>
|
1010
|
-
<div className={"bg-neutral-100 tb-l:hidden block fixed w-full tb:max-w-[480px] tb:left-auto tb:right-4 mx-auto left-4 top-0 transform transition duration-500 ease-in-out h-[calc(100%-1px)] max-h-screen overflow-y-auto" + ((mobileNavOpen) ? " translate-x-0" : " translate-x-[1240px]")}>
|
1014
|
+
<div className={"bg-neutral-100 hds-hidden-tbl block fixed w-full tb:max-w-[480px] tb:left-auto tb:right-4 mx-auto left-4 top-0 transform transition duration-500 ease-in-out h-[calc(100%-1px)] max-h-screen overflow-y-auto" + ((mobileNavOpen) ? " translate-x-0" : " translate-x-[1240px]")}>
|
1011
1015
|
|
1012
1016
|
<div className="bg-neutral-100 h-[calc(100%-80px)] rounded-2xl overflow-scroll ">
|
1013
1017
|
|
1014
|
-
<div className='pt-
|
1018
|
+
<div className='pt-9 pb-4 mr-[17px] min-h-[36px] flex flex-row justify-between items-center sticky top-0 bg-neutral-100 z-50 '>
|
1015
1019
|
{(isCompany || isDeveloper || isProduct) && <div
|
1016
1020
|
className=' flex flex-row justify-start z-10'
|
1017
1021
|
onClick={() => handleBackClick()}
|
1018
1022
|
|
1019
1023
|
>
|
1020
|
-
<Icon height='w-5 h-5 ml-4' variant={'arrowleft'} strokeClass='stroke-neutral-1000' />
|
1024
|
+
<Icon height='w-5 h-5 ml-4 cursor-pointer' variant={'arrowleft'} strokeClass='stroke-neutral-1000' />
|
1021
1025
|
</div>}
|
1022
1026
|
<motion.div
|
1023
1027
|
initial={{ opacity: 0 }}
|
@@ -1129,8 +1133,8 @@ export default function V3Header(props) {
|
|
1129
1133
|
<div>
|
1130
1134
|
|
1131
1135
|
|
1132
|
-
<div className="absolute h-[calc(100%-112px)] bg-base-0 z-[1] inset-x-0 top-0 origin-top-right hidden transform transition">
|
1133
|
-
<div className=" rounded-lg bg-neutral-0 h-[calc(100%-112px)]
|
1136
|
+
<div className="absolute h-[calc(100%-112px)] bg-base-0 z-[1] inset-x-0 top-0 origin-top-right hds-hidden transform transition">
|
1137
|
+
<div className=" rounded-lg bg-neutral-0 h-[calc(100%-112px)] shadow-lg">
|
1134
1138
|
<div className="bg-neutral-0">
|
1135
1139
|
{/* <div className="flex items-center justify-end">
|
1136
1140
|
<div className="">
|
@@ -1139,7 +1143,7 @@ export default function V3Header(props) {
|
|
1139
1143
|
</div>
|
1140
1144
|
</div>
|
1141
1145
|
</div> */}
|
1142
|
-
<div className="mt-6
|
1146
|
+
<div className="mt-6 flex flex-col gap-6">
|
1143
1147
|
<div className='flex flex-col items-center'>
|
1144
1148
|
<img
|
1145
1149
|
className="inline-block w-full px-4 "
|
@@ -1148,7 +1152,7 @@ export default function V3Header(props) {
|
|
1148
1152
|
/>
|
1149
1153
|
</div>
|
1150
1154
|
|
1151
|
-
<nav className="grid divide-y divide-neutral-200 border border-neutral-200 rounded-3xl
|
1155
|
+
<nav className="grid divide-y divide-neutral-200 border border-neutral-200 rounded-3xl hds-hidden-tbl ">
|
1152
1156
|
{props.HEADER_LIST.map((item) => (
|
1153
1157
|
<div
|
1154
1158
|
key={item['title']}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
export const INDEX_TYPES = Object.freeze({
|
2
|
+
blog: "blog",
|
3
|
+
docs: "docs",
|
4
|
+
learn: "learn",
|
5
|
+
});
|
6
|
+
|
7
|
+
export const SEARCH_INDICES = [
|
8
|
+
{ name: `blog-production`, title: `Hasura Blog`, type: INDEX_TYPES.blog },
|
9
|
+
{ name: `graphql-docs-prod`, title: `Hasura GraphQL Engine Docs`, type: INDEX_TYPES.docs },
|
10
|
+
{ name: `learn-intro-graphql`, title: `Learn Intro GraphQL`, type: INDEX_TYPES.learn },
|
11
|
+
{ name: `learn-intro-graphql-zh`, title: `Learn Intro GraphQL`, type: INDEX_TYPES.learn },
|
12
|
+
{ name: `learn-elm-graphql`, title: `Learn ELM GraphQl`, type: INDEX_TYPES.learn },
|
13
|
+
{ name: `learn-flutter-graphql`, title: `Learn Flutter GraphQL`, type: INDEX_TYPES.learn },
|
14
|
+
{ name: `learn-database-mysql`, title: `Learn Database MySQL`, type: INDEX_TYPES.learn },
|
15
|
+
{
|
16
|
+
name: `learn-database-postgresql`,
|
17
|
+
title: `Learn Database PostgreSQL`,
|
18
|
+
type: INDEX_TYPES.learn,
|
19
|
+
},
|
20
|
+
{ name: `learn-hasura-backend`, title: `Learn Hasura Backend`, type: INDEX_TYPES.learn },
|
21
|
+
{ name: `learn-hasura-backend-ja`, title: `Learn Hasura Backend`, type: INDEX_TYPES.learn },
|
22
|
+
{ name: `learn-hasura-backend-zh`, title: `Learn Hasura Backend`, type: INDEX_TYPES.learn },
|
23
|
+
{ name: `learn-hasura-backend-advanced`, title: `Learn Hasura Backend Advanced`, type: INDEX_TYPES.learn },
|
24
|
+
{ name: `learn-hasura-auth-slack`, title: `Learn Hasura Auth Slack`, type: INDEX_TYPES.learn },
|
25
|
+
{ name: `learn-react-apollo-hooks`, title: `Learn React Apollo Hooks`, type: INDEX_TYPES.learn },
|
26
|
+
{ name: `learn-react-apollo`, title: `Learn React Apollo`, type: INDEX_TYPES.learn },
|
27
|
+
{
|
28
|
+
name: `learn-typescript-react-apollo`,
|
29
|
+
title: `Learn TypeScript React Apollo`,
|
30
|
+
type: INDEX_TYPES.learn,
|
31
|
+
},
|
32
|
+
{ name: `learn-angular-apollo`, title: `Learn Angular Apollo`, type: INDEX_TYPES.learn },
|
33
|
+
{ name: `learn-vue-apollo`, title: `Learn Vue Apollo`, type: INDEX_TYPES.learn },
|
34
|
+
{ name: `learn-ios-apollo`, title: `Learn IOS Apollo`, type: INDEX_TYPES.learn },
|
35
|
+
{ name: `learn-svelte-apollo`, title: `Learn Svelte Apollo`, type: INDEX_TYPES.learn },
|
36
|
+
{ name: `learn-android-apollo`, title: `Learn Android Apollo`, type: INDEX_TYPES.learn },
|
37
|
+
{
|
38
|
+
name: `learn-react-native-apollo`,
|
39
|
+
title: `Learn React Native Apollo`,
|
40
|
+
type: INDEX_TYPES.learn,
|
41
|
+
},
|
42
|
+
{
|
43
|
+
name: `learn-reason-react-apollo`,
|
44
|
+
title: `Learn Reason React Apollo`,
|
45
|
+
type: INDEX_TYPES.learn,
|
46
|
+
},
|
47
|
+
{ name: `learn-database-mssql`, title: `Learn MicrosoftSQL`, type: INDEX_TYPES.learn },
|
48
|
+
];
|
@@ -0,0 +1 @@
|
|
1
|
+
export {default as AlgoliaSearch } from './search';
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import { useEffect, useState } from "react";
|
3
|
+
import SearchOverlay from "./searchoverlay";
|
4
|
+
import { Icon } from "../../components/common-components";
|
5
|
+
export default function Search(props) {
|
6
|
+
const [showSearch, setShowSearch] = useState(false);
|
7
|
+
|
8
|
+
const handleSearchWithKeyboard = e => {
|
9
|
+
if (e.key === "/" || e.key === "Escape") {
|
10
|
+
e.preventDefault();
|
11
|
+
if (e.key === "/") return setShowSearch(true);
|
12
|
+
if (e.key === "Escape") return onCloseSearch();
|
13
|
+
}
|
14
|
+
|
15
|
+
return null;
|
16
|
+
};
|
17
|
+
|
18
|
+
useEffect(() => {
|
19
|
+
document.addEventListener("keydown", handleSearchWithKeyboard);
|
20
|
+
|
21
|
+
return () => {
|
22
|
+
document.removeEventListener("keydown", handleSearchWithKeyboard);
|
23
|
+
};
|
24
|
+
}, []);
|
25
|
+
|
26
|
+
const onCloseSearch = () => setShowSearch(false);
|
27
|
+
return (
|
28
|
+
<>
|
29
|
+
<div className="">
|
30
|
+
<div className="max-w-7xl mx-auto">
|
31
|
+
<div className="w-full cursor-pointer gap-0.5 flex items-center" onClick={() => {setShowSearch(preShowSearch => !preShowSearch);}}>
|
32
|
+
<p className="-mt-1">/ </p>
|
33
|
+
<Icon height={'h-4 w-4 stroke-2'} variant={`searchsm`} strokeClass='stroke-neutral-1000' />
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
<SearchOverlay showSearch={showSearch} onCloseSearch={onCloseSearch} />
|
38
|
+
</>
|
39
|
+
);
|
40
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
2
|
+
import { connectSearchBox } from "react-instantsearch-dom";
|
3
|
+
import { Icon } from '../../components/common-components/Icon'
|
4
|
+
import { Typography } from '../../foundation/Typography'
|
5
|
+
const searchSuggestions = [
|
6
|
+
"GraphQL",
|
7
|
+
"Actions",
|
8
|
+
"Authentication",
|
9
|
+
"React",
|
10
|
+
"Remote Joins",
|
11
|
+
"Postgres",
|
12
|
+
];
|
13
|
+
|
14
|
+
const DebouncedSearchBox = ({
|
15
|
+
refine,
|
16
|
+
currentRefinement,
|
17
|
+
className,
|
18
|
+
delay = 500,
|
19
|
+
isSearchStalled,
|
20
|
+
}) => {
|
21
|
+
const [value, setValue] = useState(currentRefinement);
|
22
|
+
const timerId = useRef();
|
23
|
+
const searchInputRef = useRef();
|
24
|
+
useEffect(() => {
|
25
|
+
searchInputRef.current?.focus();
|
26
|
+
}, []);
|
27
|
+
|
28
|
+
const trySuggestion = inputValue => {
|
29
|
+
refine(inputValue);
|
30
|
+
setValue(inputValue);
|
31
|
+
};
|
32
|
+
|
33
|
+
const resetInput = () => {
|
34
|
+
refine("");
|
35
|
+
setValue("");
|
36
|
+
};
|
37
|
+
|
38
|
+
const onChangeDebounced = event => {
|
39
|
+
const value = event.target.value;
|
40
|
+
|
41
|
+
clearTimeout(timerId.current);
|
42
|
+
timerId.current = setTimeout(() => refine(value), delay);
|
43
|
+
|
44
|
+
setValue(value);
|
45
|
+
};
|
46
|
+
return (
|
47
|
+
<div className=" sticky -top-20 tb-m:-top-20 z-10 py-8 pt-16 tb-m:pt-16 tb-m:py-9 bg-neutral-50">
|
48
|
+
<div className="flex relative items-center w-full">
|
49
|
+
<div className="absolute left-2">
|
50
|
+
{isSearchStalled ? (
|
51
|
+
<div className="loader-spin border-[3px] border-neutral-400 border-t-blue-500 w-5 h-5 rounded-full" />
|
52
|
+
) : (
|
53
|
+
<Icon height={'w-5 h-5 stroke-[1.5px]'} variant="searchsm" strokeClass="stroke-neutral-800" />
|
54
|
+
)}
|
55
|
+
</div>
|
56
|
+
<form className={`${className} w-full`} onSubmit={e => e.preventDefault()}>
|
57
|
+
<input
|
58
|
+
id="search-input"
|
59
|
+
ref={searchInputRef}
|
60
|
+
className="w-full indent-5 rounded-xl border-neutral-400 h-12"
|
61
|
+
type="text"
|
62
|
+
placeholder="Search for tutorials, articles or docs"
|
63
|
+
aria-label="Search"
|
64
|
+
onChange={onChangeDebounced}
|
65
|
+
value={value}
|
66
|
+
/>
|
67
|
+
</form>
|
68
|
+
<div onClick={resetInput} className="absolute right-2 cursor-pointer bg-neutral-200 rounded-md">
|
69
|
+
<Icon height={'w-6 h-6 stroke-[1.5px]'} variant="xclose" strokeClass="stroke-neutral-800" />
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
<div className="hidden tb:flex flex-wrap items-center mt-4">
|
73
|
+
<Typography textStyle="body2c-bold" className="uppercase text-neutral-500 mr-4">POPULAR</Typography>
|
74
|
+
{searchSuggestions.map(suggestion => (
|
75
|
+
<button
|
76
|
+
key={suggestion}
|
77
|
+
className={`px-3 py-1 border rounded-md m-1 ${currentRefinement === suggestion ? "border-neutral-300 hover:bg-neutral-300 bg-neutral-300" : "bg-neutral-150 hover:bg-neutral-200 border-neutral-150"}`}
|
78
|
+
onClick={() => trySuggestion(suggestion)}
|
79
|
+
>
|
80
|
+
{suggestion}
|
81
|
+
</button>
|
82
|
+
))}
|
83
|
+
</div>
|
84
|
+
</div>
|
85
|
+
);
|
86
|
+
};
|
87
|
+
|
88
|
+
export default connectSearchBox(DebouncedSearchBox);
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React, { Fragment } from "react";
|
2
|
+
import { PoweredBy } from "react-instantsearch-dom";
|
3
|
+
|
4
|
+
const SearchFooter = () => (
|
5
|
+
<Fragment>
|
6
|
+
<hr className="my-5 border-t-neutral-200" />
|
7
|
+
<div className="mt-12 flex flex-wrap justify-between">
|
8
|
+
<div className="community-links-wrapper">
|
9
|
+
<p>Unable to find what you're looking for?</p>
|
10
|
+
<p>
|
11
|
+
Reach out to our{" "}
|
12
|
+
<a className="text-blue-500 hover:text-blue-700" href="https://discord.com/invite/hasura" target="_blank" rel="noopener noreferrer">
|
13
|
+
Discord Community
|
14
|
+
</a>{" "}
|
15
|
+
or start a{" "}
|
16
|
+
<a className="text-blue-500 hover:text-blue-700"
|
17
|
+
href="https://github.com/hasura/graphql-engine/discussions"
|
18
|
+
target="_blank"
|
19
|
+
rel="noopener noreferrer"
|
20
|
+
>
|
21
|
+
Discussion on GitHub
|
22
|
+
</a>
|
23
|
+
</p>
|
24
|
+
</div>
|
25
|
+
<PoweredBy />
|
26
|
+
</div>
|
27
|
+
</Fragment>
|
28
|
+
);
|
29
|
+
|
30
|
+
export default SearchFooter;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { useEffect, useState } from "react";
|
2
|
+
import { Icon } from '../../components/common-components/Icon'
|
3
|
+
import SearchWrapper from "./searchwrapper";
|
4
|
+
import { SEARCH_INDICES } from "./constants";
|
5
|
+
|
6
|
+
export default function SearchOverlay({showSearch, onCloseSearch}) {
|
7
|
+
|
8
|
+
useEffect(() => {
|
9
|
+
if (showSearch) {
|
10
|
+
document.body.style.overflow = "hidden";
|
11
|
+
}
|
12
|
+
|
13
|
+
return () => {
|
14
|
+
document.body.style.overflow = "unset";
|
15
|
+
};
|
16
|
+
}, [showSearch]);
|
17
|
+
|
18
|
+
if (!showSearch) return null;
|
19
|
+
|
20
|
+
return (
|
21
|
+
<div className="fixed left-0 top-0 w-full h-full z-[10000] bg-neutral-50">
|
22
|
+
<div className="absolute top-4 right-4 cursor-pointer z-[11]" onClick={onCloseSearch}>
|
23
|
+
<Icon height={'w-8 h-8 stroke-[1.5px]'} variant="xclose" strokeClass="stroke-neutral-800" />
|
24
|
+
</div>
|
25
|
+
<div className="py-20 overflow-y-auto h-full w-full">
|
26
|
+
<div className="px-4">
|
27
|
+
<div className="max-w-7xl mx-auto">
|
28
|
+
<div className="w-full">
|
29
|
+
<SearchWrapper indices={SEARCH_INDICES} />
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
);
|
36
|
+
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import React, { Fragment } from "react";
|
2
|
+
import { connectStateResults, Highlight, Hits, Index, Snippet } from "react-instantsearch-dom";
|
3
|
+
import { INDEX_TYPES } from "./constants";
|
4
|
+
import SearchFooter from "./searchfooter";
|
5
|
+
import { Typography } from '../../foundation/Typography'
|
6
|
+
|
7
|
+
// import SearchFooter from "./SearchFooter";
|
8
|
+
|
9
|
+
const baseDomain = 'hasura.io';
|
10
|
+
|
11
|
+
|
12
|
+
const HitsHeader = ({ searchResults, indexTitle, showSeparator }) => {
|
13
|
+
const hitCount = searchResults && searchResults.nbHits;
|
14
|
+
return hitCount > 0 ? (
|
15
|
+
<Fragment>
|
16
|
+
{showSeparator && <hr className="my-5 border-t-neutral-200" />}
|
17
|
+
{/* <div className="HitCount">
|
18
|
+
{hitCount} result{hitCount !== 1 ? `s` : ``}
|
19
|
+
</div> */}
|
20
|
+
<Typography textStyle="body2c-medium" className="pb-4">{indexTitle}</Typography>
|
21
|
+
</Fragment>
|
22
|
+
) : null;
|
23
|
+
};
|
24
|
+
|
25
|
+
const CustomHitsHeader = connectStateResults(HitsHeader);
|
26
|
+
|
27
|
+
const PageHit = ({ hit, indexType }) => (
|
28
|
+
<a href={hit.url} className="grid h-full">
|
29
|
+
<div className="self-start">
|
30
|
+
<Typography textStyle="body3c-medium" className="uppercase text-neutral-500">{indexType}</Typography>
|
31
|
+
{indexType === INDEX_TYPES.docs ? (
|
32
|
+
<Fragment>
|
33
|
+
{!!hit.hierarchy && (
|
34
|
+
<Typography textStyle="body1c-bold" className="font-semibold py-3">{`${
|
35
|
+
Object.values(hit.hierarchy)
|
36
|
+
.filter(h => !!h)
|
37
|
+
.reverse()[0]
|
38
|
+
}`}</Typography>
|
39
|
+
)}
|
40
|
+
<Typography textStyle="body2" className="text-neutral-600 break-words">
|
41
|
+
<Snippet attribute="content" hit={hit} tagName="mark" />
|
42
|
+
</Typography>
|
43
|
+
|
44
|
+
</Fragment>
|
45
|
+
) : (
|
46
|
+
<Fragment>
|
47
|
+
<Typography textStyle="body1c-bold" className="font-semibold py-3">
|
48
|
+
<Highlight attribute="title" hit={hit} tagName="mark" />
|
49
|
+
</Typography>
|
50
|
+
<Typography textStyle="body2" className="text-neutral-600 break-words">
|
51
|
+
<Snippet attribute="excerpt" hit={hit} tagName="mark" />
|
52
|
+
</Typography>
|
53
|
+
</Fragment>
|
54
|
+
)}
|
55
|
+
</div>
|
56
|
+
{hit.url ? (
|
57
|
+
<Typography textStyle="body3" className="self-end my-2 pt-3 text-neutral-600">
|
58
|
+
<span className='hit-slug break-words'>{hit.url.replace(`https://${baseDomain}/`, "/")}</span>
|
59
|
+
</Typography>
|
60
|
+
) : null}
|
61
|
+
</a>
|
62
|
+
);
|
63
|
+
|
64
|
+
const HitsByIndexType = ({ indexType }) => {
|
65
|
+
if (INDEX_TYPES[indexType] === undefined) return null;
|
66
|
+
|
67
|
+
return (
|
68
|
+
<Hits
|
69
|
+
className="Hits"
|
70
|
+
hitComponent={hitProps => <PageHit {...hitProps} indexType={indexType} />}
|
71
|
+
/>
|
72
|
+
);
|
73
|
+
};
|
74
|
+
|
75
|
+
const HitsInIndex = ({ index, show }) => (
|
76
|
+
<Index indexName={index.name}>
|
77
|
+
{show && (
|
78
|
+
<Fragment>
|
79
|
+
<CustomHitsHeader
|
80
|
+
indexTitle={index.title}
|
81
|
+
showSeparator={index.type !== INDEX_TYPES.blog}
|
82
|
+
/>
|
83
|
+
<HitsByIndexType indexType={index.type} />
|
84
|
+
</Fragment>
|
85
|
+
)}
|
86
|
+
</Index>
|
87
|
+
);
|
88
|
+
|
89
|
+
const SearchResult = ({ indices, className, id, wrapperRef, activeIndexTypes }) => (
|
90
|
+
<div id={id} className={`${className} search-results z-[2] clear-both`} ref={wrapperRef}>
|
91
|
+
{indices.map(index => (
|
92
|
+
<HitsInIndex index={index} key={index.name} show={activeIndexTypes[index.type]} />
|
93
|
+
))}
|
94
|
+
<SearchFooter />
|
95
|
+
</div>
|
96
|
+
);
|
97
|
+
|
98
|
+
export default SearchResult;
|