Skip to content

Commit

Permalink
Add components
Browse files Browse the repository at this point in the history
  • Loading branch information
itexpert120 committed May 15, 2024
1 parent 5d0234f commit 0e58e00
Show file tree
Hide file tree
Showing 40 changed files with 2,540 additions and 288 deletions.
306 changes: 18 additions & 288 deletions widget/components.jsx
Original file line number Diff line number Diff line change
@@ -1,288 +1,18 @@
const limitPerPage = 21;
let components = [];
let totalApps = 0;
let totalComponents = 0;
const componentsUrl = "#/near/widget/ComponentsPage";
const searchRequiredTag = state.selectedTab === "apps" ? "app" : null;
const searchPlaceholder = state.selectedTab === "apps" ? "Search Apps" : "Search Components";

State.init({
currentPage: 0,
selectedTab: props.tab || "all",
});

if (props.tab && props.tab !== state.selectedTab) {
State.update({
selectedTab: props.tab,
});
}

const tagsData = Social.get("*/widget/*/metadata/tags/*", "final");

const data = Social.keys("*/widget/*", "final", {
return_type: "BlockHeight",
});

if (data) {
const result = [];

Object.keys(data).forEach((accountId) => {
return Object.keys(data[accountId].widget).forEach((widgetName) => {
totalComponents++;

if (state.selectedTab === "apps") {
const hasAppTag = tagsData[accountId].widget[widgetName]?.metadata?.tags["app"] === "";
if (!hasAppTag) return;
totalApps++;
}

result.push({
accountId,
widgetName,
blockHeight: data[accountId].widget[widgetName],
});
});
});

result.sort((a, b) => b.blockHeight - a.blockHeight);
components = result.slice(0, state.currentPage * limitPerPage + limitPerPage);
}

function onSearchChange({ result, term }) {
if (term.trim()) {
State.update({ searchResults: result || [] });
} else {
State.update({ searchResults: null });
}
}

const items = state.searchResults || components;

const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 48px;
padding-bottom: 48px;
padding-top: 48px;
`;

const Header = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
`;

const Search = styled.div`
width: 246px;
@media (max-width: 500px) {
width: 100%;
}
`;

const H1 = styled.h1`
font-weight: 600;
font-size: 32px;
line-height: 39px;
color: #11181c;
margin: 0;
`;

const H2 = styled.h2`
font-weight: 400;
font-size: 20px;
line-height: 24px;
color: #687076;
margin: 0;
`;

const Text = styled.p`
margin: 0;
line-height: 1.5rem;
color: ${(p) => (p.bold ? "#11181C" : "#687076")} !important;
font-weight: ${(p) => (p.bold ? "600" : "400")};
font-size: ${(p) => (p.small ? "12px" : "14px")};
overflow: ${(p) => (p.ellipsis ? "hidden" : "")};
text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "")};
white-space: ${(p) => (p.ellipsis ? "nowrap" : "")};
overflow-wrap: anywhere;
b {
font-weight: 600;
color: #11181c;
}
&[href] {
display: inline-flex;
gap: 0.25rem;
&:hover,
&:focus {
text-decoration: underline;
}
}
`;

const Items = styled.div`
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
@media (max-width: 1200px) {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 800px) {
grid-template-columns: minmax(0, 1fr);
}
`;

const Item = styled.div``;

const Button = styled.button`
display: block;
width: 100%;
padding: 8px;
height: 32px;
background: #fbfcfd;
border: 1px solid #d7dbdf;
border-radius: 50px;
font-weight: 600;
font-size: 12px;
line-height: 15px;
text-align: center;
cursor: pointer;
color: #11181c !important;
margin: 0;
&:hover,
&:focus {
background: #ecedee;
text-decoration: none;
outline: none;
}
span {
color: #687076 !important;
}
`;

const Tabs = styled.div`
display: flex;
height: 48px;
border-bottom: 1px solid #eceef0;
margin-bottom: -24px;
overflow: auto;
scroll-behavior: smooth;
@media (max-width: 1200px) {
background: #f8f9fa;
border-top: 1px solid #eceef0;
margin-left: -12px;
margin-right: -12px;
> * {
flex: 1;
}
}
`;

