diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index a3f8dd67d..7ad86281c 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -20,7 +20,7 @@ export const Button = styled('button', { }, ctaDarkYellow: { border: 0, - fontSize: '16px', + fontSize: '14px', fontWeight: 500, fontStyle: 'normal', fontFamily: 'Inter', @@ -28,7 +28,7 @@ export const Button = styled('button', { cursor: 'pointer', color: '$omnivoreGray', bg: '$omnivoreCtaYellow', - p: '10px 12px', + p: '10px 13px', }, ctaOutlineYellow: { boxSizing: 'border-box', diff --git a/packages/web/components/elements/ProgressBar.tsx b/packages/web/components/elements/ProgressBar.tsx index c8d15b868..cc39172a5 100644 --- a/packages/web/components/elements/ProgressBar.tsx +++ b/packages/web/components/elements/ProgressBar.tsx @@ -15,6 +15,7 @@ export function ProgressBar(props: ProgressBarProps): JSX.Element { width: '100%', borderRadius: '$1', overflow: 'hidden', + backgroundColor: props.backgroundColor, }} > + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/packages/web/components/elements/images/LibraryListLayoutIcon.tsx b/packages/web/components/elements/images/LibraryListLayoutIcon.tsx new file mode 100644 index 000000000..01e413815 --- /dev/null +++ b/packages/web/components/elements/images/LibraryListLayoutIcon.tsx @@ -0,0 +1,13 @@ +type LibraryListLayoutIconProps = { + color: string +} + +export function LibraryListLayoutIcon(props: LibraryListLayoutIconProps): JSX.Element { + return ( + + + + + + ) +} \ No newline at end of file diff --git a/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx b/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx index c9278b0a7..bb4e73431 100644 --- a/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx +++ b/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx @@ -46,7 +46,7 @@ export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element { { + props.handleAction('showDetail') + }} + > + {props.item.image && ( + { + ;(e.target as HTMLElement).style.display = 'none' + }} + /> + )} + + + + {/* { + // This is here to prevent menu click events from bubbling + // up and causing us to "click" on the link item. + e.stopPropagation() + }} + > + + } + actionHandler={props.handleAction} + /> + */} + + {/* + + {props.item.author && ( + + {authoredByText(props.item.author)} + + )} + + {props.originText} + + + */} + + + + {props.item.description} + + + + + {props.item.labels?.map(({ name, color }, index) => ( + + ))} + + + + ) +} + +type CardTitleProps = { + title: string +} + +function CardTitle(props: CardTitleProps): JSX.Element { + return ( + + {props.title} + + ) +} diff --git a/packages/web/components/templates/Menu.tsx b/packages/web/components/templates/Menu.tsx new file mode 100644 index 000000000..42d5e01d4 --- /dev/null +++ b/packages/web/components/templates/Menu.tsx @@ -0,0 +1,174 @@ +import Link from 'next/link' +import React, { useEffect, useState } from 'react' + +import { useGetLabelsQuery } from '../../lib/networking/queries/useGetLabelsQuery' +import { useGetSubscriptionsQuery } from '../../lib/networking/queries/useGetSubscriptionsQuery' + +import { ProSidebar, Menu, MenuItem, SubMenu } from 'react-pro-sidebar' +import { Tag } from 'phosphor-react' + +// styles +const proSideBarStyles = { + display: 'inline-block', +} + +// Types +type MenuItems = { + label: string + query: string + active?: boolean + icon: string | JSX.Element | null + href: string +} + +type DynamicMenuItems = { + labels?: MenuItems[] + subscriptions?: MenuItems[] +} + +// Functions +const calculateTodayMenuItem = () => { + const timeZoneHourDiff = -new Date().getTimezoneOffset() / 60 + const hrefStr = `?q=in%3Ainbox+saved%3A${ + new Date(new Date().getTime() - 24 * 3600000).toISOString().split('T')[0] + }Z%${timeZoneHourDiff}B2..*` + return hrefStr +} + +const createDynamicMenuItems = ( + labels: Array, + subscriptions: Array +) => { + const labelsList: Array = [] + const subscriptionsList: Array = [] + // Create labels list + if (labels.length) { + labels.map((l) => + labelsList.push({ + label: l.name, + query: `label:"${l.name}"`, + icon: , + href: `?q=label:"${l.name}"`, + }) + ) + } + // create subscriptions list + if (subscriptions.length) { + subscriptions.map((s) => + subscriptionsList.push({ + label: s.name, + query: `subscription:"${s.subscription}"`, + icon: 'subscription', + href: `?q=subscription:"${s.subscription}"`, + }) + ) + } + return { labels: [...labelsList], subscriptions: [...subscriptionsList] } +} + +// Component +export const Menubar = () => { + const { labels } = useGetLabelsQuery() + const { subscriptions } = useGetSubscriptionsQuery() + + const [menuList, setMenuList] = useState>([]) + const [dynamicMenuItems, setDynamicMenuItems] = useState({}) + + useEffect(() => { + if (labels || subscriptions) { + setDynamicMenuItems(createDynamicMenuItems(labels, subscriptions)) + } + setMenuList([ + { + label: 'Home', + query: 'in:inbox', + icon: null, + href: '/home', + active: true, + }, + { + label: 'Today', + query: 'in:inbox-label:Newsletter', + icon: null, + href: calculateTodayMenuItem(), + }, + { + label: 'Read Later', + query: 'in:inbox-label:Newsletter', + icon: null, + href: `?q=in:inbox+-label:Newsletter`, + }, + { + label: 'HighLights', + query: 'type:highlights', + icon: null, + href: `?q=type:highlights`, + }, + { + label: 'Newsletters', + query: 'in:inbox label:Newsletter', + icon: null, + href: `?q=in:inbox+label:Newsletter`, + }, + ]) + },[labels, subscriptions]) + + return ( + + + {menuList.length > 0 && + menuList.map((item) => { + return ( + + + {item.label} + + + ) + })} + {dynamicMenuItems.labels && + + {dynamicMenuItems.labels.map((item: MenuItems) => { + return ( + + {item.label} + + ) + })} + + } + {dynamicMenuItems.subscriptions && + + {dynamicMenuItems.subscriptions.map((item: MenuItems) => { + return ( + + + {item.label} + + + ) + })} + + } + + + ) +} diff --git a/packages/web/components/templates/library/LibraryAvatar.tsx b/packages/web/components/templates/library/LibraryAvatar.tsx new file mode 100644 index 000000000..e653036f3 --- /dev/null +++ b/packages/web/components/templates/library/LibraryAvatar.tsx @@ -0,0 +1,62 @@ +import { SpanBox, VStack } from '../../elements/LayoutPrimitives' +import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' + +import { styled } from '../../tokens/stitches.config' +import { Root, Image, Fallback } from '@radix-ui/react-avatar' + +type AvatarProps = { + viewer?: UserBasicData +} + +export function LibraryAvatar(props: AvatarProps): JSX.Element { + return ( + + + + {props.viewer?.profile.pictureUrl + ? + : {props.viewer?.name.charAt(0) ?? ''} + } + + + {/* This spacer is to help align with items in the search box */} + + + ) +} + +const StyledAvatar = styled(Root, { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + verticalAlign: 'middle', + overflow: 'hidden', + userSelect: 'none', +}) + +const StyledImage = styled(Image, { + width: '100%', + height: '100%', + objectFit: 'cover', + + '&:hover': { + opacity: '48%', + }, +}) + +const StyledFallback = styled(Fallback, { + width: '100%', + height: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontSize: '20px', + backgroundColor: '$omnivoreCtaYellow', + color: '#595959', +}) diff --git a/packages/web/components/templates/library/LibraryContainer.tsx b/packages/web/components/templates/library/LibraryContainer.tsx new file mode 100644 index 000000000..47d0d883b --- /dev/null +++ b/packages/web/components/templates/library/LibraryContainer.tsx @@ -0,0 +1,35 @@ +import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives' +import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' +import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences' +import { LibraryMenu } from './LibraryMenu' +import { LibraryAvatar } from './LibraryAvatar' +import { LibrarySearchBar } from './LibrarySearchBar' +import { LibraryList } from './LibraryList' +import { LibraryHeadline } from './LibraryHeadline' + + +export function LibraryContainer(): JSX.Element { + useGetUserPreferences() + + const { viewerData } = useGetViewerQuery() + + return ( + <> + + + + + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/packages/web/components/templates/library/LibraryHeadline.tsx b/packages/web/components/templates/library/LibraryHeadline.tsx new file mode 100644 index 000000000..28c2e22c9 --- /dev/null +++ b/packages/web/components/templates/library/LibraryHeadline.tsx @@ -0,0 +1,23 @@ +import { HStack } from '../../elements/LayoutPrimitives' +import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences' +import { StyledText } from '../../elements/StyledText' +import { theme } from '../../tokens/stitches.config' +import { LibraryListLayoutIcon } from '../../elements/images/LibraryListLayoutIcon' +import { LibraryGridLayoutIcon } from '../../elements/images/LibraryGridLayoutIcon' +import { Button } from '../../elements/Button' + + +export function LibraryHeadline(): JSX.Element { + useGetUserPreferences() + + return ( + + Home + + + + + + + ) +} \ No newline at end of file diff --git a/packages/web/components/templates/library/LibraryList.tsx b/packages/web/components/templates/library/LibraryList.tsx new file mode 100644 index 000000000..d104bf572 --- /dev/null +++ b/packages/web/components/templates/library/LibraryList.tsx @@ -0,0 +1,114 @@ +import { Box } from '../../elements/LayoutPrimitives' +import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' +import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences' +import { useMemo, useState } from 'react' +import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes' +import { LibraryGridCard } from '../../patterns/LibraryCards/LibraryGridCard' + +export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' + + +export function LibraryList(): JSX.Element { + useGetUserPreferences() + + const [layout, setLayout] = useState('GRID_LAYOUT') + const { viewerData } = useGetViewerQuery() + + + const defaultQuery = { + limit: 50, + sortDescending: true, + searchQuery: undefined, + } + + const { itemsPages, size, setSize, isValidating, performActionOnItem } = + useGetLibraryItemsQuery(defaultQuery) + + const libraryItems = useMemo(() => { + const items = + itemsPages?.flatMap((ad) => { + return ad.search.edges + }) || [] + return items + }, [itemsPages, performActionOnItem]) + + return ( + + {/* // {!isValidating && items.length == 0 ? ( + // { + + // }} + // /> + // ) : ( */} + + {libraryItems.map((linkedItem) => ( + div': { + bg: '$libraryBackground', + }, + '&:focus': { + '> div': { + bg: '$grayBgActive', + }, + }, + '&:hover': { + '> div': { + bg: '$grayBgActive', + }, + }, + }} + > + {viewerData?.me && ( + { + console.log('card clicked') + }} + /> + )} + + ))} + + {/* Extra padding at bottom to give space for scrolling */} + + + ) +} \ No newline at end of file diff --git a/packages/web/components/templates/library/LibraryMenu.tsx b/packages/web/components/templates/library/LibraryMenu.tsx new file mode 100644 index 000000000..baf771a7b --- /dev/null +++ b/packages/web/components/templates/library/LibraryMenu.tsx @@ -0,0 +1,14 @@ +import { VStack } from '../../elements/LayoutPrimitives' +import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences' +import { Menubar } from '../Menu' + + +export function LibraryMenu(): JSX.Element { + useGetUserPreferences() + + return ( + + + + ) +} \ No newline at end of file diff --git a/packages/web/components/templates/library/LibrarySearchBar.tsx b/packages/web/components/templates/library/LibrarySearchBar.tsx new file mode 100644 index 000000000..8831e7fdf --- /dev/null +++ b/packages/web/components/templates/library/LibrarySearchBar.tsx @@ -0,0 +1,97 @@ +import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives' +import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' +import { useRouter } from 'next/router' +import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences' +import { useState } from 'react' +import { FormInput } from '../../elements/FormElements' +import { Button } from '../../elements/Button' +import { X } from 'phosphor-react' +import { theme } from '../../tokens/stitches.config' + + +export function LibrarySearchBar(): JSX.Element { + useGetUserPreferences() + + + const router = useRouter() + const [searchTerm, setSearchTerm] = useState('') + const { viewerData } = useGetViewerQuery() + + return ( + <> + + +
{ + event.preventDefault() + // props.applySearchQuery(searchTerm || '') + // inputRef.current?.blur() + }} + > + { + // event.target.select() + // setFocused(true) + // }} + // onBlur={() => { + // setFocused(false) + // }} + // onChange={(event) => { + // setSearchTerm(event.target.value) + // }} + /> + + {searchTerm && ( + + + + + + )} +
+ +
+ + ) +} \ No newline at end of file diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index bca8ade05..b018a3c24 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -112,6 +112,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = grayBg: '#FFFFFF', grayBgActive: '#e6e6e6', grayBorder: '#F0F0F0', + lightBorder: '#F0F0F0', grayTextContrast: '#3A3939', graySolid: '#9C9B9A', textDefault: 'rgba(255, 255, 255, 0.8)', @@ -155,6 +156,12 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = labelButtonsBg: '#F5F5F4', tooltipIcons: '#FDFAEC', + textSubtle: '#605F5D', + libraryActive: '#F8F8F8', + libraryBackground: '#FFFFFF', + border: '#F0F0F0', + + //utility textNonEssential: 'rgba(10, 8, 6, 0.4)', overlay: 'rgba(63, 62, 60, 0.2)', @@ -207,6 +214,11 @@ const darkThemeSpec = { avatarBg: '#000000', avatarFont: 'rgba(255, 255, 255, 0.8)', + textSubtle: '#AAAAAA', + libraryActive: '#3B3938', + libraryBackground: '#252525', + border: '#323232', + //utility utilityTextSubtle: 'rgba(255, 255, 255, 0.65)', textNonEssential: 'rgba(10, 8, 6, 0.4)', diff --git a/packages/web/package.json b/packages/web/package.json index cf0f6317f..8088e84b3 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -43,6 +43,7 @@ "react-colorful": "^5.5.1", "react-dom": "^17.0.2", "react-hot-toast": "^2.1.1", + "react-pro-sidebar": "^0.7.1", "react-super-responsive-table": "^5.2.1", "react-topbar-progress-indicator": "^4.1.1", "react-twitter-widgets": "^1.10.0", diff --git a/packages/web/pages/_app.tsx b/packages/web/pages/_app.tsx index d04f1b632..7daafb7dc 100644 --- a/packages/web/pages/_app.tsx +++ b/packages/web/pages/_app.tsx @@ -1,5 +1,8 @@ import '../styles/globals.css' import '../styles/articleInnerStyling.css' +import 'react-pro-sidebar/dist/css/styles.css' +import '../styles/menu.css' + import type { AppProps } from 'next/app' import { IdProvider } from '@radix-ui/react-id' import { NextRouter, useRouter } from 'next/router' diff --git a/packages/web/pages/library.tsx b/packages/web/pages/library.tsx new file mode 100644 index 000000000..839ff3251 --- /dev/null +++ b/packages/web/pages/library.tsx @@ -0,0 +1,5 @@ +import { LibraryContainer } from '../components/templates/library/LibraryContainer' + +export default function Library(): JSX.Element { + return +} diff --git a/packages/web/stories/Menu.stories.tsx b/packages/web/stories/Menu.stories.tsx new file mode 100644 index 000000000..cf488b9c8 --- /dev/null +++ b/packages/web/stories/Menu.stories.tsx @@ -0,0 +1,16 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react' +//import { updateThemeLocally } from '../lib/themeUpdater' +//import { ThemeId } from '../components/tokens/stitches.config' +import { Menubar } from '../components/templates/Menu' + +export default { + title: 'Components/Menu', + component: Menubar, +} as ComponentMeta + +const Template: ComponentStory = () => ( + +) + +export const MenuStory = Template.bind({}) + diff --git a/packages/web/styles/menu.css b/packages/web/styles/menu.css new file mode 100644 index 000000000..e711cdca9 --- /dev/null +++ b/packages/web/styles/menu.css @@ -0,0 +1,30 @@ +/* Menu Override styles */ + +.pro-sidebar > .pro-sidebar-inner, .pro-sidebar .pro-menu a { + background-color: var(--colors-libraryBackground); + color: var(--colors-utilityTextDefault); +} + +.pro-sidebar .pro-menu > ul > .pro-sub-menu > .pro-inner-list-item { + color: var(--colors-utilityTextDefault); + background-color: var(--colors-libraryBackground); +} + +.pro-sidebar .pro-menu a:hover { + color: var(--colors-utilityTextDefault); +} + +.pro-sidebar .pro-menu .pro-menu-item > .pro-inner-item { + padding: 8px 20px 3px 20px; +} + +/* .pro-sidebar .pro-menu .active > .pro-inner-item { + background-color: #F8F8F8; + border-radius: 8px; +} */ + +.pro-sidebar .pro-menu .pro-menu-item > .pro-inner-item > .pro-icon-wrapper { + width: 20px; + min-width: 0; + height: 25px; +} diff --git a/yarn.lock b/yarn.lock index 9ee9257f9..864731dfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4531,7 +4531,7 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": +"@popperjs/core@^2.4.0", "@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": version "2.11.5" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== @@ -10641,6 +10641,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.6: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + clean-css@^4.2.3: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -21134,6 +21139,16 @@ react-popper@^2.2.4: react-fast-compare "^3.0.1" warning "^4.0.2" +react-pro-sidebar@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/react-pro-sidebar/-/react-pro-sidebar-0.7.1.tgz#0b5edca0809ff6a23bda188c5db370f880690ee7" + integrity sha512-Iy1X8ce4t5Vqz4CsyzjwokGUE3/IObgmYzS0ins7/2eWKle0SMUPaWdgMKFIVjtVrMr5vmjPbRicq8FxnVaf8A== + dependencies: + "@popperjs/core" "^2.4.0" + classnames "^2.2.6" + react-slidedown "^2.4.5" + resize-observer-polyfill "^1.5.1" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -21183,6 +21198,13 @@ react-sizeme@^3.0.1: shallowequal "^1.1.0" throttle-debounce "^3.0.1" +react-slidedown@^2.4.5: + version "2.4.7" + resolved "https://registry.yarnpkg.com/react-slidedown/-/react-slidedown-2.4.7.tgz#c09e72bba8aac25018fd644ece041da771854589" + integrity sha512-HGDfrqo70r1WVE0DwrySPdCT27/2wcZaJYh5kOnmuPSCtjDDJrNkDdn4Ep/cma2VVfwupeAGhbc2pbrGThU6VQ== + dependencies: + tslib "^2.0.0" + react-style-singleton@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66" @@ -21763,6 +21785,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"