Browse Source

Remove tuning

Tuning is dealt with outside of the component. Fretboard only deals with which frets are fretted on each string.
master
TheoryOfNekomata 3 years ago
parent
commit
be73ccbf74
5 changed files with 248 additions and 298 deletions
  1. +163
    -0
      src/components/Fret/Fret.tsx
  2. +11
    -10
      src/components/Fretboard/Fretboard.stories.tsx
  3. +47
    -278
      src/components/Fretboard/Fretboard.tsx
  4. +25
    -0
      src/components/FretboardString/FretboardString.tsx
  5. +2
    -10
      src/index.tsx

+ 163
- 0
src/components/Fret/Fret.tsx View File

@@ -0,0 +1,163 @@
import * as React from 'react'
import * as PropTypes from 'prop-types'

const propTypes = {
fretNumber: PropTypes.number.isRequired,
totalFrets: PropTypes.number.isRequired,
firstString: PropTypes.bool,
strings: PropTypes.number.isRequired,
landmarks: PropTypes.bool,
fretted: PropTypes.bool,
capoFret: PropTypes.number,
name: PropTypes.string,
}

type Props = PropTypes.InferProps<typeof propTypes>

const Fret: React.FC<Props> = ({
fretNumber,
totalFrets,
children,
firstString,
strings,
landmarks = false,
fretted = false,
capoFret = 0,
name,
}) => (
<div
style={{
display: 'block',
position: 'relative',
width: fretNumber > 0 ? `${(totalFrets - fretNumber) / 12 * 100 + 100}%` : '0.5rem',
height: '1rem',
flexShrink: fretNumber > 0 ? undefined : 0,
padding: 0,
borderWidth: '0 0.0625rem',
borderStyle: 'none solid',
}}
>
{
landmarks
&& (fretNumber % 12 === 3 || fretNumber % 12 === 5 || fretNumber % 12 === 7 || fretNumber % 12 === 9 || fretNumber % 12 === 0)
&& fretNumber > 0
&& firstString
&& (
<div
style={{
position: 'absolute',
top: 0,
height: `${strings! * 100}%`,
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'column',
}}
>
<div
style={{
width: '0.75rem',
height: '0.75rem',
backgroundColor: 'currentColor',
opacity: 0.5,
borderRadius: '50%',
}}
/>
{fretNumber > 0 && fretNumber % 12 === 0 && (
<div
style={{
width: '0.75rem',
height: '0.75rem',
backgroundColor: 'currentColor',
opacity: 0.5,
borderRadius: '50%',
}}
/>
)}
</div>
)
}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
}}
>
{children}
</div>
{
fretted
&& (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'column',
color: 'red',
zIndex: 1,
}}
>
<div
style={{
display: 'grid',
placeContent: 'center',
width: name ? '2rem' : '0.75rem',
height: name ? '1.5rem' : '0.75rem',
backgroundColor: 'currentColor',
borderRadius: name ? '0.75rem' : '0.375rem',
}}
>
<div
style={{
color: 'white',
}}
>
{name}
</div>
</div>
</div>
)
}
{
fretNumber > 0
&& capoFret === fretNumber
&& firstString
&& (
<div
style={{
position: 'absolute',
top: 0,
height: `${strings! * 100}%`,
width: '100%',
}}
>
<div
style={{
margin: '0 auto',
width: '0.75rem',
height: '100%',
backgroundColor: 'currentColor',
borderRadius: '0.375rem',
}}
/>
</div>
)
}
</div>
)

Fret.propTypes = propTypes

export default Fret

+ 11
- 10
src/components/Fretboard/Fretboard.stories.tsx View File

