feat: autocomplete input component, topics slider filters, sortBy mobile component

This commit is contained in:
MTG2000
2022-05-22 23:18:07 +03:00
parent 08222b9935
commit 8a93f9c820
18 changed files with 855 additions and 97 deletions

259
package-lock.json generated
View File

@@ -60,6 +60,7 @@
"react-responsive-carousel": "^3.2.23",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-select": "^5.3.2",
"react-tooltip": "^4.2.21",
"react-topbar-progress-indicator": "^4.1.1",
"remirror": "^1.0.77",
@@ -2499,6 +2500,66 @@
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
},
"node_modules/@emotion/react": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz",
"integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@emotion/babel-plugin": "^11.7.1",
"@emotion/cache": "^11.7.1",
"@emotion/serialize": "^1.0.3",
"@emotion/utils": "^1.1.0",
"@emotion/weak-memoize": "^0.2.5",
"hoist-non-react-statics": "^3.3.1"
},
"peerDependencies": {
"@babel/core": "^7.0.0",
"react": ">=16.8.0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"@types/react": {
"optional": true
}
}
},
"node_modules/@emotion/react/node_modules/@emotion/cache": {
"version": "11.7.1",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
"dependencies": {
"@emotion/memoize": "^0.7.4",
"@emotion/sheet": "^1.1.0",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"stylis": "4.0.13"
}
},
"node_modules/@emotion/react/node_modules/@emotion/serialize": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
"integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
"dependencies": {
"@emotion/hash": "^0.8.0",
"@emotion/memoize": "^0.7.4",
"@emotion/unitless": "^0.7.5",
"@emotion/utils": "^1.0.0",
"csstype": "^3.0.2"
}
},
"node_modules/@emotion/react/node_modules/@emotion/sheet": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
},
"node_modules/@emotion/react/node_modules/@emotion/utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
"integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
},
"node_modules/@emotion/serialize": {
"version": "0.11.16",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
@@ -15583,6 +15644,14 @@
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
"integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/reactcss": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
@@ -21203,6 +21272,15 @@
"utila": "~0.4"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -29800,6 +29878,11 @@
"node": ">= 4.0.0"
}
},
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"node_modules/memoizerific": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz",
@@ -59261,6 +59344,46 @@
}
}
},
"node_modules/react-select": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.3.2.tgz",
"integrity": "sha512-W6Irh7U6Ha7p5uQQ2ZnemoCQ8mcfgOtHfw3wuMzG6FAu0P+CYicgofSLOq97BhjMx8jS+h+wwWdCBeVVZ9VqlQ==",
"dependencies": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^5.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-select/node_modules/@emotion/cache": {
"version": "11.7.1",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
"dependencies": {
"@emotion/memoize": "^0.7.4",
"@emotion/sheet": "^1.1.0",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"stylis": "4.0.13"
}
},
"node_modules/react-select/node_modules/@emotion/sheet": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
},
"node_modules/react-select/node_modules/@emotion/utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
"integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
},
"node_modules/react-sizeme": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz",
@@ -59341,6 +59464,21 @@
"react": ">=16.8.0"
}
},
"node_modules/react-transition-group": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
"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"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/react-transition-state": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-1.1.4.tgz",
@@ -67962,6 +68100,56 @@
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
},
"@emotion/react": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz",
"integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==",
"requires": {
"@babel/runtime": "^7.13.10",
"@emotion/babel-plugin": "^11.7.1",
"@emotion/cache": "^11.7.1",
"@emotion/serialize": "^1.0.3",
"@emotion/utils": "^1.1.0",
"@emotion/weak-memoize": "^0.2.5",
"hoist-non-react-statics": "^3.3.1"
},
"dependencies": {
"@emotion/cache": {
"version": "11.7.1",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
"requires": {
"@emotion/memoize": "^0.7.4",
"@emotion/sheet": "^1.1.0",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"stylis": "4.0.13"
}
},
"@emotion/serialize": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
"integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
"requires": {
"@emotion/hash": "^0.8.0",
"@emotion/memoize": "^0.7.4",
"@emotion/unitless": "^0.7.5",
"@emotion/utils": "^1.0.0",
"csstype": "^3.0.2"
}
},
"@emotion/sheet": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
},
"@emotion/utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
"integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
}
}
},
"@emotion/serialize": {
"version": "0.11.16",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
@@ -78197,6 +78385,14 @@
"@types/react": "*"
}
},
"@types/react-transition-group": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
"integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
"requires": {
"@types/react": "*"
}
},
"@types/reactcss": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
@@ -82631,6 +82827,15 @@
"utila": "~0.4"
}
},
"dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"requires": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -89113,6 +89318,11 @@
"fs-monkey": "1.0.3"
}
},
"memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"memoizerific": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz",
@@ -111404,6 +111614,44 @@
"workbox-webpack-plugin": "^6.4.1"
}
},
"react-select": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.3.2.tgz",
"integrity": "sha512-W6Irh7U6Ha7p5uQQ2ZnemoCQ8mcfgOtHfw3wuMzG6FAu0P+CYicgofSLOq97BhjMx8jS+h+wwWdCBeVVZ9VqlQ==",
"requires": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^5.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0"
},
"dependencies": {
"@emotion/cache": {
"version": "11.7.1",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
"requires": {
"@emotion/memoize": "^0.7.4",
"@emotion/sheet": "^1.1.0",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"stylis": "4.0.13"
}
},
"@emotion/sheet": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
},
"@emotion/utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
"integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
}
}
},
"react-sizeme": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz",
@@ -111464,6 +111712,17 @@
"topbar": "^0.1.3"
}
},
"react-transition-group": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
"integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"react-transition-state": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-1.1.4.tgz",

