@waveform-playlist/ui-components 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +98 -43
- package/dist/index.d.ts +98 -43
- package/dist/index.js +563 -155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +551 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -1889,16 +1889,285 @@ var PlayoutProvider = ({ children }) => {
|
|
|
1889
1889
|
var usePlayoutStatus = () => useContext5(PlayoutStatusContext);
|
|
1890
1890
|
var usePlayoutStatusUpdate = () => useContext5(PlayoutStatusUpdateContext);
|
|
1891
1891
|
|
|
1892
|
-
// src/components/
|
|
1892
|
+
// src/components/SpectrogramChannel.tsx
|
|
1893
|
+
import { useLayoutEffect as useLayoutEffect2, useCallback as useCallback3, useRef as useRef4, useEffect as useEffect4 } from "react";
|
|
1894
|
+
import styled19 from "styled-components";
|
|
1893
1895
|
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1894
|
-
var
|
|
1896
|
+
var MAX_CANVAS_WIDTH2 = 1e3;
|
|
1897
|
+
var Wrapper3 = styled19.div.attrs((props) => ({
|
|
1898
|
+
style: {
|
|
1899
|
+
top: `${props.$waveHeight * props.$index}px`,
|
|
1900
|
+
width: `${props.$cssWidth}px`,
|
|
1901
|
+
height: `${props.$waveHeight}px`
|
|
1902
|
+
}
|
|
1903
|
+
}))`
|
|
1904
|
+
position: absolute;
|
|
1905
|
+
background: #000;
|
|
1906
|
+
transform: translateZ(0);
|
|
1907
|
+
backface-visibility: hidden;
|
|
1908
|
+
`;
|
|
1909
|
+
var SpectrogramCanvas = styled19.canvas.attrs((props) => ({
|
|
1910
|
+
style: {
|
|
1911
|
+
width: `${props.$cssWidth}px`,
|
|
1912
|
+
height: `${props.$waveHeight}px`
|
|
1913
|
+
}
|
|
1914
|
+
}))`
|
|
1915
|
+
float: left;
|
|
1916
|
+
position: relative;
|
|
1917
|
+
will-change: transform;
|
|
1918
|
+
image-rendering: pixelated;
|
|
1919
|
+
image-rendering: crisp-edges;
|
|
1920
|
+
`;
|
|
1921
|
+
function defaultGetColorMap() {
|
|
1922
|
+
const lut = new Uint8Array(256 * 3);
|
|
1923
|
+
for (let i = 0; i < 256; i++) {
|
|
1924
|
+
lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = i;
|
|
1925
|
+
}
|
|
1926
|
+
return lut;
|
|
1927
|
+
}
|
|
1928
|
+
var SpectrogramChannel = ({
|
|
1929
|
+
index,
|
|
1930
|
+
data,
|
|
1931
|
+
length,
|
|
1932
|
+
waveHeight,
|
|
1933
|
+
devicePixelRatio = 1,
|
|
1934
|
+
samplesPerPixel,
|
|
1935
|
+
colorLUT,
|
|
1936
|
+
frequencyScaleFn,
|
|
1937
|
+
minFrequency = 0,
|
|
1938
|
+
maxFrequency,
|
|
1939
|
+
workerApi,
|
|
1940
|
+
clipId,
|
|
1941
|
+
onCanvasesReady
|
|
1942
|
+
}) => {
|
|
1943
|
+
const canvasesRef = useRef4([]);
|
|
1944
|
+
const registeredIdsRef = useRef4([]);
|
|
1945
|
+
const isWorkerMode = !!(workerApi && clipId);
|
|
1946
|
+
const canvasRef = useCallback3(
|
|
1947
|
+
(canvas) => {
|
|
1948
|
+
if (canvas !== null) {
|
|
1949
|
+
const idx = parseInt(canvas.dataset.index, 10);
|
|
1950
|
+
canvasesRef.current[idx] = canvas;
|
|
1951
|
+
}
|
|
1952
|
+
},
|
|
1953
|
+
[]
|
|
1954
|
+
);
|
|
1955
|
+
useEffect4(() => {
|
|
1956
|
+
if (!isWorkerMode) return;
|
|
1957
|
+
const canvasCount2 = Math.ceil(length / MAX_CANVAS_WIDTH2);
|
|
1958
|
+
canvasesRef.current.length = canvasCount2;
|
|
1959
|
+
const canvases2 = canvasesRef.current;
|
|
1960
|
+
const ids = [];
|
|
1961
|
+
const widths = [];
|
|
1962
|
+
for (let i = 0; i < canvases2.length; i++) {
|
|
1963
|
+
const canvas = canvases2[i];
|
|
1964
|
+
if (!canvas) continue;
|
|
1965
|
+
const canvasId = `${clipId}-ch${index}-chunk${i}`;
|
|
1966
|
+
try {
|
|
1967
|
+
const offscreen = canvas.transferControlToOffscreen();
|
|
1968
|
+
workerApi.registerCanvas(canvasId, offscreen);
|
|
1969
|
+
ids.push(canvasId);
|
|
1970
|
+
widths.push(Math.min(length - i * MAX_CANVAS_WIDTH2, MAX_CANVAS_WIDTH2));
|
|
1971
|
+
} catch (err) {
|
|
1972
|
+
console.warn(`[spectrogram] transferControlToOffscreen failed for ${canvasId}:`, err);
|
|
1973
|
+
continue;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
registeredIdsRef.current = ids;
|
|
1977
|
+
if (ids.length > 0 && onCanvasesReady) {
|
|
1978
|
+
onCanvasesReady(ids, widths);
|
|
1979
|
+
}
|
|
1980
|
+
return () => {
|
|
1981
|
+
for (const id of registeredIdsRef.current) {
|
|
1982
|
+
workerApi.unregisterCanvas(id);
|
|
1983
|
+
}
|
|
1984
|
+
registeredIdsRef.current = [];
|
|
1985
|
+
};
|
|
1986
|
+
}, [isWorkerMode, clipId, index, length]);
|
|
1987
|
+
const lut = colorLUT ?? defaultGetColorMap();
|
|
1988
|
+
const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
|
|
1989
|
+
const scaleFn = frequencyScaleFn ?? ((f, minF, maxF2) => (f - minF) / (maxF2 - minF));
|
|
1990
|
+
useLayoutEffect2(() => {
|
|
1991
|
+
if (isWorkerMode || !data) return;
|
|
1992
|
+
const canvases2 = canvasesRef.current;
|
|
1993
|
+
const { frequencyBinCount, frameCount, hopSize, sampleRate, gainDb, rangeDb: rawRangeDb } = data;
|
|
1994
|
+
const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
|
|
1995
|
+
let globalPixelOffset = 0;
|
|
1996
|
+
const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
|
|
1997
|
+
for (let canvasIdx = 0; canvasIdx < canvases2.length; canvasIdx++) {
|
|
1998
|
+
const canvas = canvases2[canvasIdx];
|
|
1999
|
+
if (!canvas) continue;
|
|
2000
|
+
const ctx = canvas.getContext("2d");
|
|
2001
|
+
if (!ctx) continue;
|
|
2002
|
+
const canvasWidth = canvas.width / devicePixelRatio;
|
|
2003
|
+
const canvasHeight = waveHeight;
|
|
2004
|
+
ctx.resetTransform();
|
|
2005
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2006
|
+
ctx.imageSmoothingEnabled = false;
|
|
2007
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
2008
|
+
const imgData = ctx.createImageData(canvasWidth, canvasHeight);
|
|
2009
|
+
const pixels = imgData.data;
|
|
2010
|
+
for (let x = 0; x < canvasWidth; x++) {
|
|
2011
|
+
const globalX = globalPixelOffset + x;
|
|
2012
|
+
const samplePos = globalX * samplesPerPixel;
|
|
2013
|
+
const frame = Math.floor(samplePos / hopSize);
|
|
2014
|
+
if (frame < 0 || frame >= frameCount) continue;
|
|
2015
|
+
const frameOffset = frame * frequencyBinCount;
|
|
2016
|
+
for (let y = 0; y < canvasHeight; y++) {
|
|
2017
|
+
const normalizedY = 1 - y / canvasHeight;
|
|
2018
|
+
let bin = Math.floor(normalizedY * frequencyBinCount);
|
|
2019
|
+
if (frequencyScaleFn) {
|
|
2020
|
+
let lo = 0;
|
|
2021
|
+
let hi = frequencyBinCount - 1;
|
|
2022
|
+
while (lo < hi) {
|
|
2023
|
+
const mid = lo + hi >> 1;
|
|
2024
|
+
const freq = binToFreq(mid);
|
|
2025
|
+
const scaled = scaleFn(freq, minFrequency, maxF);
|
|
2026
|
+
if (scaled < normalizedY) {
|
|
2027
|
+
lo = mid + 1;
|
|
2028
|
+
} else {
|
|
2029
|
+
hi = mid;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
bin = lo;
|
|
2033
|
+
}
|
|
2034
|
+
if (bin < 0 || bin >= frequencyBinCount) continue;
|
|
2035
|
+
const db = data.data[frameOffset + bin];
|
|
2036
|
+
const normalized = Math.max(0, Math.min(1, (db + rangeDb + gainDb) / rangeDb));
|
|
2037
|
+
const colorIdx = Math.floor(normalized * 255);
|
|
2038
|
+
const pixelIdx = (y * canvasWidth + x) * 4;
|
|
2039
|
+
pixels[pixelIdx] = lut[colorIdx * 3];
|
|
2040
|
+
pixels[pixelIdx + 1] = lut[colorIdx * 3 + 1];
|
|
2041
|
+
pixels[pixelIdx + 2] = lut[colorIdx * 3 + 2];
|
|
2042
|
+
pixels[pixelIdx + 3] = 255;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
ctx.resetTransform();
|
|
2046
|
+
ctx.putImageData(imgData, 0, 0);
|
|
2047
|
+
if (devicePixelRatio !== 1) {
|
|
2048
|
+
const tmpCanvas = document.createElement("canvas");
|
|
2049
|
+
tmpCanvas.width = canvasWidth;
|
|
2050
|
+
tmpCanvas.height = canvasHeight;
|
|
2051
|
+
const tmpCtx = tmpCanvas.getContext("2d");
|
|
2052
|
+
if (!tmpCtx) continue;
|
|
2053
|
+
tmpCtx.putImageData(imgData, 0, 0);
|
|
2054
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2055
|
+
ctx.imageSmoothingEnabled = false;
|
|
2056
|
+
ctx.drawImage(tmpCanvas, 0, 0, canvas.width, canvas.height);
|
|
2057
|
+
}
|
|
2058
|
+
globalPixelOffset += canvasWidth;
|
|
2059
|
+
}
|
|
2060
|
+
}, [isWorkerMode, data, length, waveHeight, devicePixelRatio, samplesPerPixel, lut, frequencyScaleFn, minFrequency, maxF, scaleFn]);
|
|
2061
|
+
let totalWidth = length;
|
|
2062
|
+
let canvasCount = 0;
|
|
2063
|
+
const canvases = [];
|
|
2064
|
+
while (totalWidth > 0) {
|
|
2065
|
+
const currentWidth = Math.min(totalWidth, MAX_CANVAS_WIDTH2);
|
|
2066
|
+
canvases.push(
|
|
2067
|
+
/* @__PURE__ */ jsx18(
|
|
2068
|
+
SpectrogramCanvas,
|
|
2069
|
+
{
|
|
2070
|
+
$cssWidth: currentWidth,
|
|
2071
|
+
width: currentWidth * devicePixelRatio,
|
|
2072
|
+
height: waveHeight * devicePixelRatio,
|
|
2073
|
+
$waveHeight: waveHeight,
|
|
2074
|
+
"data-index": canvasCount,
|
|
2075
|
+
ref: canvasRef
|
|
2076
|
+
},
|
|
2077
|
+
`${length}-${canvasCount}`
|
|
2078
|
+
)
|
|
2079
|
+
);
|
|
2080
|
+
totalWidth -= currentWidth;
|
|
2081
|
+
canvasCount++;
|
|
2082
|
+
}
|
|
2083
|
+
return /* @__PURE__ */ jsx18(Wrapper3, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
|
|
2084
|
+
};
|
|
2085
|
+
|
|
2086
|
+
// src/components/SmartChannel.tsx
|
|
2087
|
+
import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2088
|
+
var SmartChannel = ({
|
|
2089
|
+
isSelected,
|
|
2090
|
+
transparentBackground,
|
|
2091
|
+
renderMode = "waveform",
|
|
2092
|
+
spectrogramData,
|
|
2093
|
+
spectrogramColorLUT,
|
|
2094
|
+
samplesPerPixel: sppProp,
|
|
2095
|
+
spectrogramFrequencyScaleFn,
|
|
2096
|
+
spectrogramMinFrequency,
|
|
2097
|
+
spectrogramMaxFrequency,
|
|
2098
|
+
spectrogramWorkerApi,
|
|
2099
|
+
spectrogramClipId,
|
|
2100
|
+
spectrogramOnCanvasesReady,
|
|
2101
|
+
...props
|
|
2102
|
+
}) => {
|
|
1895
2103
|
const theme = useTheme2();
|
|
1896
|
-
const { waveHeight, barWidth, barGap } = usePlaylistInfo();
|
|
2104
|
+
const { waveHeight, barWidth, barGap, samplesPerPixel: contextSpp } = usePlaylistInfo();
|
|
1897
2105
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2106
|
+
const samplesPerPixel = sppProp ?? contextSpp;
|
|
1898
2107
|
const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
|
|
1899
2108
|
const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;
|
|
1900
2109
|
const drawMode = theme?.waveformDrawMode || "inverted";
|
|
1901
|
-
|
|
2110
|
+
const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
|
|
2111
|
+
if (renderMode === "spectrogram" && hasSpectrogram) {
|
|
2112
|
+
return /* @__PURE__ */ jsx19(
|
|
2113
|
+
SpectrogramChannel,
|
|
2114
|
+
{
|
|
2115
|
+
index: props.index,
|
|
2116
|
+
data: spectrogramData,
|
|
2117
|
+
length: props.length,
|
|
2118
|
+
waveHeight,
|
|
2119
|
+
devicePixelRatio,
|
|
2120
|
+
samplesPerPixel,
|
|
2121
|
+
colorLUT: spectrogramColorLUT,
|
|
2122
|
+
frequencyScaleFn: spectrogramFrequencyScaleFn,
|
|
2123
|
+
minFrequency: spectrogramMinFrequency,
|
|
2124
|
+
maxFrequency: spectrogramMaxFrequency,
|
|
2125
|
+
workerApi: spectrogramWorkerApi,
|
|
2126
|
+
clipId: spectrogramClipId,
|
|
2127
|
+
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2128
|
+
}
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
if (renderMode === "both" && hasSpectrogram) {
|
|
2132
|
+
const halfHeight = Math.floor(waveHeight / 2);
|
|
2133
|
+
return /* @__PURE__ */ jsxs9(Fragment6, { children: [
|
|
2134
|
+
/* @__PURE__ */ jsx19(
|
|
2135
|
+
SpectrogramChannel,
|
|
2136
|
+
{
|
|
2137
|
+
index: props.index * 2,
|
|
2138
|
+
data: spectrogramData,
|
|
2139
|
+
length: props.length,
|
|
2140
|
+
waveHeight: halfHeight,
|
|
2141
|
+
devicePixelRatio,
|
|
2142
|
+
samplesPerPixel,
|
|
2143
|
+
colorLUT: spectrogramColorLUT,
|
|
2144
|
+
frequencyScaleFn: spectrogramFrequencyScaleFn,
|
|
2145
|
+
minFrequency: spectrogramMinFrequency,
|
|
2146
|
+
maxFrequency: spectrogramMaxFrequency,
|
|
2147
|
+
workerApi: spectrogramWorkerApi,
|
|
2148
|
+
clipId: spectrogramClipId,
|
|
2149
|
+
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2150
|
+
}
|
|
2151
|
+
),
|
|
2152
|
+
/* @__PURE__ */ jsx19("div", { style: { position: "absolute", top: (props.index * 2 + 1) * halfHeight, width: props.length, height: halfHeight }, children: /* @__PURE__ */ jsx19(
|
|
2153
|
+
Channel,
|
|
2154
|
+
{
|
|
2155
|
+
...props,
|
|
2156
|
+
...theme,
|
|
2157
|
+
index: 0,
|
|
2158
|
+
waveOutlineColor,
|
|
2159
|
+
waveFillColor,
|
|
2160
|
+
waveHeight: halfHeight,
|
|
2161
|
+
devicePixelRatio,
|
|
2162
|
+
barWidth,
|
|
2163
|
+
barGap,
|
|
2164
|
+
transparentBackground,
|
|
2165
|
+
drawMode
|
|
2166
|
+
}
|
|
2167
|
+
) })
|
|
2168
|
+
] });
|
|
2169
|
+
}
|
|
2170
|
+
return /* @__PURE__ */ jsx19(
|
|
1902
2171
|
Channel,
|
|
1903
2172
|
{
|
|
1904
2173
|
...props,
|
|
@@ -1915,12 +2184,112 @@ var SmartChannel = ({ isSelected, transparentBackground, ...props }) => {
|
|
|
1915
2184
|
);
|
|
1916
2185
|
};
|
|
1917
2186
|
|
|
2187
|
+
// src/components/SpectrogramLabels.tsx
|
|
2188
|
+
import { useRef as useRef5, useLayoutEffect as useLayoutEffect3 } from "react";
|
|
2189
|
+
import styled20 from "styled-components";
|
|
2190
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2191
|
+
var LABELS_WIDTH = 72;
|
|
2192
|
+
var LabelsStickyWrapper = styled20.div`
|
|
2193
|
+
position: sticky;
|
|
2194
|
+
left: 0;
|
|
2195
|
+
z-index: 101;
|
|
2196
|
+
pointer-events: none;
|
|
2197
|
+
height: 0;
|
|
2198
|
+
width: 0;
|
|
2199
|
+
overflow: visible;
|
|
2200
|
+
`;
|
|
2201
|
+
function getFrequencyLabels(minF, maxF, height) {
|
|
2202
|
+
const allCandidates = [
|
|
2203
|
+
20,
|
|
2204
|
+
50,
|
|
2205
|
+
100,
|
|
2206
|
+
200,
|
|
2207
|
+
500,
|
|
2208
|
+
1e3,
|
|
2209
|
+
2e3,
|
|
2210
|
+
3e3,
|
|
2211
|
+
4e3,
|
|
2212
|
+
5e3,
|
|
2213
|
+
8e3,
|
|
2214
|
+
1e4,
|
|
2215
|
+
12e3,
|
|
2216
|
+
16e3,
|
|
2217
|
+
2e4
|
|
2218
|
+
];
|
|
2219
|
+
const inRange = allCandidates.filter((f) => f >= minF && f <= maxF);
|
|
2220
|
+
const maxLabels = Math.max(2, Math.floor(height / 20));
|
|
2221
|
+
if (inRange.length <= maxLabels) return inRange;
|
|
2222
|
+
const step = (inRange.length - 1) / (maxLabels - 1);
|
|
2223
|
+
const result = [];
|
|
2224
|
+
for (let i = 0; i < maxLabels; i++) {
|
|
2225
|
+
result.push(inRange[Math.round(i * step)]);
|
|
2226
|
+
}
|
|
2227
|
+
return result;
|
|
2228
|
+
}
|
|
2229
|
+
var SpectrogramLabels = ({
|
|
2230
|
+
waveHeight,
|
|
2231
|
+
numChannels,
|
|
2232
|
+
frequencyScaleFn,
|
|
2233
|
+
minFrequency,
|
|
2234
|
+
maxFrequency,
|
|
2235
|
+
labelsColor = "#ccc",
|
|
2236
|
+
labelsBackground = "rgba(0,0,0,0.6)",
|
|
2237
|
+
renderMode = "spectrogram",
|
|
2238
|
+
hasClipHeaders = false
|
|
2239
|
+
}) => {
|
|
2240
|
+
const canvasRef = useRef5(null);
|
|
2241
|
+
const devicePixelRatio = useDevicePixelRatio();
|
|
2242
|
+
const spectrogramHeight = renderMode === "both" ? Math.floor(waveHeight / 2) : waveHeight;
|
|
2243
|
+
const totalHeight = numChannels * waveHeight;
|
|
2244
|
+
const clipHeaderOffset = hasClipHeaders ? 22 : 0;
|
|
2245
|
+
useLayoutEffect3(() => {
|
|
2246
|
+
const canvas = canvasRef.current;
|
|
2247
|
+
if (!canvas) return;
|
|
2248
|
+
const ctx = canvas.getContext("2d");
|
|
2249
|
+
if (!ctx) return;
|
|
2250
|
+
ctx.resetTransform();
|
|
2251
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2252
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
2253
|
+
const labelFreqs = getFrequencyLabels(minFrequency, maxFrequency, spectrogramHeight);
|
|
2254
|
+
for (let ch = 0; ch < numChannels; ch++) {
|
|
2255
|
+
const channelTop = ch * waveHeight + clipHeaderOffset;
|
|
2256
|
+
ctx.font = "11px monospace";
|
|
2257
|
+
ctx.textBaseline = "middle";
|
|
2258
|
+
for (const freq of labelFreqs) {
|
|
2259
|
+
const normalized = frequencyScaleFn(freq, minFrequency, maxFrequency);
|
|
2260
|
+
if (normalized < 0 || normalized > 1) continue;
|
|
2261
|
+
const y = channelTop + spectrogramHeight * (1 - normalized);
|
|
2262
|
+
const text = freq >= 1e3 ? `${(freq / 1e3).toFixed(1)}k` : `${freq} Hz`;
|
|
2263
|
+
const metrics = ctx.measureText(text);
|
|
2264
|
+
const padding = 3;
|
|
2265
|
+
ctx.fillStyle = labelsBackground;
|
|
2266
|
+
ctx.fillRect(0, y - 7, metrics.width + padding * 2, 14);
|
|
2267
|
+
ctx.fillStyle = labelsColor;
|
|
2268
|
+
ctx.fillText(text, padding, y);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}, [waveHeight, numChannels, frequencyScaleFn, minFrequency, maxFrequency, labelsColor, labelsBackground, devicePixelRatio, spectrogramHeight, clipHeaderOffset]);
|
|
2272
|
+
return /* @__PURE__ */ jsx20(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx20(
|
|
2273
|
+
"canvas",
|
|
2274
|
+
{
|
|
2275
|
+
ref: canvasRef,
|
|
2276
|
+
width: LABELS_WIDTH * devicePixelRatio,
|
|
2277
|
+
height: (totalHeight + clipHeaderOffset) * devicePixelRatio,
|
|
2278
|
+
style: {
|
|
2279
|
+
width: LABELS_WIDTH,
|
|
2280
|
+
height: totalHeight + clipHeaderOffset,
|
|
2281
|
+
pointerEvents: "none"
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
) });
|
|
2285
|
+
};
|
|
2286
|
+
|
|
1918
2287
|
// src/components/SmartScale.tsx
|
|
1919
2288
|
import { useContext as useContext7 } from "react";
|
|
1920
2289
|
|
|
1921
2290
|
// src/components/TimeScale.tsx
|
|
1922
|
-
import
|
|
1923
|
-
import
|
|
2291
|
+
import React12, { useRef as useRef6, useEffect as useEffect5, useContext as useContext6 } from "react";
|
|
2292
|
+
import styled21, { withTheme as withTheme2 } from "styled-components";
|
|
1924
2293
|
|
|
1925
2294
|
// src/utils/conversions.ts
|
|
1926
2295
|
function samplesToSeconds(samples, sampleRate) {
|
|
@@ -1943,14 +2312,14 @@ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
|
|
|
1943
2312
|
}
|
|
1944
2313
|
|
|
1945
2314
|
// src/components/TimeScale.tsx
|
|
1946
|
-
import { jsx as
|
|
2315
|
+
import { jsx as jsx21, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1947
2316
|
function formatTime2(milliseconds) {
|
|
1948
2317
|
const seconds = Math.floor(milliseconds / 1e3);
|
|
1949
2318
|
const s = seconds % 60;
|
|
1950
2319
|
const m = (seconds - s) / 60;
|
|
1951
2320
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
1952
2321
|
}
|
|
1953
|
-
var PlaylistTimeScaleScroll =
|
|
2322
|
+
var PlaylistTimeScaleScroll = styled21.div.attrs((props) => ({
|
|
1954
2323
|
style: {
|
|
1955
2324
|
width: `${props.$cssWidth}px`,
|
|
1956
2325
|
marginLeft: `${props.$controlWidth}px`,
|
|
@@ -1962,7 +2331,7 @@ var PlaylistTimeScaleScroll = styled19.div.attrs((props) => ({
|
|
|
1962
2331
|
border-bottom: 1px solid ${(props) => props.theme.timeColor};
|
|
1963
2332
|
box-sizing: border-box;
|
|
1964
2333
|
`;
|
|
1965
|
-
var TimeTicks =
|
|
2334
|
+
var TimeTicks = styled21.canvas.attrs((props) => ({
|
|
1966
2335
|
style: {
|
|
1967
2336
|
width: `${props.$cssWidth}px`,
|
|
1968
2337
|
height: `${props.$timeScaleHeight}px`
|
|
@@ -1973,7 +2342,7 @@ var TimeTicks = styled19.canvas.attrs((props) => ({
|
|
|
1973
2342
|
right: 0;
|
|
1974
2343
|
bottom: 0;
|
|
1975
2344
|
`;
|
|
1976
|
-
var TimeStamp =
|
|
2345
|
+
var TimeStamp = styled21.div.attrs((props) => ({
|
|
1977
2346
|
style: {
|
|
1978
2347
|
left: `${props.$left + 4}px`
|
|
1979
2348
|
// Offset 4px to the right of the tick
|
|
@@ -1995,7 +2364,7 @@ var TimeScale = (props) => {
|
|
|
1995
2364
|
} = props;
|
|
1996
2365
|
const canvasInfo = /* @__PURE__ */ new Map();
|
|
1997
2366
|
const timeMarkers = [];
|
|
1998
|
-
const canvasRef =
|
|
2367
|
+
const canvasRef = useRef6(null);
|
|
1999
2368
|
const {
|
|
2000
2369
|
sampleRate,
|
|
2001
2370
|
samplesPerPixel,
|
|
@@ -2003,7 +2372,7 @@ var TimeScale = (props) => {
|
|
|
2003
2372
|
controls: { show: showControls, width: controlWidth }
|
|
2004
2373
|
} = useContext6(PlaylistInfoContext);
|
|
2005
2374
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2006
|
-
|
|
2375
|
+
useEffect5(() => {
|
|
2007
2376
|
if (canvasRef.current !== null) {
|
|
2008
2377
|
const canvas = canvasRef.current;
|
|
2009
2378
|
const ctx = canvas.getContext("2d");
|
|
@@ -2037,7 +2406,7 @@ var TimeScale = (props) => {
|
|
|
2037
2406
|
if (counter % marker === 0) {
|
|
2038
2407
|
const timeMs = counter;
|
|
2039
2408
|
const timestamp = formatTime2(timeMs);
|
|
2040
|
-
const timestampContent = renderTimestamp ? /* @__PURE__ */
|
|
2409
|
+
const timestampContent = renderTimestamp ? /* @__PURE__ */ jsx21(React12.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx21(TimeStamp, { $left: pix, children: timestamp }, timestamp);
|
|
2041
2410
|
timeMarkers.push(timestampContent);
|
|
2042
2411
|
canvasInfo.set(pix, timeScaleHeight);
|
|
2043
2412
|
} else if (counter % bigStep === 0) {
|
|
@@ -2047,7 +2416,7 @@ var TimeScale = (props) => {
|
|
|
2047
2416
|
}
|
|
2048
2417
|
counter += secondStep;
|
|
2049
2418
|
}
|
|
2050
|
-
return /* @__PURE__ */
|
|
2419
|
+
return /* @__PURE__ */ jsxs10(
|
|
2051
2420
|
PlaylistTimeScaleScroll,
|
|
2052
2421
|
{
|
|
2053
2422
|
$cssWidth: widthX,
|
|
@@ -2055,7 +2424,7 @@ var TimeScale = (props) => {
|
|
|
2055
2424
|
$timeScaleHeight: timeScaleHeight,
|
|
2056
2425
|
children: [
|
|
2057
2426
|
timeMarkers,
|
|
2058
|
-
/* @__PURE__ */
|
|
2427
|
+
/* @__PURE__ */ jsx21(
|
|
2059
2428
|
TimeTicks,
|
|
2060
2429
|
{
|
|
2061
2430
|
$cssWidth: widthX,
|
|
@@ -2072,7 +2441,7 @@ var TimeScale = (props) => {
|
|
|
2072
2441
|
var StyledTimeScale = withTheme2(TimeScale);
|
|
2073
2442
|
|
|
2074
2443
|
// src/components/SmartScale.tsx
|
|
2075
|
-
import { jsx as
|
|
2444
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
2076
2445
|
var timeinfo = /* @__PURE__ */ new Map([
|
|
2077
2446
|
[
|
|
2078
2447
|
700,
|
|
@@ -2145,24 +2514,25 @@ function getScaleInfo(samplesPerPixel) {
|
|
|
2145
2514
|
}
|
|
2146
2515
|
return config;
|
|
2147
2516
|
}
|
|
2148
|
-
var SmartScale = () => {
|
|
2517
|
+
var SmartScale = ({ renderTimestamp }) => {
|
|
2149
2518
|
const { samplesPerPixel, duration } = useContext7(PlaylistInfoContext);
|
|
2150
2519
|
let config = getScaleInfo(samplesPerPixel);
|
|
2151
|
-
return /* @__PURE__ */
|
|
2520
|
+
return /* @__PURE__ */ jsx22(
|
|
2152
2521
|
StyledTimeScale,
|
|
2153
2522
|
{
|
|
2154
2523
|
marker: config.marker,
|
|
2155
2524
|
bigStep: config.bigStep,
|
|
2156
2525
|
secondStep: config.smallStep,
|
|
2157
|
-
duration
|
|
2526
|
+
duration,
|
|
2527
|
+
renderTimestamp
|
|
2158
2528
|
}
|
|
2159
2529
|
);
|
|
2160
2530
|
};
|
|
2161
2531
|
|
|
2162
2532
|
// src/components/TimeFormatSelect.tsx
|
|
2163
|
-
import
|
|
2164
|
-
import { jsx as
|
|
2165
|
-
var SelectWrapper =
|
|
2533
|
+
import styled22 from "styled-components";
|
|
2534
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
2535
|
+
var SelectWrapper = styled22.div`
|
|
2166
2536
|
display: inline-flex;
|
|
2167
2537
|
align-items: center;
|
|
2168
2538
|
gap: 0.5rem;
|
|
@@ -2184,7 +2554,7 @@ var TimeFormatSelect = ({
|
|
|
2184
2554
|
const handleChange = (e) => {
|
|
2185
2555
|
onChange(e.target.value);
|
|
2186
2556
|
};
|
|
2187
|
-
return /* @__PURE__ */
|
|
2557
|
+
return /* @__PURE__ */ jsx23(SelectWrapper, { className, children: /* @__PURE__ */ jsx23(
|
|
2188
2558
|
BaseSelect,
|
|
2189
2559
|
{
|
|
2190
2560
|
className: "time-format",
|
|
@@ -2192,15 +2562,15 @@ var TimeFormatSelect = ({
|
|
|
2192
2562
|
onChange: handleChange,
|
|
2193
2563
|
disabled,
|
|
2194
2564
|
"aria-label": "Time format selection",
|
|
2195
|
-
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */
|
|
2565
|
+
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx23("option", { value: option.value, children: option.label }, option.value))
|
|
2196
2566
|
}
|
|
2197
2567
|
) });
|
|
2198
2568
|
};
|
|
2199
2569
|
|
|
2200
2570
|
// src/components/Track.tsx
|
|
2201
|
-
import
|
|
2202
|
-
import { jsx as
|
|
2203
|
-
var Container =
|
|
2571
|
+
import styled23 from "styled-components";
|
|
2572
|
+
import { jsx as jsx24, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2573
|
+
var Container = styled23.div.attrs((props) => ({
|
|
2204
2574
|
style: {
|
|
2205
2575
|
height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
|
|
2206
2576
|
}
|
|
@@ -2209,7 +2579,7 @@ var Container = styled21.div.attrs((props) => ({
|
|
|
2209
2579
|
display: flex;
|
|
2210
2580
|
${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
|
|
2211
2581
|
`;
|
|
2212
|
-
var ChannelContainer =
|
|
2582
|
+
var ChannelContainer = styled23.div.attrs((props) => ({
|
|
2213
2583
|
style: {
|
|
2214
2584
|
paddingLeft: `${props.$offset || 0}px`
|
|
2215
2585
|
}
|
|
@@ -2218,13 +2588,13 @@ var ChannelContainer = styled21.div.attrs((props) => ({
|
|
|
2218
2588
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
2219
2589
|
flex: 1;
|
|
2220
2590
|
`;
|
|
2221
|
-
var ControlsWrapper =
|
|
2591
|
+
var ControlsWrapper = styled23.div.attrs((props) => ({
|
|
2222
2592
|
style: {
|
|
2223
2593
|
width: `${props.$controlWidth}px`
|
|
2224
2594
|
}
|
|
2225
2595
|
}))`
|
|
2226
2596
|
position: sticky;
|
|
2227
|
-
z-index:
|
|
2597
|
+
z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (200) */
|
|
2228
2598
|
left: 0;
|
|
2229
2599
|
height: 100%;
|
|
2230
2600
|
flex-shrink: 0;
|
|
@@ -2254,7 +2624,7 @@ var Track = ({
|
|
|
2254
2624
|
controls: { show, width: controlWidth }
|
|
2255
2625
|
} = usePlaylistInfo();
|
|
2256
2626
|
const controls = useTrackControls();
|
|
2257
|
-
return /* @__PURE__ */
|
|
2627
|
+
return /* @__PURE__ */ jsxs11(
|
|
2258
2628
|
Container,
|
|
2259
2629
|
{
|
|
2260
2630
|
$numChannels: numChannels,
|
|
@@ -2265,7 +2635,7 @@ var Track = ({
|
|
|
2265
2635
|
$hasClipHeaders: hasClipHeaders,
|
|
2266
2636
|
$isSelected: isSelected,
|
|
2267
2637
|
children: [
|
|
2268
|
-
/* @__PURE__ */
|
|
2638
|
+
/* @__PURE__ */ jsx24(
|
|
2269
2639
|
ControlsWrapper,
|
|
2270
2640
|
{
|
|
2271
2641
|
$controlWidth: show ? controlWidth : 0,
|
|
@@ -2273,7 +2643,7 @@ var Track = ({
|
|
|
2273
2643
|
children: controls
|
|
2274
2644
|
}
|
|
2275
2645
|
),
|
|
2276
|
-
/* @__PURE__ */
|
|
2646
|
+
/* @__PURE__ */ jsx24(
|
|
2277
2647
|
ChannelContainer,
|
|
2278
2648
|
{
|
|
2279
2649
|
$controlWidth: show ? controlWidth : 0,
|
|
@@ -2290,8 +2660,8 @@ var Track = ({
|
|
|
2290
2660
|
};
|
|
2291
2661
|
|
|
2292
2662
|
// src/components/TrackControls/Button.tsx
|
|
2293
|
-
import
|
|
2294
|
-
var Button =
|
|
2663
|
+
import styled24 from "styled-components";
|
|
2664
|
+
var Button = styled24.button.attrs({
|
|
2295
2665
|
type: "button"
|
|
2296
2666
|
})`
|
|
2297
2667
|
display: inline-block;
|
|
@@ -2363,8 +2733,8 @@ var Button = styled22.button.attrs({
|
|
|
2363
2733
|
`;
|
|
2364
2734
|
|
|
2365
2735
|
// src/components/TrackControls/ButtonGroup.tsx
|
|
2366
|
-
import
|
|
2367
|
-
var ButtonGroup =
|
|
2736
|
+
import styled25 from "styled-components";
|
|
2737
|
+
var ButtonGroup = styled25.div`
|
|
2368
2738
|
margin-bottom: 0.3rem;
|
|
2369
2739
|
|
|
2370
2740
|
button:not(:first-child) {
|
|
@@ -2378,9 +2748,39 @@ var ButtonGroup = styled23.div`
|
|
|
2378
2748
|
}
|
|
2379
2749
|
`;
|
|
2380
2750
|
|
|
2751
|
+
// src/components/TrackControls/CloseButton.tsx
|
|
2752
|
+
import styled26 from "styled-components";
|
|
2753
|
+
import { X as XIcon } from "@phosphor-icons/react";
|
|
2754
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
2755
|
+
var StyledCloseButton = styled26.button`
|
|
2756
|
+
position: absolute;
|
|
2757
|
+
left: 0;
|
|
2758
|
+
top: 0;
|
|
2759
|
+
border: none;
|
|
2760
|
+
background: transparent;
|
|
2761
|
+
color: inherit;
|
|
2762
|
+
cursor: pointer;
|
|
2763
|
+
font-size: 16px;
|
|
2764
|
+
padding: 2px 4px;
|
|
2765
|
+
display: flex;
|
|
2766
|
+
align-items: center;
|
|
2767
|
+
justify-content: center;
|
|
2768
|
+
opacity: 0.7;
|
|
2769
|
+
transition: opacity 0.15s, color 0.15s;
|
|
2770
|
+
|
|
2771
|
+
&:hover {
|
|
2772
|
+
opacity: 1;
|
|
2773
|
+
color: #dc3545;
|
|
2774
|
+
}
|
|
2775
|
+
`;
|
|
2776
|
+
var CloseButton = ({
|
|
2777
|
+
onClick,
|
|
2778
|
+
title = "Remove track"
|
|
2779
|
+
}) => /* @__PURE__ */ jsx25(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx25(XIcon, { size: 12, weight: "bold" }) });
|
|
2780
|
+
|
|
2381
2781
|
// src/components/TrackControls/Controls.tsx
|
|
2382
|
-
import
|
|
2383
|
-
var Controls =
|
|
2782
|
+
import styled27 from "styled-components";
|
|
2783
|
+
var Controls = styled27.div`
|
|
2384
2784
|
background: transparent;
|
|
2385
2785
|
width: 100%;
|
|
2386
2786
|
height: 100%;
|
|
@@ -2396,8 +2796,8 @@ var Controls = styled24.div`
|
|
|
2396
2796
|
`;
|
|
2397
2797
|
|
|
2398
2798
|
// src/components/TrackControls/Header.tsx
|
|
2399
|
-
import
|
|
2400
|
-
var Header =
|
|
2799
|
+
import styled28 from "styled-components";
|
|
2800
|
+
var Header = styled28.header`
|
|
2401
2801
|
overflow: hidden;
|
|
2402
2802
|
height: 26px;
|
|
2403
2803
|
width: 100%;
|
|
@@ -2412,22 +2812,27 @@ var Header = styled25.header`
|
|
|
2412
2812
|
|
|
2413
2813
|
// src/components/TrackControls/VolumeDownIcon.tsx
|
|
2414
2814
|
import { SpeakerLowIcon } from "@phosphor-icons/react";
|
|
2415
|
-
import { jsx as
|
|
2416
|
-
var VolumeDownIcon = (props) => /* @__PURE__ */
|
|
2815
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2816
|
+
var VolumeDownIcon = (props) => /* @__PURE__ */ jsx26(SpeakerLowIcon, { weight: "light", ...props });
|
|
2417
2817
|
|
|
2418
2818
|
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
2419
2819
|
import { SpeakerHighIcon } from "@phosphor-icons/react";
|
|
2420
|
-
import { jsx as
|
|
2421
|
-
var VolumeUpIcon = (props) => /* @__PURE__ */
|
|
2820
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
2821
|
+
var VolumeUpIcon = (props) => /* @__PURE__ */ jsx27(SpeakerHighIcon, { weight: "light", ...props });
|
|
2422
2822
|
|
|
2423
2823
|
// src/components/TrackControls/TrashIcon.tsx
|
|
2424
2824
|
import { TrashIcon as PhosphorTrashIcon } from "@phosphor-icons/react";
|
|
2425
|
-
import { jsx as
|
|
2426
|
-
var TrashIcon = (props) => /* @__PURE__ */
|
|
2825
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2826
|
+
var TrashIcon = (props) => /* @__PURE__ */ jsx28(PhosphorTrashIcon, { weight: "light", ...props });
|
|
2827
|
+
|
|
2828
|
+
// src/components/TrackControls/DotsIcon.tsx
|
|
2829
|
+
import { DotsThreeIcon } from "@phosphor-icons/react";
|
|
2830
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
2831
|
+
var DotsIcon = (props) => /* @__PURE__ */ jsx29(DotsThreeIcon, { weight: "bold", ...props });
|
|
2427
2832
|
|
|
2428
2833
|
// src/components/TrackControls/Slider.tsx
|
|
2429
|
-
import
|
|
2430
|
-
var Slider =
|
|
2834
|
+
import styled29 from "styled-components";
|
|
2835
|
+
var Slider = styled29(BaseSlider)`
|
|
2431
2836
|
width: 75%;
|
|
2432
2837
|
height: 5px;
|
|
2433
2838
|
background: ${(props) => props.theme.sliderTrackColor};
|
|
@@ -2479,8 +2884,8 @@ var Slider = styled26(BaseSlider)`
|
|
|
2479
2884
|
`;
|
|
2480
2885
|
|
|
2481
2886
|
// src/components/TrackControls/SliderWrapper.tsx
|
|
2482
|
-
import
|
|
2483
|
-
var SliderWrapper =
|
|
2887
|
+
import styled30 from "styled-components";
|
|
2888
|
+
var SliderWrapper = styled30.label`
|
|
2484
2889
|
width: 100%;
|
|
2485
2890
|
display: flex;
|
|
2486
2891
|
justify-content: space-between;
|
|
@@ -2490,113 +2895,108 @@ var SliderWrapper = styled27.label`
|
|
|
2490
2895
|
font-size: 14px;
|
|
2491
2896
|
`;
|
|
2492
2897
|
|
|
2493
|
-
// src/components/
|
|
2494
|
-
import
|
|
2495
|
-
import {
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
`;
|
|
2502
|
-
var TrackNameSpan = styled28.span`
|
|
2503
|
-
flex: 1;
|
|
2504
|
-
font-weight: 600;
|
|
2505
|
-
font-size: 0.875rem;
|
|
2506
|
-
overflow: hidden;
|
|
2507
|
-
text-overflow: ellipsis;
|
|
2508
|
-
white-space: nowrap;
|
|
2509
|
-
margin: 0 0.25rem;
|
|
2898
|
+
// src/components/TrackMenu.tsx
|
|
2899
|
+
import React14, { useState as useState6, useEffect as useEffect6, useRef as useRef7 } from "react";
|
|
2900
|
+
import { createPortal } from "react-dom";
|
|
2901
|
+
import styled31 from "styled-components";
|
|
2902
|
+
import { jsx as jsx30, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2903
|
+
var MenuContainer = styled31.div`
|
|
2904
|
+
position: relative;
|
|
2905
|
+
display: inline-block;
|
|
2510
2906
|
`;
|
|
2511
|
-
var
|
|
2907
|
+
var MenuButton = styled31.button`
|
|
2908
|
+
background: none;
|
|
2909
|
+
border: none;
|
|
2910
|
+
cursor: pointer;
|
|
2911
|
+
padding: 2px 4px;
|
|
2512
2912
|
display: flex;
|
|
2513
2913
|
align-items: center;
|
|
2514
2914
|
justify-content: center;
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
padding: 0;
|
|
2518
|
-
border: none;
|
|
2519
|
-
background: transparent;
|
|
2520
|
-
color: #999;
|
|
2521
|
-
cursor: pointer;
|
|
2522
|
-
font-size: 16px;
|
|
2523
|
-
line-height: 1;
|
|
2524
|
-
border-radius: 3px;
|
|
2525
|
-
transition: all 0.2s ease-in-out;
|
|
2526
|
-
flex-shrink: 0;
|
|
2915
|
+
color: inherit;
|
|
2916
|
+
opacity: 0.7;
|
|
2527
2917
|
|
|
2528
2918
|
&:hover {
|
|
2529
|
-
|
|
2530
|
-
color: white;
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
&:active {
|
|
2534
|
-
transform: scale(0.9);
|
|
2919
|
+
opacity: 1;
|
|
2535
2920
|
}
|
|
2536
2921
|
`;
|
|
2537
|
-
var
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2922
|
+
var Dropdown = styled31.div`
|
|
2923
|
+
position: fixed;
|
|
2924
|
+
top: ${(p) => p.$top}px;
|
|
2925
|
+
left: ${(p) => p.$left}px;
|
|
2926
|
+
z-index: 10000;
|
|
2927
|
+
background: ${(p) => p.theme.timescaleBackgroundColor ?? "#222"};
|
|
2928
|
+
color: ${(p) => p.theme.textColor ?? "inherit"};
|
|
2929
|
+
border: 1px solid rgba(128, 128, 128, 0.4);
|
|
2930
|
+
border-radius: 6px;
|
|
2931
|
+
padding: 0.5rem 0;
|
|
2932
|
+
min-width: 180px;
|
|
2933
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
2934
|
+
`;
|
|
2935
|
+
var Divider = styled31.hr`
|
|
2936
|
+
border: none;
|
|
2937
|
+
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
|
2938
|
+
margin: 0.35rem 0;
|
|
2939
|
+
`;
|
|
2940
|
+
var TrackMenu = ({
|
|
2941
|
+
items: itemsProp
|
|
2548
2942
|
}) => {
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2943
|
+
const [open, setOpen] = useState6(false);
|
|
2944
|
+
const close = () => setOpen(false);
|
|
2945
|
+
const items = typeof itemsProp === "function" ? itemsProp(close) : itemsProp;
|
|
2946
|
+
const [dropdownPos, setDropdownPos] = useState6({ top: 0, left: 0 });
|
|
2947
|
+
const buttonRef = useRef7(null);
|
|
2948
|
+
const dropdownRef = useRef7(null);
|
|
2949
|
+
useEffect6(() => {
|
|
2950
|
+
if (open && buttonRef.current) {
|
|
2951
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
2952
|
+
setDropdownPos({
|
|
2953
|
+
top: rect.bottom + 2,
|
|
2954
|
+
left: Math.max(0, rect.right - 180)
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
}, [open]);
|
|
2958
|
+
useEffect6(() => {
|
|
2959
|
+
if (!open) return;
|
|
2960
|
+
const handleClick = (e) => {
|
|
2961
|
+
const target = e.target;
|
|
2962
|
+
if (buttonRef.current && !buttonRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target)) {
|
|
2963
|
+
setOpen(false);
|
|
2964
|
+
}
|
|
2965
|
+
};
|
|
2966
|
+
document.addEventListener("mousedown", handleClick);
|
|
2967
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
2968
|
+
}, [open]);
|
|
2969
|
+
return /* @__PURE__ */ jsxs12(MenuContainer, { children: [
|
|
2970
|
+
/* @__PURE__ */ jsx30(
|
|
2971
|
+
MenuButton,
|
|
2972
|
+
{
|
|
2973
|
+
ref: buttonRef,
|
|
2974
|
+
onClick: (e) => {
|
|
2975
|
+
e.stopPropagation();
|
|
2976
|
+
setOpen((prev) => !prev);
|
|
2977
|
+
},
|
|
2978
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2979
|
+
title: "Track menu",
|
|
2980
|
+
"aria-label": "Track menu",
|
|
2981
|
+
children: /* @__PURE__ */ jsx30(DotsIcon, { size: 16 })
|
|
2982
|
+
}
|
|
2983
|
+
),
|
|
2984
|
+
open && typeof document !== "undefined" && createPortal(
|
|
2985
|
+
/* @__PURE__ */ jsx30(
|
|
2986
|
+
Dropdown,
|
|
2590
2987
|
{
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2988
|
+
ref: dropdownRef,
|
|
2989
|
+
$top: dropdownPos.top,
|
|
2990
|
+
$left: dropdownPos.left,
|
|
2991
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2992
|
+
children: items.map((item, index) => /* @__PURE__ */ jsxs12(React14.Fragment, { children: [
|
|
2993
|
+
index > 0 && /* @__PURE__ */ jsx30(Divider, {}),
|
|
2994
|
+
item.content
|
|
2995
|
+
] }, item.id))
|
|
2596
2996
|
}
|
|
2597
2997
|
),
|
|
2598
|
-
|
|
2599
|
-
|
|
2998
|
+
document.body
|
|
2999
|
+
)
|
|
2600
3000
|
] });
|
|
2601
3001
|
};
|
|
2602
3002
|
export {
|
|
@@ -2621,8 +3021,10 @@ export {
|
|
|
2621
3021
|
ClipBoundary,
|
|
2622
3022
|
ClipHeader,
|
|
2623
3023
|
ClipHeaderPresentational,
|
|
3024
|
+
CloseButton,
|
|
2624
3025
|
Controls,
|
|
2625
3026
|
DevicePixelRatioProvider,
|
|
3027
|
+
DotsIcon,
|
|
2626
3028
|
FadeOverlay,
|
|
2627
3029
|
Header,
|
|
2628
3030
|
InlineLabel,
|
|
@@ -2641,6 +3043,8 @@ export {
|
|
|
2641
3043
|
SliderWrapper,
|
|
2642
3044
|
SmartChannel,
|
|
2643
3045
|
SmartScale,
|
|
3046
|
+
SpectrogramChannel,
|
|
3047
|
+
SpectrogramLabels,
|
|
2644
3048
|
StyledPlaylist,
|
|
2645
3049
|
StyledTimeScale,
|
|
2646
3050
|
TimeFormatSelect,
|
|
@@ -2649,7 +3053,7 @@ export {
|
|
|
2649
3053
|
TimescaleLoopRegion,
|
|
2650
3054
|
Track,
|
|
2651
3055
|
TrackControlsContext,
|
|
2652
|
-
|
|
3056
|
+
TrackMenu,
|
|
2653
3057
|
TrashIcon,
|
|
2654
3058
|
VolumeDownIcon,
|
|
2655
3059
|
VolumeUpIcon,
|