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