@easy-editor/materials-dashboard-audio 0.0.3 → 0.0.5

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/CHANGELOG.md CHANGED
@@ -1,13 +1,27 @@
1
- # @easy-editor/materials-dashboard-audio
2
-
3
- ## 0.0.3
4
-
5
- ### Patch Changes
6
-
7
- - fix: build error
8
-
9
- ## 0.0.2
10
-
11
- ### Patch Changes
12
-
13
- - feat: new setters
1
+ # @easy-editor/materials-dashboard-audio
2
+
3
+ ## 0.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: component export error
8
+
9
+ ## 0.0.4
10
+
11
+ ### Patch Changes
12
+
13
+ - perf: configure & datasource
14
+ - Updated dependencies
15
+ - @easy-editor/materials-shared@0.0.1
16
+
17
+ ## 0.0.3
18
+
19
+ ### Patch Changes
20
+
21
+ - fix: build error
22
+
23
+ ## 0.0.2
24
+
25
+ ### Patch Changes
26
+
27
+ - feat: new setters
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @easy-editor/easypack configuration
3
+ * @type {import('@easy-editor/easypack').EasypackConfig}
4
+ */
5
+ export default {
6
+ preset: 'material',
7
+ dev: {
8
+ port: 5001,
9
+ },
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easy-editor/materials-dashboard-audio",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Audio component for EasyEditor dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -46,22 +46,15 @@
46
46
  "react-dom": "^18 || ^19"
47
47
  },
48
48
  "dependencies": {
49
- "@easy-editor/materials-shared": "0.0.0"
50
- },
51
- "devDependencies": {
52
- "@originjs/vite-plugin-federation": "^1.4.1"
49
+ "@easy-editor/materials-shared": "0.0.1"
53
50
  },
54
51
  "scripts": {
55
- "dev": "vite",
52
+ "dev": "easypack dev",
56
53
  "format": "biome format --write .",
57
54
  "lint": "biome check .",
58
55
  "build": "npm-run-all -nl build:*",
59
56
  "build:clean": "rimraf dist/",
60
- "build:js": "rollup -c",
61
- "build:types": "pnpm types",
62
- "types": "npm-run-all -nl types:*",
63
- "types:src": "tsc --project tsconfig.build.json",
64
- "test-types": "tsc --project tsconfig.test.json"
57
+ "build:js": "easypack build"
65
58
  },
66
59
  "module": "dist/index.esm.js",
67
60
  "unpkg": "dist/index.min.js"
package/src/component.tsx CHANGED
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * Audio Component
3
- * 音频播放组件
3
+ * 音频播放组件 - 支持数据源绑定和事件交互
4
4
  */
5
5
 
6
- import { useState, useRef, useEffect, type CSSProperties, type Ref } from 'react'
6
+ import { useState, useRef, useEffect, useMemo, type CSSProperties } from 'react'
7
+ import { useDataSource, type MaterialComponet } from '@easy-editor/materials-shared'
7
8
  import styles from './component.module.css'
8
9
 
9
10
  export type AudioStyle = 'custom' | 'native'
10
11
 
