Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

複数選択:選択だけ実装 #1470

Merged
merged 59 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
30b055d
(add/multiple-selection)
sevenc-nanashi Aug 4, 2023
0ad4d77
Add: 選択を実装
sevenc-nanashi Aug 5, 2023
c46a2fd
Add: 単体クリックのテストを追加
sevenc-nanashi Aug 5, 2023
fb5788b
Add: focusでも良い感じになるように
sevenc-nanashi Aug 5, 2023
f122e7c
Add: テストを追加
sevenc-nanashi Aug 5, 2023
dc13b41
Merge: main -> add/multiple-selection
sevenc-nanashi Aug 5, 2023
ec906e1
Fix: 間を押したときの挙動を修正
sevenc-nanashi Aug 5, 2023
ac15bbd
Fix: ウィンドウのフォーカスを外したときの挙動を修正
sevenc-nanashi Aug 5, 2023
1223e63
Add: 複数ある物を削除
sevenc-nanashi Aug 5, 2023
847c89c
Fix: Mac周りを修正
sevenc-nanashi Aug 5, 2023
8068fad
Add: test.failを追加
sevenc-nanashi Aug 5, 2023
3980e97
Change: 当たり判定用のdivを召喚するように
sevenc-nanashi Aug 12, 2023
e808521
Update: テストを更新
sevenc-nanashi Aug 12, 2023
79836e4
Add: Shift+カーソルの選択を追加
sevenc-nanashi Aug 12, 2023
4b03835
Change: 実験的機能に隔離
sevenc-nanashi Aug 14, 2023
6e3baba
Fix: ハイライトを修正
sevenc-nanashi Aug 14, 2023
aedd4a4
Delete: skipFocusEvent周りの処理を削除
sevenc-nanashi Aug 15, 2023
b45efb9
Revert: onInputFocus -> setActiveAudioKey
sevenc-nanashi Aug 15, 2023
aae65a4
Revert: 不要な変更をRevert
sevenc-nanashi Aug 15, 2023
ea8453e
Change: 選択食を変更
sevenc-nanashi Aug 15, 2023
0f78045
Change: 少し濃く
sevenc-nanashi Aug 15, 2023
6fe825a
Change: enableSetting -> toggleSetting
sevenc-nanashi Aug 15, 2023
9176b38
Add: Shift+上下のテストを追加
sevenc-nanashi Aug 15, 2023
f001f83
Fix: hostの値を修正
sevenc-nanashi Aug 16, 2023
3942e2b
Fix: playwrightのサーバー起動を無効化していたのを修正
sevenc-nanashi Aug 16, 2023
d67e8a3
Change: selectedではないactiveなAudioCellができるように
sevenc-nanashi Aug 16, 2023
6453ff8
Merge: main -> add/multiple-selection
sevenc-nanashi Aug 16, 2023
4e910d1
Add: コメントを追加
sevenc-nanashi Aug 16, 2023
0ae8237
Revert: 不要な変更をRevert
sevenc-nanashi Aug 16, 2023
bbec109
Refactor: onClickWithModifierKey周りをリファクタ
sevenc-nanashi Aug 16, 2023
f61e3f4
Change: e2eテストをまとめる
sevenc-nanashi Aug 17, 2023
4ef7cd6
Add: コメントを追加
sevenc-nanashi Aug 17, 2023
76e0435
Fix: hostを修正
sevenc-nanashi Aug 17, 2023
3170f53
Merge: main -> add/multiple-selection
sevenc-nanashi Aug 19, 2023
d406a4b
Delete: Macでの分岐を削除
sevenc-nanashi Aug 19, 2023
998d165
Change: selectedは常にactiveを含むように
sevenc-nanashi Aug 19, 2023
0da07d2
Update: テストを更新
sevenc-nanashi Aug 19, 2023
877f6bf
Change: prepareAudioCells -> addAudioCells
sevenc-nanashi Aug 19, 2023
8dad306
Delete: Macでテストを無効化
sevenc-nanashi Aug 19, 2023
ee08b65
Delete: data-is-multi-select-enabledを削除
sevenc-nanashi Aug 19, 2023
9b29af9
Add: filterを追加
sevenc-nanashi Aug 19, 2023
c37df7e
Refactor: onKeyup/downをkeyEventListenerに
sevenc-nanashi Aug 19, 2023
b2019a7
Change: selectedじゃない時だけactiveを移動させるように
sevenc-nanashi Aug 20, 2023
a0e98eb
Improve: コメント回りを改善
sevenc-nanashi Aug 21, 2023
96a46ed
Change: focus周りの仕様を変更
sevenc-nanashi Aug 22, 2023
072093a
Change: 上下移動はテキスト欄にfocusするように
sevenc-nanashi Aug 22, 2023
19e6810
Add: :focusの理由を追加
sevenc-nanashi Aug 22, 2023
16966b5
Change: focusTextField -> focusTarget
sevenc-nanashi Aug 23, 2023
9db22ad
Fix: フォーカス対象を修正
sevenc-nanashi Aug 24, 2023
31b6d42
Add: focusCellのドキュメントを追加
sevenc-nanashi Aug 24, 2023
2f7e79b
Add: テストを追加
sevenc-nanashi Aug 24, 2023
5adcea8
Change: throwするように
sevenc-nanashi Aug 24, 2023
6e6e864
Improve: ドキュメントのわかりやすさを向上
sevenc-nanashi Aug 24, 2023
71393a7
Change: エラーメッセージを変更
sevenc-nanashi Aug 25, 2023
e6838eb
Merge: remove -> local
sevenc-nanashi Aug 25, 2023
1512a5f
Change: AudioCellのfocusCellのfocusTargetをOptionalに
sevenc-nanashi Aug 25, 2023
bc0472b
Delete: Macの処理変更を削除
sevenc-nanashi Aug 25, 2023
f15093d
Fix: Macで落ちることを追求
sevenc-nanashi Aug 26, 2023
60453aa
Update tests/e2e/browser/複数選択.spec.ts
Hiroshiba Aug 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions src/components/AudioCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div
ref="root"
class="audio-cell"
tabindex="0"
tabindex="-1"
:class="{
active: isActiveAudioCell,
selected: isSelectedAudioCell && isMultiSelectEnabled,
Expand Down Expand Up @@ -115,9 +115,13 @@ const props =

