Last active
August 7, 2022 02:38
-
-
Save Rasukarusan/9314aee54677ec0fb998683b9b0fdcb4 to your computer and use it in GitHub Desktop.
React,styled-jsxでセグメントコントロール
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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> | |
| ) | |
| } |
Author
Author
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

呼び出し側
pages/index.tsx