From 69b0f3b678ed4b0aaf661368cd6e8d8f45174107 Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Tue, 8 Aug 2023 14:54:10 -0600 Subject: [PATCH 01/10] Basic app store layout --- src/AppStore/IndexPage.jsx | 297 +++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 src/AppStore/IndexPage.jsx diff --git a/src/AppStore/IndexPage.jsx b/src/AppStore/IndexPage.jsx new file mode 100644 index 00000000..e5ab5dcd --- /dev/null +++ b/src/AppStore/IndexPage.jsx @@ -0,0 +1,297 @@ +const Wrapper = styled.div` + padding: 100px 0; + background: url("https://ipfs.near.social/ipfs/bafkreie5t75jirebnuyozmsc5hxzhxpoqivaxmc4rypaaogab6qh7asb2i"); + background-position: right top; + background-size: 1440px auto; + background-repeat: no-repeat; +`; + +const Container = styled.div` + max-width: 1120px; + margin: 0 auto; + padding: 0 16px; +`; + +const Section = styled.div` + margin-top: 3rem; +`; + +const H1 = styled.h1` + font: var(--text-hero); + color: var(--sand12); + margin: 0 0 3rem; +`; + +const H2 = styled.h2` + font: var(--text-l); + color: var(--sand12); + margin: 0 0 1.5rem; + font-weight: 600; +`; + +const Text = styled.p` + font: var(--${(p) => p.size ?? "text-base"}); + font-weight: ${(p) => p.fontWeight}; + color: var(--${(p) => p.color ?? "sand12"}); + margin: 0; +`; + +const ThumbnailGrid = styled.div` + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 1.5rem; +`; + +const ContentGrid = styled.div` + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2.5rem; +`; + +const OverlayCard = styled.a` + display: block; + aspect-ratio: 1 / 1; + overflow: hidden; + border-radius: 1.25rem; + border: 1px solid var(--sand6); + position: relative; + cursor: pointer; + text-decoration: none !important; + outline: none; + transition: all 200ms; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border: none; + } + + &:hover, + &:focus { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); + } +`; + +const OverlayCardContent = styled.span` + display: flex; + flex-direction: column; + gap: 0.25rem; + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 1.25rem; + background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); + font: var(--text-xs); + color: var(--white); + + b { + font-weight: 600; + } +`; + +const OverlayCardTag = styled.span` + display: inline-flex; + border-bottom-right-radius: 1.25rem; + background: var(--violet7); + color: #fff; + font: var(--text-xs); + font-weight: 700; + padding: 0.25rem 0.75rem; + position: absolute; + top: 0; + left: 0; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + border-right: 1px solid rgba(0, 0, 0, 0.1); +`; + +const AppImage = styled.div` + width: 5rem; + height: 5rem; + border-radius: 1rem; + border: 1px solid var(--sand6); + overflow: hidden; + transition: all 200ms; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border: none; + } +`; + +const AppContent = styled.div` + display: flex; + flex-direction: column; + gap: 0.25rem; +`; + +const App = styled.a` + display: flex; + gap: 1.5rem; + align-items: center; + cursor: pointer; + text-decoration: none !important; + + &:hover, + &:focus { + ${AppImage} { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); + } + } +`; + +const ArticleImage = styled.div` + width: 100%; + aspect-ratio: 26 / 15; + border-radius: 1rem; + border: 1px solid var(--sand6); + overflow: hidden; + transition: all 200ms; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border: none; + } +`; + +const ArticleContent = styled.div` + display: flex; + flex-direction: column; + gap: 0.25rem; +`; + +const Article = styled.a` + display: flex; + flex-direction: column; + gap: 1rem; + align-items: row; + cursor: pointer; + text-decoration: none !important; + + &:hover, + &:focus { + ${ArticleImage} { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); + } + } +`; + +return ( + + +

d.Apps

+ +
+ + + Featured + + + + App Name + + Creator + + + + + Featured + + + + App Name + + Creator + + + + + + + + App Name + + Creator + + + + + + + + App Name + + Creator + + + +
+ +
+

Subheader

+ + + + + + + + + App Name + + Creator + + + + + + + + + App Name + + Creator + + + +
+ +
+

Subheader

+ + +
+ + + + + + Article Name + + Creator + +
+
+ + + + + + Article Name + + Creator + +
+
+
+
+
+); From 8b2083f91cd49379f1adfadbe8807788fa658aa5 Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Mon, 14 Aug 2023 11:57:22 -0600 Subject: [PATCH 02/10] Implemented DIG.InputSearch --- src/DIG/Input.jsx | 9 ++++++ src/DIG/InputSearch.jsx | 49 +++++++++++++++++++++++++++++++ src/DIG/InputSearch.metadata.json | 7 +++++ 3 files changed, 65 insertions(+) create mode 100644 src/DIG/InputSearch.jsx create mode 100644 src/DIG/InputSearch.metadata.json diff --git a/src/DIG/Input.jsx b/src/DIG/Input.jsx index d03ad0eb..efea4fe2 100644 --- a/src/DIG/Input.jsx +++ b/src/DIG/Input.jsx @@ -4,8 +4,10 @@ let { iconLeft, iconRight, inputNodeLeft, + inputNodeRight, invalid, label, + search, select, textarea, valid, @@ -58,6 +60,10 @@ const InputWrapper = styled.div` background: var(--sand2); } + [data-search="true"] & { + border-radius: 100px; + } + [data-invalid="true"] & { background: var(--red1); border-color: var(--red9); @@ -212,6 +218,7 @@ return ( data-valid={valid} data-disabled={disabled} data-textarea={textarea} + data-search={search} data-select={select} data-no-value={hasNoValue} > @@ -242,6 +249,8 @@ return ( {...forwardedProps} /> + {inputNodeRight} + {iconRight && } )} diff --git a/src/DIG/InputSearch.jsx b/src/DIG/InputSearch.jsx new file mode 100644 index 00000000..073c0a7e --- /dev/null +++ b/src/DIG/InputSearch.jsx @@ -0,0 +1,49 @@ +let { onQueryChange, placeholder, ...forwardedProps } = props; + +State.init({ + value: "", +}); + +function handleOnInput(e) { + const value = e.target.value; + + State.update({ value }); + + if (onQueryChange) { + onQueryChange(value); + } +} + +function reset() { + State.update({ value: "" }); + onQueryChange(""); +} + +const ResetButton = styled.button` + all: unset; + display: block; + + input:placeholder-shown + & { + display: none; + } +`; + +return ( + + + + ), + onInput: handleOnInput, + placeholder: placeholder ?? "Search...", + search: true, + type: "text", + value: state.value, + ...forwardedProps, + }} + /> +); diff --git a/src/DIG/InputSearch.metadata.json b/src/DIG/InputSearch.metadata.json new file mode 100644 index 00000000..6d07d17f --- /dev/null +++ b/src/DIG/InputSearch.metadata.json @@ -0,0 +1,7 @@ +{ + "description": "An input component for typing a search query.\n\n### Example\n\n```jsx\nreturn (\n console.log(query),\n }}\n />\n);\n```\n\n### Props\n\n`assistiveText`\n\n- type: string\n- Adds assistive text to the bottom of the input. Useful for info, success, and error messages.\n\n`disabled`\n\n- type: boolean\n- Disables the input\n\n`invalid`\n\n- type: boolean\n- Renders input with error variant\n\n`label`\n\n- type: string\n- Renders label above input\n\n`onQueryChange`\n\n- type: function\n- Returns search query whenever input changes\n\n### HTML Attributes\n\nAll other props will be forwarded through to the `` element. EG: `placeholder`.", + "name": "DIG.InputSearch", + "tags": { + "dig": "" + } +} From 53e469f59be24dd79c62ec22b9481fb6ce255601 Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Mon, 14 Aug 2023 12:09:14 -0600 Subject: [PATCH 03/10] Fixing search icon --- src/DIG/InputSearch.jsx | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/DIG/InputSearch.jsx b/src/DIG/InputSearch.jsx index 073c0a7e..05f9bf03 100644 --- a/src/DIG/InputSearch.jsx +++ b/src/DIG/InputSearch.jsx @@ -21,7 +21,25 @@ function reset() { const ResetButton = styled.button` all: unset; - display: block; + display: flex; + align-items: center; + justify-content: center; + height: 1.5rem; + width: 1.5rem; + border-radius: 100%; + background: var(--sand5); + text-align: center; + transition: all 200ms; + margin-right: -0.25rem; + + i { + font-size: 12px !important; + color: var(--sand12) !important; + } + + &:hover { + background: var(--sand6); + } input:placeholder-shown + & { display: none; @@ -32,10 +50,15 @@ return ( - + + ), onInput: handleOnInput, From 8050a5285c02d409901a9a0c6bf957c6406926eb Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Mon, 14 Aug 2023 14:30:40 -0600 Subject: [PATCH 04/10] Allow passing in metadata for ComponentCard to avoid Social.get() call --- src/ComponentCard.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ComponentCard.jsx b/src/ComponentCard.jsx index 69066cd8..7f940d21 100644 --- a/src/ComponentCard.jsx +++ b/src/ComponentCard.jsx @@ -1,12 +1,14 @@ -const [accountId, widget, widgetName] = props.src.split("/"); -const metadata = Social.get( - `${accountId}/widget/${widgetName}/metadata/**`, - "final" -); -const tags = Object.keys(metadata.tags || {}); +const [accountId, unused, widgetName] = props.src.split("/"); const detailsUrl = `#/${REPL_ACCOUNT}/widget/ComponentDetailsPage?src=${accountId}/widget/${widgetName}`; const appUrl = `#/${accountId}/widget/${widgetName}`; const accountUrl = `#/${REPL_ACCOUNT}/widget/ProfilePage?accountId=${accountId}`; +const metadata = + props.metadata ?? + Social.get(`${accountId}/widget/${widgetName}/metadata/**`, "final") ?? + {}; +const tags = props.metadata + ? props.metadata.tags + : Object.keys(metadata.tags || {}); const Card = styled.div` position: relative; From 14ae61a49504b41b37209656879a0574d7a403ef Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Wed, 16 Aug 2023 14:41:34 -0600 Subject: [PATCH 05/10] Breaking up app store index page into multiple components, implementing search --- src/AppStore/AppThumbnail.jsx | 85 +++++++ src/AppStore/ArticleSummary.jsx | 68 +++++ src/AppStore/IndexPage.jsx | 438 +++++++++++++++----------------- src/AppStore/Search.jsx | 130 ++++++++++ 4 files changed, 484 insertions(+), 237 deletions(-) create mode 100644 src/AppStore/AppThumbnail.jsx create mode 100644 src/AppStore/ArticleSummary.jsx create mode 100644 src/AppStore/Search.jsx diff --git a/src/AppStore/AppThumbnail.jsx b/src/AppStore/AppThumbnail.jsx new file mode 100644 index 00000000..b3d1a06d --- /dev/null +++ b/src/AppStore/AppThumbnail.jsx @@ -0,0 +1,85 @@ +const appDetailsUrl = `#/${REPL_ACCOUNT}/widget/ComponentDetailsPage?src=${props.author}/widget/${props.widgetName}`; + +const Thumbnail = styled.a` + display: block; + aspect-ratio: 1 / 1; + overflow: hidden; + border-radius: 1.25rem; + border: 1px solid var(--sand6); + position: relative; + cursor: pointer; + text-decoration: none !important; + outline: none; + transition: all 200ms; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border: none; + } + + &:hover, + &:focus { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); + } +`; + +const ThumbnailContent = styled.span` + display: flex; + flex-direction: column; + gap: 0.25rem; + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 1.25rem; + background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); + font: var(--text-xs); + color: var(--white); + + b { + font-weight: 600; + } + + @media (max-width: 650px) { + padding: 0.75rem; + } +`; + +const ThumbnailTag = styled.span` + display: inline-flex; + border-bottom-right-radius: 1.25rem; + background: var(--violet7); + color: #fff; + font: var(--text-xs); + font-weight: 700; + padding: 0.25rem 0.75rem; + position: absolute; + top: 0; + left: 0; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + border-right: 1px solid rgba(0, 0, 0, 0.1); +`; + +return ( + + + + + + {props.name ?? props.widgetName} + + {props.author} + + +); diff --git a/src/AppStore/ArticleSummary.jsx b/src/AppStore/ArticleSummary.jsx new file mode 100644 index 00000000..9cdeba14 --- /dev/null +++ b/src/AppStore/ArticleSummary.jsx @@ -0,0 +1,68 @@ +const Text = styled.p` + font: var(--${(p) => p.size ?? "text-base"}); + font-weight: ${(p) => p.fontWeight}; + color: var(--${(p) => p.color ?? "sand12"}); + margin: 0; +`; + +const ArticleImage = styled.div` + width: 100%; + aspect-ratio: 26 / 15; + border-radius: 1rem; + border: 1px solid var(--sand6); + overflow: hidden; + transition: all 200ms; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border: none; + } +`; + +const ArticleContent = styled.div` + display: flex; + flex-direction: column; + gap: 0.25rem; +`; + +const Article = styled.a` + display: flex; + flex-direction: column; + gap: 1rem; + align-items: row; + cursor: pointer; + text-decoration: none !important; + + &:hover, + &:focus { + ${ArticleImage} { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); + } + } +`; + +return ( +
+ + + + + + + {props.title} + + {props.author} + +
+); diff --git a/src/AppStore/IndexPage.jsx b/src/AppStore/IndexPage.jsx index e5ab5dcd..5a1b6e6c 100644 --- a/src/AppStore/IndexPage.jsx +++ b/src/AppStore/IndexPage.jsx @@ -1,9 +1,31 @@ +State.init({ + selectedTab: props.tab ?? "discover", +}); + +if (props.tab && props.tab !== state.selectedTab) { + State.update({ + selectedTab: props.tab, + }); +} + +const appStoreIndexUrl = "#/${REPL_ACCOUNT}/widget/AppStore.IndexPage"; + const Wrapper = styled.div` padding: 100px 0; background: url("https://ipfs.near.social/ipfs/bafkreie5t75jirebnuyozmsc5hxzhxpoqivaxmc4rypaaogab6qh7asb2i"); background-position: right top; background-size: 1440px auto; background-repeat: no-repeat; + margin-top: calc(var(--body-top-padding) * -1); + + @media (max-width: 1024px) { + padding: 50px 0; + } + + @media (max-width: 800px) { + background-image: none; + padding: 2rem 0; + } `; const Container = styled.div` @@ -12,175 +34,116 @@ const Container = styled.div` padding: 0 16px; `; -const Section = styled.div` - margin-top: 3rem; -`; - -const H1 = styled.h1` - font: var(--text-hero); - color: var(--sand12); - margin: 0 0 3rem; -`; +const Main = styled.div` + display: flex; + gap: 6.5rem; -const H2 = styled.h2` - font: var(--text-l); - color: var(--sand12); - margin: 0 0 1.5rem; - font-weight: 600; -`; + @media (max-width: 1024px) { + flex-direction: column; + gap: 3rem; + } -const Text = styled.p` - font: var(--${(p) => p.size ?? "text-base"}); - font-weight: ${(p) => p.fontWeight}; - color: var(--${(p) => p.color ?? "sand12"}); - margin: 0; + @media (max-width: 800px) { + gap: 2rem; + } `; -const ThumbnailGrid = styled.div` - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; +const Menu = styled.div` + width: 7.5rem; + flex-shrink: 0; + text-align: right; + display: flex; + flex-direction: column; gap: 1.5rem; -`; + overflow: auto; + scroll-behavior: smooth; -const ContentGrid = styled.div` - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2.5rem; + @media (max-width: 1024px) { + width: 100%; + text-align: left; + flex-direction: row; + } `; -const OverlayCard = styled.a` +const MenuLink = styled.a` display: block; - aspect-ratio: 1 / 1; - overflow: hidden; - border-radius: 1.25rem; - border: 1px solid var(--sand6); - position: relative; - cursor: pointer; - text-decoration: none !important; + font: var(--text-s); + font-weight: 600; + color: var(--sand12); outline: none; - transition: all 200ms; - - img { - display: block; - width: 100%; - height: 100%; - object-fit: cover; - border: none; - } &:hover, &:focus { - box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); + color: var(--sand12); + text-decoration: underline; + } + + &[data-active="true"] { + color: var(--violet7); } `; -const OverlayCardContent = styled.span` +const Sections = styled.div` display: flex; flex-direction: column; - gap: 0.25rem; - position: absolute; - bottom: 0; - left: 0; - right: 0; - padding: 1.25rem; - background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); - font: var(--text-xs); - color: var(--white); - - b { - font-weight: 600; - } + gap: 3rem; `; -const OverlayCardTag = styled.span` - display: inline-flex; - border-bottom-right-radius: 1.25rem; - background: var(--violet7); - color: #fff; - font: var(--text-xs); - font-weight: 700; - padding: 0.25rem 0.75rem; - position: absolute; - top: 0; - left: 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - border-right: 1px solid rgba(0, 0, 0, 0.1); -`; +const Section = styled.div``; -const AppImage = styled.div` - width: 5rem; - height: 5rem; - border-radius: 1rem; - border: 1px solid var(--sand6); - overflow: hidden; - transition: all 200ms; +const H1 = styled.h1` + font: var(--text-hero); + color: var(--sand12); + margin: 0 0 3rem; + padding-left: 14rem; - img { - display: block; - width: 100%; - height: 100%; - object-fit: cover; - border: none; + @media (max-width: 1024px) { + padding-left: 0; } -`; -const AppContent = styled.div` - display: flex; - flex-direction: column; - gap: 0.25rem; + @media (max-width: 800px) { + font: var(--text-3xl); + font-weight: 600; + margin: 0 0 2rem; + } `; -const App = styled.a` - display: flex; - gap: 1.5rem; - align-items: center; - cursor: pointer; - text-decoration: none !important; +const H2 = styled.h2` + font: var(--text-l); + color: var(--sand12); + margin: 0 0 1.5rem; + font-weight: 600; +`; - &:hover, - &:focus { - ${AppImage} { - box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); - } - } +const Text = styled.p` + font: var(--${(p) => p.size ?? "text-base"}); + font-weight: ${(p) => p.fontWeight}; + color: var(--${(p) => p.color ?? "sand12"}); + margin: 0; `; -const ArticleImage = styled.div` - width: 100%; - aspect-ratio: 26 / 15; - border-radius: 1rem; - border: 1px solid var(--sand6); - overflow: hidden; - transition: all 200ms; +const ThumbnailGrid = styled.div` + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 1.5rem; - img { - display: block; - width: 100%; - height: 100%; - object-fit: cover; - border: none; + @media (max-width: 850px) { + grid-template-columns: 1fr 1fr 1fr; } -`; -const ArticleContent = styled.div` - display: flex; - flex-direction: column; - gap: 0.25rem; + @media (max-width: 550px) { + gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } `; -const Article = styled.a` - display: flex; - flex-direction: column; - gap: 1rem; - align-items: row; - cursor: pointer; - text-decoration: none !important; +const ContentGrid = styled.div` + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; - &:hover, - &:focus { - ${ArticleImage} { - box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); - } + @media (max-width: 650px) { + grid-template-columns: 1fr; } `; @@ -189,109 +152,110 @@ return (

