react-modern-audio-player 1.4.0-rc.1 → 1.4.0-rc.2
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/README.md +4 -4
- package/dist/index.es.js +52 -22
- package/dist/types/components/AudioPlayer/Context/StateContext/audio.d.ts +4 -6
- package/dist/types/components/AudioPlayer/Context/StateContext/index.d.ts +5 -2
- package/dist/types/components/AudioPlayer/Context/dispatchContext.d.ts +7 -3
- package/dist/types/components/AudioPlayer/Player/index.d.ts +2 -2
- package/dist/types/components/AudioPlayer/Player/usePropsStateEffect.d.ts +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ function Player (){
|
|
|
74
74
|
```tsx
|
|
75
75
|
interface AudioPlayerProps {
|
|
76
76
|
playList: PlayList;
|
|
77
|
-
audioInitialState?:
|
|
77
|
+
audioInitialState?: InitialStates;
|
|
78
78
|
audioRef?: React.MutableRefObject<HTMLAudioElement>;
|
|
79
79
|
activeUI?: ActiveUI;
|
|
80
80
|
customIcons?: CustomIcons;
|
|
@@ -92,7 +92,7 @@ interface AudioPlayerProps {
|
|
|
92
92
|
Prop | Type | Default
|
|
93
93
|
--- | --- | ---
|
|
94
94
|
`playList` | [PlayList](#playlist) | [ ]
|
|
95
|
-
`audioInitialState` | [
|
|
95
|
+
`audioInitialState` | [InitialStates](#InitialStates) | isPlaying: false </br>repeatType: "ALL" </br>volume: 1
|
|
96
96
|
`activeUI` | [ActiveUI](#activeui) | playButton : true
|
|
97
97
|
`customIcons` | [CustomIcons](#customicons) | undefined
|
|
98
98
|
`coverImgsCss` | [CoverImgsCss](#coverimgscss) | undefined
|
|
@@ -114,10 +114,10 @@ type AudioData = {
|
|
|
114
114
|
};
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
##
|
|
117
|
+
## InitialStates
|
|
118
118
|
|
|
119
119
|
```tsx
|
|
120
|
-
type
|
|
120
|
+
type InitialStates = Omit<
|
|
121
121
|
React.AudioHTMLAttributes<HTMLAudioElement>,
|
|
122
122
|
"autoPlay"
|
|
123
123
|
> & {
|
package/dist/index.es.js
CHANGED
|
@@ -1251,7 +1251,11 @@ const audioPlayerReducer = (state, action) => {
|
|
|
1251
1251
|
return {
|
|
1252
1252
|
...state,
|
|
1253
1253
|
curPlayId: state.playList[randomIdx].id,
|
|
1254
|
-
curIdx: randomIdx
|
|
1254
|
+
curIdx: randomIdx,
|
|
1255
|
+
curAudioState: {
|
|
1256
|
+
...state.curAudioState,
|
|
1257
|
+
isLoadedMetaData: false
|
|
1258
|
+
}
|
|
1255
1259
|
};
|
|
1256
1260
|
}
|
|
1257
1261
|
const infiniteLoopNextIdx = (state.curIdx + 1) % state.playList.length;
|
|
@@ -1282,7 +1286,11 @@ const audioPlayerReducer = (state, action) => {
|
|
|
1282
1286
|
return {
|
|
1283
1287
|
...state,
|
|
1284
1288
|
curPlayId: state.playList[infiniteLoopPrevIdx].id,
|
|
1285
|
-
curIdx: infiniteLoopPrevIdx
|
|
1289
|
+
curIdx: infiniteLoopPrevIdx,
|
|
1290
|
+
curAudioState: {
|
|
1291
|
+
...state.curAudioState,
|
|
1292
|
+
isLoadedMetaData: false
|
|
1293
|
+
}
|
|
1286
1294
|
};
|
|
1287
1295
|
}
|
|
1288
1296
|
case "UPDATE_PLAY_LIST": {
|
|
@@ -1312,11 +1320,16 @@ const audioPlayerReducer = (state, action) => {
|
|
|
1312
1320
|
volume: action.volume
|
|
1313
1321
|
}
|
|
1314
1322
|
};
|
|
1315
|
-
case "
|
|
1323
|
+
case "SET_AUDIO_STATE":
|
|
1316
1324
|
return {
|
|
1317
1325
|
...state,
|
|
1318
|
-
curAudioState: { ...state.curAudioState, ...action.
|
|
1319
|
-
|
|
1326
|
+
curAudioState: { ...state.curAudioState, ...action.audioState }
|
|
1327
|
+
};
|
|
1328
|
+
case "SET_INITIAL_STATES":
|
|
1329
|
+
return {
|
|
1330
|
+
...state,
|
|
1331
|
+
curAudioState: { ...state.curAudioState, ...action.audioState },
|
|
1332
|
+
curPlayId: action.curPlayId
|
|
1320
1333
|
};
|
|
1321
1334
|
case "CHANGE_PLAYING_STATE":
|
|
1322
1335
|
if (action.state !== void 0) {
|
|
@@ -1339,7 +1352,11 @@ const audioPlayerReducer = (state, action) => {
|
|
|
1339
1352
|
return {
|
|
1340
1353
|
...state,
|
|
1341
1354
|
curPlayId: action.currentAudioId,
|
|
1342
|
-
curIdx: action.currentIndex
|
|
1355
|
+
curIdx: action.currentIndex,
|
|
1356
|
+
curAudioState: {
|
|
1357
|
+
...state.curAudioState,
|
|
1358
|
+
isLoadedMetaData: false
|
|
1359
|
+
}
|
|
1343
1360
|
};
|
|
1344
1361
|
case "SET_REPEAT_TYPE":
|
|
1345
1362
|
return {
|
|
@@ -1533,6 +1550,10 @@ const useAudio = () => {
|
|
|
1533
1550
|
return;
|
|
1534
1551
|
const { duration } = e.currentTarget;
|
|
1535
1552
|
resetAudioValues(elementRefs, duration);
|
|
1553
|
+
audioPlayerDispatch({
|
|
1554
|
+
type: "SET_AUDIO_STATE",
|
|
1555
|
+
audioState: { isLoadedMetaData: true }
|
|
1556
|
+
});
|
|
1536
1557
|
},
|
|
1537
1558
|
[elementRefs]
|
|
1538
1559
|
);
|
|
@@ -1546,7 +1567,7 @@ const useAudio = () => {
|
|
|
1546
1567
|
}
|
|
1547
1568
|
}, [elementRefs == null ? void 0 : elementRefs.audioEl, curAudioState.isPlaying, audioPlayerDispatch]);
|
|
1548
1569
|
useEffect(() => {
|
|
1549
|
-
if (!(elementRefs == null ? void 0 : elementRefs.audioEl))
|
|
1570
|
+
if (!(elementRefs == null ? void 0 : elementRefs.audioEl) || !curAudioState.volume)
|
|
1550
1571
|
return;
|
|
1551
1572
|
elementRefs.audioEl.volume = curAudioState.volume;
|
|
1552
1573
|
}, [elementRefs == null ? void 0 : elementRefs.audioEl, curAudioState.volume]);
|
|
@@ -1568,7 +1589,7 @@ const Audio = ({
|
|
|
1568
1589
|
const audioPlayerDispatch = useNonNullableContext(audioPlayerDispatchContext);
|
|
1569
1590
|
const curPlayedAudioData = playList.find((audioData) => audioData.id === curPlayId);
|
|
1570
1591
|
const audioNativeStates = Object.fromEntries(Object.entries(curAudioState).filter((state) => {
|
|
1571
|
-
if (state[0] === "isPlaying" || state[0] === "repeatType" || state[0] === "curPlayId") {
|
|
1592
|
+
if (state[0] === "isPlaying" || state[0] === "repeatType" || state[0] === "curPlayId" || state[0] === "isLoadedMetaData") {
|
|
1572
1593
|
return false;
|
|
1573
1594
|
}
|
|
1574
1595
|
return true;
|
|
@@ -1886,7 +1907,8 @@ const PlayListTriggerBtn = ({
|
|
|
1886
1907
|
const VolumeTriggerBtn = forwardRef((_, ref) => {
|
|
1887
1908
|
const {
|
|
1888
1909
|
curAudioState,
|
|
1889
|
-
customIcons
|
|
1910
|
+
customIcons,
|
|
1911
|
+
elementRefs
|
|
1890
1912
|
} = useNonNullableContext(audioPlayerStateContext);
|
|
1891
1913
|
const audioPlayerDispatch = useNonNullableContext(audioPlayerDispatchContext);
|
|
1892
1914
|
const changeMuteState = useCallback(() => audioPlayerDispatch({
|
|
@@ -1894,6 +1916,7 @@ const VolumeTriggerBtn = forwardRef((_, ref) => {
|
|
|
1894
1916
|
muted: !curAudioState.muted
|
|
1895
1917
|
}), [audioPlayerDispatch, curAudioState.muted]);
|
|
1896
1918
|
const VolumeIcon = useMemo(() => {
|
|
1919
|
+
var _a;
|
|
1897
1920
|
const volumeOpt = {
|
|
1898
1921
|
size: "100%"
|
|
1899
1922
|
};
|
|
@@ -1912,7 +1935,7 @@ const VolumeTriggerBtn = forwardRef((_, ref) => {
|
|
|
1912
1935
|
if (value > 0.5)
|
|
1913
1936
|
return "high";
|
|
1914
1937
|
};
|
|
1915
|
-
switch (volumeState(curAudioState.volume)) {
|
|
1938
|
+
switch (volumeState(curAudioState.volume || ((_a = elementRefs == null ? void 0 : elementRefs.audioEl) == null ? void 0 : _a.volume) || 0)) {
|
|
1916
1939
|
case "mute":
|
|
1917
1940
|
return /* @__PURE__ */ jsx(Icon, {
|
|
1918
1941
|
render: /* @__PURE__ */ jsx(TbVolume3, {
|
|
@@ -2708,11 +2731,13 @@ const useRefsDispatch = ({ refs }, deps) => {
|
|
|
2708
2731
|
}, [audioPlayerDispatch, ...Object.values(refs), ...deps]);
|
|
2709
2732
|
};
|
|
2710
2733
|
const useProgress = () => {
|
|
2711
|
-
const { elementRefs } = useNonNullableContext(
|
|
2734
|
+
const { elementRefs, curAudioState } = useNonNullableContext(
|
|
2735
|
+
audioPlayerStateContext
|
|
2736
|
+
);
|
|
2712
2737
|
const [isTimeChangeActive, setTimeChangeActive] = useState(false);
|
|
2713
2738
|
const moveAudioTime = useCallback(
|
|
2714
2739
|
(e) => {
|
|
2715
|
-
if (!(elementRefs == null ? void 0 : elementRefs.audioEl))
|
|
2740
|
+
if (!(elementRefs == null ? void 0 : elementRefs.audioEl) || !(curAudioState == null ? void 0 : curAudioState.isLoadedMetaData))
|
|
2716
2741
|
return;
|
|
2717
2742
|
const { clientX } = e;
|
|
2718
2743
|
const { clientWidth } = e.currentTarget;
|
|
@@ -2722,7 +2747,7 @@ const useProgress = () => {
|
|
|
2722
2747
|
const curPositionTime = curPositionPercent * elementRefs.audioEl.duration;
|
|
2723
2748
|
elementRefs.audioEl.currentTime = curPositionTime;
|
|
2724
2749
|
},
|
|
2725
|
-
[elementRefs == null ? void 0 : elementRefs.audioEl]
|
|
2750
|
+
[curAudioState == null ? void 0 : curAudioState.isLoadedMetaData, elementRefs == null ? void 0 : elementRefs.audioEl]
|
|
2726
2751
|
);
|
|
2727
2752
|
const setSelectStartActive = useCallback(
|
|
2728
2753
|
(state) => document.onselectstart = () => state,
|
|
@@ -2756,13 +2781,13 @@ const BarProgress = ({
|
|
|
2756
2781
|
curAudioState
|
|
2757
2782
|
} = useNonNullableContext(audioPlayerStateContext);
|
|
2758
2783
|
useEffect(() => {
|
|
2759
|
-
if (!progressBarRef.current || !progressValueRef.current || !progressHandleRef.current || !(elementRefs == null ? void 0 : elementRefs.audioEl) || curAudioState.isPlaying)
|
|
2784
|
+
if (!progressBarRef.current || !progressValueRef.current || !progressHandleRef.current || !(elementRefs == null ? void 0 : elementRefs.audioEl) || !curAudioState.isLoadedMetaData || curAudioState.isPlaying)
|
|
2760
2785
|
return;
|
|
2761
2786
|
const progressBarWidth = progressBarRef.current.clientWidth;
|
|
2762
2787
|
const progressHandlePosition = elementRefs.audioEl.currentTime / elementRefs.audioEl.duration * progressBarWidth;
|
|
2763
2788
|
progressValueRef.current.style.transform = `scaleX(${elementRefs.audioEl.currentTime / elementRefs.audioEl.duration})`;
|
|
2764
2789
|
progressHandleRef.current.style.transform = `translateX(${progressHandlePosition}px)`;
|
|
2765
|
-
}, [isActive]);
|
|
2790
|
+
}, [isActive, curAudioState.isLoadedMetaData]);
|
|
2766
2791
|
const eventProps = useProgress();
|
|
2767
2792
|
return isActive ? /* @__PURE__ */ jsxs(BarProgressWrapper, {
|
|
2768
2793
|
className: "bar-progress-wrapper",
|
|
@@ -7046,7 +7071,9 @@ const useWaveSurfer = (waveformRef) => {
|
|
|
7046
7071
|
return;
|
|
7047
7072
|
elementRefs.audioEl.pause();
|
|
7048
7073
|
elementRefs.waveformInst.load(elementRefs == null ? void 0 : elementRefs.audioEl);
|
|
7049
|
-
|
|
7074
|
+
if (curAudioState.volume) {
|
|
7075
|
+
elementRefs.audioEl.volume = curAudioState.volume;
|
|
7076
|
+
}
|
|
7050
7077
|
if (curAudioState.isPlaying)
|
|
7051
7078
|
(_a = elementRefs == null ? void 0 : elementRefs.audioEl) == null ? void 0 : _a.play();
|
|
7052
7079
|
}, [curPlayId, elementRefs == null ? void 0 : elementRefs.audioEl, elementRefs == null ? void 0 : elementRefs.waveformInst]);
|
|
@@ -7109,10 +7136,10 @@ const WaveformProgress = ({
|
|
|
7109
7136
|
} = useNonNullableContext(audioPlayerStateContext);
|
|
7110
7137
|
useWaveSurfer(waveformRef);
|
|
7111
7138
|
useEffect(() => {
|
|
7112
|
-
if (!isActive || !(elementRefs == null ? void 0 : elementRefs.waveformInst) || !(elementRefs == null ? void 0 : elementRefs.audioEl) || curAudioState.isPlaying)
|
|
7139
|
+
if (!isActive || !(elementRefs == null ? void 0 : elementRefs.waveformInst) || !(elementRefs == null ? void 0 : elementRefs.audioEl) || !curAudioState.isLoadedMetaData || curAudioState.isPlaying)
|
|
7113
7140
|
return;
|
|
7114
7141
|
elementRefs.waveformInst.seekTo(elementRefs.audioEl.currentTime / elementRefs.audioEl.duration);
|
|
7115
|
-
}, [isActive]);
|
|
7142
|
+
}, [isActive, curAudioState.isLoadedMetaData]);
|
|
7116
7143
|
const eventProps = useProgress();
|
|
7117
7144
|
return /* @__PURE__ */ jsx(WaveformWrapper, {
|
|
7118
7145
|
className: "waveform-wrapper",
|
|
@@ -7159,9 +7186,11 @@ Grid.Item = GridItem;
|
|
|
7159
7186
|
const VolumeSlider = ({
|
|
7160
7187
|
placement
|
|
7161
7188
|
}) => {
|
|
7189
|
+
var _a;
|
|
7162
7190
|
const contentRef = useRef(null);
|
|
7163
7191
|
const {
|
|
7164
|
-
curAudioState
|
|
7192
|
+
curAudioState,
|
|
7193
|
+
elementRefs
|
|
7165
7194
|
} = useNonNullableContext(audioPlayerStateContext);
|
|
7166
7195
|
const audioPlayerDispatch = useNonNullableContext(audioPlayerDispatchContext);
|
|
7167
7196
|
const onChangeVolume = useCallback((e) => {
|
|
@@ -7184,7 +7213,7 @@ const VolumeSlider = ({
|
|
|
7184
7213
|
}, [curAudioState.muted, audioPlayerDispatch]);
|
|
7185
7214
|
return /* @__PURE__ */ jsx(VolumeSliderContainer, {
|
|
7186
7215
|
contentPlacement: placement,
|
|
7187
|
-
volumeValue: curAudioState.volume * 100,
|
|
7216
|
+
volumeValue: (curAudioState.volume || ((_a = elementRefs == null ? void 0 : elementRefs.audioEl) == null ? void 0 : _a.volume) || 0) * 100,
|
|
7188
7217
|
ref: contentRef,
|
|
7189
7218
|
className: "volume-content-container",
|
|
7190
7219
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -8059,8 +8088,9 @@ const usePropsStateEffect = ({
|
|
|
8059
8088
|
if (!isMounted || !audioInitialState)
|
|
8060
8089
|
return;
|
|
8061
8090
|
audioPlayerDispatch({
|
|
8062
|
-
type: "
|
|
8063
|
-
audioInitialState
|
|
8091
|
+
type: "SET_INITIAL_STATES",
|
|
8092
|
+
audioState: audioInitialState,
|
|
8093
|
+
curPlayId: audioInitialState.curPlayId
|
|
8064
8094
|
});
|
|
8065
8095
|
}, [audioInitialState, audioPlayerDispatch]);
|
|
8066
8096
|
useEffect(() => {
|
|
@@ -2,9 +2,10 @@ import { ReactNode } from "react";
|
|
|
2
2
|
export declare type AudioNativeProps = Omit<React.AudioHTMLAttributes<HTMLAudioElement>, "autoPlay">;
|
|
3
3
|
export declare type RepeatType = "ALL" | "SHUFFLE" | "ONE" | "NONE";
|
|
4
4
|
export declare type AudioCustomProps = {
|
|
5
|
-
|
|
5
|
+
isLoadedMetaData?: boolean;
|
|
6
|
+
isPlaying?: boolean;
|
|
6
7
|
repeatType?: RepeatType;
|
|
7
|
-
volume
|
|
8
|
+
volume?: number;
|
|
8
9
|
currentTime?: number;
|
|
9
10
|
duration?: number;
|
|
10
11
|
};
|
|
@@ -17,7 +18,4 @@ export declare type AudioData = {
|
|
|
17
18
|
description?: string | ReactNode;
|
|
18
19
|
customTrackInfo?: string | ReactNode;
|
|
19
20
|
};
|
|
20
|
-
export declare type
|
|
21
|
-
export declare type AudioInitialState = Partial<CurAudioState> & {
|
|
22
|
-
curPlayId: number;
|
|
23
|
-
};
|
|
21
|
+
export declare type AudioState = AudioNativeProps & AudioCustomProps;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AudioState } from "./audio";
|
|
2
2
|
import { ActiveUI, CoverImgsCss, ElementRefs, CustomIcons, PlayList } from "./element";
|
|
3
3
|
import { PlayListPlacement, InterfacePlacement, PlayerPlacement, VolumeSliderPlacement } from "./placement";
|
|
4
4
|
export interface AudioPlayerStateContext {
|
|
5
5
|
playList: PlayList;
|
|
6
6
|
curPlayId: number;
|
|
7
7
|
curIdx: number;
|
|
8
|
-
curAudioState:
|
|
8
|
+
curAudioState: AudioState;
|
|
9
9
|
activeUI: ActiveUI;
|
|
10
10
|
playListPlacement: PlayListPlacement;
|
|
11
11
|
playerPlacement?: PlayerPlacement;
|
|
@@ -15,6 +15,9 @@ export interface AudioPlayerStateContext {
|
|
|
15
15
|
customIcons?: CustomIcons;
|
|
16
16
|
coverImgsCss?: CoverImgsCss;
|
|
17
17
|
}
|
|
18
|
+
export declare type InitialStates = AudioState & {
|
|
19
|
+
curPlayId: number;
|
|
20
|
+
};
|
|
18
21
|
export * from "./audio";
|
|
19
22
|
export * from "./element";
|
|
20
23
|
export * from "./placement";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Dispatch } from "react";
|
|
2
|
-
import { RepeatType, PlayList, PlayListPlacement, PlayerPlacement, ActiveUI, ElementRefs, CustomIcons,
|
|
2
|
+
import { RepeatType, PlayList, PlayListPlacement, PlayerPlacement, ActiveUI, ElementRefs, CustomIcons, InterfacePlacement, CoverImgsCss, VolumeSliderPlacement, defaultInterfacePlacementMaxLength, AudioState } from "./StateContext";
|
|
3
3
|
export declare type AudioContextAction<TInterfacePlacementLength extends number = typeof defaultInterfacePlacementMaxLength> = {
|
|
4
4
|
type: "NEXT_AUDIO";
|
|
5
5
|
} | {
|
|
@@ -8,8 +8,12 @@ export declare type AudioContextAction<TInterfacePlacementLength extends number
|
|
|
8
8
|
type: "UPDATE_PLAY_LIST";
|
|
9
9
|
playList: PlayList;
|
|
10
10
|
} | {
|
|
11
|
-
type: "
|
|
12
|
-
|
|
11
|
+
type: "SET_AUDIO_STATE";
|
|
12
|
+
audioState: AudioState;
|
|
13
|
+
} | {
|
|
14
|
+
type: "SET_INITIAL_STATES";
|
|
15
|
+
audioState: AudioState;
|
|
16
|
+
curPlayId: number;
|
|
13
17
|
} | {
|
|
14
18
|
type: "CHANGE_PLAYING_STATE";
|
|
15
19
|
state?: boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ActiveUI, PlayListPlacement, CustomIcons, PlayerPlacement, PlayList,
|
|
1
|
+
import { ActiveUI, PlayListPlacement, CustomIcons, PlayerPlacement, PlayList, InitialStates, InterfacePlacement, CoverImgsCss, VolumeSliderPlacement } from '../../AudioPlayer/Context';
|
|
2
2
|
export interface AudioPlayerProps<TInterfacePlacementLength extends number> {
|
|
3
3
|
children?: React.ReactNode;
|
|
4
4
|
playList: PlayList;
|
|
5
|
-
audioInitialState?:
|
|
5
|
+
audioInitialState?: InitialStates;
|
|
6
6
|
audioRef?: React.MutableRefObject<HTMLAudioElement>;
|
|
7
7
|
activeUI?: ActiveUI;
|
|
8
8
|
customIcons?: CustomIcons;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AudioPlayerProps } from ".";
|
|
2
|
-
export declare const usePropsStateEffect: <TInterfacePlacementLength extends number
|
|
2
|
+
export declare const usePropsStateEffect: <TInterfacePlacementLength extends number>({ placement, activeUI, coverImgsCss, audioInitialState, playList, customIcons, }: Omit<AudioPlayerProps<TInterfacePlacementLength>, "children">) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-modern-audio-player",
|
|
3
|
-
"version": "1.4.0-rc.
|
|
3
|
+
"version": "1.4.0-rc.2",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "LYH",
|
|
6
6
|
"email": "slash9494@naver.com",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"dev": "vite --host",
|
|
24
24
|
"build": "rm -rf dist && tsc && vite build",
|
|
25
|
-
"preview": "vite preview"
|
|
25
|
+
"preview": "vite preview",
|
|
26
|
+
"typeCheck": "tsc --project ./tsconfig.json --noEmit"
|
|
26
27
|
},
|
|
27
28
|
"peerDependencies": {
|
|
28
29
|
"react": ">=16.8.0",
|