@easy-editor/materials-dashboard-audio 0.0.4 → 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,21 +1,27 @@
1
- # @easy-editor/materials-dashboard-audio
2
-
3
- ## 0.0.4
4
-
5
- ### Patch Changes
6
-
7
- - perf: configure & datasource
8
- - Updated dependencies
9
- - @easy-editor/materials-shared@0.0.1
10
-
11
- ## 0.0.3
12
-
13
- ### Patch Changes
14
-
15
- - fix: build error
16
-
17
- ## 0.0.2
18
-
19
- ### Patch Changes
20
-
21
- - 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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easy-editor/materials-dashboard-audio",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Audio component for EasyEditor dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/component.tsx CHANGED
@@ -1,222 +1,224 @@
1
- /**
2
- * Audio Component
3
- * 音频播放组件 - 支持数据源绑定和事件交互
4
- */
5
-
6
- import { useState, useRef, useEffect, useMemo, type CSSProperties } from 'react'
7
- import { useDataSource, type MaterialComponet } from '@easy-editor/materials-shared'
8
- import styles from './component.module.css'
9
-
10
- export type AudioStyle = 'custom' | 'native'
11
-
12
- export interface AudioProps extends MaterialComponet {
13
- /** 音频地址(兼容旧版) */
14
- src?: string
15
- /** 标题 */
16
- title?: string
17
- /** 自动播放 */
18
- autoPlay?: boolean
19
- /** 循环播放 */
20
- loop?: boolean
21
- /** 样式类型 */
22
- audioStyle?: AudioStyle
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
41
- }
42
-
43
- const formatTime = (seconds: number): string => {
44
- const mins = Math.floor(seconds / 60)
45
- const secs = Math.floor(seconds % 60)
46
- return `${mins}:${secs.toString().padStart(2, '0')}`
47
- }
48
-
49
- export const Audio: React.FC<AudioProps> = ({
50
- ref,
51
- $data,
52
- __dataSource,
53
- src: staticSrc = '',
54
- title = '音频文件',
55
- autoPlay = false,
56
- loop = false,
57
- audioStyle = 'custom',
58
- playbackRate = 1,
59
- volume = 100,
60
- rotation = 0,
61
- opacity = 100,
62
- background = 'transparent',
63
- style: externalStyle,
64
- onClick,
65
- onDoubleClick,
66
- onMouseEnter,
67
- onMouseLeave,
68
- onPlay,
69
- onPause,
70
- onEnded,
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
-
81
- const audioRef = useRef<HTMLAudioElement>(null)
82
- const [isPlaying, setIsPlaying] = useState(false)
83
- const [currentTime, setCurrentTime] = useState(0)
84
- const [duration, setDuration] = useState(0)
85
-
86
- useEffect(() => {
87
- const audio = audioRef.current
88
- if (!audio) {
89
- return
90
- }
91
-
92
- const handleTimeUpdate = () => setCurrentTime(audio.currentTime)
93
- const handleLoadedMetadata = () => setDuration(audio.duration)
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
- }
106
-
107
- audio.addEventListener('timeupdate', handleTimeUpdate)
108
- audio.addEventListener('loadedmetadata', handleLoadedMetadata)
109
- audio.addEventListener('ended', handleEnded)
110
- audio.addEventListener('play', handlePlay)
111
- audio.addEventListener('pause', handlePause)
112
-
113
- return () => {
114
- audio.removeEventListener('timeupdate', handleTimeUpdate)
115
- audio.removeEventListener('loadedmetadata', handleLoadedMetadata)
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
127
- }
128
- }, [playbackRate, volume])
129
-
130
- const togglePlay = () => {
131
- if (audioRef.current) {
132
- if (isPlaying) {
133
- audioRef.current.pause()
134
- } else {
135
- audioRef.current.play()
136
- }
137
- }
138
- }
139
-
140
- const handleProgressClick = (e: React.MouseEvent<HTMLDivElement>) => {
141
- if (audioRef.current && duration) {
142
- const rect = e.currentTarget.getBoundingClientRect()
143
- const percent = (e.clientX - rect.left) / rect.width
144
- audioRef.current.currentTime = percent * duration
145
- }
146
- }
147
-
148
- const progress = duration ? (currentTime / duration) * 100 : 0
149
-
150
- const containerStyle: CSSProperties = {
151
- transform: rotation !== 0 ? `rotate(${rotation}deg)` : undefined,
152
- opacity: opacity / 100,
153
- backgroundColor: background,
154
- ...externalStyle,
155
- }
156
-
157
- // 原生样式
158
- if (audioStyle === 'native') {
159
- return (
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
- >
169
- <audio autoPlay={autoPlay} className={styles.nativeAudio} controls loop={loop} ref={audioRef} src={src} />
170
- </div>
171
- )
172
- }
173
-
174
- // 自定义样式
175
- return (
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
- >
185
- <audio autoPlay={autoPlay} loop={loop} ref={audioRef} src={src} />
186
-
187
- <button
188
- aria-label={isPlaying ? 'Pause' : 'Play'}
189
- className={styles.playButton}
190
- onClick={togglePlay}
191
- type='button'
192
- >
193
- {isPlaying ? (
194
- <div className={styles.pauseIcon}>
195
- <div className={styles.pauseBar} />
196
- <div className={styles.pauseBar} />
197
- </div>
198
- ) : (
199
- <div className={styles.playIcon} />
200
- )}
201
- </button>
202
-
203
- <div className={styles.info}>
204
- <div className={styles.title}>{title}</div>
205
- <div className={styles.progressContainer}>
206
- <button
207
- aria-label='Seek to position'
208
- className={styles.progressBar}
209
- // @ts-expect-error
210
- onClick={handleProgressClick}
211
- type='button'
212
- >
213
- <div className={styles.progressFill} style={{ width: `${progress}%` }} />
214
- </button>
215
- <span className={styles.time}>
216
- {formatTime(currentTime)} / {formatTime(duration || 0)}
217
- </span>
218
- </div>
219
- </div>
220
- </div>
221
- )
222
- }
1
+ /**
2
+ * Audio Component
3
+ * 音频播放组件 - 支持数据源绑定和事件交互
4
+ */
5
+
6
+ import { useState, useRef, useEffect, useMemo, type CSSProperties } from 'react'
7
+ import { useDataSource, type MaterialComponet } from '@easy-editor/materials-shared'
8
+ import styles from './component.module.css'
9
+
10
+ export type AudioStyle = 'custom' | 'native'
11
+
12
+ export interface AudioProps extends MaterialComponet {
13
+ /** 音频地址(兼容旧版) */
14
+ src?: string
15
+ /** 标题 */
16
+ title?: string
17
+ /** 自动播放 */
18
+ autoPlay?: boolean
19
+ /** 循环播放 */
20
+ loop?: boolean
21
+ /** 样式类型 */
22
+ audioStyle?: AudioStyle
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
41
+ }
42
+
43
+ const formatTime = (seconds: number): string => {
44
+ const mins = Math.floor(seconds / 60)
45
+ const secs = Math.floor(seconds % 60)
46
+ return `${mins}:${secs.toString().padStart(2, '0')}`
47
+ }
48
+
49
+ export const Audio: React.FC<AudioProps> = ({
50
+ ref,
51
+ $data,
52
+ __dataSource,
53
+ src: staticSrc = '',
54
+ title = '音频文件',
55
+ autoPlay = false,
56
+ loop = false,
57
+ audioStyle = 'custom',
58
+ playbackRate = 1,
59
+ volume = 100,
60
+ rotation = 0,
61
+ opacity = 100,
62
+ background = 'transparent',
63
+ style: externalStyle,
64
+ onClick,
65
+ onDoubleClick,
66
+ onMouseEnter,
67
+ onMouseLeave,
68
+ onPlay,
69
+ onPause,
70
+ onEnded,
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
+
81
+ const audioRef = useRef<HTMLAudioElement>(null)
82
+ const [isPlaying, setIsPlaying] = useState(false)
83
+ const [currentTime, setCurrentTime] = useState(0)
84
+ const [duration, setDuration] = useState(0)
85
+
86
+ useEffect(() => {
87
+ const audio = audioRef.current
88
+ if (!audio) {
89
+ return
90
+ }
91
+
92
+ const handleTimeUpdate = () => setCurrentTime(audio.currentTime)
93
+ const handleLoadedMetadata = () => setDuration(audio.duration)
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
+ }
106
+
107
+ audio.addEventListener('timeupdate', handleTimeUpdate)
108
+ audio.addEventListener('loadedmetadata', handleLoadedMetadata)
109
+ audio.addEventListener('ended', handleEnded)
110
+ audio.addEventListener('play', handlePlay)
111
+ audio.addEventListener('pause', handlePause)
112
+
113
+ return () => {
114
+ audio.removeEventListener('timeupdate', handleTimeUpdate)
115
+ audio.removeEventListener('loadedmetadata', handleLoadedMetadata)
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
127
+ }
128
+ }, [playbackRate, volume])
129
+
130
+ const togglePlay = () => {
131
+ if (audioRef.current) {
132
+ if (isPlaying) {
133
+ audioRef.current.pause()
134
+ } else {
135
+ audioRef.current.play()
136
+ }
137
+ }
138
+ }
139
+
140
+ const handleProgressClick = (e: React.MouseEvent<HTMLDivElement>) => {
141
+ if (audioRef.current && duration) {
142
+ const rect = e.currentTarget.getBoundingClientRect()
143
+ const percent = (e.clientX - rect.left) / rect.width
144
+ audioRef.current.currentTime = percent * duration
145
+ }
146
+ }
147
+
148
+ const progress = duration ? (currentTime / duration) * 100 : 0
149
+
150
+ const containerStyle: CSSProperties = {
151
+ transform: rotation !== 0 ? `rotate(${rotation}deg)` : undefined,
152
+ opacity: opacity / 100,
153
+ backgroundColor: background,
154
+ ...externalStyle,
155
+ }
156
+
157
+ // 原生样式
158
+ if (audioStyle === 'native') {
159
+ return (
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
+ >
169
+ <audio autoPlay={autoPlay} className={styles.nativeAudio} controls loop={loop} ref={audioRef} src={src} />
170
+ </div>
171
+ )
172
+ }
173
+
174
+ // 自定义样式
175
+ return (
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
+ >
185
+ <audio autoPlay={autoPlay} loop={loop} ref={audioRef} src={src} />
186
+
187
+ <button
188
+ aria-label={isPlaying ? 'Pause' : 'Play'}
189
+ className={styles.playButton}
190
+ onClick={togglePlay}
191
+ type='button'
192
+ >
193
+ {isPlaying ? (
194
+ <div className={styles.pauseIcon}>
195
+ <div className={styles.pauseBar} />
196
+ <div className={styles.pauseBar} />
197
+ </div>
198
+ ) : (
199
+ <div className={styles.playIcon} />
200
+ )}
201
+ </button>
202
+
203
+ <div className={styles.info}>
204
+ <div className={styles.title}>{title}</div>
205
+ <div className={styles.progressContainer}>
206
+ <button
207
+ aria-label='Seek to position'
208
+ className={styles.progressBar}
209
+ // @ts-expect-error
210
+ onClick={handleProgressClick}
211
+ type='button'
212
+ >
213
+ <div className={styles.progressFill} style={{ width: `${progress}%` }} />
214
+ </button>
215
+ <span className={styles.time}>
216
+ {formatTime(currentTime)} / {formatTime(duration || 0)}
217
+ </span>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ )
222
+ }
223
+
224
+ export default Audio
package/src/meta.ts CHANGED
@@ -1,26 +1,28 @@
1
- /**
2
- * Audio Meta
3
- * 音频组件元数据
4
- */
5
-
6
- import type { ComponentMetadata } from '@easy-editor/core'
7
- import { MaterialGroup } from '@easy-editor/materials-shared'
8
- import { COMPONENT_NAME, PACKAGE_NAME } from './constants'
9
- import { configure } from './configure'
10
- import { snippets } from './snippets'
11
- import pkg from '../package.json'
12
-
13
- export const meta: ComponentMetadata = {
14
- componentName: COMPONENT_NAME,
15
- title: '音频',
16
- group: MaterialGroup.MEDIA,
17
- devMode: 'proCode',
18
- npm: {
19
- package: PACKAGE_NAME,
20
- version: pkg.version,
21
- globalName: COMPONENT_NAME,
22
- componentName: COMPONENT_NAME,
23
- },
24
- snippets,
25
- configure,
26
- }
1
+ /**
2
+ * Audio Meta
3
+ * 音频组件元数据
4
+ */
5
+
6
+ import type { ComponentMetadata } from '@easy-editor/core'
7
+ import { MaterialGroup } from '@easy-editor/materials-shared'
8
+ import { COMPONENT_NAME, PACKAGE_NAME } from './constants'
9
+ import { configure } from './configure'
10
+ import { snippets } from './snippets'
11
+ import pkg from '../package.json'
12
+
13
+ export const meta: ComponentMetadata = {
14
+ componentName: COMPONENT_NAME,
15
+ title: '音频',
16
+ group: MaterialGroup.MEDIA,
17
+ devMode: 'proCode',
18
+ npm: {
19
+ package: PACKAGE_NAME,
20
+ version: pkg.version,
21
+ globalName: COMPONENT_NAME,
22
+ componentName: COMPONENT_NAME,
23
+ },
24
+ snippets,
25
+ configure,
26
+ }
27
+
28
+ export default meta
@@ -1 +0,0 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("react/jsx-runtime")):"function"==typeof define&&define.amd?define(["exports","react","react/jsx-runtime"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).EasyEditorMaterialsAudioComponent={},e.React,e.jsxRuntime)}(this,function(e,t,o){"use strict";const r=(e,t)=>{if(!t)return e;const o=t.split(".");let r=e;for(const e of o){if(null==r)return;if("object"!=typeof r)return;r=r[e]}return r},n=e=>void 0===e?[]:Array.isArray(e)?e:e&&"object"==typeof e?[e]:[],a=(e,t)=>{const o=((e,t)=>{if(!e)return[];if("static"===e.sourceType)return Array.isArray(e.staticData)?e.staticData:[];if("global"===e.sourceType&&e.datasourceId){const o=t?.page?.[e.datasourceId];return n(o)}if("datasource"===e.sourceType&&e.datasourceId){const o=t?.component?.[e.datasourceId];return n(o)}return[]})(e,t);return((e,t)=>t&&0!==t.length?e.map(e=>{const o={};for(const{componentField:n,sourceField:a}of t)n&&a&&(o[n]=r(e,a));return o}):e)(o,e?.fieldMappings)};const s=(e,t,o)=>({type:"group",title:e,setter:{componentName:"CollapseSetter",props:{icon:!1,...o}},items:t});s("基础配置",[{name:"title",title:"标题",setter:"StringSetter",extraProps:{getValue:e=>e.getExtraPropValue("title"),setValue(e,t){e.setExtraPropValue("title",t)}}},{name:"rect",title:"位置尺寸",setter:"RectSetter",extraProps:{getValue:e=>e.getExtraPropValue("$dashboard.rect"),setValue(e,t){e.setExtraPropValue("$dashboard.rect",t)}}},{name:"rotation",title:"旋转角度",setter:{componentName:"SliderSetter",props:{min:0,max:360,suffix:"°"}},extraProps:{defaultValue:0}},{name:"opacity",title:"不透明度",setter:{componentName:"SliderSetter",props:{min:0,max:100,suffix:"%"}},extraProps:{defaultValue:100}},{name:"background",title:"背景颜色",setter:"ColorSetter",extraProps:{defaultValue:"transparent"}}]);const i=[{title:"点击事件",children:[{label:"点击",value:"onClick",description:"鼠标点击时触发"},{label:"双击",value:"onDoubleClick",description:"鼠标双击时触发"}]},{title:"鼠标事件",children:[{label:"鼠标进入",value:"onMouseEnter",description:"鼠标进入时触发"},{label:"鼠标离开",value:"onMouseLeave",description:"鼠标离开时触发"}]}];((e=i)=>{s("事件绑定",[{name:"events",title:"事件",setter:{componentName:"EventSetter",props:{events:e}},extraProps:{label:!1}}])})(),s("高级配置",[{title:"条件渲染",setter:"SwitchSetter",extraProps:{supportVariable:!0,getValue:e=>e.getNode().getExtraPropValue("condition"),setValue(e,t){e.getNode().setExtraProp("condition",t)}}}]);var l="component-module__container___VbZSk",d="component-module__playButton___M6QVA",c="component-module__playIcon___t8-WV",u="component-module__pauseIcon___8XmSk",p="component-module__pauseBar___eM6DG",m="component-module__info___VyTlr",_="component-module__title___Hj54k",f="component-module__progressContainer___HvygE",x="component-module__progressBar___wROZC",g="component-module__progressFill___F8n5d",h="component-module__time___-5GMu",y="component-module__nativeAudio___vCo17";!function(e,t){void 0===t&&(t={});var o=t.insertAt;if("undefined"!=typeof document){var r=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css","top"===o&&r.firstChild?r.insertBefore(n,r.firstChild):r.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e))}}(".component-module__container___VbZSk{align-items:center;background:rgba(10,10,26,.95);border:1px solid rgba(26,26,62,.8);border-radius:8px;display:flex;gap:12px;height:100%;padding:12px 16px;width:100%}.component-module__playButton___M6QVA{align-items:center;background:linear-gradient(135deg,#00d4ff,#9b59b6);border:none;border-radius:50%;cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;transition:transform .2s ease;width:40px}.component-module__playButton___M6QVA:hover{transform:scale(1.05)}.component-module__playIcon___t8-WV{border-bottom:8px solid transparent;border-left:12px solid #fff;border-top:8px solid transparent;height:0;margin-left:3px;width:0}.component-module__pauseIcon___8XmSk{display:flex;gap:3px}.component-module__pauseBar___eM6DG{background:#fff;border-radius:2px;height:14px;width:4px}.component-module__info___VyTlr{flex:1;min-width:0}.component-module__title___Hj54k{color:#e6e6e6;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.component-module__progressContainer___HvygE{align-items:center;display:flex;gap:8px;margin-top:4px}.component-module__progressBar___wROZC{background:rgba(26,26,62,.8);border-radius:2px;cursor:pointer;flex:1;height:4px;overflow:hidden}.component-module__progressFill___F8n5d{background:linear-gradient(90deg,#00d4ff,#9b59b6);border-radius:2px;height:100%;transition:width .1s linear}.component-module__time___-5GMu{color:hsla(0,0%,100%,.6);font-family:Courier New,Courier,monospace;font-size:12px;min-width:80px;text-align:right}.component-module__nativeAudio___vCo17{width:100%}");const v=e=>`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,"0")}`;e.Audio=({ref:e,$data:r,__dataSource:n,src:s="",title:i="音频文件",autoPlay:b=!1,loop:E=!1,audioStyle:k="custom",playbackRate:C=1,volume:S=100,rotation:j=0,opacity:V=100,background:N="transparent",style:M,onClick:w,onDoubleClick:P,onMouseEnter:L,onMouseLeave:T,onPlay:A,onPause:B,onEnded:R})=>{const D=function(e,o){return t.useMemo(()=>a(e,o),[e,o])}(r,n),I=t.useMemo(()=>D.length>0&&"string"==typeof D[0]?.src?D[0].src:s,[D,s]),$=t.useRef(null),[F,G]=t.useState(!1),[H,Z]=t.useState(0),[Q,X]=t.useState(0);t.useEffect(()=>{const e=$.current;if(!e)return;const t=()=>Z(e.currentTime),o=()=>X(e.duration),r=()=>{G(!1),R?.()},n=()=>{G(!0),A?.()},a=()=>{G(!1),B?.()};return e.addEventListener("timeupdate",t),e.addEventListener("loadedmetadata",o),e.addEventListener("ended",r),e.addEventListener("play",n),e.addEventListener("pause",a),()=>{e.removeEventListener("timeupdate",t),e.removeEventListener("loadedmetadata",o),e.removeEventListener("ended",r),e.removeEventListener("play",n),e.removeEventListener("pause",a)}},[R,A,B]),t.useEffect(()=>{$.current&&($.current.playbackRate=C,$.current.volume=S/100)},[C,S]);const q=Q?H/Q*100:0,z={transform:0!==j?`rotate(${j}deg)`:void 0,opacity:V/100,backgroundColor:N,...M};return"native"===k?o.jsx("div",{className:l,onClick:w,onDoubleClick:P,onMouseEnter:L,onMouseLeave:T,ref:e,style:z,children:o.jsx("audio",{autoPlay:b,className:y,controls:!0,loop:E,ref:$,src:I})}):o.jsxs("div",{className:l,onClick:w,onDoubleClick:P,onMouseEnter:L,onMouseLeave:T,ref:e,style:z,children:[o.jsx("audio",{autoPlay:b,loop:E,ref:$,src:I}),o.jsx("button",{"aria-label":F?"Pause":"Play",className:d,onClick:()=>{$.current&&(F?$.current.pause():$.current.play())},type:"button",children:F?o.jsxs("div",{className:u,children:[o.jsx("div",{className:p}),o.jsx("div",{className:p})]}):o.jsx("div",{className:c})}),o.jsxs("div",{className:m,children:[o.jsx("div",{className:_,children:i}),o.jsxs("div",{className:f,children:[o.jsx("button",{"aria-label":"Seek to position",className:x,onClick:e=>{if($.current&&Q){const t=e.currentTarget.getBoundingClientRect(),o=(e.clientX-t.left)/t.width;$.current.currentTime=o*Q}},type:"button",children:o.jsx("div",{className:g,style:{width:`${q}%`}})}),o.jsxs("span",{className:h,children:[v(H)," / ",v(Q||0)]})]})]})]})}});
package/dist/index.min.js DELETED
@@ -1 +0,0 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("react/jsx-runtime")):"function"==typeof define&&define.amd?define(["exports","react","react/jsx-runtime"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).EasyEditorMaterialsAudio={},e.React,e.jsxRuntime)}(this,function(e,t,o){"use strict";const r=(e,t)=>{if(!t)return e;const o=t.split(".");let r=e;for(const e of o){if(null==r)return;if("object"!=typeof r)return;r=r[e]}return r},a=e=>void 0===e?[]:Array.isArray(e)?e:e&&"object"==typeof e?[e]:[],n=(e,t)=>{const o=((e,t)=>{if(!e)return[];if("static"===e.sourceType)return Array.isArray(e.staticData)?e.staticData:[];if("global"===e.sourceType&&e.datasourceId){const o=t?.page?.[e.datasourceId];return a(o)}if("datasource"===e.sourceType&&e.datasourceId){const o=t?.component?.[e.datasourceId];return a(o)}return[]})(e,t);return((e,t)=>t&&0!==t.length?e.map(e=>{const o={};for(const{componentField:a,sourceField:n}of t)a&&n&&(o[a]=r(e,n));return o}):e)(o,e?.fieldMappings)};const s=(e,t)=>{const o=Array.isArray(e)?e:[e],r=o[0]||{};return{sourceType:"static",staticData:o,fieldMappings:Object.keys(r).map(e=>({componentField:e,sourceField:e}))}},i=(e,t,o)=>({type:"group",title:e,setter:{componentName:"CollapseSetter",props:{icon:!1,...o}},items:t}),l={name:"nodeInfo",title:"节点信息",setter:"NodeInfoSetter",extraProps:{label:!1}},p=i("基础配置",[{name:"title",title:"标题",setter:"StringSetter",extraProps:{getValue:e=>e.getExtraPropValue("title"),setValue(e,t){e.setExtraPropValue("title",t)}}},{name:"rect",title:"位置尺寸",setter:"RectSetter",extraProps:{getValue:e=>e.getExtraPropValue("$dashboard.rect"),setValue(e,t){e.setExtraPropValue("$dashboard.rect",t)}}},{name:"rotation",title:"旋转角度",setter:{componentName:"SliderSetter",props:{min:0,max:360,suffix:"°"}},extraProps:{defaultValue:0}},{name:"opacity",title:"不透明度",setter:{componentName:"SliderSetter",props:{min:0,max:100,suffix:"%"}},extraProps:{defaultValue:100}},{name:"background",title:"背景颜色",setter:"ColorSetter",extraProps:{defaultValue:"transparent"}}]),d=[{title:"点击事件",children:[{label:"点击",value:"onClick",description:"鼠标点击时触发"},{label:"双击",value:"onDoubleClick",description:"鼠标双击时触发"}]},{title:"鼠标事件",children:[{label:"鼠标进入",value:"onMouseEnter",description:"鼠标进入时触发"},{label:"鼠标离开",value:"onMouseLeave",description:"鼠标离开时触发"}]}],c=((e=d)=>i("事件绑定",[{name:"events",title:"事件",setter:{componentName:"EventSetter",props:{events:e}},extraProps:{label:!1}}]))(),u=i("高级配置",[{title:"条件渲染",setter:"SwitchSetter",extraProps:{supportVariable:!0,getValue:e=>e.getNode().getExtraPropValue("condition"),setValue(e,t){e.getNode().setExtraProp("condition",t)}}}]);var m="component-module__container___VbZSk",_="component-module__playButton___M6QVA",f="component-module__playIcon___t8-WV",x="component-module__pauseIcon___8XmSk",g="component-module__pauseBar___eM6DG",y="component-module__info___VyTlr",h="component-module__title___Hj54k",b="component-module__progressContainer___HvygE",v="component-module__progressBar___wROZC",S="component-module__progressFill___F8n5d",k="component-module__time___-5GMu",P="component-module__nativeAudio___vCo17";!function(e,t){void 0===t&&(t={});var o=t.insertAt;if("undefined"!=typeof document){var r=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css","top"===o&&r.firstChild?r.insertBefore(a,r.firstChild):r.appendChild(a),a.styleSheet?a.styleSheet.cssText=e:a.appendChild(document.createTextNode(e))}}(".component-module__container___VbZSk{align-items:center;background:rgba(10,10,26,.95);border:1px solid rgba(26,26,62,.8);border-radius:8px;display:flex;gap:12px;height:100%;padding:12px 16px;width:100%}.component-module__playButton___M6QVA{align-items:center;background:linear-gradient(135deg,#00d4ff,#9b59b6);border:none;border-radius:50%;cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;transition:transform .2s ease;width:40px}.component-module__playButton___M6QVA:hover{transform:scale(1.05)}.component-module__playIcon___t8-WV{border-bottom:8px solid transparent;border-left:12px solid #fff;border-top:8px solid transparent;height:0;margin-left:3px;width:0}.component-module__pauseIcon___8XmSk{display:flex;gap:3px}.component-module__pauseBar___eM6DG{background:#fff;border-radius:2px;height:14px;width:4px}.component-module__info___VyTlr{flex:1;min-width:0}.component-module__title___Hj54k{color:#e6e6e6;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.component-module__progressContainer___HvygE{align-items:center;display:flex;gap:8px;margin-top:4px}.component-module__progressBar___wROZC{background:rgba(26,26,62,.8);border-radius:2px;cursor:pointer;flex:1;height:4px;overflow:hidden}.component-module__progressFill___F8n5d{background:linear-gradient(90deg,#00d4ff,#9b59b6);border-radius:2px;height:100%;transition:width .1s linear}.component-module__time___-5GMu{color:hsla(0,0%,100%,.6);font-family:Courier New,Courier,monospace;font-size:12px;min-width:80px;text-align:right}.component-module__nativeAudio___vCo17{width:100%}");const V=e=>`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,"0")}`,E="EasyEditorMaterialsAudio";const N=((e,t)=>({props:[{type:"group",title:"属性",setter:"TabSetter",items:[{type:"group",key:"config",title:"配置",items:[l,p,e]},{type:"group",key:"data",title:"数据",items:[l,t]},{type:"group",key:"advanced",title:"高级",items:[l,c,u]}]}],component:{},supports:{},advanced:{}}))(i("组件配置",[{type:"group",title:"组件配置",setter:"SubTabSetter",items:[{type:"group",key:"content",title:"内容",items:[{name:"__upload",title:"上传",setter:{componentName:"UploadSetter",props:{accept:".mp3,.wav,.ogg"}},extraProps:{setValue(e,t){if(t){const{base64:o,raw:r}=t;o&&e.parent.setPropValue("src",o),r?.width&&e.parent.setExtraPropValue("$dashboard.rect.width",r.width),r?.height&&e.parent.setExtraPropValue("$dashboard.rect.height",r.height)}else e.parent.clearPropValue("src")}}},{name:"src",title:"音频地址",setter:"StringSetter"},{name:"title",title:"标题",setter:"StringSetter",extraProps:{defaultValue:"音频文件"}}]},{type:"group",key:"playback",title:"播放",items:[{name:"autoPlay",title:"自动播放",setter:"SwitchSetter",extraProps:{defaultValue:!1}},{name:"loop",title:"循环播放",setter:"SwitchSetter",extraProps:{defaultValue:!1}},{name:"playbackRate",title:"播放速度",setter:{componentName:"SelectSetter",props:{options:[{label:"0.5x",value:.5},{label:"0.75x",value:.75},{label:"1x (正常)",value:1},{label:"1.25x",value:1.25},{label:"1.5x",value:1.5},{label:"2x",value:2}]}},extraProps:{defaultValue:1}},{name:"volume",title:"音量",setter:{componentName:"SliderSetter",props:{min:0,max:100,step:1,suffix:"%"}},extraProps:{defaultValue:100}}]},{type:"group",key:"style",title:"样式",items:[{name:"audioStyle",title:"样式类型",setter:{componentName:"SegmentedSetter",props:{options:[{label:"自定义",value:"custom"},{label:"原生",value:"native"}]}},extraProps:{defaultValue:"custom"}}]}]}],{padding:"6px 16px 12px"}),{name:"$data",title:"数据配置",setter:{componentName:"DataSetter",props:{expectedFields:[{name:"src",label:"src",type:"string",required:!0,description:"音频地址"}],showPreview:!0,previewLimit:10}},extraProps:{label:!1}});const w={componentName:E,title:"音频",group:"media",devMode:"proCode",npm:{package:"@easy-editor/materials-dashboard-audio",version:"0.0.4",globalName:E,componentName:E},snippets:[{title:"音频播放器",screenshot:"",schema:{componentName:E,title:"音频播放器",props:{$data:s({src:""}),title:"音频文件",autoPlay:!1,loop:!1,audioStyle:"custom",playbackRate:1,volume:100,rotation:0,opacity:100,background:"transparent"},$dashboard:{rect:{width:300,height:60}}}},{title:"原生音频",screenshot:"",schema:{componentName:E,title:"原生音频",props:{$data:s({src:""}),title:"音频文件",audioStyle:"native",rotation:0,opacity:100,background:"transparent"},$dashboard:{rect:{width:300,height:60}}}}],configure:N};e.component=({ref:e,$data:r,__dataSource:a,src:s="",title:i="音频文件",autoPlay:l=!1,loop:p=!1,audioStyle:d="custom",playbackRate:c=1,volume:u=100,rotation:E=0,opacity:N=100,background:w="transparent",style:C,onClick:j,onDoubleClick:M,onMouseEnter:T,onMouseLeave:L,onPlay:A,onPause:$,onEnded:R})=>{const B=function(e,o){return t.useMemo(()=>n(e,o),[e,o])}(r,a),D=t.useMemo(()=>B.length>0&&"string"==typeof B[0]?.src?B[0].src:s,[B,s]),I=t.useRef(null),[F,G]=t.useState(!1),[H,Z]=t.useState(0),[q,O]=t.useState(0);t.useEffect(()=>{const e=I.current;if(!e)return;const t=()=>Z(e.currentTime),o=()=>O(e.duration),r=()=>{G(!1),R?.()},a=()=>{G(!0),A?.()},n=()=>{G(!1),$?.()};return e.addEventListener("timeupdate",t),e.addEventListener("loadedmetadata",o),e.addEventListener("ended",r),e.addEventListener("play",a),e.addEventListener("pause",n),()=>{e.removeEventListener("timeupdate",t),e.removeEventListener("loadedmetadata",o),e.removeEventListener("ended",r),e.removeEventListener("play",a),e.removeEventListener("pause",n)}},[R,A,$]),t.useEffect(()=>{I.current&&(I.current.playbackRate=c,I.current.volume=u/100)},[c,u]);const Q=q?H/q*100:0,X={transform:0!==E?`rotate(${E}deg)`:void 0,opacity:N/100,backgroundColor:w,...C};return"native"===d?o.jsx("div",{className:m,onClick:j,onDoubleClick:M,onMouseEnter:T,onMouseLeave:L,ref:e,style:X,children:o.jsx("audio",{autoPlay:l,className:P,controls:!0,loop:p,ref:I,src:D})}):o.jsxs("div",{className:m,onClick:j,onDoubleClick:M,onMouseEnter:T,onMouseLeave:L,ref:e,style:X,children:[o.jsx("audio",{autoPlay:l,loop:p,ref:I,src:D}),o.jsx("button",{"aria-label":F?"Pause":"Play",className:_,onClick:()=>{I.current&&(F?I.current.pause():I.current.play())},type:"button",children:F?o.jsxs("div",{className:x,children:[o.jsx("div",{className:g}),o.jsx("div",{className:g})]}):o.jsx("div",{className:f})}),o.jsxs("div",{className:y,children:[o.jsx("div",{className:h,children:i}),o.jsxs("div",{className:b,children:[o.jsx("button",{"aria-label":"Seek to position",className:v,onClick:e=>{if(I.current&&q){const t=e.currentTarget.getBoundingClientRect(),o=(e.clientX-t.left)/t.width;I.current.currentTime=o*q}},type:"button",children:o.jsx("div",{className:S,style:{width:`${Q}%`}})}),o.jsxs("span",{className:k,children:[V(H)," / ",V(q||0)]})]})]})]})},e.meta=w});
package/dist/meta.min.js DELETED
@@ -1 +0,0 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).EasyEditorMaterialsAudioMeta={})}(this,function(e){"use strict";const t=(e,t)=>{const a=Array.isArray(e)?e:[e],r=a[0]||{};return{sourceType:"static",staticData:a,fieldMappings:Object.keys(r).map(e=>({componentField:e,sourceField:e}))}},a=(e,t,a)=>({type:"group",title:e,setter:{componentName:"CollapseSetter",props:{icon:!1,...a}},items:t}),r={name:"nodeInfo",title:"节点信息",setter:"NodeInfoSetter",extraProps:{label:!1}},o=a("基础配置",[{name:"title",title:"标题",setter:"StringSetter",extraProps:{getValue:e=>e.getExtraPropValue("title"),setValue(e,t){e.setExtraPropValue("title",t)}}},{name:"rect",title:"位置尺寸",setter:"RectSetter",extraProps:{getValue:e=>e.getExtraPropValue("$dashboard.rect"),setValue(e,t){e.setExtraPropValue("$dashboard.rect",t)}}},{name:"rotation",title:"旋转角度",setter:{componentName:"SliderSetter",props:{min:0,max:360,suffix:"°"}},extraProps:{defaultValue:0}},{name:"opacity",title:"不透明度",setter:{componentName:"SliderSetter",props:{min:0,max:100,suffix:"%"}},extraProps:{defaultValue:100}},{name:"background",title:"背景颜色",setter:"ColorSetter",extraProps:{defaultValue:"transparent"}}]),l=[{title:"点击事件",children:[{label:"点击",value:"onClick",description:"鼠标点击时触发"},{label:"双击",value:"onDoubleClick",description:"鼠标双击时触发"}]},{title:"鼠标事件",children:[{label:"鼠标进入",value:"onMouseEnter",description:"鼠标进入时触发"},{label:"鼠标离开",value:"onMouseLeave",description:"鼠标离开时触发"}]}],i=((e=l)=>a("事件绑定",[{name:"events",title:"事件",setter:{componentName:"EventSetter",props:{events:e}},extraProps:{label:!1}}]))(),s=a("高级配置",[{title:"条件渲染",setter:"SwitchSetter",extraProps:{supportVariable:!0,getValue:e=>e.getNode().getExtraPropValue("condition"),setValue(e,t){e.getNode().setExtraProp("condition",t)}}}]),p="EasyEditorMaterialsAudio";const n=((e,t)=>({props:[{type:"group",title:"属性",setter:"TabSetter",items:[{type:"group",key:"config",title:"配置",items:[r,o,e]},{type:"group",key:"data",title:"数据",items:[r,t]},{type:"group",key:"advanced",title:"高级",items:[r,i,s]}]}],component:{},supports:{},advanced:{}}))(a("组件配置",[{type:"group",title:"组件配置",setter:"SubTabSetter",items:[{type:"group",key:"content",title:"内容",items:[{name:"__upload",title:"上传",setter:{componentName:"UploadSetter",props:{accept:".mp3,.wav,.ogg"}},extraProps:{setValue(e,t){if(t){const{base64:a,raw:r}=t;a&&e.parent.setPropValue("src",a),r?.width&&e.parent.setExtraPropValue("$dashboard.rect.width",r.width),r?.height&&e.parent.setExtraPropValue("$dashboard.rect.height",r.height)}else e.parent.clearPropValue("src")}}},{name:"src",title:"音频地址",setter:"StringSetter"},{name:"title",title:"标题",setter:"StringSetter",extraProps:{defaultValue:"音频文件"}}]},{type:"group",key:"playback",title:"播放",items:[{name:"autoPlay",title:"自动播放",setter:"SwitchSetter",extraProps:{defaultValue:!1}},{name:"loop",title:"循环播放",setter:"SwitchSetter",extraProps:{defaultValue:!1}},{name:"playbackRate",title:"播放速度",setter:{componentName:"SelectSetter",props:{options:[{label:"0.5x",value:.5},{label:"0.75x",value:.75},{label:"1x (正常)",value:1},{label:"1.25x",value:1.25},{label:"1.5x",value:1.5},{label:"2x",value:2}]}},extraProps:{defaultValue:1}},{name:"volume",title:"音量",setter:{componentName:"SliderSetter",props:{min:0,max:100,step:1,suffix:"%"}},extraProps:{defaultValue:100}}]},{type:"group",key:"style",title:"样式",items:[{name:"audioStyle",title:"样式类型",setter:{componentName:"SegmentedSetter",props:{options:[{label:"自定义",value:"custom"},{label:"原生",value:"native"}]}},extraProps:{defaultValue:"custom"}}]}]}],{padding:"6px 16px 12px"}),{name:"$data",title:"数据配置",setter:{componentName:"DataSetter",props:{expectedFields:[{name:"src",label:"src",type:"string",required:!0,description:"音频地址"}],showPreview:!0,previewLimit:10}},extraProps:{label:!1}});const u={componentName:p,title:"音频",group:"media",devMode:"proCode",npm:{package:"@easy-editor/materials-dashboard-audio",version:"0.0.4",globalName:p,componentName:p},snippets:[{title:"音频播放器",screenshot:"",schema:{componentName:p,title:"音频播放器",props:{$data:t({src:""}),title:"音频文件",autoPlay:!1,loop:!1,audioStyle:"custom",playbackRate:1,volume:100,rotation:0,opacity:100,background:"transparent"},$dashboard:{rect:{width:300,height:60}}}},{title:"原生音频",screenshot:"",schema:{componentName:p,title:"原生音频",props:{$data:t({src:""}),title:"音频文件",audioStyle:"native",rotation:0,opacity:100,background:"transparent"},$dashboard:{rect:{width:300,height:60}}}}],configure:n};e.meta=u});