View File

@@ -55,6 +55,7 @@
"react-responsive-carousel": "^3.2.23",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-select": "^5.3.2",
"react-tooltip": "^4.2.21",
"react-topbar-progress-indicator": "^4.1.1",
"remirror": "^1.0.77",

View File

@@ -0,0 +1,171 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { useWatch } from 'react-hook-form';
import { WrapForm } from 'src/utils/storybook/decorators';
import Autocomplete from './Autocomplete';
export default {
title: 'Shared/Inputs/AutoComplete',
component: Autocomplete,
decorators: [WrapForm({
defaultValues: {
autocomplete: null
}
})],
} as ComponentMeta<typeof Autocomplete>;
const options = [
{
"id": "20f0eb8d-c0cd-4e12-8a08-0d9846fc8704",
"name": "Nichole Bailey",
"username": "Cassie14",
"email": "Daisy_Auer50@hotmail.com",
"address": {
"street": "Anastasia Tunnel",
"suite": 95587,
"city": "Port Casperview",
"zipcode": "04167-6996",
"geo": {
"lat": "-73.4727",
"lng": "-142.9435"
}
},
"phone": "324-615-9195 x5902",
"website": "ron.net",
"company": {
"name": "Roberts, Tremblay and Christiansen",
"catchPhrase": "Vision-oriented actuating access",
"bs": "bricks-and-clicks strategize portals"
}
},
{
"id": "62b70f76-85ba-4241-9ffd-07582008c497",
"name": "Robert Blick",
"username": "Madilyn93",
"email": "Ronaldo82@gmail.com",
"address": {
"street": "Charlie Plain",
"suite": 83070,
"city": "Lake Bonitaland",
"zipcode": "01109",
"geo": {
"lat": "50.0971",
"lng": "-2.3057"
}
},
"phone": "1-541-367-2047 x9006",
"website": "jovani.com",
"company": {
"name": "Parisian - Kling",
"catchPhrase": "Multi-tiered tertiary toolset",
"bs": "plug-and-play benchmark content"
}
},
{
"id": "d02f74d9-bf99-4e41-b678-15e903abc1b3",
"name": "Eli O'Kon",
"username": "Rosario.Davis",
"email": "Mckayla59@hotmail.com",
"address": {
"street": "Wilford Drive",
"suite": 69742,
"city": "North Dianna",
"zipcode": "80620",
"geo": {
"lat": "-61.4191",
"lng": "126.7878"
}
},
"phone": "(339) 709-4080",
"website": "clay.name",
"company": {
"name": "Gerlach - Metz",
"catchPhrase": "Pre-emptive user-facing service-desk",
"bs": "frictionless monetize markets"
}
},
{
"id": "21077fa6-6a53-4b84-8407-6cd949718945",
"name": "Marilie Feil",
"username": "Antwon.Carter92",
"email": "Demario.Hyatt20@yahoo.com",
"address": {
"street": "Kenton Spurs",
"suite": 20079,
"city": "Beahanberg",
"zipcode": "79385",
"geo": {
"lat": "-70.7199",
"lng": "4.6977"
}
},
"phone": "608.750.4947",
"website": "jacynthe.org",
"company": {
"name": "Kuhn and Sons",
"catchPhrase": "Total eco-centric matrices",
"bs": "out-of-the-box target communities"
}
},
{
"id": "e07cf1b4-ff43-4c4a-a670-fd7417d6bbaf",
"name": "Ella Pagac",
"username": "Damien.Jaskolski",
"email": "Delmer1@gmail.com",
"address": {
"street": "VonRueden Shoals",
"suite": 14035,
"city": "Starkmouth",
"zipcode": "72448-1915",
"geo": {
"lat": "55.2157",
"lng": "98.0822"
}
},
"phone": "(165) 247-5332 x71067",
"website": "chad.info",
"company": {
"name": "Nicolas, Doyle and Rempel",
"catchPhrase": "Adaptive real-time strategy",
"bs": "innovative whiteboard supply-chains"
}
}
]
const Template: ComponentStory<typeof Autocomplete> = (args) => {
const value = useWatch({ name: 'autocomplete' })
console.log(value);
return <Autocomplete
options={options}
labelField='name'
valueField='name'
{...args as any}
/>
}
export const Default = Template.bind({});
Default.args = {
onChange: console.log
}
export const Lodaing = Template.bind({});
Lodaing.args = {
isLoading: true
}
export const Clearable = Template.bind({});
Clearable.args = {
isClearable: true
}
export const MultipleAllowed = Template.bind({});
MultipleAllowed.args = {
isMulti: true
}

View File

@@ -0,0 +1,97 @@
import Select, { StylesConfig } from "react-select";
type Props<T extends object | string> = {
options: T[];
labelField?: keyof T
valueField?: keyof T
placeholder?: string
disabled?: boolean
isLoading?: boolean;
isClearable?: boolean;
control?: any,
name?: string,
className?: string,
onBlur?: () => void;
} &
(
{
isMulti: true
onChange?: (values: T[] | null) => void
value?: T[] | null
}
|
{
isMulti?: false
onChange?: (values: T | null) => void
value?: T | null
}
)
const colourStyles: StylesConfig = {
input: (styles, state) => ({
...styles,
" input": {
boxShadow: 'none !important'
}
}),
};
export default function AutoComplete<T extends object>({
options,
labelField,
valueField,
placeholder = "Select Option...",
isMulti,
isClearable,
disabled,
className,
value,
onChange,
onBlur,
...props
}: Props<T>) {
return (
<div className='w-full'>
<Select
options={options}
placeholder={placeholder}
className={className}
isMulti={isMulti}
isClearable={isClearable}
isLoading={props.isLoading}
getOptionLabel={o => o[labelField]}
getOptionValue={o => o[valueField]}
value={value as any}
onChange={v => onChange?.(v as any)}
onBlur={onBlur}
styles={colourStyles}
theme={(theme) => ({
...theme,
borderRadius: 8,
colors: {
...theme.colors,
primary: 'var(--primary)',
},
})}
/>
</div>
);
}

View File

@@ -0,0 +1,35 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { MdOutlineKingBed } from 'react-icons/md';
import SelectInput from './SelectInput';
export default {
title: 'Shared/SelectInput',
component: SelectInput,
} as ComponentMeta<typeof SelectInput>;
const Template: ComponentStory<typeof SelectInput> = (args) => <SelectInput {...args} />
export const Default = Template.bind({});
Default.args = {
// defaultValue: 4,
options: [
{ value: 1, label: "Option 1" },
{ value: 2, label: 'Option 2' },
{ value: 3, label: 'Option 3' },
{ value: 4, label: 'Option 4' },
],
onChange: (nv) => alert("New value is: " + nv)
}
export const Loading = Template.bind({});
Loading.args = {
isLoading: true,
onChange: (nv) => alert("New value is: " + nv)
}

View File

