Skip to content

Instantly share code, notes, and snippets.

@Rasukarusan
Last active August 7, 2022 02:38
Show Gist options
  • Save Rasukarusan/9314aee54677ec0fb998683b9b0fdcb4 to your computer and use it in GitHub Desktop.
Save Rasukarusan/9314aee54677ec0fb998683b9b0fdcb4 to your computer and use it in GitHub Desktop.
React,styled-jsxでセグメントコントロール
import css from 'styled-jsx/css'
import { useState, useEffect } from 'react'
const styles = css`
.controls {
display: inline-flex;
justify-content: space-between;
background: #e7e7e7;
border-radius: 8px;
overflow: hidden;
position: relative;
width: 100%;
}
.controls::before {
content: '';
background: #5465ff;
border-radius: 8px;
position: absolute;
top: 0px;
bottom: 0px;
left: 0;
transition: transform 0.3s ease, width 0.3s ease;
}
.segment {
color: gray;
position: relative;
text-align: center;
}
.segment.active {
color: #fff;
}
label {
cursor: pointer;
display: block;
padding: 5px 0px;
transition: color 0.5s ease;
}
input {
opacity: 0;
margin: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
cursor: pointer;
width: 100%;
height: 100%;
}
`
interface Props {
segments: {
label: string
value: string
}[]
callback: (value: string, index: number) => void
defaultIndex?: number
}
export const SegmentedControl: React.FC<Props> = ({
segments,
callback,
defaultIndex = 0,
}) => {
const [activeIndex, setActiveIndex] = useState(defaultIndex)
const [segmentLeft, setSegmentLeft] = useState(0)
const segmentWidth = `${100 / segments.length}%`
useEffect(() => {
const id = `${activeIndex}-${segments[activeIndex].value}`
const segment = document.getElementById(id)
const { width } = segment.getBoundingClientRect()
setSegmentLeft(width * activeIndex)
}, [activeIndex])
const onChange = (value, index) => {
setActiveIndex(index)
callback(value, index)
}
return (
<div className="controls">
{segments.map((item, i) => {
return (
<div
id={`${i}-${item.value}`}
key={item.value}
className={`segment ${i === activeIndex && 'active'}`}
>
<input
type="radio"
value={item.value}
onChange={() => onChange(item.value, i)}
checked={i === activeIndex}
/>
<label htmlFor={item.label}>{item.label}</label>
</div>
)
})}
<style jsx>{styles}</style>
<style jsx>{`
.segment {
min-width: ${segmentWidth};
}
.controls::before {
width: ${segmentWidth};
transform: translateX(${segmentLeft}px);
}
`}</style>
</div>
)
}
@Rasukarusan
Copy link
Author

呼び出し側
pages/index.tsx

import { SegmentedControl } from './SegmentControl'

const IndexPage = () => {
  const onSegmentChange = (value, index) => {
    console.log(value, index)
  }
  return (
    <div style={{ padding: 20 }}>
      <SegmentedControl
        callback={onSegmentChange}
        defaultIndex={0}
        segments={[
          {
            label: 'すべて',
            value: 'first',
          },
          {
            label: 'フォロー中',
            value: 'second',
          },
        ]}
      />
    </div>
  )
}
export default IndexPage

@Rasukarusan
Copy link
Author

Rasukarusan commented Apr 9, 2022

@Rasukarusan
Copy link
Author

めちゃくちゃ参考にさせていただいたサイト
https://letsbuildui.dev/articles/building-a-segmented-control-component

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment