Skip to content

Instantly share code, notes, and snippets.

@nexpr
Created July 7, 2022 15:08
Show Gist options
  • Save nexpr/05085ec26af45f3b0d1080942ee73e11 to your computer and use it in GitHub Desktop.
Save nexpr/05085ec26af45f3b0d1080942ee73e11 to your computer and use it in GitHub Desktop.
ユーザーが操作して要素を並び替えられるコンポーネント/Shift+↑↓やShift+クリック
  • クリックで選択
  • Shift クリックでそこまで移動
  • Ctrl クリックでそこと入れ替え
  • 上下キーで選択移動
  • Shift+上下キーで入れ替え
  • Ctrl+Shift 上下キーで一番上下まで移動
const App = () => {
const [state, setState] = useState(() => {
return Array.from(Array(10), (x, i) => ({ name: "ABCDEFGHIJK"[i]}))
})
return (
<div style={{ width: "300px" }}>
<ReSort
items={state}
onChange={items => {
setState(items)
}}
itemRender={(item, selected) => (
<div style={{ border: "1px solid silver", padding: "3px 6px", margin: "2px" }}>
{selected ? "■" : "□"} {item.name}
</div>
)}
/>
<button>focus point1</button>
<button>focus point2</button>
</div>
)
}
const ReSort = ({ items, onChange, keySelector, itemRender }) => {
const [selected, setSelected] = useState(null)
const onClick = (item) => (event) => {
if (!selected) {
setSelected(item)
} else if (selected === item) {
setSelected(null)
} else {
const selected_index = items.indexOf(selected)
const clicked_index = items.indexOf(item)
const changed = items.slice()
if (event.shiftKey) {
const [item] = changed.splice(selected_index, 1)
changed.splice(clicked_index, 0, item)
onChange(changed)
} else if (event.ctrlKey) {
changed[selected_index] = items[clicked_index]
changed[clicked_index] = items[selected_index]
onChange(changed)
} else {
setSelected(item)
}
}
}
const onKeyDown = (event) => {
if (event.code !== "ArrowUp" && event.code !== "ArrowDown") return
if (!selected) return
const index = items.indexOf(selected)
const next_index = index + (event.code === "ArrowUp" ? -1 : 1)
if (next_index < 0 || next_index >= items.length) return
if (event.shiftKey) {
const changed = items.slice()
if (event.ctrlKey) {
// to top/bottom
const [item] = changed.splice(index, 1)
if (index < next_index) {
changed.push(item)
} else {
changed.unshift(item)
}
} else {
// swap
changed[index] = items[next_index]
changed[next_index] = items[index]
}
onChange(changed)
} else {
setSelected(items[next_index])
}
}
const onBlur = () => {
setSelected(null)
}
return (
<div onKeyDown={onKeyDown} onBlur={onBlur} tabIndex="0" style={{ userSelect: "none" }}>
{items.map((item, index) => {
const key = keySelector?.(item) ?? `autokey-${index}`
const is_selected = item === selected
return (
<div key={key} onClick={onClick(item)}>
{itemRender(item, is_selected)}
</div>
)
})}
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment