diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx index 818898f06..d5f3b78c6 100644 --- a/packages/web/components/elements/DropdownElements.tsx +++ b/packages/web/components/elements/DropdownElements.tsx @@ -65,6 +65,7 @@ const StyledLabel = styled(Label, { }) export type DropdownAlignment = 'start' | 'end' | 'center' +export type DropdownSide = 'top' | 'right' | 'bottom' | 'left' type DropdownProps = { labelText?: string @@ -73,6 +74,8 @@ type DropdownProps = { children: React.ReactNode styledArrow?: boolean align?: DropdownAlignment + side?: DropdownSide + sideOffset?: number disabled?: boolean css?: CSS } @@ -108,8 +111,11 @@ export function Dropdown({ labelText, showArrow = true, disabled = false, + side = 'bottom', + sideOffset = 0, css }: DropdownProps): JSX.Element { + console.log('side', side) return ( {triggerElement} @@ -119,6 +125,8 @@ export function Dropdown({ // remove focus from dropdown ;(document.activeElement as HTMLElement).blur() }} + side={side} + sideOffset={sideOffset} align={align ? align : 'center'} > {labelText && {labelText}} diff --git a/packages/web/components/elements/TickedRangeSlider.tsx b/packages/web/components/elements/TickedRangeSlider.tsx new file mode 100644 index 000000000..c112d8d73 --- /dev/null +++ b/packages/web/components/elements/TickedRangeSlider.tsx @@ -0,0 +1,39 @@ + + +import { Box, HStack } from './LayoutPrimitives' +import { styled, theme } from '../tokens/stitches.config' + +type TickedRangeSliderProps = { + ticks?: number, + value: number, + onChange: (value: number) => void, + min?: number, + max?: number, + step?: number, +} + +const Tick = styled(Box, { + background: theme.colors.grayBorderHover, + width: 2, + height: 8, +}) + +export function TickedRangeSlider({ + ticks = 8, + min = 10, + max = 28, + step = 1, + value, + onChange, + } : TickedRangeSliderProps +): JSX.Element { + + return ( + + onChange(e.target.value as any)} value={value} type="range" min={min} max={max} step={step} className='slider'/> + + {[...Array(ticks)].map((val, idx) => )} + + + ) +} \ No newline at end of file diff --git a/packages/web/components/elements/images/AIcon.tsx b/packages/web/components/elements/images/AIcon.tsx new file mode 100644 index 000000000..f7972a2f0 --- /dev/null +++ b/packages/web/components/elements/images/AIcon.tsx @@ -0,0 +1,14 @@ +type AIconProps = { + size: number + color: string + style?: React.CSSProperties +} + +export function AIcon(props: AIconProps): JSX.Element { + return ( + + + + + ) +} \ No newline at end of file diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx index 8be346b8c..0dab2698f 100644 --- a/packages/web/components/templates/article/ArticleActionsMenu.tsx +++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx @@ -1,20 +1,26 @@ import { Separator } from "@radix-ui/react-separator" import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react" import { Button } from "../../elements/Button" -import { Box } from "../../elements/LayoutPrimitives" +import { Dropdown } from "../../elements/DropdownElements" +import { Box, SpanBox } from "../../elements/LayoutPrimitives" import { styled, theme } from "../../tokens/stitches.config" +import { ReaderSettings } from "./ReaderSettingsModal" export type ArticleActionsMenuLayout = 'horizontal' | 'vertical' type ArticleActionsMenuProps = { layout: ArticleActionsMenuLayout + articleActionHandler: (action: string, arg?: number) => void } -export function MenuSeparator(props: ArticleActionsMenuProps) { +type MenuSeparatorProps = { + layout: ArticleActionsMenuLayout +} + +export function MenuSeparator(props: MenuSeparatorProps) { const LineSeparator = styled(Separator, { width: '100%', margin: 0, - backgroundColor: 'red', borderBottom: `1px solid ${theme.colors.grayLine.toString()}`, my: '8px', }) @@ -23,12 +29,12 @@ export function MenuSeparator(props: ArticleActionsMenuProps) { export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element { return ( + <> - + + + + } + css={{ m: '0px', p: '0px', outlineStyle: 'solid', outlineWidth: '1px', outlineColor: theme.colors.grayLine.toString() }} + > + + + - + + + + } + css={{ background: 'red' }} + > + {/* */} + - - {/* @@ -60,5 +85,6 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element */} + ) } \ No newline at end of file diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx index 6b3e043d5..d06cc6e04 100644 --- a/packages/web/components/templates/article/EditLabelsModal.tsx +++ b/packages/web/components/templates/article/EditLabelsModal.tsx @@ -15,7 +15,6 @@ import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticle import { LabelChip } from '../../elements/LabelChip' type EditLabelsModalProps = { - labels: Label[] article: ArticleAttributes onOpenChange: (open: boolean) => void setLabels: (labels: Label[]) => void diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx new file mode 100644 index 000000000..b51669c2c --- /dev/null +++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx @@ -0,0 +1,90 @@ +import { + ModalRoot, + ModalOverlay, + ModalContent, +} from '../../elements/ModalPrimitives' +import { Box, HStack, VStack, Separator, SpanBox } from '../../elements/LayoutPrimitives' +import { Button } from '../../elements/Button' +import { StyledText } from '../../elements/StyledText' +import { CrossIcon } from '../../elements/images/CrossIcon' +import { CommentIcon } from '../../elements/images/CommentIcon' +import { TrashIcon } from '../../elements/images/TrashIcon' +import { styled, theme } from '../../tokens/stitches.config' +import type { Highlight } from '../../../lib/networking/fragments/highlightFragment' +import { HighlightView } from '../../patterns/HighlightView' +import { useCallback, useState } from 'react' +import { StyledTextArea } from '../../elements/StyledTextArea' +import { ConfirmationModal } from '../../patterns/ConfirmationModal' +import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash } from 'phosphor-react' +import { AIcon } from '../../elements/images/AIcon' +import { TickedRangeSlider } from '../../elements/TickedRangeSlider' + +type ReaderSettingsProps = { + +} + +export function ReaderSettings(props: ReaderSettingsProps): JSX.Element { + const VerticalDivider = styled(SpanBox, { + width: '1px', + height: '100%', + background: `${theme.colors.grayLine.toString()}`, + }) + return ( + + + + + + + + Margin: + + + {}} /> + + + + + Line Height: + + + {}} /> + + + + + ) +} diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 14b61bcb2..f35f94e8a 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -29,6 +29,8 @@ import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'pho import { Separator } from '@radix-ui/react-separator' import { Article } from '../../../components/templates/article/Article' import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu' +import { HighlightsModal } from '../../../components/templates/article/HighlightsModal' +import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation' const MenuSeparator = styled(Separator, { width: '100%', @@ -48,6 +50,7 @@ export default function Home(): JSX.Element { const scrollRef = useRef(null) const { slug } = router.query const [showLabelsModal, setShowLabelsModal] = useState(false) + const [showHighlightsModal, setShowHighlightsModal] = useState(false) // Populate data cache const { viewerData } = useGetViewerQuery() @@ -73,33 +76,51 @@ export default function Home(): JSX.Element { setMarginWidth(newMargin) } + const actionHandler = async (action: string, arg?: number) => { + switch (action) { + case 'archive': + if (article) { + await setLinkArchivedMutation({ + linkId: article.id, + archived: true, + }) + // TODO: merge from article actions PR + // removeItemFromCache(cache, mutate, props.article.id) + router.push(`/home`) + } + break + case 'openOriginalArticle': + const url = article?.url + if (url) { + window.open(url, '_blank') + } + break + case 'showHighlights': + setShowHighlightsModal(true) + break + case 'incrementFontSize': + await updateFontSize(Math.min(fontSize + 2, 28)) + break + case 'decrementFontSize': + await updateFontSize(Math.max(fontSize - 2, 10)) + break + case 'incrementMarginWidth': + updateMarginWidth(Math.min(marginWidth + 50, 560)) + break + case 'decrementMarginWidth': + updateMarginWidth(Math.max(marginWidth - 50, 200)) + break + case 'editLabels': + if (viewerData?.me && isVipUser(viewerData?.me)) { + setShowLabelsModal(true) + } + break + } + }; + useKeyboardShortcuts( articleKeyboardCommands(router, async (action) => { - switch (action) { - case 'openOriginalArticle': - const url = article?.url - if (url) { - window.open(url, '_blank') - } - break - case 'incrementFontSize': - await updateFontSize(Math.min(fontSize + 2, 28)) - break - case 'decrementFontSize': - await updateFontSize(Math.max(fontSize - 2, 10)) - break - case 'incrementMarginWidth': - updateMarginWidth(Math.min(marginWidth + 50, 560)) - break - case 'decrementMarginWidth': - updateMarginWidth(Math.max(marginWidth - 50, 200)) - break - case 'editLabels': - if (viewerData?.me && isVipUser(viewerData?.me)) { - setShowLabelsModal(true) - } - break - } + actionHandler(action) }) ) @@ -124,19 +145,22 @@ export default function Home(): JSX.Element { - - + position: 'fixed', + flexDirection: 'row-reverse', + top: '-120px', + left: 8, + height: '100%', + width: '48px', + '@lgDown': { + display: 'none', + }, + }} + > + + {article.contentReader == 'PDF' ? ( + + )} + {showHighlightsModal && ( + setShowHighlightsModal(false)} + deleteHighlightAction={(highlightId: string) => { + // removeHighlightCallback(highlightId) + }} + /> + )} {/* {showLabelsModal && ( )} */} - - )} ) } diff --git a/packages/web/styles/globals.css b/packages/web/styles/globals.css index 573f58d7f..dda271556 100644 --- a/packages/web/styles/globals.css +++ b/packages/web/styles/globals.css @@ -99,3 +99,39 @@ div#appleid-signin { border-bottom-color: transparent; border-left-color: transparent; } + +.slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 1px; + background: var(--colors-grayBorderHover); +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px !important; + height: 16px; + border-radius: 50%; + background: var(--colors-utilityTextContrast); + cursor: pointer; +} + +.slider::-moz-range-thumb { + -webkit-appearance: none; + width: 16px !important; + height: 16px; + border-radius: 50%; + background: var(--colors-utilityTextContrast); + cursor: pointer; +} + +input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + border: none; + height: 16px; + width: 16px; + border-radius: 50%; + background: var(--colors-grayTextContrast); +} \ No newline at end of file