mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-04-24 23:18:46 +02:00
add magazine and compact views
This commit is contained in:
parent
378c74c98b
commit
05d70ff73a
288
dist/styles/cards.css
vendored
288
dist/styles/cards.css
vendored
@ -50,29 +50,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 256px;
|
|
||||||
height: 264px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--white);
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: #0004 0px 5px 20px;
|
|
||||||
margin: 18px 12px;
|
|
||||||
color: var(--neutralDarker);
|
color: var(--neutralDarker);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: box-shadow linear .08s;
|
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
animation-fill-mode: none;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.card:focus, .list-card:focus {
|
.card:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.card:hover, .ms-Fabric--isFocusVisible .card:focus {
|
.ms-Fabric--isFocusVisible .card:focus::after {
|
||||||
box-shadow: #0006 0px 5px 40px;
|
|
||||||
}
|
|
||||||
.ms-Fabric--isFocusVisible .card:focus::after, .ms-Fabric--isFocusVisible .list-card:focus::after {
|
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
@ -82,70 +70,7 @@
|
|||||||
border: 1px solid var(--white);
|
border: 1px solid var(--white);
|
||||||
outline: 2px solid #0078d4;
|
outline: 2px solid #0078d4;
|
||||||
}
|
}
|
||||||
.card:active {
|
.card.hidden::after {
|
||||||
transform: scale(.97);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .bg {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.card img.bg {
|
|
||||||
object-fit: cover;
|
|
||||||
filter: saturate(150%) blur(20px);
|
|
||||||
}
|
|
||||||
.card div.bg {
|
|
||||||
background-color: #fffb;
|
|
||||||
}
|
|
||||||
.card img.head {
|
|
||||||
display: block;
|
|
||||||
object-fit: cover;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 144px;
|
|
||||||
-webkit-user-drag: none;
|
|
||||||
}
|
|
||||||
.card img.head, .card p, .card h3 {
|
|
||||||
transition: transform ease-out .12s;
|
|
||||||
}
|
|
||||||
.card.transform:hover img.head, .card.transform:hover p, .card.transform:hover h3,
|
|
||||||
.ms-Fabric--isFocusVisible .card.transform:focus img.head,
|
|
||||||
.ms-Fabric--isFocusVisible .card.transform:focus p,
|
|
||||||
.ms-Fabric--isFocusVisible .card.transform:focus h3 {
|
|
||||||
transform: translateY(-144px);
|
|
||||||
}
|
|
||||||
.card h3.title {
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 22px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 10px 12px;
|
|
||||||
position: relative;
|
|
||||||
-webkit-line-clamp: 3;
|
|
||||||
overflow: hidden;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
.card p.snippet {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
margin: 10px 12px;
|
|
||||||
display: -webkit-box;
|
|
||||||
position: relative;
|
|
||||||
-webkit-line-clamp: 7;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
transform: translateY(64px);
|
|
||||||
}
|
|
||||||
.card:hover p.snippet {
|
|
||||||
transform: translateY(-144px);
|
|
||||||
}
|
|
||||||
.card p.snippet.show {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
.card.hidden::after, .list-card.hidden::after {
|
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -156,23 +81,96 @@
|
|||||||
background: #0004;
|
background: #0004;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.default-card {
|
||||||
|
display: inline-block;
|
||||||
|
width: 256px;
|
||||||
|
height: 264px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--white);
|
||||||
|
box-shadow: #0004 0 5px 20px;
|
||||||
|
margin: 18px 12px;
|
||||||
|
transition: box-shadow linear .08s, transform linear .08s;
|
||||||
|
animation-fill-mode: none;
|
||||||
|
}
|
||||||
|
.default-card:hover, .ms-Fabric--isFocusVisible .default-card:focus {
|
||||||
|
box-shadow: #0006 0 5px 40px;
|
||||||
|
}
|
||||||
|
.default-card:active {
|
||||||
|
transform: scale(.97);
|
||||||
|
box-shadow: #0004 0 5px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default-card .bg {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.default-card img.bg {
|
||||||
|
object-fit: cover;
|
||||||
|
filter: saturate(150%) blur(20px);
|
||||||
|
}
|
||||||
|
.default-card div.bg {
|
||||||
|
background-color: #fffb;
|
||||||
|
}
|
||||||
|
.default-card img.head {
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 144px;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
.default-card img.head, .default-card p, .default-card h3 {
|
||||||
|
transition: transform ease-out .12s;
|
||||||
|
}
|
||||||
|
.default-card.transform:hover img.head, .default-card.transform:hover p, .default-card.transform:hover h3,
|
||||||
|
.ms-Fabric--isFocusVisible .default-card.transform:focus img.head,
|
||||||
|
.ms-Fabric--isFocusVisible .default-card.transform:focus p,
|
||||||
|
.ms-Fabric--isFocusVisible .default-card.transform:focus h3 {
|
||||||
|
transform: translateY(-144px);
|
||||||
|
}
|
||||||
|
.default-card h3.title {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 10px 12px;
|
||||||
|
position: relative;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
.default-card p.snippet {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin: 10px 12px;
|
||||||
|
display: -webkit-box;
|
||||||
|
position: relative;
|
||||||
|
-webkit-line-clamp: 7;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateY(64px);
|
||||||
|
}
|
||||||
|
.default-card:hover p.snippet {
|
||||||
|
transform: translateY(-144px);
|
||||||
|
}
|
||||||
|
.default-card p.snippet.show {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
.list-card {
|
.list-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
color: var(--neutralDarker);
|
|
||||||
user-select: none;
|
|
||||||
transition: box-shadow linear .08s;
|
transition: box-shadow linear .08s;
|
||||||
border-bottom: 1px solid var(--neutralQuaternaryAlt);
|
border-bottom: 1px solid var(--neutralQuaternaryAlt);
|
||||||
transform: scale(1);
|
box-shadow: #0000 0 5px 15px;
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: #0000 0px 5px 15px;
|
|
||||||
}
|
}
|
||||||
.list-card:hover, .ms-Fabric--isFocusVisible .list-card:focus {
|
.list-card:hover, .ms-Fabric--isFocusVisible .list-card:focus {
|
||||||
box-shadow: #0004 0px 5px 15px;
|
box-shadow: #0004 0 5px 15px;
|
||||||
}
|
}
|
||||||
.list-card:active {
|
.list-card:active {
|
||||||
box-shadow: #0000 0px 5px 15px, inset #0004 0px 0px 15px;
|
box-shadow: #0000 0 5px 15px, inset #0004 0 0 15px;
|
||||||
}
|
}
|
||||||
.list-card div.head {
|
.list-card div.head {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
@ -203,3 +201,117 @@
|
|||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.magazine-card {
|
||||||
|
width: 700px;
|
||||||
|
padding: 24px;
|
||||||
|
max-height: 160px;
|
||||||
|
display: flex;
|
||||||
|
transition: box-shadow linear .08s, background-color linear .08s, transform linear .08s;
|
||||||
|
border-bottom: 1px solid var(--neutralQuaternaryAlt);
|
||||||
|
box-shadow: #0000 0 5px 20px;
|
||||||
|
}
|
||||||
|
.magazine-card.read {
|
||||||
|
color: var(--neutralSecondaryAlt);
|
||||||
|
}
|
||||||
|
.magazine-card:hover, .ms-Fabric--isFocusVisible .magazine-card:focus {
|
||||||
|
box-shadow: #0004 0 5px 20px;
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
.magazine-card:active {
|
||||||
|
box-shadow: #0000 0 5px 20px;
|
||||||
|
transform: scale(.97);
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
.magazine-card div.head {
|
||||||
|
width: 200px;
|
||||||
|
height: 160px;
|
||||||
|
margin-right: 25px;
|
||||||
|
}
|
||||||
|
.magazine-card div.head img {
|
||||||
|
width: 200px;
|
||||||
|
height: 160px;
|
||||||
|
object-fit: cover;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
.magazine-card .data {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.magazine-card .data > *:first-child {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.magazine-card .info {
|
||||||
|
height: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.magazine-card h3.title, .magazine-card p.snippet {
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
}
|
||||||
|
.magazine-card h3.title {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 27px;
|
||||||
|
font-weight: 600;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
}
|
||||||
|
.magazine-card p.snippet {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 21px;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-card {
|
||||||
|
height: 31px;
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid var(--neutralQuaternaryAlt);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 31px;
|
||||||
|
padding: 0 9px;
|
||||||
|
transition: box-shadow linear .08s, background-color linear .08s;
|
||||||
|
}
|
||||||
|
.compact-card:hover, .ms-Fabric--isFocusVisible .compact-card:focus {
|
||||||
|
box-shadow: #0004 0 0 10px;
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
.compact-card:active {
|
||||||
|
box-shadow: #0000 0 0 10px;
|
||||||
|
}
|
||||||
|
.compact-card > * {
|
||||||
|
margin: 0 3px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.compact-card .info {
|
||||||
|
display: flex;
|
||||||
|
line-height: 31px;
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
.compact-card .info .name {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.compact-card .info img,
|
||||||
|
.compact-card .info .read-indicator,
|
||||||
|
.compact-card .info .starred-indicator {
|
||||||
|
margin: 7.5px 5px 7.5px 0;
|
||||||
|
}
|
||||||
|
.compact-card .data {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.compact-card .data .title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
.compact-card .data .snippet {
|
||||||
|
color: var(--neutralSecondaryAlt);
|
||||||
|
}
|
||||||
|
.compact-card .time {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
20
dist/styles/dark.css
vendored
20
dist/styles/dark.css
vendored
@ -11,19 +11,31 @@
|
|||||||
.settings .loading {
|
.settings .loading {
|
||||||
background-color: #000a;
|
background-color: #000a;
|
||||||
}
|
}
|
||||||
.card {
|
.default-card {
|
||||||
box-shadow: #0006 0px 5px 20px;
|
box-shadow: #0006 0px 5px 20px;
|
||||||
}
|
}
|
||||||
.card:hover {
|
.default-card:hover, .ms-Fabric--isFocusVisible .default-card:focus {
|
||||||
box-shadow: #0008 0px 5px 40px;
|
box-shadow: #0008 0px 5px 40px;
|
||||||
}
|
}
|
||||||
.card div.bg {
|
.default-card div.bg {
|
||||||
background-color: #000b;
|
background-color: #000b;
|
||||||
}
|
}
|
||||||
.list-card:hover {
|
.list-card:hover, .ms-Fabric--isFocusVisible .list-card:focus {
|
||||||
box-shadow: #0006 0px 5px 15px;
|
box-shadow: #0006 0px 5px 15px;
|
||||||
}
|
}
|
||||||
.list-card:active {
|
.list-card:active {
|
||||||
box-shadow: #0000 0px 5px 15px, inset #0006 0px 0px 15px;
|
box-shadow: #0000 0px 5px 15px, inset #0006 0px 0px 15px;
|
||||||
}
|
}
|
||||||
|
.magazine-card:hover, .ms-Fabric--isFocusVisible .magazine-card:focus {
|
||||||
|
box-shadow: #0006 0px 5px 20px;
|
||||||
|
}
|
||||||
|
.magazine-card:active {
|
||||||
|
box-shadow: #0000 0px 5px 20px;
|
||||||
|
}
|
||||||
|
.compact-card:hover, .ms-Fabric--isFocusVisible .compact-card:focus {
|
||||||
|
box-shadow: #0008 0 0 10px;
|
||||||
|
}
|
||||||
|
.compact-card:active {
|
||||||
|
box-shadow: #0000 0 0 10px;
|
||||||
|
}
|
||||||
}
|
}
|
21
dist/styles/feeds.css
vendored
21
dist/styles/feeds.css
vendored
@ -126,11 +126,25 @@
|
|||||||
overflow: hidden scroll;
|
overflow: hidden scroll;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.list-feed > div.load-more-wrapper {
|
.list-feed > div.load-more-wrapper,
|
||||||
|
.magazine-feed > div.load-more-wrapper,
|
||||||
|
.compact-feed > div.load-more-wrapper {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.magazine-feed, .compact-feed {
|
||||||
|
padding-top: 28px;
|
||||||
|
height: calc(100% - 60px);
|
||||||
|
overflow: hidden scroll;
|
||||||
|
margin-top: var(--navHeight);
|
||||||
|
}
|
||||||
|
.magazine-feed .ms-List-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.cards-feed-container {
|
.cards-feed-container {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -155,7 +169,10 @@
|
|||||||
.flex-fix {
|
.flex-fix {
|
||||||
min-width: 280px;
|
min-width: 280px;
|
||||||
}
|
}
|
||||||
.cards-feed-container > .empty, .list-feed > .empty {
|
.cards-feed-container > .empty,
|
||||||
|
.list-feed > .empty,
|
||||||
|
.magazine-feed > .empty,
|
||||||
|
.compact-feed > .empty {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 64px);
|
height: calc(100vh - 64px);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
29
src/components/cards/compact-card.tsx
Normal file
29
src/components/cards/compact-card.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Card } from "./card"
|
||||||
|
import CardInfo from "./info"
|
||||||
|
import Time from "../utils/time"
|
||||||
|
|
||||||
|
const className = (props: Card.Props) => {
|
||||||
|
let cn = ["card", "compact-card"]
|
||||||
|
if (props.item.hasRead) cn.push("read")
|
||||||
|
return cn.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompactCard: React.FunctionComponent<Card.Props> = (props) => (
|
||||||
|
<div
|
||||||
|
className={className(props)}
|
||||||
|
onClick={e => Card.onClick(props, e)}
|
||||||
|
onMouseUp={e => Card.onMouseUp(props, e)}
|
||||||
|
onKeyDown={e => Card.onKeyDown(props, e)}
|
||||||
|
data-iid={props.item._id}
|
||||||
|
data-is-focusable>
|
||||||
|
<CardInfo source={props.source} item={props.item} hideTime />
|
||||||
|
<div className="data">
|
||||||
|
<span className="title">{props.item.title}</span>
|
||||||
|
<span className={"snippet" + (props.item.thumb ? "" : " show")}>{props.item.snippet.slice(0, 325)}</span>
|
||||||
|
</div>
|
||||||
|
<Time date={props.item.date} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default CompactCard
|
@ -3,7 +3,7 @@ import { Card } from "./card"
|
|||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
|
|
||||||
const className = (props: Card.Props) => {
|
const className = (props: Card.Props) => {
|
||||||
let cn = ["card"]
|
let cn = ["card", "default-card"]
|
||||||
if (props.item.snippet && props.item.thumb) cn.push("transform")
|
if (props.item.snippet && props.item.thumb) cn.push("transform")
|
||||||
if (props.item.hidden) cn.push("hidden")
|
if (props.item.hidden) cn.push("hidden")
|
||||||
return cn.join(" ")
|
return cn.join(" ")
|
||||||
|
@ -6,13 +6,14 @@ import { RSSItem } from "../../scripts/models/item"
|
|||||||
type CardInfoProps = {
|
type CardInfoProps = {
|
||||||
source: RSSSource
|
source: RSSSource
|
||||||
item: RSSItem
|
item: RSSItem
|
||||||
|
hideTime?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardInfo: React.FunctionComponent<CardInfoProps> = (props) => (
|
const CardInfo: React.FunctionComponent<CardInfoProps> = (props) => (
|
||||||
<p className="info">
|
<p className="info">
|
||||||
{props.source.iconurl ? <img src={props.source.iconurl} /> : null}
|
{props.source.iconurl ? <img src={props.source.iconurl} /> : null}
|
||||||
<span className="name">{props.source.name}</span>
|
<span className="name">{props.source.name}</span>
|
||||||
<Time date={props.item.date} />
|
{props.hideTime ? null : <Time date={props.item.date} />}
|
||||||
{props.item.hasRead ? null : <span className="read-indicator"></span>}
|
{props.item.hasRead ? null : <span className="read-indicator"></span>}
|
||||||
{props.item.starred ? <span className="starred-indicator"></span> : null}
|
{props.item.starred ? <span className="starred-indicator"></span> : null}
|
||||||
</p>
|
</p>
|
||||||
|
@ -3,7 +3,7 @@ import { Card } from "./card"
|
|||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
|
|
||||||
const className = (props: Card.Props) => {
|
const className = (props: Card.Props) => {
|
||||||
let cn = ["list-card"]
|
let cn = ["card", "list-card"]
|
||||||
if (props.item.hidden) cn.push("hidden")
|
if (props.item.hidden) cn.push("hidden")
|
||||||
return cn.join(" ")
|
return cn.join(" ")
|
||||||
}
|
}
|
||||||
|
33
src/components/cards/magazine-card.tsx
Normal file
33
src/components/cards/magazine-card.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Card } from "./card"
|
||||||
|
import CardInfo from "./info"
|
||||||
|
|
||||||
|
const className = (props: Card.Props) => {
|
||||||
|
let cn = ["card", "magazine-card"]
|
||||||
|
if (props.item.hasRead) cn.push("read")
|
||||||
|
if (props.item.hidden) cn.push("hidden")
|
||||||
|
return cn.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
const MagazineCard: React.FunctionComponent<Card.Props> = (props) => (
|
||||||
|
<div
|
||||||
|
className={className(props)}
|
||||||
|
onClick={e => Card.onClick(props, e)}
|
||||||
|
onMouseUp={e => Card.onMouseUp(props, e)}
|
||||||
|
onKeyDown={e => Card.onKeyDown(props, e)}
|
||||||
|
data-iid={props.item._id}
|
||||||
|
data-is-focusable>
|
||||||
|
{props.item.thumb ? (
|
||||||
|
<div className="head"><img src={props.item.thumb} /></div>
|
||||||
|
) : null}
|
||||||
|
<div className="data">
|
||||||
|
<div>
|
||||||
|
<h3 className="title">{props.item.title}</h3>
|
||||||
|
<p className={"snippet" + (props.item.thumb ? "" : " show")}>{props.item.snippet.slice(0, 325)}</p>
|
||||||
|
</div>
|
||||||
|
<CardInfo source={props.source} item={props.item} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default MagazineCard
|
@ -168,6 +168,22 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
checked: this.props.viewType === ViewType.List,
|
checked: this.props.viewType === ViewType.List,
|
||||||
onClick: () => this.props.switchView(ViewType.List)
|
onClick: () => this.props.switchView(ViewType.List)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "magazineView",
|
||||||
|
text: intl.get("context.magazineView"),
|
||||||
|
iconProps: { iconName: "Articles" },
|
||||||
|
canCheck: true,
|
||||||
|
checked: this.props.viewType === ViewType.Magazine,
|
||||||
|
onClick: () => this.props.switchView(ViewType.Magazine)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "compactView",
|
||||||
|
text: intl.get("context.compactView"),
|
||||||
|
iconProps: { iconName: "BulletedList" },
|
||||||
|
canCheck: true,
|
||||||
|
checked: this.props.viewType === ViewType.Compact,
|
||||||
|
onClick: () => this.props.switchView(ViewType.Compact)
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,8 @@ export class Feed extends React.Component<FeedProps> {
|
|||||||
case (ViewType.Cards): return (
|
case (ViewType.Cards): return (
|
||||||
<CardsFeed {...this.props} />
|
<CardsFeed {...this.props} />
|
||||||
)
|
)
|
||||||
|
case (ViewType.Magazine):
|
||||||
|
case (ViewType.Compact):
|
||||||
case (ViewType.List): return (
|
case (ViewType.List): return (
|
||||||
<ListFeed {...this.props} />
|
<ListFeed {...this.props} />
|
||||||
)
|
)
|
||||||
|
@ -1,27 +1,49 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import intl from "react-intl-universal"
|
import intl from "react-intl-universal"
|
||||||
import { FeedProps } from "./feed"
|
import { FeedProps } from "./feed"
|
||||||
import { DefaultButton, FocusZone, FocusZoneDirection, List } from 'office-ui-fabric-react';
|
import { PrimaryButton, FocusZone, FocusZoneDirection, List } from 'office-ui-fabric-react';
|
||||||
import ListCard from "../cards/list-card";
|
|
||||||
import { RSSItem } from "../../scripts/models/item";
|
import { RSSItem } from "../../scripts/models/item";
|
||||||
import { AnimationClassNames } from "@fluentui/react";
|
import { AnimationClassNames } from "@fluentui/react";
|
||||||
|
import { ViewType } from "../../schema-types";
|
||||||
|
import ListCard from "../cards/list-card";
|
||||||
|
import MagazineCard from "../cards/magazine-card";
|
||||||
|
import CompactCard from "../cards/compact-card";
|
||||||
|
|
||||||
class ListFeed extends React.Component<FeedProps> {
|
class ListFeed extends React.Component<FeedProps> {
|
||||||
onRenderItem = (item: RSSItem) => (
|
onRenderItem = (item: RSSItem) => {
|
||||||
<ListCard
|
const props = {
|
||||||
feedId={this.props.feed._id}
|
feedId: this.props.feed._id,
|
||||||
key={item._id}
|
key: item._id,
|
||||||
item={item}
|
item: item,
|
||||||
source={this.props.sourceMap[item.source]}
|
source: this.props.sourceMap[item.source],
|
||||||
shortcuts={this.props.shortcuts}
|
shortcuts: this.props.shortcuts,
|
||||||
markRead={this.props.markRead}
|
markRead: this.props.markRead,
|
||||||
contextMenu={this.props.contextMenu}
|
contextMenu: this.props.contextMenu,
|
||||||
showItem={this.props.showItem} />
|
showItem: this.props.showItem,
|
||||||
)
|
}
|
||||||
|
|
||||||
|
switch (this.props.viewType) {
|
||||||
|
case (ViewType.Magazine): return <MagazineCard {...props} />
|
||||||
|
case (ViewType.Compact): return <CompactCard {...props} />
|
||||||
|
default: return <ListCard {...props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getClassName = () => {
|
||||||
|
switch (this.props.viewType) {
|
||||||
|
case (ViewType.Magazine): return "magazine-feed"
|
||||||
|
case (ViewType.Compact): return "compact-feed"
|
||||||
|
default: return "list-feed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.props.feed.loaded && (
|
return this.props.feed.loaded && (
|
||||||
<FocusZone as="div" id="refocus" direction={FocusZoneDirection.vertical} className="list-feed" data-is-scrollable>
|
<FocusZone as="div"
|
||||||
|
id="refocus"
|
||||||
|
direction={FocusZoneDirection.vertical}
|
||||||
|
className={this.getClassName()}
|
||||||
|
data-is-scrollable>
|
||||||
<List
|
<List
|
||||||
className={AnimationClassNames.slideUpIn10}
|
className={AnimationClassNames.slideUpIn10}
|
||||||
items={this.props.items}
|
items={this.props.items}
|
||||||
@ -29,7 +51,7 @@ class ListFeed extends React.Component<FeedProps> {
|
|||||||
usePageCache />
|
usePageCache />
|
||||||
{
|
{
|
||||||
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
||||||
? <div className="load-more-wrapper"><DefaultButton
|
? <div className="load-more-wrapper"><PrimaryButton
|
||||||
text={intl.get("loadMore")}
|
text={intl.get("loadMore")}
|
||||||
disabled={this.props.feed.loading}
|
disabled={this.props.feed.loading}
|
||||||
onClick={() => this.props.loadMore(this.props.feed)} /></div>
|
onClick={() => this.props.loadMore(this.props.feed)} /></div>
|
||||||
|
@ -25,14 +25,14 @@ class Page extends React.Component<PageProps> {
|
|||||||
prevItem = (event: React.MouseEvent) => this.offsetItem(event, -1)
|
prevItem = (event: React.MouseEvent) => this.offsetItem(event, -1)
|
||||||
nextItem = (event: React.MouseEvent) => this.offsetItem(event, 1)
|
nextItem = (event: React.MouseEvent) => this.offsetItem(event, 1)
|
||||||
|
|
||||||
render = () => this.props.viewType == ViewType.Cards
|
render = () => this.props.viewType !== ViewType.List
|
||||||
? (
|
? (
|
||||||
<>
|
<>
|
||||||
{this.props.settingsOn ? null :
|
{this.props.settingsOn ? null :
|
||||||
<div key="card" className={"main" + (this.props.menuOn ? " menu-on" : "")}>
|
<div key="card" className={"main" + (this.props.menuOn ? " menu-on" : "")}>
|
||||||
<ArticleSearch />
|
<ArticleSearch />
|
||||||
{this.props.feeds.map(fid => (
|
{this.props.feeds.map(fid => (
|
||||||
<FeedContainer viewType={this.props.viewType} feedId={fid} key={fid} />
|
<FeedContainer viewType={this.props.viewType} feedId={fid} key={fid + this.props.viewType} />
|
||||||
))}
|
))}
|
||||||
</div>}
|
</div>}
|
||||||
{this.props.itemId && (
|
{this.props.itemId && (
|
||||||
|
@ -18,11 +18,11 @@ export class SourceGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ViewType {
|
export const enum ViewType {
|
||||||
Cards, List, Customized
|
Cards, List, Magazine, Compact, Customized
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ThemeSettings {
|
export const enum ThemeSettings {
|
||||||
Default = "system",
|
Default = "system",
|
||||||
Light = "light",
|
Light = "light",
|
||||||
Dark = "dark"
|
Dark = "dark"
|
||||||
|
@ -74,6 +74,8 @@
|
|||||||
"view": "View",
|
"view": "View",
|
||||||
"cardView": "Card view",
|
"cardView": "Card view",
|
||||||
"listView": "List view",
|
"listView": "List view",
|
||||||
|
"magazineView": "Magazine view",
|
||||||
|
"compactView": "Compact view",
|
||||||
"filter": "Filtering",
|
"filter": "Filtering",
|
||||||
"unreadOnly": "Unread only",
|
"unreadOnly": "Unread only",
|
||||||
"starredOnly": "Starred only",
|
"starredOnly": "Starred only",
|
||||||
|
@ -68,6 +68,8 @@
|
|||||||
"view": "Ver",
|
"view": "Ver",
|
||||||
"cardView": "Vista en modo tarjeta",
|
"cardView": "Vista en modo tarjeta",
|
||||||
"listView": "Vista en modo listado",
|
"listView": "Vista en modo listado",
|
||||||
|
"magazineView": "Vista en modo revista",
|
||||||
|
"compactView": "Vista en modo compacta",
|
||||||
"filter": "Filtrando",
|
"filter": "Filtrando",
|
||||||
"unreadOnly": "Solo no leídos",
|
"unreadOnly": "Solo no leídos",
|
||||||
"starredOnly": "Solo destacados",
|
"starredOnly": "Solo destacados",
|
||||||
|
@ -67,7 +67,9 @@
|
|||||||
"search": "Rechercher \"{text}\" sur Google",
|
"search": "Rechercher \"{text}\" sur Google",
|
||||||
"view": "Affichage",
|
"view": "Affichage",
|
||||||
"cardView": "Vue par carte",
|
"cardView": "Vue par carte",
|
||||||
"listView": "vue par liste",
|
"listView": "Vue par liste",
|
||||||
|
"magazineView": "Vue par magazine",
|
||||||
|
"compactView": "Vue par compact",
|
||||||
"filter": "Filtrer",
|
"filter": "Filtrer",
|
||||||
"unreadOnly": "Non lu uniquement",
|
"unreadOnly": "Non lu uniquement",
|
||||||
"starredOnly": "Favoris uniquement",
|
"starredOnly": "Favoris uniquement",
|
||||||
|
@ -74,6 +74,8 @@
|
|||||||
"view": "视图",
|
"view": "视图",
|
||||||
"cardView": "卡片视图",
|
"cardView": "卡片视图",
|
||||||
"listView": "列表视图",
|
"listView": "列表视图",
|
||||||
|
"magazineView": "杂志视图",
|
||||||
|
"compactView": "紧凑视图",
|
||||||
"filter": "筛选",
|
"filter": "筛选",
|
||||||
"unreadOnly": "仅未读文章",
|
"unreadOnly": "仅未读文章",
|
||||||
"starredOnly": "仅星标文章",
|
"starredOnly": "仅星标文章",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user