@@ -0,0 +1,63 @@
import { ChangeEvent } from "react";
import { ThreeDots } from "react-loader-spinner";
import './selectinput.style.css'
interface Props {
options?: {
value: number | string | undefined,
label: string
}[]
classes?: {
containerClasses?: string,
inputClasses?: string
};
valueAsNumber?: boolean;
defaultValue?: number | string,
placeholder?: string;
value?: number | string,
isLoading?: boolean;
onChange?: (newValue: string) => void
onBlur?: () => void
[key: string]: any
}
export default function SelectInput({ options = [], classes, defaultValue, value, isLoading, onChange, onBlur, placeholder, ...props }: Props) {
const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
let value = props.valueAsNumber ? Number(e.target.value) : e.target.value;
onChange?.(value as any);
}
return (
<div className={`selectdiv relative ${classes?.containerClasses}`}>
<select className={`
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-primary-700 focus:ring focus:ring-primary-600 focus:ring-opacity-50
cursor-pointer
${classes?.inputClasses}
`}
disabled={isLoading}
value={value}
onChange={handleChange}
onBlur={onBlur}
defaultValue={defaultValue}
{...props}
>
{placeholder && <option value="" className="py-12">{placeholder}</option>}
{options.map(o => <option key={o.value} value={o.value} className="py-12">{o.label}</option>)}
</select>
{isLoading &&
<div className="absolute top-1/2 -translate-y-1/2 right-48">
<ThreeDots width={40} />
</div>
}
</div>
)
}

View File

@@ -0,0 +1,30 @@
.selectdiv:after {
content: "<>";
font: 15px "Consolas", monospace;
color: inherit;
-webkit-transform: translateY(-50%) rotate(90deg);
-moz-transform: translateY(-50%) rotate(90deg);
-ms-transform: translateY(-50%) rotate(90deg);
transform: translateY(-50%) rotate(90deg);
right: 11px;
/*Adjust for position however you want*/
top: 50%;
padding: 0 0 2px;
border-bottom: 1px solid inherit;
/*left line */
position: absolute;
pointer-events: none;
}
.selectdiv select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
/* Add some styling */
background-image: none;
-ms-word-break: normal;
word-break: normal;
}

View File

@@ -48,7 +48,6 @@ export default function Navbar() {
}));
const isLargeScreen = useMediaQuery(MEDIA_QUERIES.isLarge)
console.log(isLargeScreen, MEDIA_QUERIES.isLarge);
const onConnectWallet = () => {

View File

@@ -33,7 +33,6 @@ export default function HackathonCard({ hackathon }: Props) {
</div>
<div className="mt-16 flex flex-wrap gap-8">
{hackathon.topics.map(topic => <div key={topic.id} className="p-8 bg-gray-50 rounded-8 text-body5">{topic.icon} {topic.title}</div>)}
</div>
<Button href={hackathon.website} newTab color="gray" fullWidth className="mt-16">
Learn more

View File

@@ -1,4 +1,7 @@
import { useMediaQuery } from '@react-hookz/web';
import React, { useState } from 'react'
import AutoComplete from 'src/Components/Inputs/Autocomplete/Autocomplete';
import { MEDIA_QUERIES } from 'src/utils/theme';
const filters = [
{
@@ -21,23 +24,40 @@ export default function SortByFilter({ filterChanged }: Props) {
const [selected, setSelected] = useState<string | null>(null);
const filterClicked = (_newValue: string) => {
const filterClicked = (_newValue: string | null) => {
const newValue = selected !== _newValue ? _newValue : null;
setSelected(newValue);
filterChanged?.(newValue);
}
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<div className='bg-white border rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Sort By</p>
<ul>
{filters.map((f, idx) => <li
key={f.value}
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(f.value)}
>
{f.text}
</li>)}
</ul>
</div>
<>
{
isMdScreen ?
<div className='bg-white border rounded-12 p-16'>
< p className="text-body2 font-bolder text-black mb-16" > Sort By</p >
<ul>
{filters.map((f, idx) => <li
key={f.value}
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(f.value)}
>
{f.text}
</li>)}
</ul>
</div >
:
<AutoComplete
isClearable
placeholder='Sort By'
options={filters}
labelField='text'
valueField='value'
onChange={(o) => filterClicked(o ? o.value : null)} />
}</>
)
}

View File

@@ -1,6 +1,9 @@
import { useMediaQuery } from '@react-hookz/web';
import React, { useState } from 'react'
import Skeleton from 'react-loading-skeleton';
import Slider from 'src/Components/Slider/Slider';
import { useAllTopicsQuery, usePopularTopicsQuery } from 'src/graphql';
import { MEDIA_QUERIES } from 'src/utils/theme';
@@ -21,33 +24,59 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
filterChanged?.(newValue);
}
return (
<div className='bg-white border rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
<ul className=' flex flex-col gap-16'>
{topicsQuery.loading ?
Array(4).fill(0).map((_, idx) => <li
key={idx}
className={`flex items-start rounded-8 font-bold`}
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<>
{isMdScreen ?
<div className='bg-white border rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
<ul className=' flex flex-col gap-16'>
{topicsQuery.loading ?
Array(4).fill(0).map((_, idx) => <li
key={idx}
className={`flex items-start rounded-8 font-bold`}
>
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'> </span>
<span className="self-center px-16"><Skeleton width={'7ch'} />
</span>
</li>
)
:
topicsQuery.data?.allTopics.map((topic, idx) => <li
key={topic.id}
className={`flex items-start rounded-8 cursor-pointer font-bold ${topic.id === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(topic.id)}
>
<span className={`${topic.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{topic.icon}</span>
<span className="self-center px-16">
{topic.title}
</span>
</li>)}
</ul>
</div>
:
<>
{
topicsQuery.loading ?
<ul className="flex gap-8 ">
{Array(4).fill(0).map((_, idx) => <div key={idx} className="py-12 px-16 bg-gray-100 rounded-8 text-body5"><span className="opacity-0">Category</span></div>)}
</ul>
:
<Slider>
{topicsQuery.data?.allTopics.map(topic =>
<div
key={topic.id}
onClick={() => filterClicked(topic.id)}
className={`${topic.id === selected ? 'bg-gray-200' : "bg-gray-100"} py-12 px-16 rounded-8 text-body5`}
>{topic.icon} {topic.title}</div>)}
</Slider>
}
</>
}
</>
>
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'> </span>
<span className="self-center px-16"><Skeleton width={'7ch'} />
</span>
</li>
)
:
topicsQuery.data?.allTopics.map((f, idx) => <li
key={f.id}
className={`flex items-start rounded-8 cursor-pointer font-bold ${f.id === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(f.id)}
>
<span className={`${f.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{f.icon}</span>
<span className="self-center px-16">
{f.title}
</span>
</li>)}
</ul>
</div>
)
}

View File

@@ -29,11 +29,10 @@ export default function HackathonsPage() {
className={`page-container pt-16 w-full ${styles.grid}`}
>
<aside className='no-scrollbar'>
<div className="sticky flex flex-col gap-24"
<div className="sticky flex flex-col gap-24 md:overflow-y-scroll"
style={{
top: `${navHeight + 16}px`,
maxHeight: `calc(100vh - ${navHeight}px - 16px)`,
overflowY: "scroll",
}}>
<SortByFilter
filterChanged={setSortByFilter}

View File

@@ -1,10 +1,10 @@
.grid {
display: grid;
grid-template-columns: 0 1fr 0;
gap: 0;
grid-template-columns: 100%;
gap: 32px;
@media screen and (min-width: 680px) {
grid-template-columns: 1fr 2fr;
@media screen and (min-width: 768px) {
grid-template-columns: 1fr 2fr 0;
gap: 32px;
}

View File

@@ -37,16 +37,15 @@ export default function FeedPage() {
className={`page-container pt-16 w-full ${styles.grid}`}
>
<aside className='no-scrollbar'>
<div className="sticky"
<div className="sticky md:overflow-y-scroll"
style={{
top: `${navHeight + 16}px`,
maxHeight: `calc(100vh - ${navHeight}px - 16px)`,
overflowY: "scroll",
}}>
{/* <SortBy
<SortBy
filterChanged={setSortByFilter}
/>
<div className="my-24"></div> */}
<div className="my-24"></div>
<PopularTopicsFilter
filterChanged={setTopicFilter}
/>
@@ -58,7 +57,7 @@ export default function FeedPage() {
isFetching={isFetchingMore}
onReachedBottom={fetchMore}
/>
<aside className='no-scrollbar'>
<aside className='no-scrollbar hidden md:block'>
<div className="sticky"
style={{
top: `${navHeight + 16}px`,

View File

@@ -1,6 +1,9 @@
import { useMediaQuery } from '@react-hookz/web';
import React, { useState } from 'react'
import Skeleton from 'react-loading-skeleton';
import Slider from 'src/Components/Slider/Slider';
import { usePopularTopicsQuery } from 'src/graphql';
import { MEDIA_QUERIES } from 'src/utils/theme';
interface Props {
@@ -20,33 +23,59 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
filterChanged?.(newValue);
}
return (
<div className='bg-white border rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
<ul className=' flex flex-col gap-16'>
{topicsQuery.loading ?
Array(4).fill(0).map((_, idx) => <li
key={idx}
className={`flex items-start rounded-8 font-bold`}
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<>
{isMdScreen ?
<div className='bg-white border rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
<ul className=' flex flex-col gap-16'>
{topicsQuery.loading ?
Array(4).fill(0).map((_, idx) => <li
key={idx}
className={`flex items-start rounded-8 font-bold`}
>
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'> </span>
<span className="self-center px-16"><Skeleton width={'7ch'} />
</span>
</li>
)
:
topicsQuery.data?.popularTopics.map((topic, idx) => <li
key={topic.id}
className={`flex items-start rounded-8 cursor-pointer font-bold ${topic.id === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(topic.id)}
>
<span className={`${topic.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{topic.icon}</span>
<span className="self-center px-16">
{topic.title}
</span>
</li>)}
</ul>
</div>
:
<>
{
topicsQuery.loading ?
<ul className="flex gap-8 ">
{Array(4).fill(0).map((_, idx) => <div key={idx} className="py-12 px-16 bg-gray-100 rounded-8 text-body5"><span className="opacity-0">Category</span></div>)}
</ul>
:
<Slider>
{topicsQuery.data?.popularTopics.map(topic =>
<div
key={topic.id}
onClick={() => filterClicked(topic.id)}
className={`${topic.id === selected ? 'bg-gray-200' : "bg-gray-100"} py-12 px-16 rounded-8 text-body5`}
>{topic.icon} {topic.title}</div>)}
</Slider>
}
</>
}
</>
>
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'> </span>
<span className="self-center px-16"><Skeleton width={'7ch'} />
</span>
</li>
)
:
topicsQuery.data?.popularTopics.map((f, idx) => <li
key={f.id}
className={`flex items-start rounded-8 cursor-pointer font-bold ${f.id === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(f.id)}
>
<span className={`${f.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{f.icon}</span>
<span className="self-center px-16">
{f.title}
</span>
</li>)}
</ul>
</div>
)
}

View File

@@ -1,5 +1,8 @@
import { useMediaQuery } from '@react-hookz/web';
import React, { useState } from 'react'
import { Nullable } from 'remirror';
import AutoComplete from 'src/Components/Inputs/Autocomplete/Autocomplete';
import { MEDIA_QUERIES } from 'src/utils/theme';
const filters = [
{
@@ -22,24 +25,42 @@ export default function SortBy({ filterChanged }: Props) {
const [selected, setSelected] = useState<Nullable<string>>(filters[0].value);
const filterClicked = (_newValue: string) => {
const filterClicked = (_newValue: string | null) => {
const newValue = selected !== _newValue ? _newValue : null;
setSelected(newValue);
filterChanged?.(newValue);
}
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<div className='bg-white border rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Sort By</p>
<ul>
{filters.map((f, idx) => <li
key={f.value}
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(f.value)}
>
{f.text}
</li>)}
</ul>
</div>
<>
{
isMdScreen ?
<div className='bg-white border rounded-12 p-16'>
< p className="text-body2 font-bolder text-black mb-16" > Sort By</p >
<ul>
{filters.map((f, idx) => <li
key={f.value}
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-200'}`}
onClick={() => filterClicked(f.value)}
>
{f.text}
</li>)}
</ul>
</div >
:
<AutoComplete
isClearable
placeholder='Sort By'
options={filters}
labelField='text'
valueField='value'
onChange={(o) => filterClicked(o ? o.value : null)} />
}</>
)
}

View File

@@ -1,9 +1,9 @@
.grid {
display: grid;
grid-template-columns: 0 1fr 0;
gap: 0;
grid-template-columns: 100%;
gap: 32px;
@media screen and (min-width: 680px) {
@media screen and (min-width: 768px) {
grid-template-columns: 1fr 2fr 0;
gap: 32px;
}

View File

@@ -11,10 +11,17 @@ body {
}
.page-container {
width: calc(min(100% - 64px, 1440px));
width: calc(min(100% - 32px, 1440px));
margin: 0 auto;
}
@media screen and (min-width: 780px) {
.page-container {
width: calc(min(100% - 64px, 1440px));
margin: 0 auto;
}
}
svg {
display: inline-block;
}