nessprim-planby-pro/README.md
2024-08-04 16:14:27 -06:00

1719 lines
56 KiB
Markdown

<div align="center" style="margin-bottom: 10px">
<a href="https://www.npmjs.com/package/planby">
<img src="https://raw.githubusercontent.com/karolkozer/planby-demo-resources/master/planby-banner-pro-small.png" alt="Planby logo" />
</a>
</div>
<div align="center" style="margin-bottom: 20px">
<a href="https://www.npmjs.com/package/planby">
<img alt="npm" src="https://img.shields.io/npm/v/planby" />
</a>
<a href="https://npmjs.org/package/planby">
<img alt="downloads" src="https://badgen.net/npm/dm/planby" />
</a>
<a href="https://npmjs.org/package/planby">
<img alt="downloads" src="https://img.shields.io/npm/dt/planby?color=%2327ae60&label=recent%20downloads" />
</a>
<a href="https://opencollective.com/planby#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
</div>
## Description
Planby PRO is a React based component for a quick implementation of schedules, timelines, EPG, live streaming, music events and many more ideas. It uses a custom virtual view which allows you to operate on a really big number of data. The component has a simple API that you can easily integrate with other third party UI libraries. The component theme is customized to the needs of the application design.
<div align="center" style="margin-bottom: 10px">
<a href="https://planby.netlify.app/">
<img src="https://i.postimg.cc/6p2GDGMX/tv-preview-custom.png" alt="Planby preview" />
</a>
</div>
<div align="center" style="margin-bottom: 10px">
<a href="https://planby.netlify.app/">
<img src="https://i.postimg.cc/s2Pn9jGZ/planby-conf-event.png" alt="Planby preview" />
</a>
</div>
<div align="center" style="margin-bottom: 10px">
<a href="https://planby.netlify.app/">
<img src="https://raw.githubusercontent.com/karolkozer/planby-demo-resources/master/planby-planner-week.png" alt="Planby preview" />
</a>
</div>
<div align="center" style="margin-bottom: 10px">
<a href="https://planby.netlify.app/">
<img src="https://i.postimg.cc/50qZ05ST/planby-music-festival-event.png" alt="Planby preview" />
</a>
</div>
## Download example
[Download - code examples](https://github.com/Nessprim/planby-pro/tree/main/examples)
## Testimonials
<div align="center" >
<a href="https://planby.netlify.app/#testimonials">
<img src="https://raw.githubusercontent.com/karolkozer/planby-demo-resources/master/they-use-planby.png" alt="Planby preview" />
</a>
</div>
<div align="center" style="margin-bottom: 10px">
<a href="https://planby.netlify.app/#testimonials">
<img src="https://raw.githubusercontent.com/karolkozer/planby-demo-resources/master/testimonials.png" alt="Planby preview" />
</a>
</div>
# Getting Started
### Planby PRO version has a number of major updates so please read documentation about eg. render components, theme, styles, Typescript support.
## Option 1
Login to npm registry with the Github account with your username and personal access token (classic) - read:packages.
```sh
npm login --registry=https://npm.pkg.github.com --scope=@nessprim
```
```sh
> Username: USERNAME
> Password: TOKEN
```
In the same directory as your package.json file, create or edit an .npmrc file to include a line specifying GitHub Packages URL
```sh
@nessprim:registry=https://npm.pkg.github.com
```
## Option 2
To your per-user authenticate by adding your personal access token (classic) to your ~/.npmrc file, edit the ~/.npmrc file for your project to include the following line, replacing TOKEN with your personal access token (classic).
```sh
//npm.pkg.github.com/:_authToken=TOKEN
```
In the same directory as your package.json file, create or edit an .npmrc file to include a line specifying GitHub Packages URL
```sh
@nessprim:registry=https://npm.pkg.github.com
```
## Option 3
In the same directory as your package.json file, create or edit an .npmrc file to include a line specifying GitHub Packages URL and your personal access token (classic)
```sh
@nessprim:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=TOKEN
```
## Installation
Specify the version which you want to install.
- yarn
```sh
yarn add @nessprim/planby-pro
```
- npm
```sh
npm install @nessprim/planby-pro
```
## Usage
```tsx
import { useEpg, Epg, Layout } from '@nessprim/planby-pro';
const channels = React.useMemo(
() => [
{
logo: 'https://via.placeholder.com',
uuid: '10339a4b-7c48-40ab-abad-f3bcaf95d9fa',
...
},
],
[]
);
const epg = React.useMemo(
() => [
{
channelUuid: '30f5ff1c-1346-480a-8047-a999dd908c1e',
description:
'Ut anim nisi consequat minim deserunt...',
id: 'b67ccaa3-3dd2-4121-8256-33dbddc7f0e6',
image: 'https://via.placeholder.com',
since: "2022-02-02T23:50:00",
till: "2022-02-02T00:55:00",
title: 'Title',
...
},
],
[]
);
const {
getEpgProps,
getLayoutProps,
onScrollToNow,
onScrollLeft,
onScrollRight,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
/>
</Epg>
</div>
</div>
);
```
or
#### Custom width and height
```tsx
const {
getEpgProps,
getLayoutProps,
...
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
width: 1200,
height: 600
});
return (
<div>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
/>
</Epg>
</div>
```
or
#### Time range
```tsx
const {
getEpgProps,
getLayoutProps,
...
} = useEpg({
epg,
channels,
startDate: '2022-02-02T10:00:00',
endDate: '2022-02-02T20:00:00',
width: 1200,
height: 600
});
return (
<div>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
/>
</Epg>
</div>
```
#### Week mode
```tsx
const epg = React.useMemo(
() => [
{
channelUuid: '30f5ff1c-1346-480a-8047-a999dd908c1e',
description:
'Ut anim nisi consequat minim deserunt...',
id: 'b67ccaa3-3dd2-4121-8256-33dbddc7f0e6',
image: 'https://via.placeholder.com',
since: "2023-05-01T00:00:00",
till: "2023-05-03T24:00:00",
title: 'Title',
...
},
],
[]
);
const {
getEpgProps,
getLayoutProps,
...
} = useEpg({
epg,
channels,
startDate: '2023-05-01T00:00:00', // Required day with time 00:00:00
endDate: '2023-05-25T00:00:00', // Required day with time 00:00:00
mode: {type: 'week', style: 'modern'}
...
});
...
```
#### Month mode
```tsx
const epg = React.useMemo(
() => [
{
channelUuid: '30f5ff1c-1346-480a-8047-a999dd908c1e',
description:
'Ut anim nisi consequat minim deserunt...',
id: 'b67ccaa3-3dd2-4121-8256-33dbddc7f0e6',
image: 'https://via.placeholder.com',
since: "2023-05-01T00:00:00",
till: "2023-08-31T24:00:00",
title: 'Title',
...
},
],
[]
);
const {
getEpgProps,
getLayoutProps,
...
} = useEpg({
epg,
channels,
startDate: '2023-05-01T00:00:00', // First day of the month with required time 00:00:00
endDate: '2023-11-30T00:00:00', // Last day of the month with required time 00:00:00
mode: {type: 'month', style: 'modern'}
...
});
...
```
# API
### useEpg
#### Options
Available options in useEpg
| Property | Type | Status | Description | Access |
| ------------------------ | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
| `channels` | `array` | required | Array with channels data | |
| `epg` | `array` | required | Array with EPG data | |
| `width` | `number` | optional | EPG width | |
| `height` | `number` | optional | EPG height | |
| `sidebarWidth` | `number` | optional | Width of the sidebar with channels | |
| `timelineHeight` | `number` | optional | Height of the timeline | `PRO` |
| `itemHeight` | `number` | optional | Height of channels and programs in the EPG. Default value is 80 | |
| `dayWidth` | `number` | optional | Width of the day. Default value is 7200. Calculation to set up day width with own hour width value e.g., 24h \* 300px (your custom hour width) = 7200px -> `dayWidth` | |
| `startDate` | `string` | optional | Date format `2022/02/02` or `2022-02-02T00:00:00`. You can set your own start time, e.g., `2022-02-02T10:00:00`, `2022-02-02T14:00:00`, etc. Full clock hours only | |
| `endDate` | `string` | optional | Date format `2022-02-02T00:00:00`, `2022-02-02T20:00:00`, etc. Must be within the same 24-hour period as `startDate`. Full clock hours only. Scroll through `multiple days` and timeline mode is available only in `PRO` plan. | `PRO` |
| `timelineDividers` | `number` | optional | Set own dividers to Timeline and Grid bg. Default value is `4`. | `PRO` |
| `hoursInDays` | `array` | optional | Set start time and end time of each day in `multiple days` feature if your data for each day has some time spaces between items in the day. [Read more](#hours-in-days) | `PRO` |
| `initialScrollPositions` | `object` | optional | Set initial scroll position in Layout, e.g., `initialScrollPositions: { top: 500, left: 800 }` | `PRO` |
| `liveRefreshTime` | `number` | optional | Live refresh time of the events. Default value is 120 sec. | `PRO` |
| `isBaseTimeFormat` | `boolean` | optional | Convert to 12-hour format, e.g., `2:00am`, `4:00pm`, etc. Default value is false. | |
| `isCurrentTime` | `boolean` | optional | Show current time in Timeline. Default value is false. | `PRO` |
| `isInitialScrollToNow` | `boolean` | optional | Scroll to the current live element. | `PRO` |
| `isVerticalMode` | `boolean` | optional | Show Timeline in vertical view. Default value is false. | `PRO` |
| `isResize` | `boolean` | optional | Possibility to resize the element. | `PRO` |
| `isSidebar` | `boolean` | optional | Show/hide sidebar | |
| `isTimeline` | `boolean` | optional | Show/hide timeline | |
| `isLine` | `boolean` | optional | Show/hide line | |
| `isRTL` | `boolean` | optional | Change direction to RTL or LTR. Default value is false. | `PRO` |
| `theme` | `object` | optional | Object with theme schema [Read more](#theme) | |
| `timezone` | `object` | optional | Convert and display data from UTC format to your own time zone [Read more](#timezone) | `PRO` |
| `areas` | `array` | optional | Area gives possibilities to add field ranges to the Timeline layout. [Read more](#areas) | `PRO` |
| `mode` | `object` | optional | Type values: `day/week/month`. Style values: `default/modern` Define the mode and style of the timeline. Default mode is `day` and style is `default` [Read more](#week-and-month-mode) | `PRO` |
| `overlap` | `object` | optional | Enable the element overlaps in the layout. Mode values: `stack/layer`, layerOverlapLevel: `number` [Read more](#overlaps) | `PRO` |
| `dnd - drag and drop` | `object` | optional | Drag and move the element in the layout. Mode values: `row/multi-rows` [Read more](#drag-and-drop) | `PRO` |
| `snap - dnd snapping` | `object` | optional | Activate the snap option to facilitate Drag and Drop (DnD) or resize operations, ensuring alignment with specified snap values. Example value: `snap:{x: 50, y: 75}` | `PRO` |
| `grid layout` | `object` | optional | Background grid on the layout. Mode hoverHighlight values: `true/false`, `onGridItemClick`: function with all the properties on clicked item grid, `onGridItemDrop`: function with all the properties on drop item grid [Read more](#grid) | `PRO` |
| `channelMapKey` | `string` | optional | The Channel `uuid` attribute can be controlled by prop. Key map gives a possibilities to use specific prop from own data instead of needing to map to uuid in own data | `PRO` |
| `programChannelMapKey` | `string` | optional | The Programs `channelUuid` attributes can be controlled by prop. Key map gives a possibilities to use a specific prop from own data instead of needing to map to channelUuid in your data | `PRO` |
| `globalStyles` | `string` | optional | Inject custom global styles and font. Font weight: 400,500,600. Default font is "Inter" | `PRO` |
## Note about width and height props
Without declaring the `width` and `length` properties, the component takes the dimensions of the parent element.
## Timezone
Convert and display data from UTC format to your own time zone
#### Timezone schema
| Property | Type | Status | Values |
| --------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `enabled` | `boolean` | required |
| `mode` | `string` | required | `utc` |
| `zone` | `string` | required | Specify your timezone eg. `Europe/Paris`, `America/New_York`. Please make user your timezone value is correct |
## Areas
Areas fields gives possibilities to add info ranges to the Timeline layout.
#### Area schema
| Property | Type | Status |
| ------------- | --------------- | -------- |
| `startDate` | `string` | required |
| `endDate` | `string` | optional |
| `styles` | `CSSProperties` | required |
| `onClick` | `function` | optional |
| `annotations` | `object` | optional |
#### Annotations schema
| Property | Type | Status |
| ----------- | --------------- | -------- |
| `textStart` | `string` | optional |
| `textEnd` | `string` | optional |
| `styles` | `CSSProperties` | required |
Example:
```tsx
[
{
startDate: '2023-05-05T00:00:00',
endDate: '2023-05-08T00:00:00',
styles: {
background: '#00800012',
borderLeft: '2px dotted #38A169',
borderRight: '2px dotted #38A169',
},
onClick: () => alert('Click on area'),
annotations: {
styles: {
background: '#38A169',
color: 'white',
},
textStart: 'Testing Start',
textEnd: 'Testing End',
},
},
{
startDate: '2023-05-14T00:00:00',
endDate: '2023-05-17T00:00:00',
styles: {
borderLeft: '2px dotted #D69E2E',
borderRight: '2px dotted #D69E2E',
},
annotations: {
styles: {
background: '#D69E2E',
color: 'white',
},
textStart: 'Testing2 Start',
textEnd: 'Testing2 End',
},
},
{
startDate: '2023-05-11T00:00:00',
styles: {
borderLeft: '2px dotted #C53030',
},
annotations: {
styles: {
background: '#C53030',
color: 'white',
},
textStart: 'Release',
},
},
];
```
## Week and Month mode
#### Mode schema
| Property | Type | Status | Values |
| -------- | -------- | -------- | ---------------- |
| `type` | `string` | optional | `day/week/month` |
| `style` | `string` | optional | `default/modern` |
#### Month mode
| Property | Description |
| ----------- | ---------------------------------------------------------------------------------------------------------- |
| `startDate` | You have to set the first day of the month with start time of the day `00:00:00` eg. `2023-05-01T00:00:00` |
| `endDate` | You have to set the last day of the month with start time of the day `00:00:00` eg.`2023-11-30T00:00:00` |
#### Week mode
| Property | Description |
| ----------- | ---------------------------------------------------------------------------------------------------------- |
| `startDate` | You have to set the first day of the month with start time of the day `00:00:00` eg. `2023-05-01T00:00:00` |
| `endDate` | You have to set the first day of the month with start time of the day `00:00:00` eg.`2023-05-20T00:00:00` |
#### Week / Month EPG Item
| Property | Description |
| -------- | ------------------------------------------------------------------------------ |
| `since` | You have to set the start time of the day `00:00:00` eg. `2023-05-01T00:00:00` |
| `till` | You have to set the end time of the day `24:00:00` eg. `2023-05-09T24:00:00` |
## Overlaps
Enable the element overlaps in the layout
#### Mode schema
| Property | Type | Description | Status |
| ------------------- | --------- | -------------------------------------------------- | ------------------------- |
| `enabled` | `boolean` | | required |
| `mode` | `string` | required | values: `stack` / `layer` |
| `layerOverlapLevel` | `number` | The percentage of elements layer on top each other | required |
## Drag and Drop
Enable the element overlaps in the layout
#### DnD schema
| Property | Type | Description | Status |
| -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| `enabled` | `boolean` | | required |
| `mode` | `string` | required | values: `row` / `multi-rows` |
| `onDnDMouseUp` | `function` | Callback function with custom logic to check if new since/till time meets custom requirements | optional |
| `onDnDSuccess` | `function` | Callback function to add custom logic when drag event is successful. Return `true` value is custom logic is correct or 'false' value to restor initial element position in the layout | optional |
```tsx
const { getEpgProps, getLayoutProps } = useEpg({
startDate: '2023-05-02T00:00:00',
endDate: '2023-05-05T24:00:00',
dnd: {
enabled: true,
mode: 'multi-rows',
onDnDMouseUp: async event => {
// Event object contains new since and till values of the dragged program
// event = { id, since, till }
console.log('event');
return true; // true or false
},
onDnDSuccess: event => {
// event = { channelUuid, id, index, title, description, since, till, image, channelIndex, channelPosition }
console.log('event'),
}
},
});
```
## Grid
Background grid on the layout with functionality to click on the grid item
#### OnClick event props schema
| Property | Type |
| ------------- | -------- |
| `since` | `string` |
| `till` | `string` |
| `date` | `string` |
| `channelUuid` | `string` |
#### onGridItemDrop event props schema
| Property | Type |
| ------------- | -------- |
| `since` | `string` |
| `till` | `string` |
| `date` | `string` |
| `channelUuid` | `string` |
And other props pass to the dragged element
## Hours in Days
Set start time and end time of each day in multiple days feature if you data for each day has some time spaces between items in the day. You can set your own startDate and endDate time eg. `2023-05-02T00:00:00`, `2023-05-05T24:00:00` with start time `00:00:00` and end time eg.`24:00:00`
#### Mode schema
| Property | Type | Status |
| --------------- | -------- | -------- |
| `date` | `string` | required |
| `startTimeHour` | `string` | required |
| `endTimeHour` | `string` | required |
Example:
```tsx
const { getEpgProps, getLayoutProps } = useEpg({
startDate:"2023-05-02T00:00:00",
endDate:"2023-05-05T24:00:00",
...
});
const hoursInDays = [
{
date: '2023-05-02',
startTimeHour: '2023-05-02T09:00:00',
endTimeHour: '2023-05-02T16:00:00',
},
{
date: '2023-05-03',
startTimeHour: '2023-05-03T09:00:00',
endTimeHour: '2023-05-03T13:00:00',
},
{
date: '2023-05-04',
startTimeHour: '2023-05-04T09:00:00',
endTimeHour: '2023-05-04T17:00:00',
},
{
date: '2023-05-05',
startTimeHour: '2023-05-05T09:00:00',
endTimeHour: '2023-05-05T15:00:00',
},
];
```
### globalStyles
Inject own custom font and other global styles.
```tsx
const globalStyles = `
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap");
/* Available in sponsors plan */
.planby {
font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica,
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
/* Layout */
.planby-layout {}
.planby-corner-box {}
/* Line */
.planby-line {}
/* Current time */
.planby-current-time {}
.planby-current-content {}
/* Channels */
.planby-channels {}
/* Channel */
.planby-channel {}
/* Program */
.planby-program {}
.planby-program-content {}
.planby-program-flex {}
.planby-program-stack {}
.planby-program-title {}
.planby-program-text {}
/* Timeline */
.planby-timeline-wrapper {}
.planby-timeline-box {}
.planby-timeline-time {}
.planby-timeline-dividers {}
.planby-timeline-wrapper {}
}
`;
```
#### Instance Properties
Properties returned from useEpg
| Property | Type | Description | Access |
| ----------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------ |
| `scrollY` | `number` | Current scroll y value |
| `scrollX` | `number` | Current scroll x value |
| `onScrollLeft` | `function(value: number)` | Default value is 300 |
| `onScrollRight` | `function(value: number)` | Default value is 300 |
| `onScrollToNow` | `function()` | Scroll to current time/live programs |
| `onScrollTop` | `function(value: number)` | Default value is 300 |
| `getLayoutData` | `function()` | Generate and get all the layout data | `PRO` |
| `getDropItemData` | `function()` | Get converted item data after external drop into grid action | `PRO` |
| `isLayoutBottom` | `function(offset:number)` | Check if the layout has reached the bottom. Add an offset value to perform various actions before reaching the bottom. | `PRO` |
| `isLayoutRight` | `function(offset:number)` | Check if the layout has reached the right side. Add an offset value to perform various actions before reaching the right. | `PRO` |
### Channel schema
You can add other properties that you wish to have according to your specific requirements or functionalities you want to implement.
| Property | Type | Status | Description | Access |
| ------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | ------ |
| `logo` | `string` | required | | |
| `uuid` | `string` | required | | |
| `title` | `string` | optional | | |
| `groupTree` | `boolean` | optional | Set to true if the channel is the first main top channel in a group, used to display the channel group name in sidebar | `PRO` |
| `parentChannelUuid` | `string` | optional | Set for nested children. Required when main channel has `groupTree` set to `true` | `PRO` |
| `isOpen` | `boolean` | optional | Used to open the channel group in the sidebar | `PRO` |
### Epg schema
You can add other properties that you wish to have according to your specific requirements or functionalities you want to implement.
| Property | Type | Status | Description | Access |
| ----------------- | --------- | -------- | -------------------------------------------------------------------- | ------ |
| `channelUuid` | `string` | required |
| `id` | `string` | required |
| `image` | `string` | required |
| `since` | `string` | required |
| `till` | `string` | required |
| `title` | `string` | required |
| `fixedVisibility` | `boolean` | optional | The element is always visible in the layout during the scroll events | `PRO` |
### Epg
#### Base props
Available props in Epg
| Property | Type | Description | Status | Access |
| ----------- | ----------- | ----------------------- | -------- | ------ |
| `isLoading` | `boolean` | Loader state | optional |
| `loader` | `Component` | Loader custom component | optional | `PRO` |
### Layout
#### Base props
Available props in Layout.
| Property | Type | Description | Status | Access |
| ------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -------- | ------ |
| `renderProgram` | `function({ program: { data: object, position: object})` | `data` object contains all properties related to the program, `position` object includes all position styles | optional |
| `renderChannel` | `function({ channel: { ..., position: object})` | `channel` object contains all properties related to the channel, `position` object includes all position styles | optional |
| `renderTimeline` | `function({sidebarWidth: number})` | `sidebarWidth` value of the channel's sidebar width | optional |
| `renderGridCell` | `function(object)` | render custom Grid Cell element in Layout grid | `PRO` |
| `renderLine` | `function({styles: object})` | basic `styles` and `position` values for the custom live tracking Line | optional | `PRO` |
| `renderCurrentTime` | `function({styles: object, isRTL: boolean, isBaseTimeFormat: boolean, time: string})` | basic `styles` values for the custom current time | optional | `PRO` |
| `onLayoutBgClick` | `function()` | Possibility to click on Layout background to trigger some custom events | optional | `PRO` |
# Render functions
You can use Planby's style components to develop main features. Moreover, you can integrate with third party UI library eg. Chakra UI, Material UI etc or make custom styles.
## renderProgram
Below is an example that allows you to render your custom Program component using Plaby's style components.
```tsx
import {
useEpg,
Epg,
Layout,
ProgramBox,
ProgramContent,
ProgramFlex,
ProgramStack,
ProgramTitle,
ProgramText,
ProgramImage,
useProgram,
Program,
ProgramItem
} from "planby";
const Item = ({program,...rest }: ProgramItem) => {
const { styles, formatTime, isLive, isMinWidth } = useProgram({program,...rest });
const { data } = program;
const { image, title, since, till } = data;
const sinceTime = formatTime(since);
const tillTime = formatTime(till);
return (
<ProgramBox width={styles.width} style={styles.position}>
<ProgramContent
width={styles.width}
isLive={isLive}
>
<ProgramFlex>
{isLive && isMinWidth && <ProgramImage src={image} alt="Preview" />}
<ProgramStack>
<ProgramTitle>{title}</ProgramTitle>
<ProgramText>
{sinceTime} - {tillTime}
</ProgramText>
</ProgramStack>
</ProgramFlex>
</ProgramContent>
</ProgramBox>
);
};
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
renderProgram={({ program,...rest }) => (
<Item key={program.data.id} program={program} {...rest} />
)}
/>
</Epg>
</div>
</div>
);
}
export default App;
```
## renderProgram - Vertical Mode
Below is an example that allows you to render your custom Program component using Planby's style components with Vertical mode.
```tsx
const Item = ({isVerticalMode, program,...rest }: ProgramItem) => {
const { styles, formatTime, isLive, isMinWidth } = useProgram({isVerticalMode, program,...rest });
...
return (
<ProgramBox width={styles.width} style={styles.position}>
<ProgramContent
isVerticalMode={isVerticalMode}
width={styles.width}
isLive={isLive}
>
<ProgramFlex isVerticalMode={isVerticalMode}>
{isLive && isMinWidth && <ProgramImage isVerticalMode={isVerticalMode} src={image} alt="Preview" />}
<ProgramStack>
<ProgramTitle>{title}</ProgramTitle>
<ProgramText>
{sinceTime} - {tillTime}
</ProgramText>
</ProgramStack>
</ProgramFlex>
</ProgramContent>
</ProgramBox>
);
};
```
## renderProgram - 12 hours time format
Below is an example that allows you to render your custom Program component with 12 hours time format using Planby's style components.
```tsx
...
const Item = ({ program, ...rest }: ProgramItem) => {
const {
styles,
formatTime,
set12HoursTimeFormat,
isLive,
isMinWidth,
} = useProgram({
program,
...rest
});
const { data } = program;
const { image, title, since, till } = data;
const sinceTime = formatTime(since, set12HoursTimeFormat()).toLowerCase();
const tillTime = formatTime(till, set12HoursTimeFormat()).toLowerCase();
return (
<ProgramBox width={styles.width} style={styles.position}>
<ProgramContent
width={styles.width}
isLive={isLive}
>
<ProgramFlex>
{isLive && isMinWidth && <ProgramImage src={image} alt="Preview" />}
<ProgramStack>
<ProgramTitle>{title}</ProgramTitle>
<ProgramText>
{sinceTime} - {tillTime}
</ProgramText>
</ProgramStack>
</ProgramFlex>
</ProgramContent>
</ProgramBox>
);
};
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
isBaseTimeFormat: true,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
...
}
export default App;
```
## renderProgram - RTL direction
Below is an example that allows you to render your custom Program component with RTL direction using Planby's style components.
```tsx
...
const Item = ({ program, ...rest }: ProgramItem) => {
const {
isRTL,
isLive,
isMinWidth,
formatTime,
styles,
set12HoursTimeFormat,
getRTLSinceTime,
getRTLTillTime,
} = useProgram({
program,
...rest
});
const { data } = program;
const { image, title, since, till } = data;
const sinceTime = formatTime(
getRTLSinceTime(since),
set12HoursTimeFormat()
).toLowerCase();
const tillTime = formatTime(
getRTLTillTime(till),
set12HoursTimeFormat()
).toLowerCase();
return (
<ProgramBox width={styles.width} style={styles.position}>
<ProgramContent width={styles.width} isLive={isLive}>
<ProgramFlex>
{isLive && isMinWidth && <ProgramImage src={image} alt="Preview" />}
<ProgramStack isRTL={isRTL}>
<ProgramTitle>{title}</ProgramTitle>
<ProgramText>
{sinceTime} - {tillTime}
</ProgramText>
</ProgramStack>
</ProgramFlex>
</ProgramContent>
</ProgramBox>
);
};
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
isBaseTimeFormat: true,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
...
}
export default App;
```
## renderGridCell
Below is an example that allows you to render your custom GridCell component using Planby's style components.
```tsx
...
import { GridCell, GridItem, GridDivider } from "@nessprim/planby-pro";
export function ItemGrid(props: GridCell) {
const {
isDayMode,
isHoverHighlight,
item,
timelineDividers,
timelineDividerArray,
gridDividerProps,
gridItemClickProps,
} = props;
const { onItemClick, ...dividerProps } = gridDividerProps.props;
const { left, ...styles } = gridDividerProps.styles;
return (
<GridItem
isDayMode={isDayMode}
isHoverHighlight={isHoverHighlight as boolean}
{...item.position}
{...gridItemClickProps}
>
{isDayMode &&
timelineDividerArray.map((_, index) => (
<GridDivider
key={index}
{...styles}
{...dividerProps}
left={left(index)}
onClick={onItemClick(item, index)}
/>
))}
</GridItem>
);
}
```
## renderChannel
Below is an example that allows you to render your custom Channel component using Planby's style components.
Available props in ChannelItem
| Property | Type | Description | Status |
| ---------------- | --------- | ------------------------------------------------ | -------- |
| `isVerticalMode` | `boolean` | Vertical mode | optional |
| `isRTL` | `boolean` | RTL option mode | optional |
| `channel` | `object` | Object contains position styles and url for logo | optional |
```tsx
import {
useEpg,
Epg,
Layout,
ChannelBox,
ChannelLogo,
Channel,
ChannelItem,
} from '@nessprim/planby-pro';
const CustomChannelItem = ({ channel }: ChannelItem) => {
const { position, logo } = channel;
return (
<ChannelBox {...position}>
<ChannelLogo
onClick={() => console.log('channel', channel)}
src={logo}
alt="Logo"
/>
</ChannelBox>
);
};
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
renderChannel={({ channel, ...rest }) => (
<CustomChannelItem key={channel.uuid} channel={channel} {...rest} />
)}
/>
</Epg>
</div>
</div>
);
}
```
## renderChannel - Vertical Mode
Below is an example that allows you to render your custom Channel component using Planby's style components with Vertical mode.
```tsx
import {
useEpg,
Epg,
Layout,
ChannelBox,
ChannelLogo,
Channel,
ChannelItem,
} from '@nessprim/planby-pro';
const CustomChannelItem = ({ isVerticalMode, channel }: ChannelItem) => {
const { position, logo } = channel;
return (
<ChannelBox isVerticalMode={isVerticalMode} {...position}>
<ChannelLogo
onClick={() => console.log('channel', channel)}
src={logo}
alt="Logo"
/>
</ChannelBox>
);
};
```
## renderTimeline
Below is an example that allows you to render your custom Timeline component using Planby's style components.
```tsx
import {
CurrentTime,
Timeline,
TimelineWrapper,
TimelineBox,
TimelineTime,
TimelineDivider,
TimelineDividers,
useTimeline,
} from '@nessprim/planby-pro';
export function CustomTimeline(props: Timeline) {
const { time, dividers,timelineHeight, getTime, getTimelineProps, getCurrentTimeProps } =
useTimeline(props);
const {
isToday,
isBaseTimeFormat,
isCurrentTime,
isTimelineVisible,
isVerticalMode,
} = props;
const { hourWidth } = props;
const renderTime = (item: string | number, index: number) => {
const { isNewDay, time } = getTime(item);
const position = { left: hourWidth * index, width: hourWidth };
const isVisible = isTimelineVisible(position);
if (!isVisible) return null;
return (
<TimelineBox
key={index}
isToday={isToday}
isCurrentTime={isCurrentTime}
timelineHeight={timelineHeight}
{...position}
>
<TimelineTime
isNewDay={isNewDay}
isBaseTimeFormat={isBaseTimeFormat}
>
{time}
</TimelineTime>
<TimelineDividers>{renderDividers(isNewDay)}</TimelineDividers>
</TimelineBox>
);
};
const renderDividers = (isNewDay: boolean) =>
dividers.map((_, index) => (
<TimelineDivider key={index} isNewDay={isNewDay} width={hourWidth} />
));
return (
<TimelineWrapper {...getTimelineProps()}>
{isToday && isCurrentTime && <CurrentTime {...getCurrentTimeProps()} />}
{time.map((item, index) => renderTime(item, index))}
</TimelineWrapper>
);
}
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
renderTimeline={(props) => <CustomTimeline {...props} />}
/>
</Epg>
</div>
</div>
);
}
export default App;
```
## renderTimeline - Different hours in multiple days
```tsx
import {
CurrentTime,
Timeline,
TimelineWrapper,
TimelineBox,
TimelineTime,
TimelineDivider,
TimelineDividers,
useTimeline,
} from '@nessprim/planby-pro';
export function CustomTimeline(props: Timeline) {
const { isTodayInHoursInDays, areHoursInDays, time, ...rest } =
useTimeline(props);
const { timelineHeight, } = rest;
const {
getTime, getTimelineProps, getCurrentTimeProps
} = rest;
const {
isToday,
isBaseTimeFormat,
isCurrentTime,
isTimelineVisible,
isVerticalMode,
} = props;
const { hourWidth } = props;
const renderTime = (item: string | number, index: number) => {
const { isNewDay, time } = getTime(item);
const position = { left: hourWidth * index, width: hourWidth };
const isVisible = isTimelineVisible(position);
if (!isVisible) return null;
return (
<TimelineBox
key={index}
isToday={isToday}
isCurrentTime={isCurrentTime}
timelineHeight={timelineHeight}
isTodayInHoursInDays={isTodayInHoursInDays}
areHoursInDays={areHoursInDays}
{...position}
>
<TimelineTime
isNewDay={isNewDay}
isBaseTimeFormat={isBaseTimeFormat}
>
{time}
</TimelineTime>
<TimelineDividers>{renderDividers(isNewDay)}</TimelineDividers>
</TimelineBox>
);
};
...
```
## renderTimeline - RTL direction
Below is an example that allows you to render your custom Timeline component using Planby's style components.
```tsx
import {
CurrentTime,
Timeline,
TimelineWrapper,
TimelineBox,
TimelineTime,
TimelineDivider,
TimelineDividers,
useTimeline,
} from '@nessprim/planby-pro';
export function CustomTimeline(props: Timeline) {
const { time, dividers, getTime, getTimelineProps, getCurrentTimeProps } =
useTimeline(props);
const {
isToday,
isBaseTimeFormat,
isCurrentTime,
isRTL,
isTimelineVisible,
isVerticalMode,
} = props;
const { hourWidth } = props;
const renderTime = (item: string | number, index: number) => {
const { isNewDay, time } = getTime(item);
const position = { left: hourWidth * index, width: hourWidth };
const isVisible = isTimelineVisible(position);
if (!isVisible) return null;
return (
<TimelineBox key={index}
isToday={isToday}
isCurrentTime={isCurrentTime}
timelineHeight={timelineHeight}
{...position}>
<TimelineTime
isNewDay={isNewDay}
isBaseTimeFormat={isBaseTimeFormat}
isRTL={isRTL}
>
{time}
</TimelineTime>
<TimelineDividers>{renderDividers(isNewDay)}</TimelineDividers>
</TimelineBox>
);
};
...
}
```
## renderTimeline - Week or Month mode
Below is an example that allows you to render your custom Timeline component using Planby's style components.
```tsx
import {
CurrentTime,
Timeline,
TimelineWrapper,
TimelineBox,
TimelineWeekMonthBox,
TimelineWeekMonthDate,
TimelineTime,
TimelineDivider,
TimelineDividers,
useTimeline,
} from '@nessprim/planby-pro';
export function CustomTimeline(props: Timeline) {
const { isWeekMonthMode, isMonthMode, time, ...rest } = useTimeline(props);
const { timelineHeight, weekDayWidth, monthsWidth, dividers } = rest;
const {
formatWeekMonthDate,
getTime,
getDayMonthName,
getTimelineProps,
getCurrentTimeProps,
} = rest;
const {
isToday,
isBaseTimeFormat,
isCurrentTime,
isRTL,
isTimelineVisible,
isVerticalMode,
} = props;
const { mode } = props;
const { hourWidth } = props;
const renderWeekMonth = (item: string, index: number) => {
const width = isMonthMode ? monthsWidth[index].width : weekDayWidth;
const left = isMonthMode ? monthsWidth[index].left : width * index;
const position = {
left,
width,
};
const isVisible = isTimelineVisible(position);
if (!isVisible) return null;
const isModernStyle = mode.style === "modern";
return (
<TimelineWeekMonthBox
className="planby-timeline-box"
data-testid="timeline-item"
key={index}
isToday={isToday}
isWeekMonthMode={isWeekMonthMode}
isCurrentTime={isCurrentTime}
isVerticalMode={isVerticalMode}
timelineHeight={timelineHeight}
styleType={mode.style}
{...position}
>
<TimelineWeekMonthDate
className="planby-timeline-week-month-date"
isRTL={isRTL}
isVerticalMode={isVerticalMode}
styleType={mode.style}
>
{isModernStyle && <span>{getDayMonthName(item)}</span>}
<span>{formatWeekMonthDate(item)}</span>
</TimelineWeekMonthDate>
</TimelineWeekMonthBox>
);
};
return (
<TimelineWrapper
className="planby-timeline-wrapper"
data-testid="timeline"
{...getTimelineProps()}
>
{isCurrentTime && isToday && <CurrentTime {...getCurrentTimeProps()} />}
{time.map((item, index) => renderWeekMonth(item as string, index))}
</TimelineWrapper>
);
...
}
```
## renderTimeline - Vertical Mode
Below is an example that allows you to render your custom Timeline component using Planby's style components with Vertical Mode.
```tsx
import {
CurrentTime,
Timeline,
TimelineWrapper,
TimelineBox,
TimelineTime,
TimelineDivider,
TimelineDividers,
useTimeline,
} from '@nessprim/planby-pro';
export function Timeline(props: Timeline) {
const { time, dividers, getTime, getTimelineProps, getCurrentTimeProps } =
useTimeline(props);
const {
isToday,
isBaseTimeFormat,
isCurrentTime,
isRTL,
isTimelineVisible,
isVerticalMode,
} = props;
const { hourWidth } = props;
const renderTime = (item: string | number, index: number) => {
const { isNewDay, time } = getTime(item);
const position = { left: hourWidth * index, width: hourWidth };
const isVisible = isTimelineVisible(position);
if (!isVisible) return null;
return (
<TimelineBox key={index}
isToday={isToday}
isCurrentTime={isCurrentTime}
isVerticalMode={isVerticalMode}
{...position}>
<TimelineTime
isNewDay={isNewDay}
isBaseTimeFormat={isBaseTimeFormat}
isVerticalMode={isVerticalMode}
>
{time}
</TimelineTime>
<TimelineDividers isVerticalMode={isVerticalMode}>{renderDividers(isNewDay)}</TimelineDividers>
</TimelineBox>
);
};
const renderDividers = (isNewDay: boolean) =>
dividers.map((_, index) => (
<TimelineDivider
key={index}
isNewDay={isNewDay}
isVerticalMode={isVerticalMode}
width={hourWidth}
/>
));
...
}
```
## renderLine
Below is an example that allows you to render your custom Line component using Planby's style components.
Available props in Line
| Property | Type | Description | Status |
| ---------------- | --------- | ------------------------------- | -------- |
| `isVerticalMode` | `boolean` | Vertical mode | optional |
| `isTimeline` | `boolean` | Vertical mode | optional |
| `styles` | `object` | Object contains position styles | optional |
```tsx
import { Line } from '@nessprim/planby-pro';
export function CustomLine({isVerticalMode, styles }: Line) {
return (
<div
style={{
...styles.position,
background: "red",
pointerEvents: "none",
}}
/>
);
}
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
renderLine={(props) => <CustomLine {...props} />}
/>
</Epg>
</div>
</div>
);
}
export default App;
```
## renderCurrentTime
Below is an example that allows you to render your custom CurrentTime component using Planby's style components.
```tsx
import {
CurrentTimeIndicator,
CurrentTimeBox,
CurrentTimeContent,
} from '@nessprim/planby-pro';
export function CustomCurrentTime(props: CurrentTimeIndicator) {
const { isVerticalMode, isRTL, isBaseTimeFormat } = props;
const { time, styles } = props;
return (
<CurrentTimeBox {...styles.position}>
<CurrentTimeContent
isVerticalMode={isVerticalMode}
isBaseTimeFormat={isBaseTimeFormat}
isRTL={isRTL}
>
{time}
</CurrentTimeContent>
</CurrentTimeBox>
);
}
function App() {
...
const {
getEpgProps,
getLayoutProps,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
renderCurrentTime={(props) => <CustomCurrentTime {...props} />}
/>
</Epg>
</div>
</div>
);
}
export default App;
```
## Theme
### Schema
Make your theme custom. Below is theme schema that you can pass as one of the options to `useEpg` hook.
```jsx
const theme = {
primary: {
600: '#1a202c',
900: '#171923',
},
grey: { 300: '#d1d1d1' },
white: '#fff',
teal: {
// Planby PRO version
100: '#38B2AC',
},
green: {
200: '#389493',
300: '#2C7A7B',
},
loader: {
teal: '#5DDADB',
purple: '#3437A2',
pink: '#F78EB6',
bg: '#171923db',
},
scrollbar: {
border: '#ffffff',
thumb: {
bg: '#e1e1e1',
},
},
gradient: {
blue: {
300: '#002eb3',
600: '#002360',
900: '#051937',
},
},
text: {
grey: {
300: '#a0aec0',
500: '#718096',
},
},
timeline: {
divider: {
bg: '#718096',
},
},
grid: {
// Planby PRO version
item: '#7180961a',
divider: '#7180961a',
highlight: '#38B2AC',
},
};
```
## All import options
```tsx
import {
Epg,
Layout,
ChannelBox,
ChannelLogo,
CurrentTimeBox,
CurrentTimeContent,
ProgramBox,
ProgramContent,
ProgramFlex,
ProgramStack,
ProgramTitle,
ProgramText,
ProgramImage,
TimelineWrapper,
TimelineBox,
TimelineWeekMonthBox,
TimelineWeekMonthDate,
TimelineTime,
TimelineDividers,
useEpg,
useProgram,
useTimeline,
Line, // Interface
Channel, // Interface
CurrentTimeIndicator, // Interface
Program, // Interface
ProgramItem, // Interface for program render
Timeline, // Interface for timeline render
Theme, // Interface
} from '@nessprim/planby-pro';
```
## License
Custom License - All Rights Reserved. [See `LICENSE` for more information](https://planby.app/docs/planby-license.pdf).
## Contact
Karol Kozer - [@kozerkarol_twitter](https://twitter.com/kozerkarol)
Project Link: [https://github.com/karolkozer/planby](https://github.com/karolkozer/planby)