@@ -9,20 +9,21 @@ export default {
// you consume the story in a test.
export const Default = (props?: Partial<Props>) => <Fretboard {...props} />

export const ReducedFrets = (props?: Partial<Props>) => <Fretboard {...props} frets={18} />
export const ReducedFrets = (props?: Partial<Props>) => <Fretboard {...props} landmarks frets={18} />

export const FixedTunings = (props?: Partial<Props>) => <Fretboard {...props} fixedTunings displayTunings />
export const Fretted = (props?: Partial<Props>) => <Fretboard {...props} landmarks fretted={[0, 0, 1, 2, 2, 0]} />

export const Fretted = (props?: Partial<Props>) => <Fretboard {...props} fretted={[0, 0, 1, 2, 2, 0]} />
export const FrettedWithNames = (props?: Partial<Props>) => <Fretboard {...props} landmarks fretted={[0, 0, 1, 2, 2, 0]} frettedNames={['E', 'B', 'G#', 'E', 'B', 'E']} />

export const Tunable = (props?: Partial<Props>) => <Fretboard {...props} displayTunings />
export const Mirror = (props?: Partial<Props>) => <Fretboard {...props} landmarks
mirror
/>

export const LeftHanded = (props?: Partial<Props>) => <Fretboard {...props} leftHanded />
export const MirrorFrettedWithNames = (props?: Partial<Props>) => <Fretboard {...props} mirror
landmarks fretted={[0, 0, 1, 2, 2, 0]} frettedNames={['E', 'B', 'G#', 'E', 'B', 'E']} />

export const Bass = (props?: Partial<Props>) => <Fretboard {...props} tunings={[43, 38, 33, 28]} />
export const Bass = (props?: Partial<Props>) => <Fretboard {...props} landmarks strings={4} />

export const ExtendedGuitar = (props?: Partial<Props>) => <Fretboard {...props} tunings={[64, 59, 55, 50, 45, 40, 35, 30]} />
export const ExtendedGuitar = (props?: Partial<Props>) => <Fretboard {...props} landmarks strings={8} />

// export const ExtendedBass = (props?: Partial<Props>) => <Fretboard {...props} tunings={[48, 43, 38, 33, 28, 23]} />

export const WithCapo = (props?: Partial<Props>) => <Fretboard {...props} capoFret={8} />
export const WithCapo = (props?: Partial<Props>) => <Fretboard {...props} landmarks capoFret={8} />

+ 47
- 278
src/components/Fretboard/Fretboard.tsx View File

@@ -1,314 +1,83 @@
import React, {ChangeEventHandler, FC} from 'react'
import React, {FC} from 'react'
import * as PropTypes from 'prop-types'
import FretboardString from '../FretboardString/FretboardString'
import Fret from '../Fret/Fret'

const propTypes = {
tunings: PropTypes.arrayOf(PropTypes.number),
strings: PropTypes.number,
frets: PropTypes.number,
leftHanded: PropTypes.bool,
mirror: PropTypes.bool,
fixedTunings: PropTypes.bool,
displayTunings: PropTypes.bool,
capoFret: PropTypes.number,
fretted: PropTypes.arrayOf(PropTypes.number),
landmarks: PropTypes.bool,
frettedNames: PropTypes.arrayOf(PropTypes.string),
}

export type Props = PropTypes.InferProps<typeof propTypes>

const PITCHES = 'C C# D D# E F F# G G# A A# B'.split(' ')

const getPitchName = (n: number) => PITCHES[n % 12] + Math.floor(n / 12)

const Fretboard: FC<Props> = ({
tunings = [64, 59, 55, 50, 45, 40],
strings = 6,
frets = 24,
leftHanded = false,
fixedTunings = false,
displayTunings = false,
mirror = false,
capoFret = 0,
fretted = [],
landmarks = false,
frettedNames = [],
}) => {
const [clientSide, setClientSide, ] = React.useState(false)
const [pitches, setPitches, ] = React.useState<string[]>(tunings!.map(t => getPitchName(t!)))

const updateStringPitch = (currentStringNumber: number): ChangeEventHandler<HTMLInputElement> => e => {
const { value } = e.target
setPitches(oldPitches => [
...oldPitches.slice(0, currentStringNumber),
getPitchName(Number(value)),
...oldPitches.slice(currentStringNumber + 1),
])
}

React.useEffect(() => {
setClientSide(true)
}, [])

// React.useEffect(() => {
// if (!Array.isArray(tunings!)) {
// return
// }
//
// setPitches(() => tunings.map(t => getPitchName(t!)))
// }, [tunings])

if (!Array.isArray(tunings!)) {
return null
}

const strings = tunings!.map(t => new Array(frets).fill(0).map((_, i) => t! + i + 1))
const str: boolean[][] = new Array(strings)
.fill(null)
.map(() => new Array(frets)
.fill(null)
.map(() => false)
)

return (
<div
style={{
border: '0.0625rem solid',
boxSizing: 'border-box',
userSelect: 'none',
}}
>
{strings!.map((stringPitches, stringNumber) => (
{str!.map((f, stringNumber: number) => (
<div
style={{
display: 'flex',
flexDirection: leftHanded ? 'row-reverse' : 'row',
flexDirection: mirror ? 'row-reverse' : 'row',
}}
>
{
displayTunings
&& (
<div
style={{
display: 'block',
width: '5rem',
height: '1rem',
padding: '0 0 0 1rem',
boxSizing: 'border-box',
position: 'relative',
flexShrink: 0,
}}
>
<div
style={{
display: 'block',
width: '100%',
height: '100%',
padding: 0,
boxSizing: 'border-box',
position: 'relative',
}}
>
<span
style={{
display: 'block',
width: '2rem',
height: '100%',
position: 'absolute',
top: 0,
left: 0,
pointerEvents: 'none',
}}
>
{pitches[stringNumber]}
</span>
<input
type="number"
style={{
display: 'block',
width: '100%',
height: '100%',
padding: 0,
boxSizing: 'border-box',
border: 0,
backgroundColor: 'transparent',
font: 'inherit',
textAlign: 'right',
}}
readOnly={!clientSide || Boolean(fixedTunings)}
onChange={updateStringPitch(stringNumber)}
defaultValue={tunings[stringNumber]!}
min={0}
max={127}
/>
</div>
</div>
)
}
<div
style={{
display: 'block',
position: 'relative',
width: '0.5rem',
height: '1rem',
flexShrink: 0,
padding: 0,
borderWidth: '0 0.0625rem',
borderStyle: 'none solid',
}}
<Fret
fretNumber={0}
totalFrets={frets!}
firstString={stringNumber === 0}
strings={strings!}
landmarks={landmarks}
fretted={fretted![stringNumber] === 0}
capoFret={capoFret}
name={frettedNames![stringNumber]}
>
<button
type="button"
style={{
position: 'relative',
display: 'block',
width: '100%',
height: '100%',
flexShrink: 0,
padding: 0,
border: 0,
backgroundColor: 'transparent',
}}
<FretboardString
stringNumber={stringNumber}
/>
</Fret>
{f.map((_, i) => (
<Fret
fretNumber={i + 1}
totalFrets={frets!}
firstString={stringNumber === 0}
strings={strings!}
landmarks={landmarks}
fretted={fretted![stringNumber] === i + 1}
capoFret={capoFret}
name={frettedNames![stringNumber]}
>
<span
style={{
display: 'block',
width: '100%',
height: `${0.03125 + stringNumber * 0.03125}rem`,
backgroundColor: 'currentColor',
}}

<FretboardString
stringNumber={stringNumber}
/>
{
fretted![stringNumber] === 0
&& (
<span
style={{
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'column',
color: 'red',
}}
>
<span
style={{
width: '0.75rem',
height: '0.75rem',
backgroundColor: 'currentColor',
borderRadius: '50%',
}}
/>
</span>
)
}
</button>
</div>
{stringPitches.map((_, i) => (
<div
style={{
width: `${(frets! - i - 1) / 12 * 100 + 100}%`,
height: '1rem',
position: 'relative',
}}
>
{(i % 12 === 2 || i % 12 === 4 || i % 12 === 6 || i % 12 === 8 || i % 12 === 11) && stringNumber === 0 && (
<div
style={{
position: 'absolute',
top: 0,
height: `${tunings.length * 100}%`,
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'column',
}}
>
<div
style={{
width: '0.75rem',
height: '0.75rem',
backgroundColor: 'currentColor',
opacity: 0.5,
borderRadius: '50%',
}}
/>
{i % 12 === 11 && (
<div
style={{
width: '0.75rem',
height: '0.75rem',
backgroundColor: 'currentColor',
opacity: 0.5,
borderRadius: '50%',
}}
/>
)}
</div>
)}
<button
key={i}
type="button"
style={{
position: 'relative',
display: 'block',
width: '100%',
height: '1rem',
padding: 0,
borderWidth: '0 0.0625rem',
borderStyle: 'none solid',
backgroundColor: 'transparent',
}}
>
<span
style={{
display: 'block',
width: '100%',
height: `${0.03125 + stringNumber * 0.03125}rem`,
backgroundColor: 'currentColor',
}}
/>
{
fretted![stringNumber] === i + 1
&& (
<span
style={{
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'column',
color: 'red',
}}
>
<span
style={{
width: '0.75rem',
height: '0.75rem',
backgroundColor: 'currentColor',
borderRadius: '50%',
}}
/>
</span>
)
}
</button>
{
capoFret === (i + 1) && stringNumber === 0 && (
<div
style={{
position: 'absolute',
top: 0,
height: `${tunings.length * 100}%`,
width: '100%',
}}
>
<div
style={{
width: '0.75rem',
height: '100%',
backgroundColor: 'currentColor',
borderRadius: '0.375rem',
}}
/>
</div>
)
}
</div>
</Fret>
))}
</div>
))}


+ 25
- 0
src/components/FretboardString/FretboardString.tsx View File

@@ -0,0 +1,25 @@
import * as React from 'react'
import * as PropTypes from 'prop-types'

const propTypes = {
stringNumber: PropTypes.number.isRequired,
}

type Props = PropTypes.InferProps<typeof propTypes>

const FretboardString: React.FC<Props> = ({
stringNumber,
}) => (
<span
style={{
display: 'block',
width: '100%',
height: `${0.03125 + stringNumber * 0.03125}rem`,
backgroundColor: 'currentColor',
}}
/>
)

FretboardString.propTypes = propTypes

export default FretboardString

+ 2
- 10
src/index.tsx View File

@@ -1,11 +1,3 @@
import React, { FC, HTMLAttributes, ReactChild } from 'react'
import Fretboard from './components/Fretboard/Fretboard'

export interface Props extends HTMLAttributes<HTMLDivElement> {
children?: ReactChild
}

// Please do not use types off of a default export module or else Storybook Docs will suffer.
// see: https://github.com/storybookjs/storybook/issues/9556
export const Thing: FC<Props> = ({ children }) => {
return <div>{children || `the snozzberries taste like snozzberries`}</div>
}
export default Fretboard

Loading…
Cancel
Save