d.Apps

-
- - - Featured - - - - App Name - - Creator - - - - - Featured - - - - App Name - - Creator - - - - - - - - App Name - - Creator - - - - - - - - App Name - - Creator - - - -
- -
-

Subheader

- - - - - - - - - App Name - - Creator - - - - - - - - - App Name - - Creator - - - -
- -
-

Subheader

- - -
- - - - - - Article Name - - Creator - -
-
- - - - - - Article Name - - Creator - -
-
-
+
+ + + Discover + + + Earn + + + Play + + + Develop + + + Engage + + + Search + + + + {state.selectedTab === "search" ? ( + + ) : ( + +
+ + + +
+ +
+

Subheader

+ + + + +
+ +
+

Subheader

+ + + + +
+
+ )} +
); diff --git a/src/AppStore/Search.jsx b/src/AppStore/Search.jsx new file mode 100644 index 00000000..60ad6649 --- /dev/null +++ b/src/AppStore/Search.jsx @@ -0,0 +1,130 @@ +function debounce(func, wait) { + const pause = wait || 350; + let timeout; + + return (args) => { + const later = () => { + clearTimeout(timeout); + func(args); + }; + + clearTimeout(timeout); + timeout = setTimeout(later, pause); + }; +} + +function search(query) { + State.update({ + query, + }); + + if (!query) { + State.update({ + results: [], + }); + return; + } + + const body = { + query, + page: 0, + filters: "categories:widget AND tags:app AND NOT _tags:hidden", + }; + + asyncFetch("/api/search", { + body: JSON.stringify(body), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }) + .then((res) => { + State.update({ + results: res.body.hits ?? [], + }); + }) + .catch((error) => console.log(error)); +} + +function handleQueryChange(query) { + state.searchDebounced(query); +} + +State.init({ + query: "", + results: [], + searchDebounced: debounce(search, 200), +}); + +const Wrapper = styled.div` + display: flex; + width: 100%; + flex-direction: column; + gap: 3rem; +`; + +const H2 = styled.h2` + font: var(--text-l); + color: var(--sand12); + margin: 0; + font-weight: 600; +`; + +const Text = styled.p` + font: var(--${(p) => p.size ?? "text-base"}); + font-weight: ${(p) => p.fontWeight}; + color: var(--${(p) => p.color ?? "sand12"}); + margin: 0; +`; + +const ContentGrid = styled.div` + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + + @media (max-width: 650px) { + grid-template-columns: 1fr; + } +`; + +return ( + + + + {state.query && state.results.length === 0 && ( + No apps matched your search: "{state.query}" + )} + + {state.results.length > 0 && ( + <> +

All apps

+ + + {state.results.map((result) => { + return ( + + ); + })} + + + )} +
+); From 9e283b149b1609b901766501317718acd3541076 Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Mon, 28 Aug 2023 15:20:53 -0600 Subject: [PATCH 06/10] Implement data fetch for app store --- src/AppStore/AppThumbnail.jsx | 8 +- src/AppStore/IndexPage.jsx | 232 ++++++++++++++++++++-------------- src/AppStore/Search.jsx | 17 ++- 3 files changed, 156 insertions(+), 101 deletions(-) diff --git a/src/AppStore/AppThumbnail.jsx b/src/AppStore/AppThumbnail.jsx index b3d1a06d..4717cc1a 100644 --- a/src/AppStore/AppThumbnail.jsx +++ b/src/AppStore/AppThumbnail.jsx @@ -35,9 +35,15 @@ const ThumbnailContent = styled.span` left: 0; right: 0; padding: 1.25rem; - background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); + padding-top: 3.5rem; + background: linear-gradient( + to top, + rgba(0, 0, 0, 0.85) 20%, + rgba(0, 0, 0, 0) + ); font: var(--text-xs); color: var(--white); + text-shadow: 0 0 2px rgba(0, 0, 0, 0.75); b { font-weight: 600; diff --git a/src/AppStore/IndexPage.jsx b/src/AppStore/IndexPage.jsx index 5a1b6e6c..40f2f767 100644 --- a/src/AppStore/IndexPage.jsx +++ b/src/AppStore/IndexPage.jsx @@ -1,5 +1,7 @@ State.init({ - selectedTab: props.tab ?? "discover", + categories: [], + isLoading: true, + selectedTab: props.tab, }); if (props.tab && props.tab !== state.selectedTab) { @@ -9,6 +11,32 @@ if (props.tab && props.tab !== state.selectedTab) { } const appStoreIndexUrl = "#/${REPL_ACCOUNT}/widget/AppStore.IndexPage"; +const selectedCategory = state.categories.find( + (category) => category.label === state.selectedTab +); + +function loadData() { + if (state.categories.length > 0) return; + + asyncFetch( + "https://storage.googleapis.com/databricks-near-query-runner/output/app-store.json" + ) + .then((res) => { + State.update({ + categories: res.body.categories, + isLoading: false, + selectedTab: state.selectedTab ?? res.body.categories[0].label, + }); + }) + .catch((error) => { + State.update({ + isLoading: false, + }); + console.log(error); + }); +} + +loadData(); const Wrapper = styled.div` padding: 100px 0; @@ -87,6 +115,7 @@ const Sections = styled.div` display: flex; flex-direction: column; gap: 3rem; + flex-grow: 1; `; const Section = styled.div``; @@ -150,111 +179,120 @@ const ContentGrid = styled.div` return ( -

d.Apps

+

{selectedCategory?.title}

+ {state.categories.map((category) => { + return ( + + {category.label} + + ); + })} + - Discover - - - Earn - - - Play - - - Develop - - - Engage - - Search - {state.selectedTab === "search" ? ( - - ) : ( - -
- - - -
- -
-

Subheader

- - - - -
- -
-

Subheader

- - - - -
-
- )} + + {state.selectedTab === "Search" && ( + + )} + + {state.selectedTab !== "Search" && selectedCategory && ( + <> + {selectedCategory.sections.map((section) => { + switch (section.format) { + case "MEDIUM": + return ( +
+ {section.title &&

{section.title}

} + + {section.items.map((item) => { + return ( + + ); + })} + +
+ ); + + case "SMALL": + return ( +
+ {section.title &&

{section.title}

} + + + {section.items.map((item) => { + return ( + + ); + })} + +
+ ); + + case "ARTICLE": + return ( +
+ {section.title &&

{section.title}

} + + + {section.items.map((item) => { + return ( + + ); + })} + +
+ ); + + default: + return null; + } + })} + + )} +
diff --git a/src/AppStore/Search.jsx b/src/AppStore/Search.jsx index 60ad6649..74d40702 100644 --- a/src/AppStore/Search.jsx +++ b/src/AppStore/Search.jsx @@ -25,6 +25,10 @@ function search(query) { return; } + State.update({ + isLoading: true, + }); + const body = { query, page: 0, @@ -40,10 +44,16 @@ function search(query) { }) .then((res) => { State.update({ + isLoading: false, results: res.body.hits ?? [], }); }) - .catch((error) => console.log(error)); + .catch((error) => { + State.update({ + isLoading: false, + }); + console.log(error); + }); } function handleQueryChange(query) { @@ -51,9 +61,10 @@ function handleQueryChange(query) { } State.init({ + isLoading: false, query: "", results: [], - searchDebounced: debounce(search, 200), + searchDebounced: debounce(search, 300), }); const Wrapper = styled.div` @@ -97,7 +108,7 @@ return ( }} /> - {state.query && state.results.length === 0 && ( + {state.query && state.results.length === 0 && !state.isLoading && ( No apps matched your search: "{state.query}" )} From 29ad1932bd3620429e3cfb0c7ae366a93bfa8c85 Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Wed, 30 Aug 2023 11:07:25 -0600 Subject: [PATCH 07/10] Implement debounce for DIG.InputSearch, app store search pagination --- src/AppStore/IndexPage.jsx | 4 +- src/AppStore/Search.jsx | 73 +++++++++++++++++++------------ src/DIG/InputSearch.jsx | 36 ++++++++++++--- src/DIG/InputSearch.metadata.json | 2 +- 4 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/AppStore/IndexPage.jsx b/src/AppStore/IndexPage.jsx index 40f2f767..b10098b1 100644 --- a/src/AppStore/IndexPage.jsx +++ b/src/AppStore/IndexPage.jsx @@ -179,7 +179,9 @@ const ContentGrid = styled.div` return ( -

{selectedCategory?.title}

+

+ {state.selectedTab === "Search" ? "Search" : selectedCategory?.title} +

diff --git a/src/AppStore/Search.jsx b/src/AppStore/Search.jsx index 74d40702..a9b24847 100644 --- a/src/AppStore/Search.jsx +++ b/src/AppStore/Search.jsx @@ -1,37 +1,15 @@ -function debounce(func, wait) { - const pause = wait || 350; - let timeout; - - return (args) => { - const later = () => { - clearTimeout(timeout); - func(args); - }; - - clearTimeout(timeout); - timeout = setTimeout(later, pause); - }; -} - function search(query) { - State.update({ - query, - }); - if (!query) { State.update({ + isLoading: false, results: [], }); return; } - State.update({ - isLoading: true, - }); - const body = { query, - page: 0, + page: state.currentPage, filters: "categories:widget AND tags:app AND NOT _tags:hidden", }; @@ -45,7 +23,8 @@ function search(query) { .then((res) => { State.update({ isLoading: false, - results: res.body.hits ?? [], + results: [...state.results, ...res.body.hits], + totalPages: res.body.nbPages, }); }) .catch((error) => { @@ -56,15 +35,37 @@ function search(query) { }); } -function handleQueryChange(query) { - state.searchDebounced(query); +function handleOnInput() { + State.update({ + currentPage: 0, + totalPages: 0, + isLoading: true, + results: [], + }); +} + +function handleOnQueryChange(query) { + State.update({ + query, + }); + search(query); +} + +function loadMore() { + State.update({ + currentPage: state.currentPage + 1, + isLoading: true, + }); + + search(state.query); } State.init({ + currentPage: 0, + totalPages: 0, isLoading: false, query: "", results: [], - searchDebounced: debounce(search, 300), }); const Wrapper = styled.div` @@ -103,7 +104,8 @@ return ( @@ -137,5 +139,18 @@ return ( )} + + {state.currentPage + 1 < state.totalPages && ( + + )} ); diff --git a/src/DIG/InputSearch.jsx b/src/DIG/InputSearch.jsx index 05f9bf03..3e281b6b 100644 --- a/src/DIG/InputSearch.jsx +++ b/src/DIG/InputSearch.jsx @@ -1,14 +1,35 @@ -let { onQueryChange, placeholder, ...forwardedProps } = props; +let { debounceDelay, onInput, onQueryChange, placeholder, ...forwardedProps } = + props; -State.init({ - value: "", -}); +debounceDelay = debounceDelay ?? 300; + +function debounce(func, delay) { + let timeout; + + return (args) => { + const later = () => { + clearTimeout(timeout); + func(args); + }; + + clearTimeout(timeout); + + timeout = setTimeout(later, delay); + }; +} function handleOnInput(e) { const value = e.target.value; - State.update({ value }); + if (onInput) { + onInput(e); + } + + state.handleOnQueryChangeDebounced(value); +} + +function handleOnQueryChange(value) { if (onQueryChange) { onQueryChange(value); } @@ -19,6 +40,11 @@ function reset() { onQueryChange(""); } +State.init({ + value: "", + handleOnQueryChangeDebounced: debounce(handleOnQueryChange, debounceDelay), +}); + const ResetButton = styled.button` all: unset; display: flex; diff --git a/src/DIG/InputSearch.metadata.json b/src/DIG/InputSearch.metadata.json index 6d07d17f..7eae1881 100644 --- a/src/DIG/InputSearch.metadata.json +++ b/src/DIG/InputSearch.metadata.json @@ -1,5 +1,5 @@ { - "description": "An input component for typing a search query.\n\n### Example\n\n```jsx\nreturn (\n console.log(query),\n }}\n />\n);\n```\n\n### Props\n\n`assistiveText`\n\n- type: string\n- Adds assistive text to the bottom of the input. Useful for info, success, and error messages.\n\n`disabled`\n\n- type: boolean\n- Disables the input\n\n`invalid`\n\n- type: boolean\n- Renders input with error variant\n\n`label`\n\n- type: string\n- Renders label above input\n\n`onQueryChange`\n\n- type: function\n- Returns search query whenever input changes\n\n### HTML Attributes\n\nAll other props will be forwarded through to the `` element. EG: `placeholder`.", + "description": "An input component for typing a search query.\n\n### Example\n\n```jsx\nreturn (\n console.log(query),\n }}\n />\n);\n```\n\n### Props\n\n`assistiveText`\n\n- type: string\n- Adds assistive text to the bottom of the input. Useful for info, success, and error messages.\n\n`debounceDelay`\n\n- type: number\n- default: 300\n- Determines debounce delay for `onQueryChange()` in milliseconds\n\n`disabled`\n\n- type: boolean\n- Disables the input\n\n`invalid`\n\n- type: boolean\n- Renders input with error variant\n\n`label`\n\n- type: string\n- Renders label above input\n\n`onInput`\n\n- type: function\n- Non-debounced handler that returns native input event \n\n`onQueryChange`\n\n- type: function\n- Debounced handler that returns search query whenever input changes\n\n### HTML Attributes\n\nAll other props will be forwarded through to the `` element. EG: `placeholder`.", "name": "DIG.InputSearch", "tags": { "dig": "" From 677d0477fd61badd6a48a53293d733945755dea2 Mon Sep 17 00:00:00 2001 From: Dmitriy <34593263+shelegdmitriy@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:36:22 +0300 Subject: [PATCH 08/10] Fix rendering posts on profile page (#204) --- src/ProfilePage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProfilePage.jsx b/src/ProfilePage.jsx index 41827a47..57934e85 100644 --- a/src/ProfilePage.jsx +++ b/src/ProfilePage.jsx @@ -240,7 +240,7 @@ return ( )} From cc618fe24986c895f9826acac1ffa630bcc883a8 Mon Sep 17 00:00:00 2001 From: Caleb Jacob Date: Tue, 5 Sep 2023 16:28:02 -0600 Subject: [PATCH 09/10] Various DIG component fixes and documentation updates --- src/ComponentDetailsPage.jsx | 2 +- src/DIG/Accordion.jsx | 2 +- src/DIG/Button.metadata.json | 4 +-- src/DIG/Checkbox.metadata.json | 2 +- src/DIG/Chip.metadata.json | 2 +- src/DIG/Dialog.jsx | 18 ++++++---- src/DIG/Dialog.metadata.json | 2 +- src/DIG/DropdownMenu.jsx | 24 ++++++------- src/DIG/DropdownMenu.metadata.json | 2 +- src/DIG/Tabs.jsx | 58 +++++++++++++----------------- src/DIG/Tabs.metadata.json | 2 +- src/DIG/Toast.metadata.json | 2 +- src/DIG/Tooltip.metadata.json | 2 +- 13 files changed, 59 insertions(+), 63 deletions(-) diff --git a/src/ComponentDetailsPage.jsx b/src/ComponentDetailsPage.jsx index 46ac5114..543472b2 100644 --- a/src/ComponentDetailsPage.jsx +++ b/src/ComponentDetailsPage.jsx @@ -1,6 +1,6 @@ State.init({ copiedShareUrl: false, - selectedTab: props.tab ?? "source", + selectedTab: props.tab ?? "about", }); if (props.tab && props.tab !== state.selectedTab) { diff --git a/src/DIG/Accordion.jsx b/src/DIG/Accordion.jsx index 6cab8232..87ab0f41 100644 --- a/src/DIG/Accordion.jsx +++ b/src/DIG/Accordion.jsx @@ -15,7 +15,7 @@ const Item = styled("Accordion.Item")` `; const Header = styled("Accordion.Header")` - margin: 0; + margin: 0 !important; `; const Trigger = styled("Accordion.Trigger")` diff --git a/src/DIG/Button.metadata.json b/src/DIG/Button.metadata.json index 5a6efebf..a9c71154 100644 --- a/src/DIG/Button.metadata.json +++ b/src/DIG/Button.metadata.json @@ -1,7 +1,7 @@ { - "description": "A fully featured button component that can act as a `