Skip to content

Commit

Permalink
Merge pull request #651 from Stremio/feat/calendar
Browse files Browse the repository at this point in the history
feat: calendar
  • Loading branch information
tymmesyde authored Nov 28, 2024
2 parents a7d3a40 + 7b87715 commit 0372683
Show file tree
Hide file tree
Showing 53 changed files with 1,628 additions and 155 deletions.
Binary file added images/calendar_placeholder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@stylistic/eslint-plugin-jsx": "^2.9.0",
"@types/hat": "^0.0.4",
"@types/react": "^18.2.9",
"@types/react-dom": "^18.3.0",
"babel-loader": "8.2.3",
"clean-webpack-plugin": "4.0.0",
"copy-webpack-plugin": "9.0.1",
Expand Down
4 changes: 4 additions & 0 deletions src/App/routerViewsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const routerViewsConfig = [
...routesRegexp.library,
component: routes.Library
},
{
...routesRegexp.calendar,
component: routes.Calendar
},
{
...routesRegexp.continuewatching,
component: routes.Library
Expand Down
1 change: 1 addition & 0 deletions src/App/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ html {
min-height: 480px;
font-family: 'PlusJakartaSans', 'sans-serif';
overflow: auto;
overscroll-behavior: none;

body {
width: 100%;
Expand Down
102 changes: 102 additions & 0 deletions src/common/BottomSheet/BottomSheet.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (C) 2017-2024 Smart code 203358507

@import (reference) '~stremio/common/screen-sizes.less';

.bottom-sheet {
z-index: 99;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;

.backdrop {
z-index: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: var(--primary-background-color);
opacity: 0.8;
transition: opacity 0.1s ease-out;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}

.container {
z-index: 1;
position: absolute;
bottom: 0;
max-height: calc(100% - var(--horizontal-nav-bar-size));
width: 100%;
display: flex;
flex-direction: column;
gap: 1.5rem;
padding-bottom: 1rem;
border-radius: 2rem 2rem 0 0;
background-color: var(--modal-background-color);
box-shadow: var(--outer-glow);
overflow: hidden;

&:not(.dragging) {
transition: transform 0.1s ease-out;
}

.heading {
position: relative;

.handle {
position: relative;
height: 2.5rem;
width: 100%;
display: flex;
align-items: center;
justify-content: center;

&::after {
content: "";
height: 0.3rem;
width: 3rem;
border-radius: 1rem;
background-color: var(--primary-foreground-color);
opacity: 0.3;
}
}

.title {
position: relative;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 1rem;
padding-left: 1.5rem;
font-size: 1.25rem;
font-weight: 600;
color: var(--primary-foreground-color);
}
}

.content {
position: relative;
overflow-y: auto;
}
}
}

@media only screen and (min-width: @xsmall) {
.bottom-sheet {
display: none;
}
}

@media only screen and (orientation: landscape) {
.bottom-sheet {
.container {
max-width: 90%;
}
}
}
87 changes: 87 additions & 0 deletions src/common/BottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (C) 2017-2024 Smart code 203358507

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import classNames from 'classnames';
import useBinaryState from 'stremio/common/useBinaryState';
import styles from './BottomSheet.less';

const CLOSE_THRESHOLD = 100;

type Props = {
children: JSX.Element,
title: string,
show?: boolean,
onClose: () => void,
};

const BottomSheet = ({ children, title, show, onClose }: Props) => {
const containerRef = useRef<HTMLDivElement>(null);
const [startOffset, setStartOffset] = useState(0);
const [offset, setOffset] = useState(0);

const [opened, open, close] = useBinaryState();

const containerStyle = useMemo(() => ({
transform: `translateY(${offset}px)`
}), [offset]);

const containerHeight = () => containerRef.current?.offsetHeight ?? 0;

const onCloseRequest = () => setOffset(containerHeight());

const onTouchStart = ({ touches }: React.TouchEvent<HTMLDivElement>) => {
const { clientY } = touches[0];
setStartOffset(clientY);
};

const onTouchMove = useCallback(({ touches }: React.TouchEvent<HTMLDivElement>) => {
const { clientY } = touches[0];
setOffset(Math.max(0, clientY - startOffset));
}, [startOffset]);

const onTouchEnd = () => {
setOffset((offset) => offset > CLOSE_THRESHOLD ? containerHeight() : 0);
setStartOffset(0);
};

const onTransitionEnd = useCallback(() => {
(offset === containerHeight()) && close();
}, [offset]);

useEffect(() => {
setOffset(0);
show ? open() : close();
}, [show]);

useEffect(() => {
!opened && onClose();
}, [opened]);

return opened && createPortal((
<div className={styles['bottom-sheet']}>
<div className={styles['backdrop']} onClick={onCloseRequest} />
<div
ref={containerRef}
className={classNames(styles['container'], { [styles['dragging']]: startOffset }, 'animation-slide-up')}
style={containerStyle}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
onTransitionEnd={onTransitionEnd}
>
<div className={styles['heading']}>
<div className={styles['handle']} />
<div className={styles['title']}>
{title}
</div>
</div>
<div className={styles['content']} onClick={onCloseRequest}>
{children}
</div>
</div>
</div>
), document.body);
};

export default BottomSheet;
4 changes: 4 additions & 0 deletions src/common/BottomSheet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (C) 2017-2024 Smart code 203358507

import BottomSheet from './BottomSheet';
export default BottomSheet;
4 changes: 3 additions & 1 deletion src/common/Chips/Chip/Chip.less
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
background-color: transparent;
user-select: none;
overflow: hidden;
opacity: 0.6;

&:hover {
background-color: var(--overlay-color);
transition: background-color 0.1s ease-out;
opacity: 1;
}

&.active {
font-weight: 700;
opacity: 1;
background-color: var(--quaternary-accent-color);
transition: background-color 0.1s ease-in;
}
Expand Down
15 changes: 0 additions & 15 deletions src/common/Chips/Chips.less
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
// Copyright (C) 2017-2024 Smart code 203358507

@mask-width: 10%;

.chips {
position: relative;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 1rem;
overflow-x: auto;

&.left {
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 1) calc(100% - @mask-width), rgba(0, 0, 0, 0) 100%);
}

&.right {
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) @mask-width);
}