const TabsButton = styled.a`
display: inline-flex;
align-items: center;
justify-content: center;
height: 100%;
font-weight: 600;
font-size: 12px;
padding: 0 12px;
position: relative;
color: ${(p) => (p.selected ? "#11181C" : "#687076")};
background: none;
border: none;
outline: none;
text-align: center;
text-decoration: none !important;
&:hover {
color: #11181c;
}
&::after {
content: "";
display: ${(p) => (p.selected ? "block" : "none")};
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3px;
background: #59e692;
}
`;

return (
<Wrapper className="container-xl">
<Header>
{state.selectedTab === "apps" && (
<>
<H1>{totalApps} Apps</H1>
<H2>Discover the latest apps from the NEAR community.</H2>
</>
)}

{state.selectedTab !== "apps" && (
<>
<H1>{totalComponents} Components</H1>
<H2>Discover the latest components from the NEAR community.</H2>
</>
)}
</Header>

<Search>
<Widget
src="${alias_near}/widget/ComponentSearch"
props={{
limit: 21,
onChange: onSearchChange,
placeholder: searchPlaceholder,
filterTag: searchRequiredTag,
}}
/>
</Search>

{!state.searchResults && (
<Tabs>
<TabsButton href={`${componentsUrl}?tab=all`} selected={state.selectedTab === "all"}>
All
</TabsButton>

<TabsButton href={`${componentsUrl}?tab=apps`} selected={state.selectedTab === "apps"}>
Apps
</TabsButton>
</Tabs>
)}

{state.searchResults?.length === 0 && <Text>No components matched your search.</Text>}

{items.length > 0 && (
<Items>
{items.map((component, i) => (
<Item key={component.accountId + component.widgetName}>
<Widget
src="${config_account}/widget/component.card"
props={{
src: `${component.accountId}/widget/${component.widgetName}`,
blockHeight: component.blockHeight,
}}
/>
</Item>
))}
</Items>
)}

{!state.searchResults && (
<Button type="button" onClick={() => State.update({ currentPage: state.currentPage + 1 })}>
Load More
</Button>
)}
</Wrapper>
);
const { Avatar } = VM.require("${config_account}/widget/components.ui.avatar");
const { AvatarGroup } = VM.require("${config_account}/widget/components.ui.avatar-group");
const { Badge } = VM.require("${config_account}/widget/components.ui.badge");
const { Button } = VM.require("${config_account}/widget/components.ui.button");
const { Chip } = VM.require("${config_account}/widget/components.ui.chip");
const { Container } = VM.require("${config_account}/widget/components.ui.container");
const { Logo } = VM.require("${config_account}/widget/components.ui.logo");
const { Tag } = VM.require("${config_account}/widget/components.ui.tag");
return {
Avatar,
AvatarGroup,
Badge,
Button,
Chip,
Container,
Logo,
Tag,
};
53 changes: 53 additions & 0 deletions widget/components/chips.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const { Chip } = VM.require("${config_account}/widget/components") || {
Chip: () => <></>,
};

const items = props.items ?? [];
const onSelect = props.onSelect ?? (() => {});

const [selectedItems, setSelectedItems] = useState([]);

const handleClick = (item) => {
if (props.multiple) {
if (!selectedItems.includes(item)) {
setSelectedItems((prev) => [...prev, item]);
} else if (selectedItems.includes(item)) {
setSelectedItems((prev) => prev.filter((i) => i !== item));
}
} else {
if (selectedItems !== item) {
setSelectedItems(item);
} else if (selectedItems === item) {
setSelectedItems(null);
}
}
};

useEffect(() => {
onSelect(selectedItems);
}, [selectedItems, onSelect]);

if (items.length === 0) {
return <div>No items passed</div>;
}

const duplicates = items.filter((item, index) => items.indexOf(item) !== index);
if (duplicates.length > 0) {
return <div>Duplicate Items Found</div>;
}

return (
<div className="d-flex align-items-center gap-2 flex-wrap">
{items.map((item) => (
<Chip
key={item}
selected={props.multiple ? selectedItems.includes(item) : selectedItems === item}
onClick={() => handleClick(item)}
size={props.size}
multiple={props.multiple}
>
{item}
</Chip>
))}
</div>
);
Loading

0 comments on commit 0e58e00

Please sign in to comment.