From cbf02c1591d3697ea5af8bf2108df04d01fdadde Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Thu, 20 Apr 2023 13:34:31 +0800 Subject: [PATCH 1/6] reinit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80f71cb..add9fa8 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "react-dom": ">=16.8.0" }, "dependencies": { - "clrc": "^3.0.0", + "clrc": "github:Nafeij/clrc", "resize-observer-polyfill": "^1.5.1" }, "devDependencies": { From 08e01b3c17d3fba4eb31275eee0ea504832d5223 Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Thu, 20 Apr 2023 14:29:41 +0800 Subject: [PATCH 2/6] attempt migration --- .babelrc.json | 16 +++++++++++++ .eslintrc.js | 12 +++++----- .gitignore | 1 + .storybook/main.js | 21 ++++++++++++++++- examples/lrc/index.stories.tsx | 13 ++++++----- examples/multiple_lrc/index.stories.tsx | 12 +++++----- package.json | 30 ++++++++++++++++--------- 7 files changed, 77 insertions(+), 28 deletions(-) create mode 100644 .babelrc.json diff --git a/.babelrc.json b/.babelrc.json new file mode 100644 index 0000000..00ca841 --- /dev/null +++ b/.babelrc.json @@ -0,0 +1,16 @@ +{ + "sourceType": "unambiguous", + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "chrome": 100 + } + } + ], + "@babel/preset-typescript", + "@babel/preset-react" + ], + "plugins": [] +} diff --git a/.eslintrc.js b/.eslintrc.js index c475d59..7ef2f81 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { 'airbnb', 'plugin:react-hooks/recommended', 'prettier', + 'plugin:storybook/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { @@ -34,11 +35,9 @@ module.exports = { '@typescript-eslint/no-use-before-define': 'error', 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', - 'no-void': 'off', 'consistent-return': 'off', 'no-restricted-syntax': 'off', - 'import/prefer-default-export': 'off', 'import/no-extraneous-dependencies': 'off', 'import/extensions': [ @@ -47,12 +46,15 @@ module.exports = { tsx: 'never', }, ], - - 'react/jsx-filename-extension': ['error', { extensions: ['.tsx', '.mdx'] }], + 'react/jsx-filename-extension': [ + 'error', + { + extensions: ['.tsx', '.mdx'], + }, + ], 'react/jsx-props-no-spreading': 'off', 'react/require-default-props': 'off', 'react/no-unused-prop-types': 'off', - 'jsx-a11y/no-static-element-interactions': 'off', }, }; diff --git a/.gitignore b/.gitignore index a44cd8c..106e7ce 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ _/ # storybook storybook-static/ +.env diff --git a/.storybook/main.js b/.storybook/main.js index 4ecca28..a761366 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,9 +1,28 @@ +const remarkGfm = require('remark-gfm'); + module.exports = { stories: ['../examples/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ + '@storybook/preset-create-react-app', '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', + { + name: '@storybook/addon-docs', + options: { + mdxPluginOptions: { + mdxCompileOptions: { + remarkPlugins: [remarkGfm], + }, + }, + }, + }, ], - framework: '@storybook/react', + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + docs: { + autodocs: 'tag', + }, }; diff --git a/examples/lrc/index.stories.tsx b/examples/lrc/index.stories.tsx index 1ab143f..7dae139 100644 --- a/examples/lrc/index.stories.tsx +++ b/examples/lrc/index.stories.tsx @@ -1,17 +1,18 @@ /* eslint-disable react/function-component-definition */ import React from 'react'; -import { ComponentStory } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react'; import StaticComponent from './static'; import AutoScrollComponent from './auto_scroll'; import { lrc } from '../data'; export default { title: 'Lrc', -}; +} as Meta; + +const AutoScrollTemplate: StoryFn = (args) => ( + +); -const AutoScrollTemplate: ComponentStory = ( - args, -) => ; export const AutoScroll = AutoScrollTemplate.bind({}); AutoScroll.args = { lrc, @@ -19,7 +20,7 @@ AutoScroll.args = { verticalSpace: true, }; -const StaticTemplate: ComponentStory = (args) => ( +const StaticTemplate: StoryFn = (args) => ( ); export const Static = StaticTemplate.bind({}); diff --git a/examples/multiple_lrc/index.stories.tsx b/examples/multiple_lrc/index.stories.tsx index 616a0f5..36c0a8a 100644 --- a/examples/multiple_lrc/index.stories.tsx +++ b/examples/multiple_lrc/index.stories.tsx @@ -1,17 +1,17 @@ /* eslint-disable react/function-component-definition */ import React from 'react'; -import { ComponentStory } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react'; import StaticComponent from './static'; import { originalLrc, translatedLrc } from '../data'; import AutoScrollComponent from './auto_scroll'; export default { title: 'MultipleLrc', -}; +} as Meta; -const AutoScrollTemplate: ComponentStory = ( - args, -) => ; +const AutoScrollTemplate: StoryFn = (args) => ( + +); export const AutoScroll = AutoScrollTemplate.bind({}); AutoScroll.args = { lrcs: [originalLrc, translatedLrc], @@ -19,7 +19,7 @@ AutoScroll.args = { verticalSpace: true, }; -const StaticTemplate: ComponentStory = (args) => ( +const StaticTemplate: StoryFn = (args) => ( ); export const Static = StaticTemplate.bind({}); diff --git a/package.json b/package.json index add9fa8..b576121 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "build": "rimraf build && tsc --declaration", "prepublish": "npm run build", "lint-staged": "lint-staged", - "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook" + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "author": { "name": "mebtte", @@ -37,25 +37,33 @@ "react-dom": ">=16.8.0" }, "dependencies": { + "@storybook/preset-create-react-app": "^7.0.6", + "@types/ws": "^8.5.4", "clrc": "github:Nafeij/clrc", + "react-scripts": "5.0.0", "resize-observer-polyfill": "^1.5.1" }, "devDependencies": { "@babel/core": "^7.19.0", - "@storybook/addon-actions": "^6.5.10", - "@storybook/addon-essentials": "^6.5.10", - "@storybook/addon-interactions": "^6.5.10", - "@storybook/addon-links": "^6.5.10", - "@storybook/builder-webpack4": "^6.5.10", - "@storybook/manager-webpack4": "^6.5.10", - "@storybook/react": "^6.5.10", - "@storybook/testing-library": "^0.0.13", + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.4", + "@storybook/addon-actions": "^7.0.6", + "@storybook/addon-essentials": "^7.0.6", + "@storybook/addon-interactions": "^7.0.6", + "@storybook/addon-links": "^7.0.6", + "@storybook/addon-mdx-gfm": "^7.0.6", + "@storybook/builder-webpack5": "^7.0.6", + "@storybook/react": "^7.0.6", + "@storybook/react-webpack5": "^7.0.6", + "@storybook/testing-library": "^0.0.14-next.2", "@types/react": "^16.14.31", "@types/react-dom": "^16.9.16", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.36.2", "@typescript-eslint/parser": "^5.36.2", "babel-loader": "^8.2.5", + "dotenv-webpack": "^8.0.1", "eslint": "^8.23.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^8.5.0", @@ -63,12 +71,14 @@ "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.31.7", "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-storybook": "^0.6.11", "husky": "^8.0.1", "lint-staged": "^13.0.3", "prettier": "^2.7.1", "react": "^16.14.0", "react-dom": "^16.14.0", "rimraf": "^3.0.2", + "storybook": "^7.0.6", "styled-components": "^5.3.5", "typescript": "^4.8.2" } From 794d194d11dde20a3ba4892275f24b93738ff3ec Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Thu, 20 Apr 2023 16:25:44 +0800 Subject: [PATCH 3/6] - migrate to storybook7 / webpack5 / node18 - rewrote playground to workaround storybook-react not allowing render of multiple components in same story --- .storybook/main.js | 25 ++++++------- .storybook/webpack.config.js | 11 ++++++ examples/lrc/index.stories.tsx | 48 ++++++++++++++----------- examples/multiple_lrc/index.stories.tsx | 45 +++++++++++++---------- examples/utils.ts | 12 +++++++ package.json | 8 +++-- 6 files changed, 93 insertions(+), 56 deletions(-) create mode 100644 .storybook/webpack.config.js diff --git a/.storybook/main.js b/.storybook/main.js index a761366..823967c 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,5 +1,3 @@ -const remarkGfm = require('remark-gfm'); - module.exports = { stories: ['../examples/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ @@ -7,22 +5,19 @@ module.exports = { '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', - { - name: '@storybook/addon-docs', - options: { - mdxPluginOptions: { - mdxCompileOptions: { - remarkPlugins: [remarkGfm], - }, - }, - }, - }, ], + typescript: { + check: false, + checkOptions: {}, + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + propFilter: (prop) => + prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, + }, + }, framework: { name: '@storybook/react-webpack5', options: {}, }, - docs: { - autodocs: 'tag', - }, }; diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js new file mode 100644 index 0000000..5a3af63 --- /dev/null +++ b/.storybook/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = ({ config, mode }) => { + config.module.rules.push({ + test: /\.(ts|tsx)$/, + loader: require.resolve('babel-loader'), + options: { + presets: [['react-app', { flow: false, typescript: true }]], + }, + }); + config.resolve.extensions.push('.ts', '.tsx'); + return config; +}; diff --git a/examples/lrc/index.stories.tsx b/examples/lrc/index.stories.tsx index 7dae139..6aaa589 100644 --- a/examples/lrc/index.stories.tsx +++ b/examples/lrc/index.stories.tsx @@ -1,29 +1,37 @@ /* eslint-disable react/function-component-definition */ -import React from 'react'; -import { Meta, StoryFn } from '@storybook/react'; -import StaticComponent from './static'; -import AutoScrollComponent from './auto_scroll'; +import { StoryObj } from '@storybook/react'; import { lrc } from '../data'; +import AutoScrollComponent from './auto_scroll'; +import StaticComponent from './static'; +import { Renderer } from '../utils'; + +type CompArgs = { + lrc: string; + recoverAutoScrollInterval?: number; + verticalSpace?: boolean; +}; export default { title: 'Lrc', -} as Meta; - -const AutoScrollTemplate: StoryFn = (args) => ( - -); + component: Renderer, +}; -export const AutoScroll = AutoScrollTemplate.bind({}); -AutoScroll.args = { - lrc, - recoverAutoScrollInterval: 5000, - verticalSpace: true, +export const AutoScroll: StoryObj> = { + args: { + compArgs: { + lrc, + recoverAutoScrollInterval: 5000, + verticalSpace: true, + }, + component: AutoScrollComponent, + }, }; -const StaticTemplate: StoryFn = (args) => ( - -); -export const Static = StaticTemplate.bind({}); -Static.args = { - lrc, +export const Static: StoryObj> = { + args: { + compArgs: { + lrc, + }, + component: StaticComponent, + }, }; diff --git a/examples/multiple_lrc/index.stories.tsx b/examples/multiple_lrc/index.stories.tsx index 36c0a8a..1fd4fdb 100644 --- a/examples/multiple_lrc/index.stories.tsx +++ b/examples/multiple_lrc/index.stories.tsx @@ -1,28 +1,37 @@ /* eslint-disable react/function-component-definition */ -import React from 'react'; -import { Meta, StoryFn } from '@storybook/react'; -import StaticComponent from './static'; +import { StoryObj } from '@storybook/react'; import { originalLrc, translatedLrc } from '../data'; import AutoScrollComponent from './auto_scroll'; +import StaticComponent from './static'; +import { Renderer } from '../utils'; + +type CompArgs = { + lrcs: string[]; + recoverAutoScrollInterval?: number; + verticalSpace?: boolean; +}; export default { title: 'MultipleLrc', -} as Meta; + component: Renderer, +}; -const AutoScrollTemplate: StoryFn = (args) => ( - -); -export const AutoScroll = AutoScrollTemplate.bind({}); -AutoScroll.args = { - lrcs: [originalLrc, translatedLrc], - recoverAutoScrollInterval: 5000, - verticalSpace: true, +export const AutoScroll: StoryObj> = { + args: { + compArgs: { + lrcs: [originalLrc, translatedLrc], + recoverAutoScrollInterval: 5000, + verticalSpace: true, + }, + component: AutoScrollComponent, + }, }; -const StaticTemplate: StoryFn = (args) => ( - -); -export const Static = StaticTemplate.bind({}); -Static.args = { - lrcs: [originalLrc, translatedLrc], +export const Static: StoryObj> = { + args: { + compArgs: { + lrcs: [originalLrc, translatedLrc], + }, + component: StaticComponent, + }, }; diff --git a/examples/utils.ts b/examples/utils.ts index 4d592f9..e4ed5df 100644 --- a/examples/utils.ts +++ b/examples/utils.ts @@ -1,3 +1,5 @@ +import { Component } from 'react'; + export function formatMillisecond(ms: number) { const minute = Math.floor(ms / 1000 / 60); const second = Math.floor(ms / 1000) % 60; @@ -6,3 +8,13 @@ export function formatMillisecond(ms: number) { .toString() .padStart(2, '0')}.${millisecond.toString().padStart(3, '0')}`; } + +export function Renderer({ + compArgs, + component, +}: { + compArgs: ArgType; + component: (args: ArgType) => Component; +}) { + return component(compArgs); +} diff --git a/package.json b/package.json index b576121..3300488 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ }, "dependencies": { "@storybook/preset-create-react-app": "^7.0.6", + "@storybook/react-webpack5": "^7.0.6", "@types/ws": "^8.5.4", "clrc": "github:Nafeij/clrc", "react-scripts": "5.0.0", @@ -49,14 +50,14 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.4", "@storybook/addon-actions": "^7.0.6", + "@storybook/addon-docs": "^7.0.6", "@storybook/addon-essentials": "^7.0.6", "@storybook/addon-interactions": "^7.0.6", "@storybook/addon-links": "^7.0.6", "@storybook/addon-mdx-gfm": "^7.0.6", - "@storybook/builder-webpack5": "^7.0.6", "@storybook/react": "^7.0.6", - "@storybook/react-webpack5": "^7.0.6", "@storybook/testing-library": "^0.0.14-next.2", + "@types/node": "^18.15.12", "@types/react": "^16.14.31", "@types/react-dom": "^16.9.16", "@types/styled-components": "^5.1.26", @@ -80,6 +81,7 @@ "rimraf": "^3.0.2", "storybook": "^7.0.6", "styled-components": "^5.3.5", - "typescript": "^4.8.2" + "typescript": "^4.8.2", + "webpack": "^5.80.0" } } From 944dadaa2b6cfe8dd3a9e55d8d50579f4d200b95 Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Thu, 20 Apr 2023 17:15:59 +0800 Subject: [PATCH 4/6] reinit --- examples/data.ts | 12 +++ examples/enhanced_lrc/auto_scroll.tsx | 94 +++++++++++++++++++ examples/enhanced_lrc/index.stories.tsx | 37 ++++++++ examples/enhanced_lrc/static.tsx | 31 ++++++ src/components/enhanced_lrc/constants.ts | 13 +++ src/components/enhanced_lrc/enhanced_lrc.tsx | 16 ++++ src/components/enhanced_lrc/index.ts | 5 + .../enhanced_lrc/use_enhanced_lrc.ts | 33 +++++++ src/index.ts | 7 ++ 9 files changed, 248 insertions(+) create mode 100644 examples/enhanced_lrc/auto_scroll.tsx create mode 100644 examples/enhanced_lrc/index.stories.tsx create mode 100644 examples/enhanced_lrc/static.tsx create mode 100644 src/components/enhanced_lrc/constants.ts create mode 100644 src/components/enhanced_lrc/enhanced_lrc.tsx create mode 100644 src/components/enhanced_lrc/index.ts create mode 100644 src/components/enhanced_lrc/use_enhanced_lrc.ts diff --git a/examples/data.ts b/examples/data.ts index a0b5d98..2320fff 100644 --- a/examples/data.ts +++ b/examples/data.ts @@ -109,3 +109,15 @@ export const translatedLrc = `[00:13.62]错过了末班电车,我们并肩站 [03:32.43]花了太长的时间,我才察觉到真正重要的是什么 [03:37.38]再不要放开相牵的手,留在我身边 [03:44.58]再次和你携手`; + +export const extraLrc = ` +[00:00.00]Little <00:00.79>jagged <00:01.35>edge<00:02.46> +[00:02.71]I'm <00:03.10>leaning <00:04.01>in <00:04.80>again<00:05.67> +[00:05.92]I <00:06.40>felt <00:06.74>it <00:07.06>when <00:07.58>you <00:08.02>said<00:08.79> +[00:09.04]That <00:09.55>I am <00:10.22>just <00:10.75>a <00:11.14>mess<00:11.85> +[00:12.10]There was <00:12.65>nothing <00:13.25>I <00:13.96>could <00:14.22>do<00:15.36> +[00:15.61]But <00:15.91>see <00:16.25>your <00:16.61>point <00:17.03>of <00:17.31>view<00:18.21> +[00:19.24]<00:19.28> +[00:19.49]And <00:19.73>that's <00:20.19>the <00:20.40>truth<00:21.29> +[00:22.88]<00:22.92> +`; diff --git a/examples/enhanced_lrc/auto_scroll.tsx b/examples/enhanced_lrc/auto_scroll.tsx new file mode 100644 index 0000000..c677910 --- /dev/null +++ b/examples/enhanced_lrc/auto_scroll.tsx @@ -0,0 +1,94 @@ +/* eslint-disable react/no-unstable-nested-components */ +import React, { CSSProperties } from 'react'; +import styled, { css } from 'styled-components'; +import { EnhancedLrc, useRecoverAutoScrollImmediately } from '../..'; +import Control from '../control'; +import useTimer from '../use_timer'; + +const Root = styled.div` + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + + display: flex; + flex-direction: column; + + > .lrc-box { + position: relative; + + flex: 1; + min-height: 0; + + &::before { + content: ''; + width: 100%; + height: 1px; + + position: absolute; + top: 50%; + left: 0; + + background: rgb(255 0 0 / 0.15); + } + } +`; +const lrcStyle: CSSProperties = { + height: '100%', + padding: '5px 0', +}; +const Line = styled.div<{ active: boolean }>` + min-height: 10px; + padding: 5px 20px; + + font-size: 16px; + text-align: center; + + ${({ active }) => css` + color: ${active ? 'green' : 'black'}; + `} +`; + +function LrcDemo({ + lrc, + recoverAutoScrollInterval, + verticalSpace, +}: { + lrc: string; + recoverAutoScrollInterval: number; + verticalSpace: boolean; +}) { + const { currentMillisecond, setCurrentMillisecond, reset, play, pause } = + useTimer(1); + const { signal, recoverAutoScrollImmediately } = + useRecoverAutoScrollImmediately(); + + return ( + + +
+ ( + {content} + )} + currentMillisecond={currentMillisecond} + verticalSpace={verticalSpace} + style={lrcStyle} + recoverAutoScrollSingal={signal} + recoverAutoScrollInterval={recoverAutoScrollInterval} + /> +
+
+ ); +} + +export default LrcDemo; diff --git a/examples/enhanced_lrc/index.stories.tsx b/examples/enhanced_lrc/index.stories.tsx new file mode 100644 index 0000000..e2bc423 --- /dev/null +++ b/examples/enhanced_lrc/index.stories.tsx @@ -0,0 +1,37 @@ +/* eslint-disable react/function-component-definition */ +import { StoryObj } from '@storybook/react'; +import { extraLrc } from '../data'; +import AutoScrollComponent from './auto_scroll'; +import StaticComponent from './static'; +import { Renderer } from '../utils'; + +type CompArgs = { + lrc: string; + recoverAutoScrollInterval?: number; + verticalSpace?: boolean; +}; + +export default { + title: 'EnhancedLrc', + component: Renderer, +}; + +export const AutoScroll: StoryObj> = { + args: { + compArgs: { + lrc: extraLrc, + recoverAutoScrollInterval: 5000, + verticalSpace: true, + }, + component: AutoScrollComponent, + }, +}; + +export const Static: StoryObj> = { + args: { + compArgs: { + lrc: extraLrc, + }, + component: StaticComponent, + }, +}; diff --git a/examples/enhanced_lrc/static.tsx b/examples/enhanced_lrc/static.tsx new file mode 100644 index 0000000..c187404 --- /dev/null +++ b/examples/enhanced_lrc/static.tsx @@ -0,0 +1,31 @@ +/* eslint-disable react/no-unstable-nested-components */ +import React from 'react'; +import styled from 'styled-components'; +import { Lrc } from '../..'; +import { formatMillisecond } from '../utils'; + +const Line = styled.div` + > .time { + color: orange; + font-family: monospace; + } +`; + +function StaticLrc({ lrc }: { lrc: string }) { + return ( + ( + + + {formatMillisecond(line.startMillisecond)} + +   + {line.content} + + )} + /> + ); +} + +export default StaticLrc; diff --git a/src/components/enhanced_lrc/constants.ts b/src/components/enhanced_lrc/constants.ts new file mode 100644 index 0000000..055983d --- /dev/null +++ b/src/components/enhanced_lrc/constants.ts @@ -0,0 +1,13 @@ +import { Syllable } from 'clrc'; +import { BaseLine, BaseProps } from '../../constants'; + +export interface Line extends BaseLine { + lineNumber: number; + raw: string; + content: string; + syllables: Syllable[]; +} + +export interface Props extends BaseProps { + lrc: string; +} diff --git a/src/components/enhanced_lrc/enhanced_lrc.tsx b/src/components/enhanced_lrc/enhanced_lrc.tsx new file mode 100644 index 0000000..88c0c2b --- /dev/null +++ b/src/components/enhanced_lrc/enhanced_lrc.tsx @@ -0,0 +1,16 @@ +import React, { ForwardedRef, forwardRef, HtmlHTMLAttributes } from 'react'; +import BaseLrc from '../base_lrc'; +import { Props, Line } from './constants'; +import useEnhLrc from './use_enhanced_lrc'; + +const Lrc = forwardRef( + ( + { lrc, ...props }: Props & HtmlHTMLAttributes, + ref: ForwardedRef, + ) => { + const lines = useEnhLrc(lrc); + return {...props} lines={lines} ref={ref} />; + }, +); + +export default Lrc; diff --git a/src/components/enhanced_lrc/index.ts b/src/components/enhanced_lrc/index.ts new file mode 100644 index 0000000..82b07b2 --- /dev/null +++ b/src/components/enhanced_lrc/index.ts @@ -0,0 +1,5 @@ +import Lrc from './enhanced_lrc'; +import { Line, Props } from './constants'; + +export { Line, Props }; +export default Lrc; diff --git a/src/components/enhanced_lrc/use_enhanced_lrc.ts b/src/components/enhanced_lrc/use_enhanced_lrc.ts new file mode 100644 index 0000000..7c6d63c --- /dev/null +++ b/src/components/enhanced_lrc/use_enhanced_lrc.ts @@ -0,0 +1,33 @@ +import { LyricExtLine as ClrcLyricExtLine, LineType, parse } from 'clrc'; +import { useMemo } from 'react'; +import getRandomString from '../../utils/get_random_string'; +import { Line } from './constants'; + +type timed = { startMillisecond: number }; + +const byTime = (a: timed, b: timed) => a.startMillisecond - b.startMillisecond; + +function useEnhLrc(lrc: string) { + const lines = useMemo( + () => + ( + parse(lrc, { enhanced: true }).filter( + (line) => line.type === LineType.LYRIC_ENH, + ) as ClrcLyricExtLine[] + ) + .map((l) => ({ + id: getRandomString(), + lineNumber: l.lineNumber, + raw: l.raw, + startMillisecond: l.startMillisecond, + content: l.content, + syllables: l.syllables.sort(byTime), + })) + .sort(byTime), + [lrc], + ); + + return lines; +} + +export default useEnhLrc; diff --git a/src/index.ts b/src/index.ts index 83fa5c6..2cf4d04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,10 @@ import MultipleLrc, { Line as MultipleLrcLine, Props as MultipleLrcProps, } from './components/multiple_lrc'; +import EnhancedLrc, { + Line as EnhancedLrcLine, + Props as EnhancedLrcProps, +} from './components/enhanced_lrc'; import useRecoverAutoScrollImmediately from './utils/use_recover_auto_scroll_immediately'; export { @@ -12,5 +16,8 @@ export { MultipleLrc, MultipleLrcLine, MultipleLrcProps, + EnhancedLrc, + EnhancedLrcLine, + EnhancedLrcProps, useRecoverAutoScrollImmediately, }; From c4e9ead706b9feb3f73815585b88a73ead8b8cab Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Thu, 20 Apr 2023 18:13:25 +0800 Subject: [PATCH 5/6] done --- examples/enhanced_lrc/auto_scroll.tsx | 26 ++++++---- examples/enhanced_lrc/static.tsx | 47 +++++++++++++++++-- examples/utils.ts | 5 ++ src/components/enhanced_lrc/constants.ts | 6 ++- src/components/enhanced_lrc/index.ts | 4 +- .../enhanced_lrc/use_enhanced_lrc.ts | 10 +++- src/index.ts | 2 + 7 files changed, 84 insertions(+), 16 deletions(-) diff --git a/examples/enhanced_lrc/auto_scroll.tsx b/examples/enhanced_lrc/auto_scroll.tsx index c677910..f8187bf 100644 --- a/examples/enhanced_lrc/auto_scroll.tsx +++ b/examples/enhanced_lrc/auto_scroll.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-unstable-nested-components */ import React, { CSSProperties } from 'react'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import { EnhancedLrc, useRecoverAutoScrollImmediately } from '../..'; import Control from '../control'; import useTimer from '../use_timer'; @@ -38,16 +38,13 @@ const lrcStyle: CSSProperties = { height: '100%', padding: '5px 0', }; -const Line = styled.div<{ active: boolean }>` + +const Line = styled.div` min-height: 10px; padding: 5px 20px; font-size: 16px; text-align: center; - - ${({ active }) => css` - color: ${active ? 'green' : 'black'}; - `} `; function LrcDemo({ @@ -77,8 +74,21 @@ function LrcDemo({
( - {content} + lineRenderer={({ active, line: { syllables } }) => ( + + {syllables.map((s) => ( + + {s.content} + + ))} + )} currentMillisecond={currentMillisecond} verticalSpace={verticalSpace} diff --git a/examples/enhanced_lrc/static.tsx b/examples/enhanced_lrc/static.tsx index c187404..9d2eb0f 100644 --- a/examples/enhanced_lrc/static.tsx +++ b/examples/enhanced_lrc/static.tsx @@ -1,19 +1,42 @@ /* eslint-disable react/no-unstable-nested-components */ import React from 'react'; import styled from 'styled-components'; -import { Lrc } from '../..'; -import { formatMillisecond } from '../utils'; +import { EnhancedLrc } from '../..'; +import { formatMillisecond, formatOffset } from '../utils'; const Line = styled.div` + height: 2em; + font-size: 30px; + > .time { color: orange; font-family: monospace; + font-size: 0.7em; + } + + > .anchor:not(under) { + position: relative; + display: inline-block; + white-space: pre; + } + + .under { + color: green; + position: absolute; + top: 2.3rem; + font-size: 0.5em; + font-family: monospace; + rotate: 45deg; + + &::before { + content: '+'; + } } `; function StaticLrc({ lrc }: { lrc: string }) { return ( - ( @@ -21,7 +44,23 @@ function StaticLrc({ lrc }: { lrc: string }) { {formatMillisecond(line.startMillisecond)}   - {line.content} + {line.syllables.map((syllable) => ( +
+ {syllable.content} + {syllable.content && + formatOffset( + syllable.startMillisecond, + line.startMillisecond, + ) && ( +
+ {formatOffset( + syllable.startMillisecond, + line.startMillisecond, + )} +
+ )} +
+ ))}
)} /> diff --git a/examples/utils.ts b/examples/utils.ts index e4ed5df..21c45bc 100644 --- a/examples/utils.ts +++ b/examples/utils.ts @@ -9,6 +9,11 @@ export function formatMillisecond(ms: number) { .padStart(2, '0')}.${millisecond.toString().padStart(3, '0')}`; } +export function formatOffset(ms: number, offset: number) { + const msOffset = ms - offset; + return msOffset ? msOffset.toPrecision(2).slice(0, 3) : ''; +} + export function Renderer({ compArgs, component, diff --git a/src/components/enhanced_lrc/constants.ts b/src/components/enhanced_lrc/constants.ts index 055983d..3345c0f 100644 --- a/src/components/enhanced_lrc/constants.ts +++ b/src/components/enhanced_lrc/constants.ts @@ -1,6 +1,10 @@ -import { Syllable } from 'clrc'; import { BaseLine, BaseProps } from '../../constants'; +export interface Syllable extends BaseLine { + sylNumber: number; + raw: string; + content: string; +} export interface Line extends BaseLine { lineNumber: number; raw: string; diff --git a/src/components/enhanced_lrc/index.ts b/src/components/enhanced_lrc/index.ts index 82b07b2..6e898a2 100644 --- a/src/components/enhanced_lrc/index.ts +++ b/src/components/enhanced_lrc/index.ts @@ -1,5 +1,5 @@ import Lrc from './enhanced_lrc'; -import { Line, Props } from './constants'; +import { Line, Syllable, Props } from './constants'; -export { Line, Props }; +export { Line, Syllable, Props }; export default Lrc; diff --git a/src/components/enhanced_lrc/use_enhanced_lrc.ts b/src/components/enhanced_lrc/use_enhanced_lrc.ts index 7c6d63c..99d885f 100644 --- a/src/components/enhanced_lrc/use_enhanced_lrc.ts +++ b/src/components/enhanced_lrc/use_enhanced_lrc.ts @@ -21,7 +21,15 @@ function useEnhLrc(lrc: string) { raw: l.raw, startMillisecond: l.startMillisecond, content: l.content, - syllables: l.syllables.sort(byTime), + syllables: l.syllables + .map((s) => ({ + id: getRandomString(), + sylNumber: s.sylNumber, + raw: s.raw, + startMillisecond: s.startMillisecond, + content: s.content, + })) + .sort(byTime), })) .sort(byTime), [lrc], diff --git a/src/index.ts b/src/index.ts index 2cf4d04..2df8398 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import MultipleLrc, { import EnhancedLrc, { Line as EnhancedLrcLine, Props as EnhancedLrcProps, + Syllable, } from './components/enhanced_lrc'; import useRecoverAutoScrollImmediately from './utils/use_recover_auto_scroll_immediately'; @@ -19,5 +20,6 @@ export { EnhancedLrc, EnhancedLrcLine, EnhancedLrcProps, + Syllable, useRecoverAutoScrollImmediately, }; From 8dd82877bbf9d08f79f877d025d8531ac76ac930 Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Thu, 20 Apr 2023 18:49:23 +0800 Subject: [PATCH 6/6] Update README.md --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fea28d9..4ca9a74 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ const Demo = () => { #### `lineRenderer`: ({ index: number, active: boolean, line: Line }) => React.ReactNode -The method to render every valid line of parsed lrc. `active` means whether it is current line. `Line` is `LrcLine` when using `Lrc` component or is `MultipleLrcLine` when `MultipleLrc`. +The method to render every valid line of parsed lrc. `active` means whether it is current line. `Line` is `LrcLine` when using `Lrc` component, is `EnhancedLrcLine` when `EnhancedLrc` or is `MultipleLrcLine` when `MultipleLrc`. #### `currentMillisecond`?: number @@ -59,7 +59,7 @@ with verticalSpace: #### `onLineUpdate`?: ({ index: number, line: Line | null }) => void -Call this when current line changed. `Line` is `LrcLine` when using `Lrc` component or is `MultipleLrcLine` when `MultipleLrc`. +Call this when current line changed. `Line` is `LrcLine` when using `Lrc` component, is `EnhancedLrcLine` when `EnhancedLrc`, or is `MultipleLrcLine` when `MultipleLrc`. #### `recoverAutoScrollInterval` @@ -71,6 +71,12 @@ The interval of recovering auto scroll after user scroll. It is `millisecond`, d The lrc. +### Component `EnhancedLrc` + +#### `lrc`: string + +The lrc. + ### Component `MultipleLrc` #### `lrcs`: string[] @@ -135,4 +141,48 @@ const Demo = () => { ## License +[MIT](./LICENSE) + + + + ); +}; +``` + +## Q & A + +### How to prevent user scroll ? + +```jsx + +``` + +### How to hide scrollbar ? + +```scss +.lrc { + /* webkit */ + &::-webkit-scrollbar { + width: 0; + height: 0; + } + + /* firefox */ + scrollbar-width: none; + + /* ie */ + -ms-overflow-style: none; +} +``` + +```jsx + +``` + +## License + [MIT](./LICENSE)