&.center {
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) @mask-width, rgba(0, 0, 0, 1) calc(100% - @mask-width), rgba(0, 0, 0, 0) 100%);
}
}
27 changes: 4 additions & 23 deletions src/common/Chips/Chips.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (C) 2017-2024 Smart code 203358507

import React, { memo, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';
import React, { memo } from 'react';
import HorizontalScroll from '../HorizontalScroll';
import Chip from './Chip';
import styles from './Chips.less';

Expand All @@ -16,28 +16,9 @@ type Props = {
onSelect: (value: string) => {},
};

const SCROLL_THRESHOLD = 1;

const Chips = memo(({ options, selected, onSelect }: Props) => {
const ref = useRef<HTMLDivElement>(null);
const [scrollPosition, setScrollPosition] = useState('left');

useEffect(() => {
const onScroll = ({ target }: Event) => {
const { scrollLeft, scrollWidth, offsetWidth} = target as HTMLDivElement;
const position =
(scrollLeft - SCROLL_THRESHOLD) <= 0 ? 'left' :
(scrollLeft + offsetWidth + SCROLL_THRESHOLD) >= scrollWidth ? 'right' :
'center';
setScrollPosition(position);
};

ref.current?.addEventListener('scroll', onScroll);
return () => ref.current?.removeEventListener('scroll', onScroll);
}, []);

return (
<div ref={ref} className={classNames(styles['chips'], [styles[scrollPosition]])}>
<HorizontalScroll className={styles['chips']}>
{
options.map(({ label, value }) => (
<Chip
Expand All @@ -49,7 +30,7 @@ const Chips = memo(({ options, selected, onSelect }: Props) => {
/>
))
}
</div>
</HorizontalScroll>
);
});

Expand Down
20 changes: 20 additions & 0 deletions src/common/HorizontalScroll/HorizontalScroll.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2017-2024 Smart code 203358507

@mask-width: 10%;

.horizontal-scroll {
position: relative;
overflow-x: auto;

&.left {
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 1) calc(100% - @mask-width), rgba(0, 0, 0, 0) 100%);
}

&.right {
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) @mask-width);
}

&.center {
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) @mask-width, rgba(0, 0, 0, 1) calc(100% - @mask-width), rgba(0, 0, 0, 0) 100%);
}
}
Loading

0 comments on commit 0372683

Please sign in to comment.