diff --git a/.gitignore b/.gitignore index db014bc..89b0fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,4 @@ dist .yarn/unplugged .yarn/build-state.yml .pnp.* +keys.html diff --git a/src/components/Keyboard.stories.tsx b/src/components/Keyboard.stories.tsx index a4aceaa..d82e3fe 100644 --- a/src/components/Keyboard.stories.tsx +++ b/src/components/Keyboard.stories.tsx @@ -1,5 +1,6 @@ import * as React from 'react' import * as PropTypes from 'prop-types' +import styled from 'styled-components' import Keyboard, { propTypes, } from './Keyboard' export default { @@ -10,4 +11,431 @@ type Props = PropTypes.InferProps // By passing optional props to this story, you can control the props of the component when // you consume the story in a test. -export const Default = (props?: Partial) => ; +export const Default = (props?: Partial) => ( + +) + +export const WithActiveKeys = (props?: Partial) => ( + +) + +const Base = styled('div')({ + width: '100%', + height: '100%', + position: 'relative', + '--natural-key-color': '#eee', + '--accidental-key-color': '#333', +}) + +const N1 = styled('div')`width: 100%; height: 100%; position: relative;` + +const N2 = styled('div')` + width: 100%; + height: 100%; + background-color: black; + position: absolute; + top: 0; + left: 0; +` + +const N3 = styled('div')` +width: 100%; + height: 100%; + padding: 1px 0 1px 1px; + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; +` + +const N4 = styled('div')` +width: 100%; + height: 100%; + background-color: var(--natural-key-color, white); + border-radius: 0 0 1px 1px; +` + +const N5 = styled('div')` +width: 100%; + height: calc(33 / 80 * 100%); + background-color: white; + padding: 0 1px 2px 2px; + box-sizing: border-box; + background-clip: content-box; + position: absolute; + bottom: 0; + left: 0; + opacity: 0.25; + mask-image: linear-gradient(to bottom, transparent, white); + -webkit-mask-image: linear-gradient(to bottom, transparent, white); +` + +const N6 = styled('div')` +width: 100%; + height: 100%; + background-color: black; + padding: 1px 2px 3px 3px; + box-sizing: border-box; + background-clip: content-box; + position: absolute; + bottom: 0; + left: 0; + opacity: 0.08; + mask-image: linear-gradient(to bottom, transparent, white); + -webkit-mask-image: linear-gradient(to bottom, transparent, white); +` + +const N7 = styled('div')` +width: 100%; + height: 2px; + padding: 0 0 1px 1px; + box-sizing: border-box; + position: absolute; + bottom: 0; + left: 0; +` + +const N8 = styled('div')` +width: 100%; + height: 100%; + background-color: black; + border-radius: 0 0 1px 1px; + opacity: 0.25; +` +const N9 = styled('div')` +width: 2px; + height: 100%; + padding: 1px 0 1px 1px; + box-sizing: border-box; + position: absolute; + bottom: 0; + left: 0; +` +const N10 = styled('div')` +width: 100%; + height: 100%; + background-color: black; + border-radius: 0 0 0 1px; + opacity: 0.07; +` +const N11 = styled('div')` +width: 100%; + height: 6px; + padding: 1px 0 0 1px; + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; +` +const N12 = styled('div')` +width: 100%; + height: 100%; + background-color: black; + mask-image: linear-gradient(to bottom, white, transparent); + -webkit-mask-image: linear-gradient(to bottom, white, transparent); + opacity: 0.12; +` +const N13 = styled('div')` +width: 100%; + height: 3px; + padding: 1px 0 0 1px; + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; +` +const N14 = styled('div')` +width: 100%; + height: 100%; + background-color: black; + opacity: 0.12; +` +const N15 = styled('div')` +width: 1px; + height: 100%; + padding: 1px 0 1px 0; + box-sizing: border-box; + position: absolute; + bottom: 0; + right: 0; +` +const N16 = styled('div')` +width: 100%; + height: 100%; + background-color: white; + border-radius: 0 0 1px 0; + opacity: 0.12; +` + +const N17 = styled('div')` + padding: 1px 0 1px 1px; + box-sizing: border-box; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; +` + +const N18 = styled('div')` + position: relative; + width: 100%; + height: 100%; +` + +const B1 = styled('div')` +width: 100%; + height: 100%; + position: relative; + border-radius: 1px; + box-shadow: 0 0 0 1px rgba(0,0,0,0.25); +` +const B2 = styled('div')` +width: 100%; + height: calc(6 / 50 * 100%); + position: absolute; + border-radius: 0 0 1px 1px; + bottom: 0; + left: 0; + background-color: var(--accidental-key-color, #222); + mask-image: linear-gradient(to bottom, white, rgba(0,0,0,0.9)); + -webkit-mask-image: linear-gradient(to bottom, white, rgba(0,0,0,0.9)); +` +const B3 = styled('div')` +width: 100%; + height: calc(44 / 50 * 100%); + position: absolute; + top: 0; + left: 0; + background-color: var(--accidental-key-color, #222); +` +const B4 = styled('div')` +width: 100%; + height: 4px; + padding: 1px 0 0 0; + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; +` +const B5 = styled('div')` +width: 100%; + height: 100%; + background-color: black; + opacity: 0.12; +` +const B6 = styled('div')` +width: 2px; + height: 11px; + padding: 1px 1px 1px 0; + box-sizing: border-box; + position: absolute; + top: 0; + right: 0; +` +const B7 = styled('div')` +width: 100%; + height: 100%; + background-color: white; + mask-image: linear-gradient(to bottom, transparent, white); + -webkit-mask-image: linear-gradient(to bottom, transparent, white); + opacity: 0.4; +` +const B8 = styled('div')` +width: 2px; + height: 100%; + padding: 10px 1px 6px 0; + box-sizing: border-box; + position: absolute; + top: 0; + right: 0; +` +const B9 = styled('div')` +width: 100%; + height: 100%; + background-color: white; + opacity: 0.4; +` +const B10 = styled('div')` +width: 100%; + height: 4px; + padding: 0 1px 1px 1px; + box-sizing: border-box; + position: absolute; + bottom: 0; + left: 0; +` +const B11 = styled('div')` +width: 100%; + height: 100%; + background-color: white; + border-radius: 4px 4px 1px 1px; + opacity: 0.12; +` +const B12 = styled('div')` +width: 100%; + height: 100%; + padding: 3px 3px 7px 3px; + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; +` +const B13 = styled('div')` +width: 100%; + height: 100%; + background-color: white; + mask-image: linear-gradient(to bottom, transparent, white); + -webkit-mask-image: linear-gradient(to bottom, transparent, white); + border-radius: 99999px; + opacity: 0.12; +` +const B14 = styled('div')` +width: 100%; + height: 6px; + padding: 0 1px 5px 1px; + box-sizing: border-box; + position: absolute; + bottom: 0; + left: 0; +` +const B15 = styled('div')` +width: 100%; + height: 100%; + background-color: white; + border-radius: 0 0 1px 1px; + opacity: 0.4; +` + +const B16 = styled('div')` + padding: 1px 1px 1px 1px; + box-sizing: border-box; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; +` + +const B17 = styled('div')` + position: relative; + width: 100%; + height: 100%; + border-radius: 0 0 1px 1px; + overflow: hidden; +` + +export const Styled = (props?: Partial) => ( + ( + + + + + + + + + + + + + + + + + + + + + + + + + + + {children} + + + + ), + accidental: ({ children, }) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + {children} + + + + ), + }} + /> +) diff --git a/src/components/Keyboard.tsx b/src/components/Keyboard.tsx index 1e2fe00..a00ffaa 100644 --- a/src/components/Keyboard.tsx +++ b/src/components/Keyboard.tsx @@ -12,24 +12,34 @@ const Base = styled('div')({ const Key = styled('div')({ position: 'absolute', - border: '1px solid', - boxSizing: 'border-box', top: 0, }) -const NaturalKey = styled(Key)({ - zIndex: 0, - backgroundColor: 'transparent', +const DefaultNaturalKey = styled('div')({ + width: '100%', + height: '100%', + backgroundColor: 'white', + border: '1px solid', + boxSizing: 'border-box', + position: 'relative', }) -const AccidentalKey = styled(Key)({ - zIndex: 2, - backgroundColor: 'currentColor', +const DefaultAccidentalKey = styled('div')({ + width: '100%', + height: '100%', + backgroundColor: 'black', + border: '1px solid', + boxSizing: 'border-box', + position: 'relative', }) const Highlight = styled('div')({ width: '100%', height: '100%', + position: 'absolute', + top: 0, + left: 0, + opacity: 0.75, }) export const propTypes = { @@ -83,6 +93,10 @@ const Keyboard: React.FC = ({ keyChannels = [], channelColors = DEFAULT_CHANNEL_COLORS, width = '100%', + keyComponents: { + natural: NaturalKey = DefaultNaturalKey, + accidental: AccidentalKey = DefaultAccidentalKey, + } = {}, height = 64, }) => { const [keys, setKeys, ] = React.useState([]) @@ -96,6 +110,8 @@ const Keyboard: React.FC = ({ style={{ width: width!, height: height!, + backgroundColor: 'black', + overflow: 'hidden', }} > {keys.map(k => { @@ -111,25 +127,28 @@ const Keyboard: React.FC = ({ ) return ( - - { - Array.isArray(currentKeyChannels) - && currentKeyChannels.map(c => ( - - )) - } - + + { + Array.isArray(currentKeyChannels) + && currentKeyChannels.map(c => ( + + )) + } + + ) })} diff --git a/src/services/getKeyWidth.ts b/src/services/getKeyWidth.ts index ab9a91a..b43f374 100644 --- a/src/services/getKeyWidth.ts +++ b/src/services/getKeyWidth.ts @@ -12,7 +12,7 @@ interface GetKeyWidthDecorator { (startKey: number, endKey: number): GetKeyWidth, } -const ACCIDENTAL_KEY_TO_NATURAL_KEY_WIDTH_RATIO = 18 / 36 +const ACCIDENTAL_KEY_TO_NATURAL_KEY_WIDTH_RATIO = 13 / 23 const getKeyWidthDecorator: GetKeyWidthDecorator = (startKey, endKey): GetKeyWidth => (k) => { const dummyKeys = generateKeys(startKey, endKey) @@ -40,7 +40,7 @@ const getKeyWidthDecorator: GetKeyWidthDecorator = (startKey, endKey): GetKeyWid ) const octaveCount = getOctaveCount(startKey, endKey) const naturalKeyWidth = 100 * (octaveCount / fractionalOctaveCount) / (octaveCount * 7) - return isNaturalKey(k) ? naturalKeyWidth : naturalKeyWidth * ACCIDENTAL_KEY_TO_NATURAL_KEY_WIDTH_RATIO // naturalKeyWidth * 13.7 / 23.5} + return isNaturalKey(k) ? naturalKeyWidth : naturalKeyWidth * ACCIDENTAL_KEY_TO_NATURAL_KEY_WIDTH_RATIO } export default getKeyWidthDecorator