Comparing version 0.4.1 to 0.4.5
@@ -105,2 +105,29 @@ import { CSSProperties } from 'react'; | ||
export declare const directions: { | ||
readonly VERTICAL: { | ||
readonly x1: "0"; | ||
readonly y1: "0"; | ||
readonly x2: "0"; | ||
readonly y2: "1"; | ||
}; | ||
readonly HORIZONTAL: { | ||
readonly x1: "0"; | ||
readonly y1: "0"; | ||
readonly x2: "1"; | ||
readonly y2: "0"; | ||
}; | ||
readonly DIAGONAL_TL_BR: { | ||
readonly x1: "0"; | ||
readonly y1: "0"; | ||
readonly x2: "1"; | ||
readonly y2: "1"; | ||
}; | ||
readonly DIAGONAL_BL_TR: { | ||
readonly x1: "0"; | ||
readonly y1: "1"; | ||
readonly x2: "1"; | ||
readonly y2: "0"; | ||
}; | ||
}; | ||
declare type Exact<P, I extends P> = P & Record<Exclude<keyof I, keyof P>, never>; | ||
@@ -642,2 +669,4 @@ | ||
export declare type GraphGradientDirection = keyof typeof directions; | ||
export declare type GraphPoint = { | ||
@@ -687,2 +716,3 @@ x: number; | ||
stop: CSSProperties['color']; | ||
direction: GraphGradientDirection; | ||
}; | ||
@@ -689,0 +719,0 @@ label: { |
1089
dist/index.js
@@ -1,4 +0,556 @@ | ||
import React, { useState, useCallback, useMemo, useEffect, useRef, forwardRef, useImperativeHandle, createContext, useContext } from 'react'; | ||
import React, { forwardRef, useRef, useImperativeHandle, createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react'; | ||
import { jsxs, Fragment, jsx } from 'react/jsx-runtime'; | ||
const GraphGainGrid = () => { | ||
const { | ||
height, | ||
scale: { minGain, maxGain, dbSteps }, | ||
theme: { | ||
background: { | ||
grid: { dotted, lineColor, lineWidth }, | ||
label: { color: labelColor, fontSize, fontFamily } | ||
} | ||
} | ||
} = useGraph(); | ||
if (!dbSteps) return null; | ||
const steps = dbSteps || maxGain; | ||
const dBs = Array.from( | ||
{ length: (maxGain - minGain) / steps + 1 }, | ||
(_, i) => { | ||
return maxGain - i * steps; | ||
} | ||
); | ||
const centerY = getCenterLine(minGain, maxGain, height); | ||
const strokeDasharray = "1,2"; | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
dBs.slice(0, -1).map((tick, index) => { | ||
if (index === 0) return null; | ||
const tickY = `${index / (dBs.length - 1) * 100}%`; | ||
const tickLabel = tick > 0 ? `+${tick}` : tick; | ||
return /* @__PURE__ */ jsxs(React.Fragment, { children: [ | ||
/* @__PURE__ */ jsx( | ||
"line", | ||
{ | ||
x1: "0", | ||
x2: "100%", | ||
y1: tickY, | ||
y2: tickY, | ||
stroke: lineColor, | ||
strokeWidth: lineWidth.minor, | ||
...dotted ? { strokeDasharray } : {} | ||
} | ||
), | ||
index !== 0 && index !== dBs.length - 1 && /* @__PURE__ */ jsx( | ||
"text", | ||
{ | ||
x: 3, | ||
y: tickY, | ||
fill: labelColor, | ||
fontSize, | ||
fontFamily, | ||
textAnchor: "start", | ||
transform: "translate(0 -3)", | ||
children: tickLabel | ||
} | ||
) | ||
] }, tick); | ||
}), | ||
/* @__PURE__ */ jsx( | ||
"line", | ||
{ | ||
id: "centerLine", | ||
x1: "0", | ||
x2: "100%", | ||
y1: centerY, | ||
y2: centerY, | ||
stroke: lineColor, | ||
strokeWidth: lineWidth.center, | ||
...dotted ? { strokeDasharray } : {} | ||
} | ||
), | ||
/* @__PURE__ */ jsx( | ||
"text", | ||
{ | ||
y: 12, | ||
x: 5, | ||
fill: labelColor, | ||
fontSize, | ||
fontFamily, | ||
children: "dB" | ||
} | ||
) | ||
] }); | ||
}; | ||
const GraphFrequencyGrid = () => { | ||
const { | ||
height, | ||
logScale, | ||
scale: { octaveLabels, octaveTicks, majorTicks }, | ||
theme: { | ||
background: { | ||
grid: { dotted, lineColor, lineWidth }, | ||
label: { color: labelColor, fontSize, fontFamily } | ||
} | ||
} | ||
} = useGraph(); | ||
const ticks = octaveTicks ? logScale.ticks(octaveTicks) : [0, 0]; | ||
const strokeDasharray = "1,2"; | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
ticks.slice(1, -1).map((tick) => { | ||
const tickX = logScale.x(tick); | ||
const width = majorTicks.includes(tick) ? lineWidth.major : lineWidth.minor; | ||
return /* @__PURE__ */ jsx( | ||
"line", | ||
{ | ||
x1: tickX, | ||
x2: tickX, | ||
y1: "0", | ||
y2: "100%", | ||
stroke: lineColor, | ||
strokeWidth: width, | ||
...dotted ? { strokeDasharray } : {} | ||
}, | ||
tick | ||
); | ||
}), | ||
octaveLabels.map((octave) => { | ||
const octaveX = logScale.x(octave); | ||
return /* @__PURE__ */ jsx( | ||
"text", | ||
{ | ||
y: height - 4, | ||
x: octaveX + (octave > 1e4 ? -4 : 4), | ||
textAnchor: octave > 1e4 ? "end" : "start", | ||
fill: labelColor, | ||
fontSize, | ||
fontFamily, | ||
children: (octave < 1e3 ? octave : `${octave / 1e3}k`) + "Hz" | ||
}, | ||
octave | ||
); | ||
}) | ||
] }); | ||
}; | ||
function getDefaultExportFromCjs (x) { | ||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; | ||
} | ||
var cjs; | ||
var hasRequiredCjs; | ||
function requireCjs () { | ||
if (hasRequiredCjs) return cjs; | ||
hasRequiredCjs = 1; | ||
var isMergeableObject = function isMergeableObject(value) { | ||
return isNonNullObject(value) | ||
&& !isSpecial(value) | ||
}; | ||
function isNonNullObject(value) { | ||
return !!value && typeof value === 'object' | ||
} | ||
function isSpecial(value) { | ||
var stringValue = Object.prototype.toString.call(value); | ||
return stringValue === '[object RegExp]' | ||
|| stringValue === '[object Date]' | ||
|| isReactElement(value) | ||
} | ||
// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25 | ||
var canUseSymbol = typeof Symbol === 'function' && Symbol.for; | ||
var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7; | ||
function isReactElement(value) { | ||
return value.$$typeof === REACT_ELEMENT_TYPE | ||
} | ||
function emptyTarget(val) { | ||
return Array.isArray(val) ? [] : {} | ||
} | ||
function cloneUnlessOtherwiseSpecified(value, options) { | ||
return (options.clone !== false && options.isMergeableObject(value)) | ||
? deepmerge(emptyTarget(value), value, options) | ||
: value | ||
} | ||
function defaultArrayMerge(target, source, options) { | ||
return target.concat(source).map(function(element) { | ||
return cloneUnlessOtherwiseSpecified(element, options) | ||
}) | ||
} | ||
function getMergeFunction(key, options) { | ||
if (!options.customMerge) { | ||
return deepmerge | ||
} | ||
var customMerge = options.customMerge(key); | ||
return typeof customMerge === 'function' ? customMerge : deepmerge | ||
} | ||
function getEnumerableOwnPropertySymbols(target) { | ||
return Object.getOwnPropertySymbols | ||
? Object.getOwnPropertySymbols(target).filter(function(symbol) { | ||
return Object.propertyIsEnumerable.call(target, symbol) | ||
}) | ||
: [] | ||
} | ||
function getKeys(target) { | ||
return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target)) | ||
} | ||
function propertyIsOnObject(object, property) { | ||
try { | ||
return property in object | ||
} catch(_) { | ||
return false | ||
} | ||
} | ||
// Protects from prototype poisoning and unexpected merging up the prototype chain. | ||
function propertyIsUnsafe(target, key) { | ||
return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet, | ||
&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain, | ||
&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable. | ||
} | ||
function mergeObject(target, source, options) { | ||
var destination = {}; | ||
if (options.isMergeableObject(target)) { | ||
getKeys(target).forEach(function(key) { | ||
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options); | ||
}); | ||
} | ||
getKeys(source).forEach(function(key) { | ||
if (propertyIsUnsafe(target, key)) { | ||
return | ||
} | ||
if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) { | ||
destination[key] = getMergeFunction(key, options)(target[key], source[key], options); | ||
} else { | ||
destination[key] = cloneUnlessOtherwiseSpecified(source[key], options); | ||
} | ||
}); | ||
return destination | ||
} | ||
function deepmerge(target, source, options) { | ||
options = options || {}; | ||
options.arrayMerge = options.arrayMerge || defaultArrayMerge; | ||
options.isMergeableObject = options.isMergeableObject || isMergeableObject; | ||
// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge() | ||
// implementations can use it. The caller may not replace it. | ||
options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified; | ||
var sourceIsArray = Array.isArray(source); | ||
var targetIsArray = Array.isArray(target); | ||
var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray; | ||
if (!sourceAndTargetTypesMatch) { | ||
return cloneUnlessOtherwiseSpecified(source, options) | ||
} else if (sourceIsArray) { | ||
return options.arrayMerge(target, source, options) | ||
} else { | ||
return mergeObject(target, source, options) | ||
} | ||
} | ||
deepmerge.all = function deepmergeAll(array, options) { | ||
if (!Array.isArray(array)) { | ||
throw new Error('first argument should be an array') | ||
} | ||
return array.reduce(function(prev, next) { | ||
return deepmerge(prev, next, options) | ||
}, {}) | ||
}; | ||
var deepmerge_1 = deepmerge; | ||
cjs = deepmerge_1; | ||
return cjs; | ||
} | ||
var cjsExports = requireCjs(); | ||
const merge = /*@__PURE__*/getDefaultExportFromCjs(cjsExports); | ||
const defaultScale = { | ||
minFreq: 20, | ||
maxFreq: 2e4, | ||
sampleRate: 44100, | ||
// 48000 / 96000 / 192000 | ||
minGain: -16, | ||
maxGain: 16, | ||
dbSteps: 4, | ||
// 0 to disable | ||
octaveTicks: 10, | ||
// ticks per octave (0 to disable) | ||
octaveLabels: [20, 40, 60, 100, 200, 500, 1e3, 2e3, 5e3, 1e4, 2e4], | ||
majorTicks: [100, 1e3, 1e4] | ||
// ticks with the major line width, same as zero gain | ||
}; | ||
const defaultTheme = { | ||
background: { | ||
// background grid lines | ||
grid: { | ||
dotted: false, | ||
lineColor: "#3D4C5F", | ||
lineWidth: { minor: 0.25, major: 0.5, center: 1, border: 0.25 } | ||
}, | ||
// background gradient | ||
gradient: { | ||
start: "#1E2530", | ||
stop: "#000000", | ||
direction: "VERTICAL" | ||
}, | ||
// frequency and gain labels | ||
label: { | ||
fontSize: 10, | ||
fontFamily: "sans-serif", | ||
color: "#626F84" | ||
}, | ||
// mouse tracker | ||
tracker: { | ||
lineWidth: 0.5, | ||
lineColor: "#7B899D", | ||
labelColor: "#626F84", | ||
backgroundColor: "#070C18" | ||
} | ||
}, | ||
// basic frequency response and composite curves | ||
curve: { | ||
width: 1.5, | ||
opacity: 1, | ||
color: "#FFFFFF" | ||
}, | ||
filters: { | ||
// filter curves | ||
curve: { | ||
width: { normal: 1, active: 1 }, | ||
opacity: { normal: 0.5, active: 0.7 } | ||
}, | ||
// filter points | ||
point: { | ||
radius: 16, | ||
lineWidth: 2, | ||
backgroundOpacity: { | ||
normal: 0.2, | ||
active: 0.6, | ||
drag: 0.8 | ||
}, | ||
// label inside the point | ||
// size and color applicable to filter icons as well | ||
label: { | ||
fontSize: 24, | ||
fontFamily: "monospace", | ||
color: "inherit" | ||
} | ||
}, | ||
// styles of the empty / zero gain filters | ||
zeroPoint: { | ||
color: "#626F84", | ||
background: "#97A3B4" | ||
}, | ||
gradientOpacity: 0.7, | ||
fill: false, | ||
// default color for filters, points, and curves | ||
defaultColor: "#66FF66", | ||
// empty placeholder of filter colors | ||
colors: [] | ||
} | ||
}; | ||
const FrequencyResponseGraph = forwardRef((props, forwardedRef) => { | ||
const ref = useRef(null); | ||
useImperativeHandle(forwardedRef, () => ref.current); | ||
const { | ||
width, | ||
height, | ||
scale = {}, | ||
theme = {}, | ||
style = {}, | ||
className = "", | ||
children | ||
} = props; | ||
const mergedTheme = merge(defaultTheme, theme); | ||
const mergedScale = merge(defaultScale, scale, { | ||
arrayMerge: (_, source) => source | ||
// overwrite arrays | ||
}); | ||
const { minFreq, maxFreq } = mergedScale; | ||
const logScale = getLogScaleFn(minFreq, maxFreq, width); | ||
FrequencyResponseGraph.displayName = "FrequencyResponseGraph"; | ||
const graphId = `frequency-response-graph-${String(Math.random()).slice(2, 9)}`; | ||
const resetStyles = ` | ||
#${graphId} * { | ||
pointer-events: none; | ||
}`; | ||
return /* @__PURE__ */ jsxs( | ||
"svg", | ||
{ | ||
ref, | ||
id: graphId, | ||
className, | ||
viewBox: `0 0 ${width} ${height}`, | ||
style: { | ||
width, | ||
height, | ||
position: "relative", | ||
verticalAlign: "middle", | ||
userSelect: "none", | ||
...style | ||
}, | ||
children: [ | ||
/* @__PURE__ */ jsx("style", { children: resetStyles }), | ||
/* @__PURE__ */ jsxs( | ||
GraphProvider, | ||
{ | ||
svgRef: ref, | ||
width, | ||
height, | ||
theme: mergedTheme, | ||
scale: mergedScale, | ||
logScale, | ||
children: [ | ||
/* @__PURE__ */ jsx(GraphGradient, {}), | ||
/* @__PURE__ */ jsx(GraphGainGrid, {}), | ||
/* @__PURE__ */ jsx(GraphFrequencyGrid, {}), | ||
children | ||
] | ||
} | ||
) | ||
] | ||
} | ||
); | ||
}); | ||
const GraphContext = createContext( | ||
undefined | ||
); | ||
const GraphProvider = ({ | ||
children, | ||
svgRef, | ||
scale, | ||
logScale, | ||
height, | ||
width, | ||
theme | ||
}) => { | ||
const contextValue = { | ||
svgRef, | ||
theme, | ||
scale, | ||
logScale, | ||
height, | ||
width | ||
}; | ||
return /* @__PURE__ */ jsx(GraphContext.Provider, { value: contextValue, children }); | ||
}; | ||
const useGraph = () => { | ||
const context = useContext(GraphContext); | ||
if (context === undefined) { | ||
throw new Error( | ||
"useGraph must be used within an FrequencyResponseGraphProvider" | ||
); | ||
} | ||
return context; | ||
}; | ||
const directions = { | ||
VERTICAL: { | ||
x1: "0", | ||
y1: "0", | ||
x2: "0", | ||
y2: "1" | ||
}, | ||
HORIZONTAL: { | ||
x1: "0", | ||
y1: "0", | ||
x2: "1", | ||
y2: "0" | ||
}, | ||
DIAGONAL_TL_BR: { | ||
x1: "0", | ||
y1: "0", | ||
x2: "1", | ||
y2: "1" | ||
}, | ||
DIAGONAL_BL_TR: { | ||
x1: "0", | ||
y1: "1", | ||
x2: "1", | ||
y2: "0" | ||
} | ||
}; | ||
const GraphGradient = () => { | ||
const { | ||
theme: { | ||
background: { | ||
gradient: { start, stop, direction }, | ||
grid: { | ||
lineColor, | ||
lineWidth: { border: borderWidth } | ||
} | ||
} | ||
} | ||
} = useGraph(); | ||
const id = `gBg${Math.random().toString().substring(2, 9)}`; | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
/* @__PURE__ */ jsxs( | ||
"linearGradient", | ||
{ | ||
id, | ||
...directions[direction], | ||
children: [ | ||
/* @__PURE__ */ jsx( | ||
"stop", | ||
{ | ||
offset: "0%", | ||
stopColor: start | ||
} | ||
), | ||
/* @__PURE__ */ jsx( | ||
"stop", | ||
{ | ||
offset: "100%", | ||
stopColor: stop | ||
} | ||
) | ||
] | ||
} | ||
), | ||
/* @__PURE__ */ jsx( | ||
"rect", | ||
{ | ||
x: "0", | ||
y: "0", | ||
width: "100%", | ||
height: "100%", | ||
fill: `url(#${id})` | ||
} | ||
), | ||
Boolean(borderWidth) && /* @__PURE__ */ jsx( | ||
"rect", | ||
{ | ||
x: borderWidth / 2, | ||
y: borderWidth / 2, | ||
width: `calc(100% - ${borderWidth}px)`, | ||
height: `calc(100% - ${borderWidth}px)`, | ||
fill: "none", | ||
stroke: lineColor, | ||
strokeWidth: borderWidth | ||
} | ||
) | ||
] }); | ||
}; | ||
const filterTypes = { | ||
@@ -343,88 +895,2 @@ BYPASS: 0, | ||
const defaultScale = { | ||
minFreq: 20, | ||
maxFreq: 2e4, | ||
sampleRate: 44100, | ||
// 48000 / 96000 / 192000 | ||
minGain: -16, | ||
maxGain: 16, | ||
dbSteps: 4, | ||
// 0 to disable | ||
octaveTicks: 10, | ||
// ticks per octave (0 to disable) | ||
octaveLabels: [20, 40, 60, 100, 200, 500, 1e3, 2e3, 5e3, 1e4, 2e4], | ||
majorTicks: [100, 1e3, 1e4] | ||
// ticks with the major line width, same as zero gain | ||
}; | ||
const defaultTheme = { | ||
background: { | ||
// background grid lines | ||
grid: { | ||
dotted: false, | ||
lineColor: "#3D4C5F", | ||
lineWidth: { minor: 0.25, major: 0.5, center: 1, border: 0.25 } | ||
}, | ||
// background gradient | ||
gradient: { | ||
start: "#1E2530", | ||
stop: "#000000" | ||
}, | ||
// frequency and gain labels | ||
label: { | ||
fontSize: 10, | ||
fontFamily: "sans-serif", | ||
color: "#626F84" | ||
}, | ||
// mouse tracker | ||
tracker: { | ||
lineWidth: 0.5, | ||
lineColor: "#7B899D", | ||
labelColor: "#626F84", | ||
backgroundColor: "#070C18" | ||
} | ||
}, | ||
// basic frequency response and composite curves | ||
curve: { | ||
width: 1.5, | ||
opacity: 1, | ||
color: "#FFFFFF" | ||
}, | ||
filters: { | ||
// filter curves | ||
curve: { | ||
width: { normal: 1, active: 1 }, | ||
opacity: { normal: 0.5, active: 0.7 } | ||
}, | ||
// filter points | ||
point: { | ||
radius: 16, | ||
lineWidth: 2, | ||
backgroundOpacity: { | ||
normal: 0.2, | ||
active: 0.6, | ||
drag: 0.8 | ||
}, | ||
// label inside the point | ||
// size and color applicable to filter icons as well | ||
label: { | ||
fontSize: 24, | ||
fontFamily: "monospace", | ||
color: "inherit" | ||
} | ||
}, | ||
// styles of the empty / zero gain filters | ||
zeroPoint: { | ||
color: "#626F84", | ||
background: "#97A3B4" | ||
}, | ||
gradientOpacity: 0.7, | ||
fill: false, | ||
// default color for filters, points, and curves | ||
defaultColor: "#66FF66", | ||
// empty placeholder of filter colors | ||
colors: [] | ||
} | ||
}; | ||
const getPointerPosition = (e) => { | ||
@@ -531,3 +997,3 @@ const CTM = e.target.getScreenCTM(); | ||
const vars = calcBiQuadCoefficients(type, freq, gain, q, sampleRate); | ||
newCache[key] = calcMagnitudes(vars, steps, minFreq, maxFreq, sampleRate); | ||
newCache[key] = calcMagnitudes(vars, steps, minFreq, maxFreq, sampleRate) || []; | ||
} | ||
@@ -592,3 +1058,3 @@ }); | ||
const filterHash = JSON.stringify(filter); | ||
if (prevFilterHashRef.current !== filterHash) { | ||
if (vars && prevFilterHashRef.current !== filterHash) { | ||
onVarsChange?.(index, vars); | ||
@@ -1108,445 +1574,2 @@ prevFilterHashRef.current = filterHash; | ||
const GraphGainGrid = () => { | ||
const { | ||
height, | ||
scale: { minGain, maxGain, dbSteps }, | ||
theme: { | ||
background: { | ||
grid: { dotted, lineColor, lineWidth }, | ||
label: { color: labelColor, fontSize, fontFamily } | ||
} | ||
} | ||
} = useGraph(); | ||
if (!dbSteps) return null; | ||
const steps = dbSteps || maxGain; | ||
const dBs = Array.from( | ||
{ length: (maxGain - minGain) / steps + 1 }, | ||
(_, i) => { | ||
return maxGain - i * steps; | ||
} | ||
); | ||
const centerY = getCenterLine(minGain, maxGain, height); | ||
const strokeDasharray = "1,2"; | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
dBs.slice(0, -1).map((tick, index) => { | ||
if (index === 0) return null; | ||
const tickY = `${index / (dBs.length - 1) * 100}%`; | ||
const tickLabel = tick > 0 ? `+${tick}` : tick; | ||
return /* @__PURE__ */ jsxs(React.Fragment, { children: [ | ||
/* @__PURE__ */ jsx( | ||
"line", | ||
{ | ||
x1: "0", | ||
x2: "100%", | ||
y1: tickY, | ||
y2: tickY, | ||
stroke: lineColor, | ||
strokeWidth: lineWidth.minor, | ||
...dotted ? { strokeDasharray } : {} | ||
} | ||
), | ||
index !== 0 && index !== dBs.length - 1 && /* @__PURE__ */ jsx( | ||
"text", | ||
{ | ||
x: 3, | ||
y: tickY, | ||
fill: labelColor, | ||
fontSize, | ||
fontFamily, | ||
textAnchor: "start", | ||
transform: "translate(0 -3)", | ||
children: tickLabel | ||
} | ||
) | ||
] }, tick); | ||
}), | ||
/* @__PURE__ */ jsx( | ||
"line", | ||
{ | ||
id: "centerLine", | ||
x1: "0", | ||
x2: "100%", | ||
y1: centerY, | ||
y2: centerY, | ||
stroke: lineColor, | ||
strokeWidth: lineWidth.center, | ||
...dotted ? { strokeDasharray } : {} | ||
} | ||
), | ||
/* @__PURE__ */ jsx( | ||
"text", | ||
{ | ||
y: 12, | ||
x: 5, | ||
fill: labelColor, | ||
fontSize, | ||
fontFamily, | ||
children: "dB" | ||
} | ||
) | ||
] }); | ||
}; | ||
const GraphFrequencyGrid = () => { | ||
const { | ||
height, | ||
logScale, | ||
scale: { octaveLabels, octaveTicks, majorTicks }, | ||
theme: { | ||
background: { | ||
grid: { dotted, lineColor, lineWidth }, | ||
label: { color: labelColor, fontSize, fontFamily } | ||
} | ||
} | ||
} = useGraph(); | ||
const ticks = octaveTicks ? logScale.ticks(octaveTicks) : [0, 0]; | ||
const lastOctave = octaveLabels.at(-1); | ||
const strokeDasharray = "1,2"; | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
ticks.slice(1, -1).map((tick) => { | ||
const tickX = logScale.x(tick); | ||
const width = majorTicks.includes(tick) ? lineWidth.major : lineWidth.minor; | ||
return /* @__PURE__ */ jsx( | ||
"line", | ||
{ | ||
x1: tickX, | ||
x2: tickX, | ||
y1: "0", | ||
y2: "100%", | ||
stroke: lineColor, | ||
strokeWidth: width, | ||
...dotted ? { strokeDasharray } : {} | ||
}, | ||
tick | ||
); | ||
}), | ||
octaveLabels.map((octave) => { | ||
const octaveX = logScale.x(octave); | ||
return /* @__PURE__ */ jsx( | ||
"text", | ||
{ | ||
y: height - 4, | ||
x: octaveX + (octave === lastOctave ? -4 : 4), | ||
textAnchor: octave === lastOctave ? "end" : "start", | ||
fill: labelColor, | ||
fontSize, | ||
fontFamily, | ||
children: (octave < 1e3 ? octave : `${octave / 1e3}k`) + "Hz" | ||
}, | ||
octave | ||
); | ||
}) | ||
] }); | ||
}; | ||
const GraphGradient = () => { | ||
const { | ||
theme: { | ||
background: { | ||
gradient: { start, stop }, | ||
grid: { | ||
lineColor, | ||
lineWidth: { border: borderWidth } | ||
} | ||
} | ||
} | ||
} = useGraph(); | ||
const id = `gBg${Math.random().toString().substring(2, 9)}`; | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
/* @__PURE__ */ jsxs( | ||
"linearGradient", | ||
{ | ||
id, | ||
x1: "0", | ||
y1: "0", | ||
x2: "0", | ||
y2: "1", | ||
children: [ | ||
/* @__PURE__ */ jsx( | ||
"stop", | ||
{ | ||
offset: "0%", | ||
stopColor: start | ||
} | ||
), | ||
/* @__PURE__ */ jsx( | ||
"stop", | ||
{ | ||
offset: "100%", | ||
stopColor: stop | ||
} | ||
) | ||
] | ||
} | ||
), | ||
/* @__PURE__ */ jsx( | ||
"rect", | ||
{ | ||
x: "0", | ||
y: "0", | ||
width: "100%", | ||
height: "100%", | ||
fill: `url(#${id})` | ||
} | ||
), | ||
Boolean(borderWidth) && /* @__PURE__ */ jsx( | ||
"rect", | ||
{ | ||
x: borderWidth / 2, | ||
y: borderWidth / 2, | ||
width: `calc(100% - ${borderWidth}px)`, | ||
height: `calc(100% - ${borderWidth}px)`, | ||
fill: "none", | ||
stroke: lineColor, | ||
strokeWidth: borderWidth | ||
} | ||
) | ||
] }); | ||
}; | ||
function getDefaultExportFromCjs (x) { | ||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; | ||
} | ||
var cjs; | ||
var hasRequiredCjs; | ||
function requireCjs () { | ||
if (hasRequiredCjs) return cjs; | ||
hasRequiredCjs = 1; | ||
var isMergeableObject = function isMergeableObject(value) { | ||
return isNonNullObject(value) | ||
&& !isSpecial(value) | ||
}; | ||
function isNonNullObject(value) { | ||
return !!value && typeof value === 'object' | ||
} | ||
function isSpecial(value) { | ||
var stringValue = Object.prototype.toString.call(value); | ||
return stringValue === '[object RegExp]' | ||
|| stringValue === '[object Date]' | ||
|| isReactElement(value) | ||
} | ||
// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25 | ||
var canUseSymbol = typeof Symbol === 'function' && Symbol.for; | ||
var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7; | ||
function isReactElement(value) { | ||
return value.$$typeof === REACT_ELEMENT_TYPE | ||
} | ||
function emptyTarget(val) { | ||
return Array.isArray(val) ? [] : {} | ||
} | ||
function cloneUnlessOtherwiseSpecified(value, options) { | ||
return (options.clone !== false && options.isMergeableObject(value)) | ||
? deepmerge(emptyTarget(value), value, options) | ||
: value | ||
} | ||
function defaultArrayMerge(target, source, options) { | ||
return target.concat(source).map(function(element) { | ||
return cloneUnlessOtherwiseSpecified(element, options) | ||
}) | ||
} | ||
function getMergeFunction(key, options) { | ||
if (!options.customMerge) { | ||
return deepmerge | ||
} | ||
var customMerge = options.customMerge(key); | ||
return typeof customMerge === 'function' ? customMerge : deepmerge | ||
} | ||
function getEnumerableOwnPropertySymbols(target) { | ||
return Object.getOwnPropertySymbols | ||
? Object.getOwnPropertySymbols(target).filter(function(symbol) { | ||
return Object.propertyIsEnumerable.call(target, symbol) | ||
}) | ||
: [] | ||
} | ||
function getKeys(target) { | ||
return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target)) | ||
} | ||
function propertyIsOnObject(object, property) { | ||
try { | ||
return property in object | ||
} catch(_) { | ||
return false | ||
} | ||
} | ||
// Protects from prototype poisoning and unexpected merging up the prototype chain. | ||
function propertyIsUnsafe(target, key) { | ||
return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet, | ||
&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain, | ||
&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable. | ||
} | ||
function mergeObject(target, source, options) { | ||
var destination = {}; | ||
if (options.isMergeableObject(target)) { | ||
getKeys(target).forEach(function(key) { | ||
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options); | ||
}); | ||
} | ||
getKeys(source).forEach(function(key) { | ||
if (propertyIsUnsafe(target, key)) { | ||
return | ||
} | ||
if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) { | ||
destination[key] = getMergeFunction(key, options)(target[key], source[key], options); | ||
} else { | ||
destination[key] = cloneUnlessOtherwiseSpecified(source[key], options); | ||
} | ||
}); | ||
return destination | ||
} | ||
function deepmerge(target, source, options) { | ||
options = options || {}; | ||
options.arrayMerge = options.arrayMerge || defaultArrayMerge; | ||
options.isMergeableObject = options.isMergeableObject || isMergeableObject; | ||
// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge() | ||
// implementations can use it. The caller may not replace it. | ||
options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified; | ||
var sourceIsArray = Array.isArray(source); | ||
var targetIsArray = Array.isArray(target); | ||
var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray; | ||
if (!sourceAndTargetTypesMatch) { | ||
return cloneUnlessOtherwiseSpecified(source, options) | ||
} else if (sourceIsArray) { | ||
return options.arrayMerge(target, source, options) | ||
} else { | ||
return mergeObject(target, source, options) | ||
} | ||
} | ||
deepmerge.all = function deepmergeAll(array, options) { | ||
if (!Array.isArray(array)) { | ||
throw new Error('first argument should be an array') | ||
} | ||
return array.reduce(function(prev, next) { | ||
return deepmerge(prev, next, options) | ||
}, {}) | ||
}; | ||
var deepmerge_1 = deepmerge; | ||
cjs = deepmerge_1; | ||
return cjs; | ||
} | ||
var cjsExports = requireCjs(); | ||
const merge = /*@__PURE__*/getDefaultExportFromCjs(cjsExports); | ||
const FrequencyResponseGraph = forwardRef((props, forwardedRef) => { | ||
const ref = useRef(null); | ||
useImperativeHandle(forwardedRef, () => ref.current); | ||
const { | ||
width, | ||
height, | ||
scale = {}, | ||
theme = {}, | ||
style = {}, | ||
className = "", | ||
children | ||
} = props; | ||
const mergedTheme = merge(defaultTheme, theme); | ||
const mergedScale = merge(defaultScale, scale, { | ||
arrayMerge: (_, source) => source | ||
// overwrite arrays | ||
}); | ||
const { minFreq, maxFreq } = mergedScale; | ||
const logScale = getLogScaleFn(minFreq, maxFreq, width); | ||
FrequencyResponseGraph.displayName = "FrequencyResponseGraph"; | ||
const graphId = `frequency-response-graph-${String(Math.random()).slice(2, 9)}`; | ||
const resetStyles = ` | ||
#${graphId} * { | ||
pointer-events: none; | ||
}`; | ||
return /* @__PURE__ */ jsxs( | ||
"svg", | ||
{ | ||
ref, | ||
id: graphId, | ||
className, | ||
viewBox: `0 0 ${width} ${height}`, | ||
style: { | ||
width, | ||
height, | ||
position: "relative", | ||
verticalAlign: "middle", | ||
userSelect: "none", | ||
...style | ||
}, | ||
children: [ | ||
/* @__PURE__ */ jsx("style", { children: resetStyles }), | ||
/* @__PURE__ */ jsxs( | ||
GraphProvider, | ||
{ | ||
svgRef: ref, | ||
width, | ||
height, | ||
theme: mergedTheme, | ||
scale: mergedScale, | ||
logScale, | ||
children: [ | ||
/* @__PURE__ */ jsx(GraphGradient, {}), | ||
/* @__PURE__ */ jsx(GraphGainGrid, {}), | ||
/* @__PURE__ */ jsx(GraphFrequencyGrid, {}), | ||
children | ||
] | ||
} | ||
) | ||
] | ||
} | ||
); | ||
}); | ||
const GraphContext = createContext( | ||
undefined | ||
); | ||
const GraphProvider = ({ | ||
children, | ||
svgRef, | ||
scale, | ||
logScale, | ||
height, | ||
width, | ||
theme | ||
}) => { | ||
const contextValue = { | ||
svgRef, | ||
theme, | ||
scale, | ||
logScale, | ||
height, | ||
width | ||
}; | ||
return /* @__PURE__ */ jsx(GraphContext.Provider, { value: contextValue, children }); | ||
}; | ||
const useGraph = () => { | ||
const context = useContext(GraphContext); | ||
if (context === undefined) { | ||
throw new Error( | ||
"useGraph must be used within an FrequencyResponseGraphProvider" | ||
); | ||
} | ||
return context; | ||
}; | ||
const PointerTracker = ({ | ||
@@ -1719,2 +1742,2 @@ lineWidth, | ||
export { BandPassIcon, BypassIcon, CompositeCurve, FilterCurve, FilterGradient, FilterIcon, FilterPin, FilterPoint, FrequencyResponseCurve, FrequencyResponseGraph, GainIcon, GraphContext, GraphFrequencyGrid, GraphGainGrid, GraphGradient, GraphProvider, HighPassIcon, HighShelfIcon, LowPassIcon, LowShelfIcon, NotchIcon, PeakIcon, PointerTracker, calcAmplitudeForFrequency, calcBiQuadCoefficients, calcCompositeMagnitudes, calcFilterCoefficients, calcFilterMagnitudes, calcFrequency, calcMagnitude, calcMagnitudeForFrequency, calcMagnitudes, calcStandardDeviation, defaultScale, defaultTheme, fastFloor, fastRound, filterTypeKeys, filterTypes, getCenterLine, getIconStyles, getIconSymbol, getLogScaleFn, getPointerPosition, getZeroFreq, getZeroGain, getZeroQ, limitRange, plotCurve, reducePoints, scaleMagnitude, scaleMagnitudes, stripTail, useGraph }; | ||
export { BandPassIcon, BypassIcon, CompositeCurve, FilterCurve, FilterGradient, FilterIcon, FilterPin, FilterPoint, FrequencyResponseCurve, FrequencyResponseGraph, GainIcon, GraphContext, GraphFrequencyGrid, GraphGainGrid, GraphGradient, GraphProvider, HighPassIcon, HighShelfIcon, LowPassIcon, LowShelfIcon, NotchIcon, PeakIcon, PointerTracker, calcAmplitudeForFrequency, calcBiQuadCoefficients, calcCompositeMagnitudes, calcFilterCoefficients, calcFilterMagnitudes, calcFrequency, calcMagnitude, calcMagnitudeForFrequency, calcMagnitudes, calcStandardDeviation, defaultScale, defaultTheme, directions, fastFloor, fastRound, filterTypeKeys, filterTypes, getCenterLine, getIconStyles, getIconSymbol, getLogScaleFn, getPointerPosition, getZeroFreq, getZeroGain, getZeroQ, limitRange, plotCurve, reducePoints, scaleMagnitude, scaleMagnitudes, stripTail, useGraph }; |
@@ -5,3 +5,3 @@ { | ||
"private": false, | ||
"version": "0.4.1", | ||
"version": "0.4.5", | ||
"author": "Oleksiy Strelets", | ||
@@ -82,2 +82,3 @@ "license": "AGPL-3.0-only", | ||
"version-prepatch": "npm version prepatch --preid=alpha", | ||
"publish": "npm publish", | ||
"publish:alpha": "npm publish --tag alpha" | ||
@@ -87,3 +88,3 @@ }, | ||
"@chromatic-com/storybook": "^3.2.4", | ||
"@eslint/compat": "^1.2.5", | ||
"@eslint/compat": "^1.2.6", | ||
"@eslint/eslintrc": "^3.2.0", | ||
@@ -95,19 +96,19 @@ "@eslint/js": "^9.19.0", | ||
"@fontsource/monaspace-krypton": "^5.1.0", | ||
"@storybook/addon-actions": "^8.5.2", | ||
"@storybook/addon-docs": "^8.5.2", | ||
"@storybook/addon-essentials": "^8.5.2", | ||
"@storybook/blocks": "^8.5.2", | ||
"@storybook/builder-vite": "^8.5.2", | ||
"@storybook/manager-api": "^8.5.2", | ||
"@storybook/react": "^8.5.2", | ||
"@storybook/react-vite": "^8.5.2", | ||
"@storybook/theming": "^8.5.2", | ||
"@storybook/addon-actions": "^8.5.3", | ||
"@storybook/addon-docs": "^8.5.3", | ||
"@storybook/addon-essentials": "^8.5.3", | ||
"@storybook/blocks": "^8.5.3", | ||
"@storybook/builder-vite": "^8.5.3", | ||
"@storybook/manager-api": "^8.5.3", | ||
"@storybook/react": "^8.5.3", | ||
"@storybook/react-vite": "^8.5.3", | ||
"@storybook/theming": "^8.5.3", | ||
"@testing-library/jest-dom": "^6.6.3", | ||
"@testing-library/react": "^16.2.0", | ||
"@testing-library/user-event": "^14.6.1", | ||
"@types/node": "^22.10.10", | ||
"@types/node": "^22.13.0", | ||
"@types/react": "^18.3.18", | ||
"@types/react-dom": "^18.3.5", | ||
"@typescript-eslint/eslint-plugin": "^8.21.0", | ||
"@typescript-eslint/parser": "^8.21.0", | ||
"@typescript-eslint/eslint-plugin": "^8.22.0", | ||
"@typescript-eslint/parser": "^8.22.0", | ||
"@vitejs/plugin-react-swc": "^3.7.2", | ||
@@ -135,3 +136,3 @@ "@vitest/coverage-v8": "^2.1.8", | ||
"remark-gfm": "^4.0.0", | ||
"storybook": "^8.5.2", | ||
"storybook": "^8.5.3", | ||
"tailwind-merge": "^2.6.0", | ||
@@ -138,0 +139,0 @@ "tailwindcss": "^3.4.17", |
Sorry, the diff of this file is not supported yet
182707
4328