mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-20 10:42:12 +00:00
feat(Calendar): implement selector
This commit is contained in:
parent
941e3795d8
commit
e5e67d547a
12 changed files with 177 additions and 189 deletions
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const classnames = require('classnames');
|
||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||
const Button = require('stremio/common/Button');
|
||||
const styles = require('./styles');
|
||||
|
||||
const PaginationInput = ({ className, label, dataset, onSelect, ...props }) => {
|
||||
const prevNextButtonOnClick = React.useCallback((event) => {
|
||||
if (typeof onSelect === 'function') {
|
||||
onSelect({
|
||||
type: 'change-page',
|
||||
value: event.currentTarget.dataset.value,
|
||||
dataset: dataset,
|
||||
reactEvent: event,
|
||||
nativeEvent: event.nativeEvent
|
||||
});
|
||||
}
|
||||
}, [dataset, onSelect]);
|
||||
return (
|
||||
<div {...props} className={classnames(className, styles['pagination-input-container'])} >
|
||||
<Button className={styles['prev-button-container']} title={'Previous page'} data-value={'prev'} onClick={prevNextButtonOnClick}>
|
||||
<Icon className={styles['icon']} name={'chevron-back'} />
|
||||
</Button>
|
||||
<div className={styles['label-container']} title={label}>
|
||||
<div className={styles['label']}>{label}</div>
|
||||
</div>
|
||||
<Button className={styles['next-button-container']} title={'Next page'} data-value={'next'} onClick={prevNextButtonOnClick}>
|
||||
<Icon className={styles['icon']} name={'chevron-forward'} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PaginationInput.propTypes = {
|
||||
className: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
dataset: PropTypes.object,
|
||||
onSelect: PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = PaginationInput;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const PaginationInput = require('./PaginationInput');
|
||||
|
||||
module.exports = PaginationInput;
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
.pagination-input-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
.prev-button-container, .next-button-container {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--overlay-color);
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
color: var(--primary-foreground-color);
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.1s ease-in;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-container {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--overlay-color);
|
||||
|
||||
.label {
|
||||
flex: none;
|
||||
min-width: 1.2rem;
|
||||
max-width: 3rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
color: var(--primary-foreground-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ const Multiselect = require('./Multiselect');
|
|||
const { default: MultiselectMenu } = require('./MultiselectMenu');
|
||||
const { HorizontalNavBar, VerticalNavBar } = require('./NavBar');
|
||||
const { default: HorizontalScroll } = require('./HorizontalScroll');
|
||||
const PaginationInput = require('./PaginationInput');
|
||||
const { PlatformProvider, usePlatform } = require('./Platform');
|
||||
const PlayIconCircleCentered = require('./PlayIconCircleCentered');
|
||||
const Popup = require('./Popup');
|
||||
|
|
@ -71,7 +70,6 @@ module.exports = {
|
|||
HorizontalNavBar,
|
||||
HorizontalScroll,
|
||||
VerticalNavBar,
|
||||
PaginationInput,
|
||||
PlatformProvider,
|
||||
usePlatform,
|
||||
PlayIconCircleCentered,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,6 @@
|
|||
|
||||
@import (reference) '~stremio/common/screen-sizes.less';
|
||||
|
||||
:import('~stremio/common/PaginationInput/styles.less') {
|
||||
pagination-prev-button-container: prev-button-container;
|
||||
pagination-next-button-container: next-button-container;
|
||||
pagination-button-icon: icon;
|
||||
pagination-label: label;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
@ -29,34 +22,6 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
.inputs {
|
||||
flex: none;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
.pagination-input {
|
||||
flex: none;
|
||||
height: 3rem;
|
||||
|
||||
.pagination-prev-button-container, .pagination-next-button-container {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
|
||||
.pagination-button-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-label {
|
||||
width: 9rem;
|
||||
max-width: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright (C) 2017-2024 Smart code 203358507
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { MainNavBars, PaginationInput, BottomSheet, useProfile, withCoreSuspender } from 'stremio/common';
|
||||
import { MainNavBars, BottomSheet, useProfile, withCoreSuspender } from 'stremio/common';
|
||||
import Selector from './Selector';
|
||||
import Table from './Table';
|
||||
import List from './List';
|
||||
import Details from './Details';
|
||||
import Placeholder from './Placeholder';
|
||||
import useCalendar from './useCalendar';
|
||||
import useCalendarDate from './useCalendarDate';
|
||||
import useSelectableInputs from './useSelectableInputs';
|
||||
import styles from './Calendar.less';
|
||||
|
||||
type Props = {
|
||||
|
|
@ -19,7 +19,6 @@ const Calendar = ({ urlParams }: Props) => {
|
|||
const calendar = useCalendar(urlParams);
|
||||
const profile = useProfile();
|
||||
|
||||
const [paginationInput] = useSelectableInputs(calendar, profile);
|
||||
const { toDayMonth } = useCalendarDate(profile);
|
||||
|
||||
const [selected, setSelected] = useState<CalendarDate | null>(null);
|
||||
|
|
@ -36,14 +35,11 @@ const Calendar = ({ urlParams }: Props) => {
|
|||
profile.auth !== null ?
|
||||
<div className={styles['content']}>
|
||||
<div className={styles['main']}>
|
||||
<div className={styles['inputs']}>
|
||||
{
|
||||
paginationInput !== null ?
|
||||
<PaginationInput {...paginationInput} className={styles['pagination-input']} />
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
<Selector
|
||||
selected={calendar.selected}
|
||||
selectable={calendar.selectable}
|
||||
profile={profile}
|
||||
/>
|
||||
<Table
|
||||
items={calendar.items}
|
||||
selected={selected}
|
||||
|
|
|
|||
95
src/routes/Calendar/Selector/Selector.less
Normal file
95
src/routes/Calendar/Selector/Selector.less
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
@import (reference) '~stremio/common/screen-sizes.less';
|
||||
|
||||
.selector {
|
||||
flex: none;
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 3rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.prev, .next {
|
||||
position: relative;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
transition: background-color 0.1s ease-out;
|
||||
|
||||
.label, .icon {
|
||||
color: var(--primary-foreground-color);
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.1s ease-out;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.label, .icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
background-color: var(--overlay-color);
|
||||
}
|
||||
}
|
||||
|
||||
.prev {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.next {
|
||||
padding-left: 1rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.selected {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
.year {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 100%;
|
||||
color: var(--primary-foreground-color);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.month {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--primary-foreground-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: @small) {
|
||||
.selector {
|
||||
.prev, .next {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.prev {
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
.next {
|
||||
right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/routes/Calendar/Selector/Selector.tsx
Normal file
60
src/routes/Calendar/Selector/Selector.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import React, { useCallback, useMemo } from 'react';
|
||||
import Icon from '@stremio/stremio-icons/react';
|
||||
import { Button } from 'stremio/common';
|
||||
import useCalendarDate from '../useCalendarDate';
|
||||
import styles from './Selector.less';
|
||||
|
||||
type Props = {
|
||||
selected: CalendarSelected,
|
||||
selectable: CalendarSelectable,
|
||||
profile: Profile,
|
||||
};
|
||||
|
||||
const Selector = ({ selected, selectable, profile }: Props) => {
|
||||
const { toMonth } = useCalendarDate(profile);
|
||||
|
||||
const [prev, next] = useMemo(() => (
|
||||
[selectable.prev, selectable.next]
|
||||
), [selectable]);
|
||||
|
||||
const onPrev = useCallback(() => {
|
||||
window.location.href = prev.deepLinks.calendar;
|
||||
}, [prev]);
|
||||
|
||||
const onNext = useCallback(() => {
|
||||
window.location.href = next.deepLinks.calendar;
|
||||
}, [next]);
|
||||
|
||||
return (
|
||||
<div className={styles['selector']}>
|
||||
<Button className={styles['prev']} onClick={onPrev}>
|
||||
<Icon
|
||||
className={styles['icon']}
|
||||
name={'chevron-back'}
|
||||
/>
|
||||
<div className={styles['label']}>
|
||||
{toMonth(prev, 'short')}
|
||||
</div>
|
||||
</Button>
|
||||
<div className={styles['selected']}>
|
||||
<div className={styles['year']}>
|
||||
{selected?.year}
|
||||
</div>
|
||||
<div className={styles['month']}>
|
||||
{toMonth(selected, 'long')}
|
||||
</div>
|
||||
</div>
|
||||
<Button className={styles['next']} onClick={onNext}>
|
||||
<div className={styles['label']}>
|
||||
{toMonth(next, 'short')}
|
||||
</div>
|
||||
<Icon
|
||||
className={styles['icon']}
|
||||
name={'chevron-forward'}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Selector;
|
||||
2
src/routes/Calendar/Selector/index.ts
Normal file
2
src/routes/Calendar/Selector/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import Selector from './Selector';
|
||||
export default Selector;
|
||||
|
|
@ -1,6 +1,18 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
const useCalendarDate = (profile: Profile) => {
|
||||
const toMonth = useCallback((calendarDate: CalendarDate | CalendarSelectableDate | null, format: 'short' | 'long'): string => {
|
||||
if (!calendarDate) return '';
|
||||
|
||||
const date = new Date();
|
||||
date.setDate(1);
|
||||
date.setMonth(calendarDate.month - 1);
|
||||
|
||||
return date.toLocaleString(profile.settings.interfaceLanguage, {
|
||||
month: format,
|
||||
});
|
||||
}, [profile.settings]);
|
||||
|
||||
const toMonthYear = useCallback((calendarDate: CalendarDate | null): string => {
|
||||
if (!calendarDate) return '';
|
||||
|
||||
|
|
@ -29,6 +41,7 @@ const useCalendarDate = (profile: Profile) => {
|
|||
}, [profile.settings]);
|
||||
|
||||
return {
|
||||
toMonth,
|
||||
toMonthYear,
|
||||
toDayMonth,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (C) 2017-2024 Smart code 203358507
|
||||
|
||||
import React from 'react';
|
||||
import useCalendarDate from './useCalendarDate';
|
||||
|
||||
const mapSelectableInputs = (calendar: Calendar, toMonthYear: (date: CalendarDate | null) => string) => {
|
||||
const paginationInput = (calendar.selectable.prev ?? calendar.selectable.next) ?
|
||||
{
|
||||
label: toMonthYear(calendar.selected),
|
||||
onSelect: ({ value }: { value: string }) => {
|
||||
if (value === 'prev' && calendar.selectable.prev) {
|
||||
window.location.href = calendar.selectable.prev.deepLinks.calendar;
|
||||
}
|
||||
if (value === 'next' && calendar.selectable.next) {
|
||||
window.location.href = calendar.selectable.next.deepLinks.calendar;
|
||||
}
|
||||
}
|
||||
}
|
||||
:
|
||||
null;
|
||||
|
||||
return [paginationInput];
|
||||
};
|
||||
|
||||
const useSelectableInputs = (calendar: Calendar, profile: Profile) => {
|
||||
const { toMonthYear } = useCalendarDate(profile);
|
||||
|
||||
const selectableInputs = React.useMemo(() => {
|
||||
return mapSelectableInputs(calendar, toMonthYear);
|
||||
}, [calendar, toMonthYear]);
|
||||
|
||||
return selectableInputs;
|
||||
};
|
||||
|
||||
export default useSelectableInputs;
|
||||
|
|
@ -11,13 +11,6 @@
|
|||
multiselect-label: label;
|
||||
}
|
||||
|
||||
:import('~stremio/common/PaginationInput/styles.less') {
|
||||
pagination-prev-button-container: prev-button-container;
|
||||
pagination-next-button-container: next-button-container;
|
||||
pagination-button-icon: icon;
|
||||
pagination-label: label;
|
||||
}
|
||||
|
||||
:import('~stremio/common/ModalDialog/styles.less') {
|
||||
selectable-inputs-modal-container: modal-dialog-container;
|
||||
selectable-inputs-modal-content: modal-dialog-content;
|
||||
|
|
|
|||
Loading…
Reference in a new issue