Skip to content

Commit

Permalink
feat: implement home and end selections
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Aug 25, 2024
1 parent ab66598 commit 6f73cbf
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 9 deletions.
37 changes: 33 additions & 4 deletions packages/core/src/useSelect/useListBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface ListBoxProps<TOption> {
orientation?: Orientation;

onToggleAll?(): void;
onToggleBefore?(): void;
onToggleAfter?(): void;
}

export interface ListBoxDomProps {
Expand Down Expand Up @@ -86,6 +88,28 @@ export function useListBox<TOption>(_props: Reactivify<ListBoxProps<TOption>>) {
return;
}

if (e.code === 'Home' && isShiftPressed.value) {
e.preventDefault();
e.stopPropagation();
props.onToggleBefore?.();

if (isMetaPressed.value) {
unfocusCurrent();
options.value.at(0)?.focus();
}
}

if (e.code === 'End' && isShiftPressed.value) {
e.preventDefault();
e.stopPropagation();
props.onToggleAfter?.();

if (isMetaPressed.value) {
unfocusCurrent();
options.value.at(-1)?.focus();
}
}

if (e.code === 'Tab') {
isOpen.value = false;
}
Expand All @@ -99,29 +123,34 @@ export function useListBox<TOption>(_props: Reactivify<ListBoxProps<TOption>>) {
}
}

function focusNext() {
function unfocusCurrent() {
const currentlyFocusedIdx = options.value.findIndex(o => o.isFocused());
options.value[currentlyFocusedIdx]?.unfocus();

return currentlyFocusedIdx;
}

function focusNext() {
const currentlyFocusedIdx = unfocusCurrent();
// Focus first one if none is focused
if (currentlyFocusedIdx === -1) {
focusAndToggleIfShiftPressed(0);
return;
}

const nextIdx = Math.min(currentlyFocusedIdx + 1, options.value.length - 1);
options.value[currentlyFocusedIdx]?.unfocus();
focusAndToggleIfShiftPressed(nextIdx);
}

function focusPrev() {
const currentlyFocusedIdx = options.value.findIndex(o => o.isFocused());
const currentlyFocusedIdx = unfocusCurrent();
// Focus first one if none is focused
if (currentlyFocusedIdx === -1) {
focusAndToggleIfShiftPressed(0);
return;
}

const nextIdx = Math.max(currentlyFocusedIdx - 1, 0);
options.value[currentlyFocusedIdx]?.unfocus();
focusAndToggleIfShiftPressed(nextIdx);
}

Expand Down
34 changes: 29 additions & 5 deletions packages/core/src/useSelect/useSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ export function useSelect<TOption>(_props: Reactivify<SelectProps<TOption>, 'sch
const { listBoxProps, isOpen, options, isShiftPressed } = useListBox<TOption>({
...props,
onToggleAll: toggleAll,
onToggleBefore: toggleBefore,
onToggleAfter: toggleAfter,
});

const { updateValidity } = useInputValidity({ field });
const { fieldValue, setValue, isTouched, errorMessage } = field;
const { displayError } = useErrorDisplay(field);
Expand Down Expand Up @@ -118,14 +121,35 @@ export function useSelect<TOption>(_props: Reactivify<SelectProps<TOption>, 'sch

lastRecentIdx = lastRecentIdx === -1 ? 0 : lastRecentIdx;
const startIdx = Math.min(lastRecentIdx, targetIdx);
const endIdx = Math.min(Math.max(lastRecentIdx, targetIdx + 1), options.value.length - 1);
const range = options.value.slice(startIdx, endIdx);
const nextValue = range.map(opt => opt.getValue());
setValue(nextValue);
updateValidity();
const endIdx = Math.min(Math.max(lastRecentIdx, targetIdx), options.value.length - 1);
selectRange(startIdx, endIdx);
},
};

function selectRange(start: number, end: number) {
const nextValue = options.value.slice(start, end + 1).map(opt => opt.getValue());
setValue(nextValue);
updateValidity();
}

function toggleBefore() {
const focusedIdx = options.value.findIndex(opt => opt.isFocused());
if (focusedIdx < 0) {
return;
}

const startIdx = 0;
const endIdx = Math.min(focusedIdx, options.value.length - 1);
selectRange(startIdx, endIdx);
}

function toggleAfter() {
const focusedIdx = options.value.findIndex(opt => opt.isFocused());
const startIdx = Math.max(0, focusedIdx);
const endIdx = options.value.length - 1;
selectRange(startIdx, endIdx);
}

function toggleAll() {
const isMultiple = toValue(props.multiple);
if (!isMultiple) {
Expand Down

0 comments on commit 6f73cbf

Please sign in to comment.