const emit =
defineEmits<{
// focusTarget:
// textField: テキストフィールドにフォーカス。特別な理由がない限りはこちらを使う。
// root: AudioCell自体にフォーカス。テキストフィールドにはフォーカスしない。
// selectedAudioKeysをactiveAudioKeyだけにしたくない時に使う。
(
e: "focusCell",
payload: { audioKey: AudioKey; focusTarget: "textField" | "root" }
payload: { audioKey: AudioKey; focusTarget?: "textField" | "root" }
): void;
}>();

Expand Down Expand Up @@ -369,7 +373,6 @@ const putMultilineText = async (texts: string[]) => {
if (audioKeys.length > 0) {
emit("focusCell", {
audioKey: audioKeys[audioKeys.length - 1],
focusTarget: "textField",
});
}
};
Expand All @@ -394,18 +397,23 @@ const moveCell = (offset: number) => (e?: KeyboardEvent) => {
const index = audioKeys.value.indexOf(props.audioKey) + offset;
if (index >= 0 && index < audioKeys.value.length) {
const selectedAudioKeys = store.getters.SELECTED_AUDIO_KEYS;
emit("focusCell", {
audioKey: audioKeys.value[index],
focusTarget: "textField",
});
if (isMultiSelectEnabled.value && e?.shiftKey) {
emit("focusCell", {
audioKey: audioKeys.value[index],
focusTarget: "root",
});
store.dispatch("SET_SELECTED_AUDIO_KEYS", {
audioKeys: [
...selectedAudioKeys,
props.audioKey,
audioKeys.value[index],
],
});
} else {
emit("focusCell", {
audioKey: audioKeys.value[index],
focusTarget: "textField",
});
}
}
};
Expand All @@ -426,12 +434,10 @@ const removeCell = async () => {
if (index > 0) {
emit("focusCell", {
audioKey: audioKeys.value[index - 1],
focusTarget: "textField",
});
} else {
emit("focusCell", {
audioKey: audioKeys.value[index + 1],
focusTarget: "textField",
});
}

Expand Down
9 changes: 7 additions & 2 deletions src/store/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,13 @@ export const audioStore = createPartialStore<AudioStoreTypes>({
{ audioKeys }: { audioKeys?: AudioKey[] }
) {
const uniqueAudioKeys = new Set(audioKeys);
if (getters.ACTIVE_AUDIO_KEY) {
uniqueAudioKeys.add(getters.ACTIVE_AUDIO_KEY);
if (
getters.ACTIVE_AUDIO_KEY &&
!uniqueAudioKeys.has(getters.ACTIVE_AUDIO_KEY)
) {
throw new Error(
"Assertion failed: audioKeys.includes(getters.ACTIVE_AUDIO_KEY)"
);
}
const sortedAudioKeys = state.audioKeys.filter((audioKey) =>
uniqueAudioKeys.has(audioKey)
Expand Down
6 changes: 4 additions & 2 deletions src/views/EditorHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,11 @@ const focusCell = ({
focusTarget,
}: {
audioKey: AudioKey;
focusTarget: "root" | "textField";
focusTarget?: "root" | "textField";
}) => {
audioCellRefs[audioKey].focusCell({ focusTarget });
audioCellRefs[audioKey].focusCell({
focusTarget: focusTarget ?? "textField",
});
};

// Electronのデフォルトのundo/redoを無効化
Expand Down
107 changes: 76 additions & 31 deletions tests/e2e/browser/複数選択.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ test.beforeEach(async ({ page }) => {
});

const ctrlLike = process.platform === "darwin" ? "Meta" : "Control";

type SelectedStatus = {
active: number;
selected: number[];
};
/**
* アクティブなAudioCellと選択されているAudioCellを取得する。
* 戻り値のインデックスは1から始まる。(nth-childのインデックスと揃えるため)
*/
async function getSelectedStatus(page: Page): Promise<{
active: number;
selected: number[];
}> {
async function getSelectedStatus(page: Page): Promise<SelectedStatus> {
const selectedAudioKeys = await page.evaluate(() => {
const audioCells = [...document.querySelectorAll(".audio-cell")];
let active: number | undefined;
Expand Down Expand Up @@ -52,24 +54,34 @@ async function addAudioCells(page: Page, count: number) {
}

test("複数選択:マウス周り", async ({ page }) => {
let selectedStatus: SelectedStatus;

// 複数選択していない状態でactiveのAudioCellをクリックしても何も起こらない
await page.locator(".audio-cell:nth-child(1)").click();

await page.waitForTimeout(100);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(1);
expect(selectedStatus.selected).toEqual([1]);

// Shift+クリックは前回選択していたAudioCellから今回クリックしたAudioCellまでを選択する
await page.locator(".audio-cell:nth-child(2)").click();
await page.keyboard.down("Shift");
await page.locator(".audio-cell:nth-child(4)").click();
await page.keyboard.up("Shift");

await page.waitForTimeout(100);
const selectedStatus1 = await getSelectedStatus(page);
expect(selectedStatus1.active).toBe(4);
expect(selectedStatus1.selected).toEqual([2, 3, 4]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(4);
expect(selectedStatus.selected).toEqual([2, 3, 4]);

// ただのクリックはactiveAudioKeyとselectedAudioKeysをクリックしたAudioCellだけにする
await page.locator(".audio-cell:nth-child(2)").click();

await page.waitForTimeout(100);
const selectedStatus2 = await getSelectedStatus(page);
expect(selectedStatus2.active).toBe(2);
expect(selectedStatus2.selected).toEqual([2]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(2);
expect(selectedStatus.selected).toEqual([2]);

if (process.platform === "darwin") {
// なぜかCmd(Meta)+クリックが動かないのでスキップする
Expand All @@ -82,19 +94,19 @@ test("複数選択:マウス周り", async ({ page }) => {
await page.keyboard.up(ctrlLike);
await page.waitForTimeout(100);

const selectedStatus3 = await getSelectedStatus(page);
expect(selectedStatus3.active).toBe(4);
expect(selectedStatus3.selected).toEqual([2, 4]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(4);
expect(selectedStatus.selected).toEqual([2, 4]);

// Ctrl+クリックは選択範囲から削除する
await page.keyboard.down(ctrlLike);
await page.locator(".audio-cell:nth-child(2)").click();
await page.keyboard.up(ctrlLike);
await page.waitForTimeout(100);

const selectedStatus4 = await getSelectedStatus(page);
expect(selectedStatus4.active).toBe(4);
expect(selectedStatus4.selected).toEqual([4]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(4);
expect(selectedStatus.selected).toEqual([4]);

// activeのAudioCellをCtrl+クリックすると選択範囲から削除して次のselectedのAudioCellをactiveにする
await page.keyboard.down(ctrlLike);
Expand All @@ -103,46 +115,79 @@ test("複数選択:マウス周り", async ({ page }) => {
await page.keyboard.up(ctrlLike);
await page.waitForTimeout(100);

const selectedStatus5 = await getSelectedStatus(page);
expect(selectedStatus5.active).toBe(4);
expect(selectedStatus5.selected).toEqual([4]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(4);
expect(selectedStatus.selected).toEqual([4]);

// selected内のCharacterButtonをクリックしても選択範囲は変わらない
await page.locator(".audio-cell:nth-child(2)").click();
await page.keyboard.down("Shift");
await page.locator(".audio-cell:nth-child(4)").click();
await page.keyboard.up("Shift");

await page.locator(".audio-cell:nth-child(2) .character-button").click();

await page.waitForTimeout(100);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(4);
expect(selectedStatus.selected).toEqual([2, 3, 4]);

// selected外のCharacterButtonをクリックすると選択範囲をそのAudioCellだけにする
await page.locator(".audio-cell:nth-child(1)").click();

await page.waitForTimeout(100);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(1);
expect(selectedStatus.selected).toEqual([1]);
});

test("複数選択:キーボード", async ({ page }) => {
let selectedStatus: SelectedStatus;
// Shift+下で下方向を選択範囲にする
await page.locator(".audio-cell:nth-child(2)").click();
await page.keyboard.down("Shift");
await page.keyboard.press("ArrowDown");
await page.keyboard.up("Shift");
await page.waitForTimeout(100);

const selectedStatus1 = await getSelectedStatus(page);
expect(selectedStatus1.active).toBe(3);
expect(selectedStatus1.selected).toEqual([2, 3]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(3);
expect(selectedStatus.selected).toEqual([2, 3]);

// ただの下で下方向をactiveにして他の選択を解除する
await page.keyboard.press("ArrowDown");

await page.waitForTimeout(100);
const selectedStatus2 = await getSelectedStatus(page);
expect(selectedStatus2.active).toBe(4);
expect(selectedStatus2.selected).toEqual([4]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(4);
expect(selectedStatus.selected).toEqual([4]);

// Shift+上で上方向を選択範囲にする
await page.keyboard.down("Shift");
await page.keyboard.press("ArrowUp");
await page.keyboard.up("Shift");
await page.waitForTimeout(100);

const selectedStatus3 = await getSelectedStatus(page);
expect(selectedStatus3.active).toBe(3);
expect(selectedStatus3.selected).toEqual([3, 4]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(3);
expect(selectedStatus.selected).toEqual([3, 4]);

// ただの上で上方向をactiveにして他の選択を解除する
await page.keyboard.press("ArrowUp");
await page.waitForTimeout(100);

const selectedStatus4 = await getSelectedStatus(page);
expect(selectedStatus4.active).toBe(2);
expect(selectedStatus4.selected).toEqual([2]);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(2);
expect(selectedStatus.selected).toEqual([2]);

// EnterでactiveのAudioCellのテキストフィールドにフォーカスし、複数選択を解除する
await page.keyboard.down("Shift");
await page.keyboard.press("ArrowDown");
await page.keyboard.up("Shift");
await page.keyboard.press("Enter");

await page.waitForTimeout(100);
selectedStatus = await getSelectedStatus(page);
expect(selectedStatus.active).toBe(3);
expect(selectedStatus.selected).toEqual([3]);
});