- クリックで選択
- Shift クリックでそこまで移動
- Ctrl クリックでそこと入れ替え
- 上下キーで選択移動
- Shift+上下キーで入れ替え
- Ctrl+Shift 上下キーで一番上下まで移動
Created
July 7, 2022 15:08
-
-
Save nexpr/05085ec26af45f3b0d1080942ee73e11 to your computer and use it in GitHub Desktop.
ユーザーが操作して要素を並び替えられるコンポーネント/Shift+↑↓やShift+クリック
This file contains 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
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> | |
) | |
} |
This file contains 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
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