finish vocoder page redesign
This commit is contained in:
@@ -4,7 +4,7 @@ import { selectById } from './timbreSlice';
|
|||||||
import VCO, { AudioIn } from './parameters/VCO';
|
import VCO, { AudioIn } from './parameters/VCO';
|
||||||
import Mixer from './parameters/Mixer';
|
import Mixer from './parameters/Mixer';
|
||||||
import Pitch from './parameters/Pitch';
|
import Pitch from './parameters/Pitch';
|
||||||
import EG from './parameters/EG';
|
import { VocoderEG } from './parameters/EG';
|
||||||
import LFO from './parameters/LFO';
|
import LFO from './parameters/LFO';
|
||||||
import { VocoderFilter, FCMod } from './parameters/Filter';
|
import { VocoderFilter, FCMod } from './parameters/Filter';
|
||||||
import { VocoderAmp } from './parameters/Amp';
|
import { VocoderAmp } from './parameters/Amp';
|
||||||
@@ -14,8 +14,9 @@ import Pans from './parameters/Pans';
|
|||||||
export default function Vocoder(props) {
|
export default function Vocoder(props) {
|
||||||
const ids = useSelector((state) => selectById(state, props.id));
|
const ids = useSelector((state) => selectById(state, props.id));
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='h-screen grid grid-cols-8 grid-rows-3 gap-4 p-6'>
|
||||||
<VCO
|
<VCO
|
||||||
|
className='col-span-2'
|
||||||
name='VCO'
|
name='VCO'
|
||||||
id={ids.vco}
|
id={ids.vco}
|
||||||
waveforms={[
|
waveforms={[
|
||||||
@@ -96,18 +97,40 @@ export default function Vocoder(props) {
|
|||||||
{ value: 'Voice4' },
|
{ value: 'Voice4' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<AudioIn name='Audio In' id={ids.audioIn} />
|
<AudioIn className='col-span-2' name='Audio In' id={ids.audioIn} />
|
||||||
<Mixer name='Mixer' id={ids.mixer} />
|
<Mixer className='col-span-1' name='Mixer' id={ids.mixer} />
|
||||||
<Pitch name='Pitch' id={ids.pitch} />
|
<Pitch className='col-span-3' name='Pitch' id={ids.pitch} />
|
||||||
<VocoderFilter name='Filter' id={ids.filter} />
|
<div className='col-span-8 grid grid-cols-9 grid-rows-1 gap-4'>
|
||||||
<FCMod name='FC Mod' id={ids.fcMod} />
|
<div />
|
||||||
<VocoderAmp name='Amp' id={ids.amp} />
|
<VocoderFilter name='Filter' id={ids.filter} />
|
||||||
<EG name='EG' id={ids.eg} />
|
<FCMod name='FC Mod' id={ids.fcMod} />
|
||||||
<div />
|
<VocoderAmp className='col-span-2' name='Amp' id={ids.amp} />
|
||||||
<LFO name='LFO 1' id={ids.lfo1} />
|
<VocoderEG className='col-span-3' name='EG' id1={ids.eg} />
|
||||||
<LFO name='LFO 2' id={ids.lfo2} />
|
</div>
|
||||||
<Levels name='Levels' id={ids.levels} />
|
<LFO
|
||||||
<Pans name='Pans' id={ids.pans} />
|
className='col-span-2'
|
||||||
|
name='LFO 1'
|
||||||
|
id={ids.lfo1}
|
||||||
|
waveforms={[
|
||||||
|
{ value: 'Saw' },
|
||||||
|
{ value: 'Square 1' },
|
||||||
|
{ value: 'Triangle' },
|
||||||
|
{ value: 'Sample & Hold' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<LFO
|
||||||
|
className='col-span-2'
|
||||||
|
name='LFO 2'
|
||||||
|
id={ids.lfo2}
|
||||||
|
waveforms={[
|
||||||
|
{ value: 'Saw' },
|
||||||
|
{ value: 'Square 2' },
|
||||||
|
{ value: 'Sine' },
|
||||||
|
{ value: 'Sample & Hold' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Levels className='col-span-2' name='Levels' id={ids.levels} />
|
||||||
|
<Pans className='col-span-2' name='Pans' id={ids.pans} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,59 +84,71 @@ export const VocoderAmp = (props) => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={props.className}>
|
<Card className={props.className} header={props.name}>
|
||||||
<span>{`${props.name}`}</span>
|
<div className='flex flex-row'>
|
||||||
Amp Level
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
<Knob
|
<h3>Amp Level</h3>
|
||||||
value={parameters.ampLevel}
|
<Knob
|
||||||
max={127}
|
value={parameters.ampLevel}
|
||||||
onChange={(value) =>
|
max={127}
|
||||||
dispatch(
|
onChange={(value) =>
|
||||||
parameterUpdated({
|
dispatch(
|
||||||
id: props.id,
|
parameterUpdated({
|
||||||
changes: { ampLevel: value },
|
id: props.id,
|
||||||
})
|
changes: { ampLevel: value },
|
||||||
)
|
})
|
||||||
}
|
)
|
||||||
/>
|
}
|
||||||
Direct Level
|
/>
|
||||||
<Knob
|
</div>
|
||||||
value={parameters.directLevel}
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
max={127}
|
<h3>Direct Level</h3>
|
||||||
onChange={(value) =>
|
<Knob
|
||||||
dispatch(
|
value={parameters.directLevel}
|
||||||
parameterUpdated({
|
max={127}
|
||||||
id: props.id,
|
onChange={(value) =>
|
||||||
changes: { directLevel: value },
|
dispatch(
|
||||||
})
|
parameterUpdated({
|
||||||
)
|
id: props.id,
|
||||||
}
|
changes: { directLevel: value },
|
||||||
/>
|
})
|
||||||
Amp Keyboard Tracking
|
)
|
||||||
<Knob
|
}
|
||||||
value={parameters.ampTrack}
|
/>
|
||||||
dual={true}
|
</div>
|
||||||
max={63}
|
</div>
|
||||||
onChange={(value) =>
|
<div className='flex flex-row'>
|
||||||
dispatch(
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
parameterUpdated({
|
<h3>Keyboard Track</h3>
|
||||||
id: props.id,
|
<Knob
|
||||||
changes: { ampTrack: value },
|
value={parameters.ampTrack}
|
||||||
})
|
dual={true}
|
||||||
)
|
max={63}
|
||||||
}
|
onChange={(value) =>
|
||||||
/>
|
dispatch(
|
||||||
<Checkbox
|
parameterUpdated({
|
||||||
value={parameters.distortion}
|
id: props.id,
|
||||||
onChange={(value) =>
|
changes: { ampTrack: value },
|
||||||
dispatch(
|
})
|
||||||
parameterUpdated({
|
)
|
||||||
id: props.id,
|
}
|
||||||
changes: { distortion: value },
|
/>
|
||||||
})
|
</div>
|
||||||
)
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
}
|
<h3>Distortion</h3>
|
||||||
/>
|
<Checkbox
|
||||||
</div>
|
value={parameters.distortion}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id,
|
||||||
|
changes: { distortion: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -168,3 +168,90 @@ export default function EG(props) {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function VocoderEG(props) {
|
||||||
|
const parameters_eg_1 = useSelector((state) => selectById(state, props.id1));
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={props.className} header={props.name}>
|
||||||
|
<div className='-mt-1 flex flex-row justify-evenly'>
|
||||||
|
<div className='flex flex-col items-center'>
|
||||||
|
<h3>Attack</h3>
|
||||||
|
<Knob
|
||||||
|
value={parameters_eg_1.attack}
|
||||||
|
max={127}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id1,
|
||||||
|
changes: { attack: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col items-center'>
|
||||||
|
<h3>Decay</h3>
|
||||||
|
<Knob
|
||||||
|
value={parameters_eg_1.decay}
|
||||||
|
max={127}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id1,
|
||||||
|
changes: { decay: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col items-center'>
|
||||||
|
<h3>Sustain</h3>
|
||||||
|
<Knob
|
||||||
|
value={parameters_eg_1.sustain}
|
||||||
|
max={127}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id1,
|
||||||
|
changes: { sustain: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col items-center'>
|
||||||
|
<h3>Release</h3>
|
||||||
|
<Knob
|
||||||
|
value={parameters_eg_1.release}
|
||||||
|
max={127}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id1,
|
||||||
|
changes: { release: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='-mt-8 flex flex-col items-center'>
|
||||||
|
<h3>EG Reset</h3>
|
||||||
|
<Checkbox
|
||||||
|
className='checkbox'
|
||||||
|
value={parameters_eg_1.egReset}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id1,
|
||||||
|
changes: { egReset: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Card from '../card';
|
|||||||
import { Select, Knob } from '../../utils/components';
|
import { Select, Knob } from '../../utils/components';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { selectById, parameterUpdated } from './parameterSlice.js';
|
import { selectById, parameterUpdated } from './parameterSlice.js';
|
||||||
import { Slider, SelectList } from '../../helpers/Helpers';
|
import { Slider } from '../../helpers/Helpers';
|
||||||
|
|
||||||
export default function Filter(props) {
|
export default function Filter(props) {
|
||||||
const parameters = useSelector((state) => selectById(state, props.id));
|
const parameters = useSelector((state) => selectById(state, props.id));
|
||||||
@@ -38,7 +38,7 @@ export default function Filter(props) {
|
|||||||
<h3 className='text-lg font-semibold text-gray-900'>Cutoff</h3>
|
<h3 className='text-lg font-semibold text-gray-900'>Cutoff</h3>
|
||||||
<Knob
|
<Knob
|
||||||
value={parameters.cutoff}
|
value={parameters.cutoff}
|
||||||
maxValue={127}
|
max={127}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
@@ -74,7 +74,7 @@ export default function Filter(props) {
|
|||||||
<Knob
|
<Knob
|
||||||
value={parameters.egIntensity}
|
value={parameters.egIntensity}
|
||||||
dual={true}
|
dual={true}
|
||||||
maxValue={63}
|
max={63}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
@@ -113,51 +113,55 @@ export const VocoderFilter = (props) => {
|
|||||||
const parameters = useSelector((state) => selectById(state, props.id));
|
const parameters = useSelector((state) => selectById(state, props.id));
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
return (
|
return (
|
||||||
<div className={props.className}>
|
<Card className={props.className} header={props.name}>
|
||||||
<span>{`${props.name}`}</span>
|
<div className='-mt-3 flex flex-col justify-around'>
|
||||||
Cutoff
|
<div className='flex flex-col items-center'>
|
||||||
<Slider
|
<h3>Cutoff</h3>
|
||||||
parameter={parameters.cutoff}
|
<Knob
|
||||||
min={0}
|
value={parameters.cutoff}
|
||||||
max={127}
|
max={127}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
id: props.id,
|
id: props.id,
|
||||||
changes: { cutoff: value },
|
changes: { cutoff: value },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
Resonance
|
</div>
|
||||||
<Slider
|
<div className='flex flex-col items-center'>
|
||||||
parameter={parameters.resonance}
|
<h3>Resonance</h3>
|
||||||
min={0}
|
<Knob
|
||||||
max={127}
|
value={parameters.resonance}
|
||||||
onChange={(value) =>
|
max={127}
|
||||||
dispatch(
|
onChange={(value) =>
|
||||||
parameterUpdated({
|
dispatch(
|
||||||
id: props.id,
|
parameterUpdated({
|
||||||
changes: { resonance: value },
|
id: props.id,
|
||||||
})
|
changes: { resonance: value },
|
||||||
)
|
})
|
||||||
}
|
)
|
||||||
/>
|
}
|
||||||
EF Sense
|
/>
|
||||||
<Slider
|
</div>
|
||||||
parameter={parameters.efSense}
|
<div className='flex flex-col items-center'>
|
||||||
min={0}
|
<h3>EF Sense</h3>
|
||||||
max={127}
|
<Knob
|
||||||
onChange={(value) =>
|
value={parameters.efSense}
|
||||||
dispatch(
|
max={127}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { efSense: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { efSense: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -165,43 +169,48 @@ export const FCMod = (props) => {
|
|||||||
const parameters = useSelector((state) => selectById(state, props.id));
|
const parameters = useSelector((state) => selectById(state, props.id));
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
return (
|
return (
|
||||||
<div>
|
<Card className={props.className} header={props.name}>
|
||||||
<span>{`${props.name}`}</span>
|
<div className='-mt-4 flex flex-col justify-around space-y-4'>
|
||||||
Source
|
<div className='flex flex-col items-center space-y-0.5'>
|
||||||
<SelectList
|
<h3>Source</h3>
|
||||||
value={parameters.source}
|
<Select
|
||||||
list={[
|
className='select w-full'
|
||||||
{ value: 'Amp EG' },
|
value={parameters.source}
|
||||||
{ value: 'LFO 1' },
|
list={[
|
||||||
{ value: 'LFO 2' },
|
{ value: 'Amp EG' },
|
||||||
{ value: 'Velocity' },
|
{ value: 'LFO 1' },
|
||||||
{ value: 'Keyboard Track' },
|
{ value: 'LFO 2' },
|
||||||
{ value: 'Pitch Bend' },
|
{ value: 'Velocity' },
|
||||||
{ value: 'Mod Wheel' },
|
{ value: 'Keyboard Track' },
|
||||||
]}
|
{ value: 'Pitch Bend' },
|
||||||
onChange={(value) =>
|
{ value: 'Mod Wheel' },
|
||||||
dispatch(
|
]}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { source: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { source: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
Intensity
|
}
|
||||||
<Slider
|
/>
|
||||||
parameter={parameters.intensity}
|
</div>
|
||||||
min={0}
|
<div className='flex flex-col items-center'>
|
||||||
max={127}
|
<h3>Intensity</h3>
|
||||||
onChange={(value) =>
|
<Knob
|
||||||
dispatch(
|
value={parameters.intensity}
|
||||||
parameterUpdated({
|
max={127}
|
||||||
id: props.id,
|
onChange={(value) =>
|
||||||
changes: { intensity: value },
|
dispatch(
|
||||||
})
|
parameterUpdated({
|
||||||
)
|
id: props.id,
|
||||||
}
|
changes: { intensity: value },
|
||||||
/>
|
})
|
||||||
</div>
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default function LFO(props) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col items-center w-1/2'>
|
<div className='flex flex-col items-center w-1/2 space-y-1'>
|
||||||
<h3>Frequency</h3>
|
<h3>Frequency</h3>
|
||||||
<Knob
|
<Knob
|
||||||
value={parameters.frequency}
|
value={parameters.frequency}
|
||||||
|
|||||||
@@ -1,132 +1,139 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import Card from '../card';
|
||||||
import { selectById, parameterUpdated } from './parameterSlice.js';
|
import { selectById, parameterUpdated } from './parameterSlice.js';
|
||||||
import { Slider } from '../../helpers/Helpers';
|
import { Knob } from '../../utils/components';
|
||||||
|
|
||||||
export default function Levels(props) {
|
export default function Levels(props) {
|
||||||
const parameters = useSelector((state) => selectById(state, props.id));
|
const parameters = useSelector((state) => selectById(state, props.id));
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Card className={props.className} header={props.name}>
|
||||||
<span>{`${props.name}`}</span>
|
<div className='flex flex-row'>
|
||||||
<div>
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
Level 1
|
<h3>Level 1</h3>
|
||||||
<Slider
|
<Knob
|
||||||
min={0}
|
max={127}
|
||||||
max={127}
|
value={parameters.level1}
|
||||||
parameter={parameters.level1}
|
onChange={(value) =>
|
||||||
onChange={(value) =>
|
dispatch(
|
||||||
dispatch(
|
parameterUpdated({
|
||||||
parameterUpdated({
|
id: props.id,
|
||||||
id: props.id,
|
changes: { level1: value },
|
||||||
changes: { level1: value },
|
})
|
||||||
})
|
)
|
||||||
)
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</div>
|
||||||
Level 2
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
<Slider
|
<h3>Level 2</h3>
|
||||||
min={0}
|
<Knob
|
||||||
max={127}
|
max={127}
|
||||||
parameter={parameters.level2}
|
value={parameters.level2}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
id: props.id,
|
id: props.id,
|
||||||
changes: { level2: value },
|
changes: { level2: value },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
Level 3
|
</div>
|
||||||
<Slider
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
min={0}
|
<h3>Level 3</h3>
|
||||||
max={127}
|
<Knob
|
||||||
parameter={parameters.level3}
|
max={127}
|
||||||
onChange={(value) =>
|
value={parameters.level3}
|
||||||
dispatch(
|
onChange={(value) =>
|
||||||
parameterUpdated({
|
dispatch(
|
||||||
id: props.id,
|
parameterUpdated({
|
||||||
changes: { level3: value },
|
id: props.id,
|
||||||
})
|
changes: { level3: value },
|
||||||
)
|
})
|
||||||
}
|
)
|
||||||
/>
|
}
|
||||||
Level 4
|
/>
|
||||||
<Slider
|
</div>
|
||||||
min={0}
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
max={127}
|
<h3>Level 4</h3>
|
||||||
parameter={parameters.level4}
|
<Knob
|
||||||
onChange={(value) =>
|
max={127}
|
||||||
dispatch(
|
value={parameters.level4}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { level4: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { level4: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div />
|
<div className='flex flex-row'>
|
||||||
<div>
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
Level 5
|
<h3>Level 5</h3>
|
||||||
<Slider
|
<Knob
|
||||||
min={0}
|
max={127}
|
||||||
max={127}
|
value={parameters.level5}
|
||||||
parameter={parameters.level5}
|
onChange={(value) =>
|
||||||
onChange={(value) =>
|
dispatch(
|
||||||
dispatch(
|
parameterUpdated({
|
||||||
parameterUpdated({
|
id: props.id,
|
||||||
id: props.id,
|
changes: { level5: value },
|
||||||
changes: { level5: value },
|
})
|
||||||
})
|
)
|
||||||
)
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</div>
|
||||||
Level 6
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
<Slider
|
<h3>Level 6</h3>
|
||||||
min={0}
|
<Knob
|
||||||
max={127}
|
max={127}
|
||||||
parameter={parameters.level6}
|
value={parameters.level6}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
id: props.id,
|
id: props.id,
|
||||||
changes: { level6: value },
|
changes: { level6: value },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
Level 7
|
</div>
|
||||||
<Slider
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
min={0}
|
<h3>Level 7</h3>
|
||||||
max={127}
|
<Knob
|
||||||
parameter={parameters.level7}
|
max={127}
|
||||||
onChange={(value) =>
|
value={parameters.level7}
|
||||||
dispatch(
|
onChange={(value) =>
|
||||||
parameterUpdated({
|
dispatch(
|
||||||
id: props.id,
|
parameterUpdated({
|
||||||
changes: { level7: value },
|
id: props.id,
|
||||||
})
|
changes: { level7: value },
|
||||||
)
|
})
|
||||||
}
|
)
|
||||||
/>
|
}
|
||||||
Level 8
|
/>
|
||||||
<Slider
|
</div>
|
||||||
min={0}
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
max={127}
|
<h3>Level 8</h3>
|
||||||
parameter={parameters.level8}
|
<Knob
|
||||||
onChange={(value) =>
|
max={127}
|
||||||
dispatch(
|
value={parameters.level8}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { level8: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { level8: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,132 +1,147 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { selectById, parameterUpdated } from './parameterSlice.js';
|
import { selectById, parameterUpdated } from './parameterSlice.js';
|
||||||
import { Slider } from '../../helpers/Helpers';
|
import Card from '../card';
|
||||||
|
import { Knob } from '../../utils/components';
|
||||||
|
|
||||||
export default function Pans(props) {
|
export default function Pans(props) {
|
||||||
const parameters = useSelector((state) => selectById(state, props.id));
|
const parameters = useSelector((state) => selectById(state, props.id));
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Card className={props.className} header={props.name}>
|
||||||
<span>{`${props.name}`}</span>
|
<div className='flex flex-row'>
|
||||||
<div>
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
Pan 1
|
<h3>Pan 1</h3>
|
||||||
<Slider
|
<Knob
|
||||||
min={0}
|
dual={true}
|
||||||
max={127}
|
max={63}
|
||||||
parameter={parameters.pan1}
|
value={parameters.pan1}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
id: props.id,
|
id: props.id,
|
||||||
changes: { pan1: value },
|
changes: { pan1: value },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
Pan 2
|
</div>
|
||||||
<Slider
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
min={0}
|
<h3>Pan 2</h3>
|
||||||
max={127}
|
<Knob
|
||||||
parameter={parameters.pan2}
|
dual={true}
|
||||||
onChange={(value) =>
|
max={63}
|
||||||
dispatch(
|
value={parameters.pan2}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { pan2: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { pan2: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
Pan 3
|
}
|
||||||
<Slider
|
/>
|
||||||
min={0}
|
</div>
|
||||||
max={127}
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
parameter={parameters.pan3}
|
<h3>Pan 3</h3>
|
||||||
onChange={(value) =>
|
<Knob
|
||||||
dispatch(
|
dual={true}
|
||||||
parameterUpdated({
|
max={63}
|
||||||
id: props.id,
|
value={parameters.pan3}
|
||||||
changes: { pan3: value },
|
onChange={(value) =>
|
||||||
})
|
dispatch(
|
||||||
)
|
parameterUpdated({
|
||||||
}
|
id: props.id,
|
||||||
/>
|
changes: { pan3: value },
|
||||||
Pan 4
|
})
|
||||||
<Slider
|
)
|
||||||
min={0}
|
}
|
||||||
max={127}
|
/>
|
||||||
parameter={parameters.pan4}
|
</div>
|
||||||
onChange={(value) =>
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
dispatch(
|
<h3>Pan 4</h3>
|
||||||
parameterUpdated({
|
<Knob
|
||||||
id: props.id,
|
dual={true}
|
||||||
changes: { pan4: value },
|
max={63}
|
||||||
})
|
value={parameters.pan4}
|
||||||
)
|
onChange={(value) =>
|
||||||
}
|
dispatch(
|
||||||
/>
|
parameterUpdated({
|
||||||
|
id: props.id,
|
||||||
|
changes: { pan4: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div />
|
<div className='flex flex-row'>
|
||||||
<div>
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
Pan 5
|
<h3>Pan 5</h3>
|
||||||
<Slider
|
<Knob
|
||||||
min={0}
|
dual={true}
|
||||||
max={127}
|
max={63}
|
||||||
parameter={parameters.pan5}
|
value={parameters.pan5}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
id: props.id,
|
id: props.id,
|
||||||
changes: { pan5: value },
|
changes: { pan5: value },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
Pan 6
|
</div>
|
||||||
<Slider
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
min={0}
|
<h3>Pan 6</h3>
|
||||||
max={127}
|
<Knob
|
||||||
parameter={parameters.pan6}
|
dual={true}
|
||||||
onChange={(value) =>
|
max={63}
|
||||||
dispatch(
|
value={parameters.pan6}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { pan6: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { pan6: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
Pan 7
|
}
|
||||||
<Slider
|
/>
|
||||||
min={0}
|
</div>
|
||||||
max={127}
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
parameter={parameters.pan7}
|
<h3>Pan 7</h3>
|
||||||
onChange={(value) =>
|
<Knob
|
||||||
dispatch(
|
dual={true}
|
||||||
parameterUpdated({
|
max={63}
|
||||||
id: props.id,
|
value={parameters.pan7}
|
||||||
changes: { pan7: value },
|
onChange={(value) =>
|
||||||
})
|
dispatch(
|
||||||
)
|
parameterUpdated({
|
||||||
}
|
id: props.id,
|
||||||
/>
|
changes: { pan7: value },
|
||||||
Pan 8
|
})
|
||||||
<Slider
|
)
|
||||||
min={0}
|
}
|
||||||
max={127}
|
/>
|
||||||
parameter={parameters.pan8}
|
</div>
|
||||||
onChange={(value) =>
|
<div className='flex flex-col items-center w-1/4'>
|
||||||
dispatch(
|
<h3>Pan 8</h3>
|
||||||
parameterUpdated({
|
<Knob
|
||||||
id: props.id,
|
dual={true}
|
||||||
changes: { pan8: value },
|
max={63}
|
||||||
})
|
value={parameters.pan8}
|
||||||
)
|
onChange={(value) =>
|
||||||
}
|
dispatch(
|
||||||
/>
|
parameterUpdated({
|
||||||
|
id: props.id,
|
||||||
|
changes: { pan8: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import React from 'react';
|
|||||||
import Card from '../card';
|
import Card from '../card';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { selectById, parameterUpdated } from './parameterSlice.js';
|
import { selectById, parameterUpdated } from './parameterSlice.js';
|
||||||
import { Select, Knob } from '../../utils/components';
|
import { Select, Knob, Checkbox } from '../../utils/components';
|
||||||
import { Slider, Checkbox } from '../../helpers/Helpers';
|
|
||||||
|
|
||||||
export default function VCO(props) {
|
export default function VCO(props) {
|
||||||
const parameters = useSelector((state) => selectById(state, props.id));
|
const parameters = useSelector((state) => selectById(state, props.id));
|
||||||
@@ -84,62 +83,73 @@ export const AudioIn = (props) => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={props.className}>
|
<Card className={props.className} header={props.name}>
|
||||||
<span>{`${props.name}`}</span>
|
<div className='-mt-3 flex flex-col items-stretch space-y-4'>
|
||||||
Gate
|
<div className='flex flex-row'>
|
||||||
<Slider
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
min={0}
|
<h3>Gate Sense</h3>
|
||||||
max={127}
|
<Knob
|
||||||
parameter={parameters.gate}
|
max={127}
|
||||||
onChange={(value) =>
|
value={parameters.gate}
|
||||||
dispatch(
|
onChange={(value) =>
|
||||||
parameterUpdated({
|
dispatch(
|
||||||
id: props.id,
|
parameterUpdated({
|
||||||
changes: { gate: value },
|
id: props.id,
|
||||||
})
|
changes: { gate: value },
|
||||||
)
|
})
|
||||||
}
|
)
|
||||||
/>
|
}
|
||||||
Threshold
|
/>
|
||||||
<Slider
|
</div>
|
||||||
min={0}
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
max={127}
|
<h3>Threshold</h3>
|
||||||
parameter={parameters.threshold}
|
<Knob
|
||||||
onChange={(value) =>
|
max={127}
|
||||||
dispatch(
|
value={parameters.threshold}
|
||||||
parameterUpdated({
|
onChange={(value) =>
|
||||||
id: props.id,
|
dispatch(
|
||||||
changes: { threshold: value },
|
parameterUpdated({
|
||||||
})
|
id: props.id,
|
||||||
)
|
changes: { threshold: value },
|
||||||
}
|
})
|
||||||
/>
|
)
|
||||||
HPF Level
|
}
|
||||||
<Slider
|
/>
|
||||||
min={0}
|
</div>
|
||||||
max={127}
|
</div>
|
||||||
parameter={parameters.hpfLevel}
|
<div className='flex flex-row'>
|
||||||
onChange={(value) =>
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
dispatch(
|
<h3>HPF Level</h3>
|
||||||
parameterUpdated({
|
<Knob
|
||||||
id: props.id,
|
max={127}
|
||||||
changes: { hpfLevel: value },
|
value={parameters.hpfLevel}
|
||||||
})
|
onChange={(value) =>
|
||||||
)
|
dispatch(
|
||||||
}
|
parameterUpdated({
|
||||||
/>
|
id: props.id,
|
||||||
<Checkbox
|
changes: { hpfLevel: value },
|
||||||
name='HPF Gate'
|
})
|
||||||
parameter={parameters.hpfGate}
|
)
|
||||||
onChange={(value) =>
|
}
|
||||||
dispatch(
|
/>
|
||||||
parameterUpdated({
|
</div>
|
||||||
id: props.id,
|
<div className='flex flex-col items-center w-1/2'>
|
||||||
changes: { hpfGate: value },
|
<h3>HPF Gate</h3>
|
||||||
})
|
<Checkbox
|
||||||
)
|
className='checkbox'
|
||||||
}
|
value={parameters.hpfGate}
|
||||||
/>
|
onChange={(value) =>
|
||||||
</div>
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id,
|
||||||
|
changes: { hpfGate: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -275,7 +275,8 @@ const defaultState = {
|
|||||||
waveform: 'Saw',
|
waveform: 'Saw',
|
||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: '1/1',
|
frequency: 0,
|
||||||
|
syncNote: '1/1',
|
||||||
},
|
},
|
||||||
patches1: {
|
patches1: {
|
||||||
id: 'patches1',
|
id: 'patches1',
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const timbreSlice = createSlice({
|
|||||||
filter: 'filter3',
|
filter: 'filter3',
|
||||||
fcMod: 'fcMod',
|
fcMod: 'fcMod',
|
||||||
eg: 'eg5',
|
eg: 'eg5',
|
||||||
|
eg2: 'unusedVocoder',
|
||||||
amp: 'amp3',
|
amp: 'amp3',
|
||||||
lfo1: 'lfo5',
|
lfo1: 'lfo5',
|
||||||
lfo2: 'lfo6',
|
lfo2: 'lfo6',
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export default function Menu() {
|
|||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
const fileRef = useRef(null);
|
const fileRef = useRef(null);
|
||||||
const state = useSelector((state) => selectEntities(state));
|
const state = useSelector((state) => selectEntities(state));
|
||||||
|
const activeTab = useSelector((state) => state.parameters.activeTab);
|
||||||
|
console.log(activeTab);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
const loadFile = (f) => {
|
const loadFile = (f) => {
|
||||||
@@ -29,6 +31,15 @@ export default function Menu() {
|
|||||||
}
|
}
|
||||||
}, [file, dispatch]);
|
}, [file, dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state.patch.mode === 'Vocoder') {
|
||||||
|
dispatch(parameterUpdateTab('Vocoder'));
|
||||||
|
}
|
||||||
|
if (state.patch.mode === 'Single' || state.patch.mode === 'Multiple') {
|
||||||
|
dispatch(parameterUpdateTab('Timbre1'));
|
||||||
|
}
|
||||||
|
}, [state.patch.mode, dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='col-span-1 flex flex-col justify-around border-solid border-r border-gray-300 bg-white px-4'>
|
<div className='col-span-1 flex flex-col justify-around border-solid border-r border-gray-300 bg-white px-4'>
|
||||||
<div className='menu-patch-container'>
|
<div className='menu-patch-container'>
|
||||||
@@ -104,7 +115,7 @@ export default function Menu() {
|
|||||||
<div className='my-1 flex flex-col items-center space-y-2'>
|
<div className='my-1 flex flex-col items-center space-y-2'>
|
||||||
<button
|
<button
|
||||||
disabled={state.patch.mode === 'Vocoder'}
|
disabled={state.patch.mode === 'Vocoder'}
|
||||||
className='btn'
|
className={activeTab === 'Timbre1' ? `btn btn-active` : `btn`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(parameterUpdateTab('Timbre1'));
|
dispatch(parameterUpdateTab('Timbre1'));
|
||||||
}}>
|
}}>
|
||||||
@@ -112,7 +123,7 @@ export default function Menu() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
disabled={state.patch.mode !== 'Multiple'}
|
disabled={state.patch.mode !== 'Multiple'}
|
||||||
className='btn'
|
className={activeTab === 'Timbre2' ? `btn btn-active` : `btn`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(parameterUpdateTab('Timbre2'));
|
dispatch(parameterUpdateTab('Timbre2'));
|
||||||
}}>
|
}}>
|
||||||
@@ -120,21 +131,21 @@ export default function Menu() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
disabled={state.patch.mode !== 'Vocoder'}
|
disabled={state.patch.mode !== 'Vocoder'}
|
||||||
className='btn'
|
className={activeTab === 'Vocoder' ? `btn btn-active` : `btn`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(parameterUpdateTab('Vocoder'));
|
dispatch(parameterUpdateTab('Vocoder'));
|
||||||
}}>
|
}}>
|
||||||
Vocoder
|
Vocoder
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className='btn'
|
className={activeTab === 'Effects' ? `btn btn-active` : `btn`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(parameterUpdateTab('Effects'));
|
dispatch(parameterUpdateTab('Effects'));
|
||||||
}}>
|
}}>
|
||||||
Effects
|
Effects
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className='btn'
|
className={activeTab === 'Arpeggio' ? `btn btn-active` : `btn`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(parameterUpdateTab('Arpeggio'));
|
dispatch(parameterUpdateTab('Arpeggio'));
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
@@ -83,14 +83,14 @@ function getKnobValue(dragStart, currentY, bipolar) {
|
|||||||
let knob_id = 0;
|
let knob_id = 0;
|
||||||
|
|
||||||
function Knob({
|
function Knob({
|
||||||
maxValue = 127,
|
max = 127,
|
||||||
dual = false,
|
dual = false,
|
||||||
size = 42,
|
size = 42,
|
||||||
value = 0,
|
value = 0,
|
||||||
fillWidth = 4,
|
fillWidth = 4,
|
||||||
onChange,
|
onChange,
|
||||||
}) {
|
}) {
|
||||||
value = value / maxValue;
|
value = value / max;
|
||||||
const uid = useMemo(() => knob_id++, []);
|
const uid = useMemo(() => knob_id++, []);
|
||||||
const radius = size / 2;
|
const radius = size / 2;
|
||||||
const dial_zero = dual ? Math.PI / 2 : START_ANGLE;
|
const dial_zero = dual ? Math.PI / 2 : START_ANGLE;
|
||||||
@@ -115,9 +115,9 @@ function Knob({
|
|||||||
if (!dragStart) return;
|
if (!dragStart) return;
|
||||||
|
|
||||||
const newValue = getKnobValue(dragStart, e.clientY, dual);
|
const newValue = getKnobValue(dragStart, e.clientY, dual);
|
||||||
onChange(Math.round(newValue * maxValue));
|
onChange(Math.round(newValue * max));
|
||||||
},
|
},
|
||||||
[dragStart, onChange, dual, maxValue]
|
[dragStart, onChange, dual, max]
|
||||||
);
|
);
|
||||||
const onMouseDown = useCallback(
|
const onMouseDown = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
@@ -158,19 +158,21 @@ function Knob({
|
|||||||
{/* TOOLTIP */}
|
{/* TOOLTIP */}
|
||||||
<div
|
<div
|
||||||
className={` ${
|
className={` ${
|
||||||
isDragging ? 'opacity-100' : 'opacity-0'
|
isDragging ? 'opacity-100' : 'opacity-0 hidden'
|
||||||
} transition duration-75 ease-in select-none pointer-events-none absolute -mt-6 bg-gray-800 text-white truncate text-xs box-content rounded text-center w-8 py-0.5`}>
|
} transition duration-75 ease-in select-none pointer-events-none absolute -mt-6 bg-gray-800 text-white truncate text-xs box-content rounded text-center w-8 py-0.5`}>
|
||||||
{Math.round(value * maxValue)}
|
{Math.round(value * max)}
|
||||||
</div>
|
</div>
|
||||||
<svg
|
<svg
|
||||||
className={` ${
|
className={` ${
|
||||||
isDragging ? 'opacity-100' : 'opacity-0'
|
isDragging ? 'opacity-100' : 'opacity-0 hidden'
|
||||||
} transition duration-75 ease-in absolute text-gray-800 w-full h-2 -mt-1`}
|
} transition duration-75 ease-in absolute text-gray-800 w-full h-2 -mt-1`}
|
||||||
x='0px'
|
x='0px'
|
||||||
y='0px'
|
y='0px'
|
||||||
viewBox='0 0 255 255'
|
viewBox='0 0 255 255'
|
||||||
xmlSpace='preserve'>
|
xmlSpace='preserve'>
|
||||||
<polygon class='fill-current' points='0,0 127.5,127.5 255,0'></polygon>
|
<polygon
|
||||||
|
className='fill-current'
|
||||||
|
points='0,0 127.5,127.5 255,0'></polygon>
|
||||||
</svg>
|
</svg>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ code {
|
|||||||
.btn:disabled {
|
.btn:disabled {
|
||||||
@apply line-through opacity-50 hover:bg-white cursor-default;
|
@apply line-through opacity-50 hover:bg-white cursor-default;
|
||||||
}
|
}
|
||||||
|
.btn-active {
|
||||||
|
@apply ring ring-blue-500;
|
||||||
|
}
|
||||||
.select {
|
.select {
|
||||||
@apply select-none block rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50;
|
@apply select-none block rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user