🌊

React と display grid でメモリ付き定規

2021/09/18に公開

メモリ付きの定規、ルーラーコンポーネントのサンプルです。

react-memory-component

コード

ruler-with-memory - CodeSandbox

title=Ruler.tsx
<Ruler
  memories={[1, 2, 4, 8, 16, 32, 64, 128]}
  colors={['#333', '#444', '#555', '#666', '#777', '#888', '#999']}
/>
function Ruler({ memories, colors }: Props) {
  if (memories.length - 1 !== colors.length)
    console.warn('expect Ruler.memories.length - 1=== Ruler.colors.length')
  return (
    <div>
      <div
        className="memories"
        style={{ gridTemplateColumns: `repeat(${memories.length}, 1fr)` }}
      >
        {memories.map((memori, i) => (
          <div key={i}>{memori}</div>
        ))}
      </div>
      <div
        className="blocks"
        style={{
          gridTemplateColumns: `1fr repeat(${memories.length - 1}, 2fr) 1fr`,
        }}
      >
        {range(memories.length + 1).map((i) => (
          <div key={i} style={{ background: colors[i - 1] || '' }}></div>
        ))}
      </div>
    </div>
  )
}
.memories {
  display: grid;
  text-align: center;
}

.blocks {
  display: grid;
}
.blocks > div {
  border: solid 1px black;
  height: 1rem;
}
.blocks > div:first-child,
.blocks > div:last-child {
  border: none;
}

ミソ

ブロック部分とメモリ部分を 0.5 ブロックずらし、メモリをtext-align: center とすることで境界に数字が来るようにしています。
memory grid css

ブロック grid-template-columns: 1fr repeat(<ブロック数> - 1}, 2fr) 1fr

メモリ部分 grid-template-columns: repeat(<ブロック数>, 1fr)

重なってる border を消す場合の css

.blocks > div {
  border: solid 1px black;
  border-left: none;
  height: 1rem;
}

.blocks > div:nth-child(2) {
  border-right: solid 1px black;
}

.blocks > div:first-child,
.blocks > div:last-child {
  border: none;
}

styled-components を使ったサンプル

ruler-with-memory-styled - CodeSandbox

const ScaleLine = styled.div<{ count: number }>`
  display: grid;
  text-align: center;
  grid-template-columns: repeat(${({ count }) => count}, 1fr);
`
const BlockLine = styled.div<{ count: number }>`
  display: grid;
  grid-template-columns: 1fr repeat(${({ count }) => count}, 2fr) 1fr;

  > div {
    border: solid 1px black;
    border-left: none;
    height: 1rem;
    &:nth-child(2) {
      border-right: solid 1px black;
    }
    &:first-child,
    &:last-child {
      border: none;
    }
  }
`

type Props = { memories: number[]; colors: string[] }
const range = (v: number) => [...Array(v).keys()]

function Ruler({ memories, colors }: Props) {
  if (memories.length - 1 !== colors.length)
    console.warn('expect Ruler.memories.length - 1=== Ruler.colors.length')
  return (
    <div>
      <ScaleLine count={memories.length}>
        {memories.map((memori, i) => (
          <div key={i}>{memori}</div>
        ))}
      </ScaleLine>
      <BlockLine count={memories.length - 1}>
        {range(memories.length + 1).map((i) => (
          <div key={i} style={{ background: colors[i - 1] || '' }}></div>
        ))}
      </BlockLine>
    </div>
  )
}
GitHubで編集を提案

Discussion