11
- export interface AudioProps {
12
- ref?: Ref<HTMLDivElement>
13
- /** 音频地址 */
12
+ export interface AudioProps extends MaterialComponet {
13
+ /** 音频地址(兼容旧版) */
14
14
  src?: string
15
15
  /** 标题 */
16
16
  title?: string
@@ -20,8 +20,24 @@ export interface AudioProps {
20
20
  loop?: boolean
21
21
  /** 样式类型 */
22
22
  audioStyle?: AudioStyle
23
- /** 外部样式 */
24
- style?: CSSProperties
23
+ /** 播放速度 */
24
+ playbackRate?: number
25
+ /** 音量 (0-100) */
26
+ volume?: number
27
+ /** 点击事件 */
28
+ onClick?: (e: React.MouseEvent) => void
29
+ /** 双击事件 */
30
+ onDoubleClick?: (e: React.MouseEvent) => void
31
+ /** 鼠标进入 */
32
+ onMouseEnter?: (e: React.MouseEvent) => void
33
+ /** 鼠标离开 */
34
+ onMouseLeave?: (e: React.MouseEvent) => void
35
+ /** 播放事件 */
36
+ onPlay?: () => void
37
+ /** 暂停事件 */
38
+ onPause?: () => void
39
+ /** 结束事件 */
40
+ onEnded?: () => void
25
41
  }
26
42
 
27
43
  const formatTime = (seconds: number): string => {
@@ -32,13 +48,36 @@ const formatTime = (seconds: number): string => {
32
48
 
33
49
  export const Audio: React.FC<AudioProps> = ({
34
50
  ref,
35
- src = '',
51
+ $data,
52
+ __dataSource,
53
+ src: staticSrc = '',
36
54
  title = '音频文件',
37
55
  autoPlay = false,
38
56
  loop = false,
39
57
  audioStyle = 'custom',
58
+ playbackRate = 1,
59
+ volume = 100,
60
+ rotation = 0,
61
+ opacity = 100,
62
+ background = 'transparent',
40
63
  style: externalStyle,
64
+ onClick,
65
+ onDoubleClick,
66
+ onMouseEnter,
67
+ onMouseLeave,
68
+ onPlay,
69
+ onPause,
70
+ onEnded,
41
71
  }) => {
72
+ // 解析数据源
73
+ const dataSource = useDataSource($data, __dataSource)
74
+ const src = useMemo<string>(() => {
75
+ if (dataSource.length > 0 && typeof dataSource[0]?.src === 'string') {
76
+ return dataSource[0].src
77
+ }
78
+ return staticSrc
79
+ }, [dataSource, staticSrc])
80
+
42
81
  const audioRef = useRef<HTMLAudioElement>(null)
43
82
  const [isPlaying, setIsPlaying] = useState(false)
44
83
  const [currentTime, setCurrentTime] = useState(0)
@@ -52,18 +91,41 @@ export const Audio: React.FC<AudioProps> = ({
52
91
 
53
92
  const handleTimeUpdate = () => setCurrentTime(audio.currentTime)
54
93
  const handleLoadedMetadata = () => setDuration(audio.duration)
55
- const handleEnded = () => setIsPlaying(false)
94
+ const handleEnded = () => {
95
+ setIsPlaying(false)
96
+ onEnded?.()
97
+ }
98
+ const handlePlay = () => {
99
+ setIsPlaying(true)
100
+ onPlay?.()
101
+ }
102
+ const handlePause = () => {
103
+ setIsPlaying(false)
104
+ onPause?.()
105
+ }
56
106
 
57
107
  audio.addEventListener('timeupdate', handleTimeUpdate)
58
108
  audio.addEventListener('loadedmetadata', handleLoadedMetadata)
59
109
  audio.addEventListener('ended', handleEnded)
110
+ audio.addEventListener('play', handlePlay)
111
+ audio.addEventListener('pause', handlePause)
60
112
 
61
113
  return () => {
62
114
  audio.removeEventListener('timeupdate', handleTimeUpdate)
63
115
  audio.removeEventListener('loadedmetadata', handleLoadedMetadata)
64
116
  audio.removeEventListener('ended', handleEnded)
117
+ audio.removeEventListener('play', handlePlay)
118
+ audio.removeEventListener('pause', handlePause)
119
+ }
120
+ }, [onEnded, onPlay, onPause])
121
+
122
+ // 更新播放速度和音量
123
+ useEffect(() => {
124
+ if (audioRef.current) {
125
+ audioRef.current.playbackRate = playbackRate
126
+ audioRef.current.volume = volume / 100
65
127
  }
66
- }, [])
128
+ }, [playbackRate, volume])
67
129
 
68
130
  const togglePlay = () => {
69
131
  if (audioRef.current) {
@@ -72,7 +134,6 @@ export const Audio: React.FC<AudioProps> = ({
72
134
  } else {
73
135
  audioRef.current.play()
74
136
  }
75
- setIsPlaying(!isPlaying)
76
137
  }
77
138
  }
78
139
 
@@ -86,10 +147,25 @@ export const Audio: React.FC<AudioProps> = ({
86
147
 
87
148
  const progress = duration ? (currentTime / duration) * 100 : 0
88
149
 
150
+ const containerStyle: CSSProperties = {
151
+ transform: rotation !== 0 ? `rotate(${rotation}deg)` : undefined,
152
+ opacity: opacity / 100,
153
+ backgroundColor: background,
154
+ ...externalStyle,
155
+ }
156
+
89
157
  // 原生样式
90
158
  if (audioStyle === 'native') {
91
159
  return (
92
- <div className={styles.container} ref={ref} style={externalStyle}>
160
+ <div
161
+ className={styles.container}
162
+ onClick={onClick}
163
+ onDoubleClick={onDoubleClick}
164
+ onMouseEnter={onMouseEnter}
165
+ onMouseLeave={onMouseLeave}
166
+ ref={ref}
167
+ style={containerStyle}
168
+ >
93
169
  <audio autoPlay={autoPlay} className={styles.nativeAudio} controls loop={loop} ref={audioRef} src={src} />
94
170
  </div>
95
171
  )
@@ -97,7 +173,15 @@ export const Audio: React.FC<AudioProps> = ({
97
173
 
98
174
  // 自定义样式
99
175
  return (
100
- <div className={styles.container} ref={ref} style={externalStyle}>
176
+ <div
177
+ className={styles.container}
178
+ onClick={onClick}
179
+ onDoubleClick={onDoubleClick}
180
+ onMouseEnter={onMouseEnter}
181
+ onMouseLeave={onMouseLeave}
182
+ ref={ref}
183
+ style={containerStyle}
184
+ >
101
185
  <audio autoPlay={autoPlay} loop={loop} ref={audioRef} src={src} />
102
186
 
103
187
  <button
package/src/configure.ts CHANGED
@@ -1,247 +1,165 @@
1
- /**
2
- * Audio Configure
3
- * 音频组件配置
4
- */
5
-
6
- import type { Configure } from '@easy-editor/core'
7
- import type { UploadValue } from '@easy-editor/materials-shared'
8
-
9
- export const configure: Configure = {
10
- props: [
11
- {
12
- type: 'group',
13
- title: '属性',
14
- setter: 'TabSetter',
15
- items: [
16
- {
17
- type: 'group',
18
- key: 'config',
19
- title: '配置',
20
- setter: {
21
- componentName: 'CollapseSetter',
22
- props: {
23
- icon: false,
24
- },
25
- },
26
- items: [
27
- // 基础配置
28
- {
29
- name: 'id',
30
- title: 'ID',
31
- setter: 'NodeIdSetter',
32
- extraProps: {
33
- // @ts-expect-error label is not a valid extra prop
34
- label: false,
35
- },
36
- },
37
- {
38
- name: 'title',
39
- title: '标题',
40
- setter: 'StringSetter',
41
- extraProps: {
42
- getValue(target) {
43
- return target.getExtraPropValue('title')
44
- },
45
- setValue(target, value) {
46
- target.setExtraPropValue('title', value)
47
- },
48
- },
49
- },
50
- {
51
- type: 'group',
52
- title: '基础属性',
53
- setter: {
54
- componentName: 'CollapseSetter',
55
- props: {
56
- icon: false,
57
- },
58
- },
59
- items: [
60
- {
61
- name: 'rect',
62
- title: '位置尺寸',
63
- setter: 'RectSetter',
64
- extraProps: {
65
- getValue(target) {
66
- return target.getExtraPropValue('$dashboard.rect')
67
- },
68
- setValue(target, value) {
69
- target.setExtraPropValue('$dashboard.rect', value)
70
- },
71
- },
72
- },
73
- ],
74
- },
75
- // 组件配置
76
- {
77
- type: 'group',
78
- title: '内容',
79
- setter: {
80
- componentName: 'CollapseSetter',
81
- props: {
82
- icon: false,
83
- },
84
- },
85
- items: [
86
- {
87
- name: '__upload',
88
- title: '上传',
89
- setter: {
90
- componentName: 'UploadSetter',
91
- props: {
92
- accept: '.mp3,.wav,.ogg',
93
- },
94
- },
95
- extraProps: {
96
- setValue(target, value: UploadValue) {
97
- if (value) {
98
- const { base64, raw } = value
99
- if (base64) {
100
- target.parent.setPropValue('src', base64)
101
- }
102
- if (raw?.width) {
103
- target.parent.setExtraPropValue('$dashboard.rect.width', raw.width)
104
- }
105
- if (raw?.height) {
106
- target.parent.setExtraPropValue('$dashboard.rect.height', raw.height)
107
- }
108
- } else {
109
- target.parent.clearPropValue('src')
110
- }
111
- },
112
- },
113
- },
114
- {
115
- name: 'src',
116
- title: '音频地址',
117
- setter: 'StringSetter',
118
- },
119
- {
120
- name: 'title',
121
- title: '标题',
122
- setter: 'StringSetter',
123
- extraProps: {
124
- defaultValue: '音频文件',
125
- },
126
- },
127
- ],
128
- },
129
- {
130
- type: 'group',
131
- title: '播放',
132
- setter: {
133
- componentName: 'CollapseSetter',
134
- props: {
135
- icon: false,
136
- },
137
- },
138
- items: [
139
- {
140
- name: 'autoPlay',
141
- title: '自动播放',
142
- setter: 'SwitchSetter',
143
- extraProps: {
144
- defaultValue: false,
145
- },
146
- },
147
- {
148
- name: 'loop',
149
- title: '循环播放',
150
- setter: 'SwitchSetter',
151
- extraProps: {
152
- defaultValue: false,
153
- },
154
- },
155
- {
156
- name: 'audioStyle',
157
- title: '样式类型',
158
- setter: {
159
- componentName: 'SelectSetter',
160
- props: {
161
- options: [
162
- { label: '自定义', value: 'custom' },
163
- { label: '原生', value: 'native' },
164
- ],
165
- },
166
- },
167
- extraProps: {
168
- defaultValue: 'custom',
169
- },
170
- },
171
- {
172
- name: 'playbackRate',
173
- title: '播放速度',
174
- setter: {
175
- componentName: 'SelectSetter',
176
- props: {
177
- options: [
178
- { label: '0.5x', value: 0.5 },
179
- { label: '0.75x', value: 0.75 },
180
- { label: '1x (正常)', value: 1 },
181
- { label: '1.25x', value: 1.25 },
182
- { label: '1.5x', value: 1.5 },
183
- { label: '2x', value: 2 },
184
- ],
185
- },
186
- },
187
- extraProps: {
188
- defaultValue: 1,
189
- },
190
- },
191
- {
192
- name: 'volume',
193
- title: '音量',
194
- setter: {
195
- componentName: 'SliderSetter',
196
- props: {
197
- min: 0,
198
- max: 100,
199
- step: 1,
200
- suffix: '%',
201
- },
202
- },
203
- extraProps: {
204
- defaultValue: 100,
205
- },
206
- },
207
- ],
208
- },
209
- ],
210
- },
211
- {
212
- type: 'group',
213
- key: 'data',
214
- title: '数据',
215
- items: [
216
- {
217
- name: 'dataBinding',
218
- title: '数据绑定',
219
- setter: 'DataBindingSetter',
220
- },
221
- ],
222
- },
223
- {
224
- type: 'group',
225
- key: 'advanced',
226
- title: '高级',
227
- items: [
228
- {
229
- name: 'condition',
230
- title: '显隐控制',
231
- setter: 'SwitchSetter',
232
- extraProps: {
233
- defaultValue: true,
234
- supportVariable: true,
235
- },
236
- },
237
- ],
238
- },
239
- ],
240
- },
241
- ],
242
- component: {},
243
- supports: {},
244
- advanced: {},
245
- }
246
-
247
- export default configure
1
+ /**
2
+ * Audio Configure
3
+ * 音频组件配置
4
+ */
5
+
6
+ import type { FieldConfig } from '@easy-editor/core'
7
+ import type { UploadValue } from '@easy-editor/materials-shared'
8
+ import { createCollapseGroup, createDataConfigGroup, createStandardConfigure } from '@easy-editor/materials-shared'
9
+
10
+ /** 组件配置 - 音频独有 */
11
+ const componentConfigGroup: FieldConfig = createCollapseGroup(
12
+ '组件配置',
13
+ [
14
+ {
15
+ type: 'group',
16
+ title: '组件配置',
17
+ setter: 'SubTabSetter',
18
+ items: [
19
+ // 内容 Tab
20
+ {
21
+ type: 'group',
22
+ key: 'content',
23
+ title: '内容',
24
+ items: [
25
+ {
26
+ name: '__upload',
27
+ title: '上传',
28
+ setter: {
29
+ componentName: 'UploadSetter',
30
+ props: {
31
+ accept: '.mp3,.wav,.ogg',
32
+ },
33
+ },
34
+ extraProps: {
35
+ setValue(target, value: UploadValue) {
36
+ if (value) {
37
+ const { base64, raw } = value
38
+ if (base64) {
39
+ target.parent.setPropValue('src', base64)
40
+ }
41
+ if (raw?.width) {
42
+ target.parent.setExtraPropValue('$dashboard.rect.width', raw.width)
43
+ }
44
+ if (raw?.height) {
45
+ target.parent.setExtraPropValue('$dashboard.rect.height', raw.height)
46
+ }
47
+ } else {
48
+ target.parent.clearPropValue('src')
49
+ }
50
+ },
51
+ },
52
+ },
53
+ {
54
+ name: 'src',
55
+ title: '音频地址',
56
+ setter: 'StringSetter',
57
+ },
58
+ {
59
+ name: 'title',
60
+ title: '标题',
61
+ setter: 'StringSetter',
62
+ extraProps: {
63
+ defaultValue: '音频文件',
64
+ },
65
+ },
66
+ ],
67
+ },
68
+ // 播放 Tab
69
+ {
70
+ type: 'group',
71
+ key: 'playback',
72
+ title: '播放',
73
+ items: [
74
+ {
75
+ name: 'autoPlay',
76
+ title: '自动播放',
77
+ setter: 'SwitchSetter',
78
+ extraProps: {
79
+ defaultValue: false,
80
+ },
81
+ },
82
+ {
83
+ name: 'loop',
84
+ title: '循环播放',
85
+ setter: 'SwitchSetter',
86
+ extraProps: {
87
+ defaultValue: false,
88
+ },
89
+ },
90
+ {
91
+ name: 'playbackRate',
92
+ title: '播放速度',
93
+ setter: {
94
+ componentName: 'SelectSetter',
95
+ props: {
96
+ options: [
97
+ { label: '0.5x', value: 0.5 },
98
+ { label: '0.75x', value: 0.75 },
99
+ { label: '1x (正常)', value: 1 },
100
+ { label: '1.25x', value: 1.25 },
101
+ { label: '1.5x', value: 1.5 },
102
+ { label: '2x', value: 2 },
103
+ ],
104
+ },
105
+ },
106
+ extraProps: {
107
+ defaultValue: 1,
108
+ },
109
+ },
110
+ {
111
+ name: 'volume',
112
+ title: '音量',
113
+ setter: {
114
+ componentName: 'SliderSetter',
115
+ props: {
116
+ min: 0,
117
+ max: 100,
118
+ step: 1,
119
+ suffix: '%',
120
+ },
121
+ },
122
+ extraProps: {
123
+ defaultValue: 100,
124
+ },
125
+ },
126
+ ],
127
+ },
128
+ // 样式 Tab
129
+ {
130
+ type: 'group',
131
+ key: 'style',
132
+ title: '样式',
133
+ items: [
134
+ {
135
+ name: 'audioStyle',
136
+ title: '样式类型',
137
+ setter: {
138
+ componentName: 'SegmentedSetter',
139
+ props: {
140
+ options: [
141
+ { label: '自定义', value: 'custom' },
142
+ { label: '原生', value: 'native' },
143
+ ],
144
+ },
145
+ },
146
+ extraProps: {
147
+ defaultValue: 'custom',
148
+ },
149
+ },
150
+ ],
151
+ },
152
+ ],
153
+ },
154
+ ],
155
+ {
156
+ padding: '6px 16px 12px',
157
+ },
158
+ )
159
+
160
+ /** 数据配置 */
161
+ const dataConfigGroup: FieldConfig = createDataConfigGroup([
162
+ { name: 'src', label: 'src', type: 'string', required: true, description: '音频地址' },
163
+ ])
164
+
165
+ export const configure = createStandardConfigure(componentConfigGroup, dataConfigGroup)