@ldelia/react-media 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.bitmap ADDED
@@ -0,0 +1,48 @@
1
+ /* THIS IS A BIT-AUTO-GENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. */
2
+
3
+ {
4
+ "ldelia.react-media/timeline@1.0.8": {
5
+ "files": [
6
+ {
7
+ "relativePath": "src/components/timeline/README.md",
8
+ "test": false,
9
+ "name": "README.md"
10
+ },
11
+ {
12
+ "relativePath": "src/components/timeline/RangeSelectorCanvas.tsx",
13
+ "test": false,
14
+ "name": "RangeSelectorCanvas.tsx"
15
+ },
16
+ {
17
+ "relativePath": "src/components/timeline/TickTime.tsx",
18
+ "test": false,
19
+ "name": "TickTime.tsx"
20
+ },
21
+ {
22
+ "relativePath": "src/components/timeline/TickTimeCollectionDisplay.tsx",
23
+ "test": false,
24
+ "name": "TickTimeCollectionDisplay.tsx"
25
+ },
26
+ {
27
+ "relativePath": "src/components/timeline/VaLueLineCanvas.tsx",
28
+ "test": false,
29
+ "name": "VaLueLineCanvas.tsx"
30
+ },
31
+ {
32
+ "relativePath": "src/components/timeline/index.tsx",
33
+ "test": false,
34
+ "name": "index.tsx"
35
+ },
36
+ {
37
+ "relativePath": "src/components/timeline/utils/utils.ts",
38
+ "test": false,
39
+ "name": "utils.ts"
40
+ }
41
+ ],
42
+ "mainFile": "src/components/timeline/index.tsx",
43
+ "trackDir": "src/components/timeline",
44
+ "origin": "AUTHORED",
45
+ "exported": true
46
+ },
47
+ "version": "14.8.8"
48
+ }
package/.eslintcache ADDED
@@ -0,0 +1 @@
1
+ [{"/home/ldelia/Proyectos/react-media/src/components/timeline/index.tsx":"1","/home/ldelia/Proyectos/react-media/src/components/timeline/VaLueLineCanvas.tsx":"2","/home/ldelia/Proyectos/react-media/src/components/timeline/TickTimeCollectionDisplay.tsx":"3","/home/ldelia/Proyectos/react-media/src/components/timeline/RangeSelectorCanvas.tsx":"4","/home/ldelia/Proyectos/react-media/src/components/timeline/utils/utils.ts":"5","/home/ldelia/Proyectos/react-media/src/components/timeline/TickTime.tsx":"6","/home/ldelia/Proyectos/react-media/src/stories/timeline.stories.tsx":"7","/home/ldelia/Proyectos/react-media/src/components/timeline/constants.ts":"8"},{"size":4115,"mtime":1612815036821,"results":"9","hashOfConfig":"10"},{"size":3108,"mtime":1611772905837,"results":"11","hashOfConfig":"10"},{"size":1159,"mtime":1611772905837,"results":"12","hashOfConfig":"10"},{"size":6266,"mtime":1611772905837,"results":"13","hashOfConfig":"10"},{"size":404,"mtime":1611772905841,"results":"14","hashOfConfig":"10"},{"size":988,"mtime":1611772905837,"results":"15","hashOfConfig":"10"},{"size":860,"mtime":1611772905841,"results":"16","hashOfConfig":"10"},{"size":319,"mtime":1612814974971,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"v8ab83",{"filePath":"20","messages":"21","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"22","messages":"23","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"24","messages":"25","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"26","messages":"27","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"28","messages":"29","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"30","messages":"31","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"32","messages":"33","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/home/ldelia/Proyectos/react-media/src/components/timeline/index.tsx",[],"/home/ldelia/Proyectos/react-media/src/components/timeline/VaLueLineCanvas.tsx",[],"/home/ldelia/Proyectos/react-media/src/components/timeline/TickTimeCollectionDisplay.tsx",[],"/home/ldelia/Proyectos/react-media/src/components/timeline/RangeSelectorCanvas.tsx",[],"/home/ldelia/Proyectos/react-media/src/components/timeline/utils/utils.ts",[],"/home/ldelia/Proyectos/react-media/src/components/timeline/TickTime.tsx",[],"/home/ldelia/Proyectos/react-media/src/stories/timeline.stories.tsx",[],"/home/ldelia/Proyectos/react-media/src/components/timeline/constants.ts",[]]
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ build
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "singleQuote": true,
3
+ "semi": true,
4
+ "trailingComma": "all",
5
+ "printWidth": 80,
6
+ "tabWidth": 2
7
+ }
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ "stories": [
3
+ "../src/**/*.stories.mdx",
4
+ "../src/**/*.stories.@(js|jsx|ts|tsx)"
5
+ ],
6
+ "addons": [
7
+ "@storybook/addon-links",
8
+ "@storybook/addon-essentials",
9
+ "@storybook/preset-create-react-app"
10
+ ],
11
+ "framework": {
12
+ name: "@storybook/react-webpack5",
13
+ options: {},
14
+ },
15
+ }
@@ -0,0 +1,18 @@
1
+ import type { StorybookConfig } from "@storybook/react-webpack5";
2
+
3
+ const config: StorybookConfig = {
4
+ stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
5
+ addons: [
6
+ "@storybook/preset-create-react-app",
7
+ "@storybook/addon-onboarding",
8
+ "@storybook/addon-links",
9
+ "@storybook/addon-essentials",
10
+ "@chromatic-com/storybook",
11
+ "@storybook/addon-interactions",
12
+ ],
13
+ framework: {
14
+ name: "@storybook/react-webpack5",
15
+ options: {},
16
+ },
17
+ };
18
+ export default config;
@@ -0,0 +1,4 @@
1
+
2
+ export const parameters = {
3
+ actions: { argTypesRegex: "^on[A-Z].*" },
4
+ }
@@ -0,0 +1,14 @@
1
+ import type { Preview } from "@storybook/react";
2
+
3
+ const preview: Preview = {
4
+ parameters: {
5
+ controls: {
6
+ matchers: {
7
+ color: /(background|color)$/i,
8
+ date: /Date$/i,
9
+ },
10
+ },
11
+ },
12
+ };
13
+
14
+ export default preview;
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # React Media
2
+
3
+ A React components collection for media-related features.
4
+
5
+ ## Available Components
6
+
7
+ - Timeline ([Docs](./src/components/timeline/README.md))
8
+
9
+ ## In Progress
10
+
11
+ - Audio/Video player
12
+
13
+ ## Storybook
14
+
15
+ Launch the Storybook playground:
16
+
17
+ ##### `npm run storybook`
18
+
19
+ Make sure storybook is already installed by running npx storybook@latest init
20
+
21
+ ## Test suite collection
22
+
23
+ Launch the test suite collection:
24
+
25
+ ##### `npm test`
26
+
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@ldelia/react-media",
3
+ "version": "0.2.0",
4
+ "description": "A React components collection for media-related features.",
5
+ "private": false,
6
+ "keywords": [
7
+ "timeline",
8
+ "react"
9
+ ],
10
+ "author": "Lisandro Delia",
11
+ "license": "MIT",
12
+ "bugs": "https://github.com/ldelia/react-media/issues",
13
+ "dependencies": {
14
+ "react": "^18.3.1",
15
+ "react-dom": "^18.3.1",
16
+ "react-scripts": "^5.0.1",
17
+ "styled-components": "^6.1.11",
18
+ "typescript": "^4.9.5",
19
+ "web-vitals": "^0.2.4"
20
+ },
21
+ "scripts": {
22
+ "start": "react-scripts start",
23
+ "build": "react-scripts build",
24
+ "test": "react-scripts test",
25
+ "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,scss,md}\"",
26
+ "eject": "react-scripts eject",
27
+ "storybook": "storybook dev -p 6006",
28
+ "build-storybook": "storybook build"
29
+ },
30
+ "eslintConfig": {
31
+ "extends": [
32
+ "react-app",
33
+ "react-app/jest",
34
+ "plugin:storybook/recommended",
35
+ "prettier"
36
+ ],
37
+ "plugins": ["prettier"],
38
+ "rules": {
39
+ "prettier/prettier": "error"
40
+ }
41
+ },
42
+ "browserslist": {
43
+ "production": [
44
+ ">0.2%",
45
+ "not dead",
46
+ "not op_mini all"
47
+ ],
48
+ "development": [
49
+ "last 1 chrome version",
50
+ "last 1 firefox version",
51
+ "last 1 safari version"
52
+ ]
53
+ },
54
+ "devDependencies": {
55
+ "@chromatic-com/storybook": "^1.5.0",
56
+ "@storybook/addon-actions": "^8.1.10",
57
+ "@storybook/addon-essentials": "^8.1.10",
58
+ "@storybook/addon-interactions": "^8.1.10",
59
+ "@storybook/addon-knobs": "^8.0.1",
60
+ "@storybook/addon-links": "^8.1.10",
61
+ "@storybook/addon-onboarding": "^8.1.10",
62
+ "@storybook/blocks": "^8.1.10",
63
+ "@storybook/node-logger": "^8.1.10",
64
+ "@storybook/preset-create-react-app": "^8.1.10",
65
+ "@storybook/react": "^8.1.10",
66
+ "@storybook/react-webpack5": "^8.1.10",
67
+ "@storybook/test": "^8.1.10",
68
+ "@testing-library/jest-dom": "^6.4.6",
69
+ "@testing-library/react": "^16.0.0",
70
+ "@testing-library/user-event": "^14.5.2",
71
+ "@types/jest": "^29.5.12",
72
+ "@types/node": "^12.19.9",
73
+ "@types/react": "^18.3.3",
74
+ "@types/react-dom": "^18.3.0",
75
+ "@types/styled-components": "^5.1.34",
76
+ "eslint-config-prettier": "^9.1.0",
77
+ "eslint-plugin-prettier": "^5.1.3",
78
+ "eslint-plugin-storybook": "^0.8.0",
79
+ "postcss-flexbugs-fixes": "^5.0.2",
80
+ "postcss-normalize": "^10.0.1",
81
+ "postcss-preset-env": "^9.5.14",
82
+ "prettier": "^3.3.2",
83
+ "prop-types": "^15.8.1",
84
+ "storybook": "^8.1.10",
85
+ "webpack": "^5.92.0"
86
+ }
87
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import Timeline, { TimelineProps } from '../../timeline';
4
+
5
+ describe('Timeline', () => {
6
+ let props: TimelineProps;
7
+
8
+ beforeEach(() => {
9
+ props = {
10
+ duration: 300,
11
+ value: 15,
12
+ onChange: jest.fn(),
13
+ onRangeChange: jest.fn(),
14
+ zoomLevel: 0,
15
+ };
16
+ });
17
+
18
+ describe('render()', () => {
19
+ it('renders a timeline', () => {
20
+ const rootElement = document.createElement('div');
21
+
22
+ const root = createRoot(rootElement);
23
+ root.render(<Timeline {...props} />);
24
+ });
25
+ });
26
+ });
@@ -0,0 +1 @@
1
+ export * from './timeline';
@@ -0,0 +1,111 @@
1
+ # Timeline
2
+
3
+ <!-- STORY -->
4
+
5
+ <hr>
6
+
7
+ A component for displaying the duration of a media file (mp3, mp4, etc.) and the current reproduction position. It also allows selecting a range for reproduction looping purposes.
8
+
9
+ ## Installation
10
+
11
+ ### NPM
12
+
13
+ ```
14
+ npm i @bit/ldelia.react-media.timeline
15
+ ```
16
+
17
+ ### YARN
18
+
19
+ ```
20
+ yarn add @bit/ldelia.react-media.timeline
21
+ ```
22
+
23
+ ### BIT
24
+
25
+ ```
26
+ bit import ldelia.react-media/timeline
27
+ ```
28
+
29
+ ## Import
30
+
31
+ ```js
32
+ import Timeline from '@bit/ldelia.react-media.timeline';
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```jsx
38
+ <Timeline
39
+ duration={301}
40
+ value={15}
41
+ onChange={() => 'Do something'}
42
+ onRangeChange={() => 'Do something'}
43
+ />
44
+ ```
45
+
46
+ ##### Required props
47
+
48
+ | Name | Type | Description |
49
+ | ---------- | -------- | --------------------------------- |
50
+ | `duration` | `number` | The media file duration |
51
+ | `value` | `number` | The current reproduction position |
52
+
53
+ ##### Optional props
54
+
55
+ | Name | Type | Default | Description |
56
+ | --------------- | ---------- | ---------- | -------------------------------------------------- |
57
+ | `onChange` | `function` | `() => {}` | Fired when the user double-clicks on the timeline |
58
+ | `onRangeChange` | `function` | `() => {}` | Fired when the user select a range in the timeline |
59
+ | `selectedRange` | `array` | `[]` | The current selected range |
60
+ | `zoomLevel` | `number` | `0` | `The current timeline zoom level` |
61
+
62
+ ## Customization
63
+
64
+ #### Timeline widget
65
+
66
+ ```
67
+ const StyledTimeline = styled(Timeline)`
68
+ background-color: green;
69
+ `;
70
+ ```
71
+
72
+ #### Value bar
73
+
74
+ ```
75
+ const StyledTimeline = styled(Timeline)`
76
+ .media-timeline-value-line {
77
+ background-color: red;
78
+ width: 5px;
79
+ }
80
+ `;
81
+ ```
82
+
83
+ #### Tick-time labels
84
+
85
+ ```
86
+ const StyledTimeline = styled(Timeline)`
87
+ .media-timeline-tick-time {
88
+ color: red;
89
+ }
90
+ `;
91
+ ```
92
+
93
+ #### Tick-time/Value bar container
94
+
95
+ ```
96
+ const StyledTimeline = styled(Timeline)`
97
+ .media-timeline-value-line-canvas {
98
+ color: red;
99
+ }
100
+ `;
101
+ ```
102
+
103
+ #### Range selector
104
+
105
+ ```
106
+ const StyledTimeline = styled(Timeline)`
107
+ .media-timeline-range-selector-canvas {
108
+ color: red;
109
+ }
110
+ `;
111
+ ```
@@ -0,0 +1,168 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { ZoomContext, ZoomContextType } from './index';
5
+ import { useContext, useEffect, useMemo, useRef } from 'react';
6
+ import { pixelToSeconds, secondsToPixel } from './utils/utils';
7
+
8
+ export interface RangeSelectorCanvasProps {
9
+ selectedRange: number[];
10
+ onChange: (value: number) => void;
11
+ onRangeChange: (value: number[]) => void;
12
+ }
13
+
14
+ const OverlayCanvas = styled.canvas`
15
+ position: absolute;
16
+ top: 0;
17
+ left: 0;
18
+ width: 100%;
19
+ height: 100%;
20
+ color: cadetblue;
21
+ `;
22
+
23
+ const RangeSelectorCanvas: React.FC<RangeSelectorCanvasProps> = ({
24
+ selectedRange,
25
+ onChange,
26
+ onRangeChange,
27
+ }) => {
28
+ const canvasRef = useRef(null);
29
+ const zoomContextValue: ZoomContextType = useContext(ZoomContext);
30
+
31
+ let isSelectingRange: boolean = false;
32
+ let selectedRangeInPixels: number[] = useMemo(() => [], []);
33
+ if (selectedRange.length === 2) {
34
+ selectedRangeInPixels = [
35
+ secondsToPixel(zoomContextValue, selectedRange[0]),
36
+ secondsToPixel(zoomContextValue, selectedRange[1]),
37
+ ];
38
+ }
39
+ let lastValidSelectedRangeInPixels: number[] = [];
40
+
41
+ const getMousePointerPixelPosition = (e: { clientX: number }) => {
42
+ const canvas: HTMLCanvasElement = canvasRef.current!;
43
+ let rect = canvas.getBoundingClientRect();
44
+ return e.clientX - rect.left;
45
+ };
46
+
47
+ const drawRect = (pixelX0: number, pixelX1: number) => {
48
+ const canvas: HTMLCanvasElement = canvasRef.current!;
49
+ const context: CanvasRenderingContext2D = canvas.getContext('2d')!;
50
+
51
+ context.globalAlpha = 0.3;
52
+ context.fillStyle = window
53
+ .getComputedStyle(canvas)
54
+ .getPropertyValue('color');
55
+ context.fillRect(pixelX0, 0, pixelX1 - pixelX0, context.canvas.height);
56
+ context.globalAlpha = 1.0;
57
+ };
58
+
59
+ const onCanvasDoubleClick = (e: { clientX: number }) => {
60
+ const x0 = getMousePointerPixelPosition(e);
61
+ onChange(pixelToSeconds(zoomContextValue, x0));
62
+ selectedRangeInPixels = lastValidSelectedRangeInPixels;
63
+ };
64
+
65
+ const isPixelNearSelectedRange = (x0: number) => {
66
+ if (selectedRangeInPixels.length === 2) {
67
+ const diff = Math.min(
68
+ Math.abs(selectedRangeInPixels[0] - x0),
69
+ Math.abs(selectedRangeInPixels[1] - x0),
70
+ );
71
+ return diff <= zoomContextValue.pixelsInSecond / 2;
72
+ }
73
+ return false;
74
+ };
75
+
76
+ const onCanvasMouseDown = (e: { clientX: number }) => {
77
+ const mouseCurrentPosition = getMousePointerPixelPosition(e);
78
+
79
+ if (!isPixelNearSelectedRange(mouseCurrentPosition)) {
80
+ // Keep track of the first position of the new range.
81
+ selectedRangeInPixels = [mouseCurrentPosition, mouseCurrentPosition];
82
+ }
83
+ isSelectingRange = true;
84
+ };
85
+
86
+ const onCanvasMouseMove = (e: { clientX: number }) => {
87
+ const canvas: HTMLCanvasElement = canvasRef.current!;
88
+ const context = canvas.getContext('2d')!;
89
+ const mouseCurrentPosition = getMousePointerPixelPosition(e);
90
+
91
+ if (selectedRangeInPixels.length === 2) {
92
+ const diff = Math.min(
93
+ Math.abs(selectedRangeInPixels[0] - mouseCurrentPosition),
94
+ Math.abs(selectedRangeInPixels[1] - mouseCurrentPosition),
95
+ );
96
+ // Change the mouse's cursor if it's near a selected range.
97
+ canvas.style.cursor =
98
+ diff <= zoomContextValue.pixelsInSecond / 2 ? 'col-resize' : 'default';
99
+
100
+ if (!isSelectingRange) return;
101
+
102
+ if (mouseCurrentPosition < selectedRangeInPixels[0]) {
103
+ // The left side must be enlarged.
104
+ selectedRangeInPixels[0] = mouseCurrentPosition;
105
+ } else if (mouseCurrentPosition > selectedRangeInPixels[1]) {
106
+ // The right side must be enlarged.
107
+ selectedRangeInPixels[1] = mouseCurrentPosition;
108
+ } else {
109
+ const diffX0 = mouseCurrentPosition - selectedRangeInPixels[0];
110
+ const diffX1 = selectedRangeInPixels[1] - mouseCurrentPosition;
111
+ if (diffX0 < diffX1) {
112
+ // The left side must be shrunk.
113
+ selectedRangeInPixels[0] = mouseCurrentPosition;
114
+ } else {
115
+ // The right side must be shrunk.
116
+ selectedRangeInPixels[1] = mouseCurrentPosition;
117
+ }
118
+ }
119
+ context.clearRect(0, 0, canvas.width, canvas.height);
120
+ drawRect(selectedRangeInPixels[0], selectedRangeInPixels[1]);
121
+ }
122
+ };
123
+
124
+ const onCanvasMouseUp = (e: { clientX: number }) => {
125
+ if (selectedRangeInPixels.length !== 2) return;
126
+
127
+ isSelectingRange = false;
128
+ const mouseCurrentPosition = getMousePointerPixelPosition(e);
129
+ const pixelRange = [
130
+ Math.min(selectedRangeInPixels[0], mouseCurrentPosition),
131
+ Math.max(selectedRangeInPixels[1], mouseCurrentPosition),
132
+ ];
133
+
134
+ if (pixelRange[1] - pixelRange[0] <= 1) {
135
+ // It was just a click
136
+ selectedRangeInPixels = lastValidSelectedRangeInPixels;
137
+ } else {
138
+ lastValidSelectedRangeInPixels = pixelRange;
139
+ onRangeChange([
140
+ pixelToSeconds(zoomContextValue, pixelRange[0]),
141
+ pixelToSeconds(zoomContextValue, pixelRange[1]),
142
+ ]);
143
+ }
144
+ };
145
+
146
+ useEffect(() => {
147
+ const canvas: HTMLCanvasElement = canvasRef.current!;
148
+
149
+ // https://stackoverflow.com/questions/8696631/canvas-drawings-like-lines-are-blurry
150
+ canvas.width = canvas.offsetWidth;
151
+ canvas.height = canvas.offsetHeight;
152
+
153
+ if (selectedRangeInPixels.length === 0) return;
154
+ drawRect(selectedRangeInPixels[0], selectedRangeInPixels[1]);
155
+ }, [selectedRangeInPixels]);
156
+
157
+ return (
158
+ <OverlayCanvas
159
+ ref={canvasRef}
160
+ onDoubleClick={onCanvasDoubleClick}
161
+ onMouseDown={onCanvasMouseDown}
162
+ onMouseMove={onCanvasMouseMove}
163
+ onMouseUp={onCanvasMouseUp}
164
+ className={'media-timeline-range-selector-canvas'}
165
+ />
166
+ );
167
+ };
168
+ export default React.memo(RangeSelectorCanvas);
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ export type Props = {
5
+ start: number;
6
+ leftPosition: string;
7
+ };
8
+
9
+ interface TickTimeContainerProps {
10
+ left: string;
11
+ }
12
+
13
+ const TickTimeContainer = styled.span.attrs<TickTimeContainerProps>(
14
+ (props) => ({
15
+ style: {
16
+ left: props.left,
17
+ },
18
+ }),
19
+ )<TickTimeContainerProps>`
20
+ background: transparent;
21
+ position: absolute;
22
+ padding-left: 8px;
23
+ color: #646464;
24
+ user-select: none;
25
+ min-width: 20px;
26
+ max-width: 20px;
27
+ align-self: flex-end;
28
+ `;
29
+
30
+ const TickTime: React.FC<Props> = ({ start, leftPosition }) => {
31
+ const minutes = Math.floor(start / 60);
32
+ let seconds = start - minutes * 60;
33
+ let secondsFormatted: string =
34
+ seconds < 10 ? '0' + seconds : seconds.toString();
35
+ return (
36
+ <TickTimeContainer
37
+ left={leftPosition}
38
+ className={'media-timeline-tick-time'}
39
+ >
40
+ {minutes}:{secondsFormatted}
41
+ </TickTimeContainer>
42
+ );
43
+ };
44
+
45
+ export default TickTime;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import TickTime from './TickTime';
4
+ import { ZoomContext } from './index';
5
+
6
+ export interface TickTimeCollectionDisplayProps {
7
+ tickTimes: number[];
8
+ }
9
+
10
+ const TickTimeCollectionDisplayContainer = styled.div`
11
+ position: absolute;
12
+ display: flex;
13
+ align-self: flex-end;
14
+ height: 100%;
15
+ `;
16
+
17
+ const TickTimeCollectionDisplay: React.FC<TickTimeCollectionDisplayProps> = ({
18
+ tickTimes = [],
19
+ }) => {
20
+ return (
21
+ <ZoomContext.Consumer>
22
+ {(value) => {
23
+ return (
24
+ <TickTimeCollectionDisplayContainer>
25
+ {tickTimes.map((tickTimeValue, index) => {
26
+ const leftPosition: string =
27
+ tickTimeValue * value.pixelsInSecond + 'px';
28
+ return (
29
+ <TickTime
30
+ key={index}
31
+ start={tickTimeValue}
32
+ leftPosition={leftPosition}
33
+ />
34
+ );
35
+ })}
36
+ </TickTimeCollectionDisplayContainer>
37
+ );
38
+ }}
39
+ </ZoomContext.Consumer>
40
+ );
41
+ };
42
+ export default TickTimeCollectionDisplay;