Fix/scope hotkeys (#581)

* WIP

* asd

* Fix

* Fix lint

* Removed console log

* asd

* Removed isDefined

* Fix/debounce company card onchange (#580)

* Add internal state and debounce for editable text card

* Use debounce for date fields too

* Update refetch

* Nit

* Removed comments

* Ménage

---------

Co-authored-by: Emilien Chauvet <emilien.chauvet.enpc@gmail.com>
This commit is contained in:
Lucas Bordeau 2023-07-11 03:53:46 +02:00 committed by GitHub
parent 1c8aaff39d
commit 5f98b70c6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 581 additions and 509 deletions

View File

@ -42,6 +42,7 @@
"recoil": "^0.7.7",
"scroll-into-view": "^1.16.2",
"ts-key-enum": "^2.0.12",
"url": "^0.11.1",
"uuid": "^9.0.0",
"web-vitals": "^2.1.4"
},

View File

@ -20,6 +20,9 @@ import { SettingsWorkspaceMembers } from '~/pages/settings/SettingsWorkspaceMemb
import { CompanyShow } from './pages/companies/CompanyShow';
import { PersonShow } from './pages/people/PersonShow';
import { SettingsWorksapce } from './pages/settings/SettingsWorkspace';
import { AppPath } from './sync-hooks/types/AppPath';
import { AuthPath } from './sync-hooks/types/AuthPath';
import { SettingsPath } from './sync-hooks/types/SettingsPath';
import { AppInternalHooks } from './AppInternalHooks';
/**
@ -33,11 +36,14 @@ function AuthRoutes() {
<AuthModal>
<AnimatePresence mode="wait">
<Routes location={location} key={location.pathname}>
<Route path="" element={<Index />} />
<Route path="callback" element={<Verify />} />
<Route path="password-login" element={<PasswordLogin />} />
<Route path="create/workspace" element={<CreateWorkspace />} />
<Route path="create/profile" element={<CreateProfile />} />
<Route path={AuthPath.Index} element={<Index />} />
<Route path={AuthPath.Callback} element={<Verify />} />
<Route path={AuthPath.PasswordLogin} element={<PasswordLogin />} />
<Route
path={AuthPath.CreateWorkspace}
element={<CreateWorkspace />}
/>
<Route path={AuthPath.CreateProfile} element={<CreateProfile />} />
</Routes>
</AnimatePresence>
</AuthModal>
@ -52,7 +58,7 @@ export function App() {
<DefaultLayout>
<Routes>
<Route
path="auth/*"
path={AppPath.AuthCatchAll}
element={
<RequireOnboarding>
<AuthLayout>
@ -66,27 +72,39 @@ export function App() {
element={
<RequireOnboarded>
<Routes>
<Route path="" element={<Navigate to="/people" replace />} />
<Route path="people" element={<People />} />
<Route path="person/:personId" element={<PersonShow />} />
<Route path="companies" element={<Companies />} />
<Route
path="companies/:companyId"
path=""
element={<Navigate to={AppPath.PeoplePage} replace />}
/>
<Route path={AppPath.PeoplePage} element={<People />} />
<Route
path={AppPath.PersonShowPage}
element={<PersonShow />}
/>
<Route path={AppPath.CompaniesPage} element={<Companies />} />
<Route
path={AppPath.CompanyShowPage}
element={<CompanyShow />}
/>
<Route path="opportunities" element={<Opportunities />} />
<Route
path="settings/*"
path={AppPath.OpportunitiesPage}
element={<Opportunities />}
/>
<Route
path={AppPath.SettingsCatchAll}
element={
<Routes>
<Route path="profile" element={<SettingsProfile />} />
<Route
path="workspace-members"
path={SettingsPath.ProfilePage}
element={<SettingsProfile />}
/>
<Route
path={SettingsPath.WorkspaceMembersPage}
element={<SettingsWorkspaceMembers />}
/>
<Route
path="workspace"
path={SettingsPath.Workspace}
element={<SettingsWorksapce />}
/>
</Routes>

View File

@ -1,13 +1,15 @@
import { AnalyticsHook } from './sync-hooks/AnalyticsHook';
import { GotoHotkeysHooks } from './sync-hooks/GotoHotkeysHooks';
import { HotkeysScopeStackAutoSyncHook } from './sync-hooks/HotkeysScopeStackAutoSyncHook';
import { HotkeysScopeAutoSyncHook } from './sync-hooks/HotkeysScopeAutoSyncHook';
import { HotkeysScopeBrowserRouterSync } from './sync-hooks/HotkeysScopeBrowserRouterSync';
export function AppInternalHooks() {
return (
<>
<AnalyticsHook />
<GotoHotkeysHooks />
<HotkeysScopeStackAutoSyncHook />
<HotkeysScopeAutoSyncHook />
<HotkeysScopeBrowserRouterSync />
</>
);
}

View File

@ -1,6 +1,6 @@
import { useRecoilState } from 'recoil';
import { useHotkeysScopeOnBooleanState } from '@/hotkeys/hooks/useHotkeysScopeOnBooleanState';
import { usePreviousHotkeysScope } from '@/hotkeys/hooks/internal/usePreviousHotkeysScope';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
@ -13,7 +13,6 @@ import {
StyledGroup,
StyledInput,
StyledList,
// StyledSeparator,
} from './CommandMenuStyles';
export function CommandMenu() {
@ -22,16 +21,26 @@ export function CommandMenu() {
useScopedHotkeys(
'ctrl+k,meta+k',
() => {
setOpen((prevOpen) => !prevOpen);
handleOpenChange(!open);
},
InternalHotkeysScope.CommandMenu,
[setOpen],
[setOpen, open, handleOpenChange],
);
useHotkeysScopeOnBooleanState(
{ scope: InternalHotkeysScope.CommandMenu },
open,
);
const {
setHotkeysScopeAndMemorizePreviousScope,
goBackToPreviousHotkeysScope,
} = usePreviousHotkeysScope();
function handleOpenChange(newOpenState: boolean) {
if (newOpenState) {
setOpen(true);
setHotkeysScopeAndMemorizePreviousScope(InternalHotkeysScope.CommandMenu);
} else {
setOpen(false);
goBackToPreviousHotkeysScope();
}
}
/*
TODO: Allow performing actions on page through CommandBar
@ -73,7 +82,7 @@ export function CommandMenu() {
return (
<StyledDialog
open={open}
onOpenChange={setOpen}
onOpenChange={handleOpenChange}
label="Global Command Menu"
>
<StyledInput placeholder="Search" />

View File

@ -11,6 +11,7 @@ import {
import { useHandleCheckableCommentThreadTargetChange } from '@/comments/hooks/useHandleCheckableCommentThreadTargetChange';
import { CommentableEntityForSelect } from '@/comments/types/CommentableEntityForSelect';
import { CompanyChip } from '@/companies/components/CompanyChip';
import { usePreviousHotkeysScope } from '@/hotkeys/hooks/internal/usePreviousHotkeysScope';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { PersonChip } from '@/people/components/PersonChip';
@ -120,8 +121,20 @@ export function CommentThreadRelationPicker({ commentThread }: OwnProps) {
searchFilter,
});
const {
setHotkeysScopeAndMemorizePreviousScope,
goBackToPreviousHotkeysScope,
} = usePreviousHotkeysScope();
function handleRelationContainerClick() {
setIsMenuOpen((isOpen) => !isOpen);
if (isMenuOpen) {
exitEditMode();
} else {
setIsMenuOpen(true);
setHotkeysScopeAndMemorizePreviousScope(
InternalHotkeysScope.RelationPicker,
);
}
}
// TODO: Place in a scoped recoil atom family
@ -134,6 +147,7 @@ export function CommentThreadRelationPicker({ commentThread }: OwnProps) {
});
function exitEditMode() {
goBackToPreviousHotkeysScope();
setIsMenuOpen(false);
setSearchFilter('');
}

View File

@ -1,8 +1,6 @@
import { useRecoilState } from 'recoil';
import { commentableEntityArrayState } from '@/comments/states/commentableEntityArrayState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
@ -12,11 +10,6 @@ import { Timeline } from '../timeline/Timeline';
export function RightDrawerTimeline() {
const [commentableEntityArray] = useRecoilState(commentableEntityArrayState);
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.RightDrawer,
customScopes: { goto: false, 'command-menu': true },
});
return (
<RightDrawerPage>
<RightDrawerTopBar />

View File

@ -1,8 +1,6 @@
import { useRecoilValue } from 'recoil';
import { viewableCommentThreadIdState } from '@/comments/states/viewableCommentThreadIdState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
@ -12,11 +10,6 @@ import { CommentThread } from '../CommentThread';
export function RightDrawerCreateCommentThread() {
const commentThreadId = useRecoilValue(viewableCommentThreadIdState);
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.RightDrawer,
customScopes: { goto: false, 'command-menu': true },
});
return (
<RightDrawerPage>
<RightDrawerTopBar title="New note" />

View File

@ -1,8 +1,6 @@
import { useRecoilValue } from 'recoil';
import { viewableCommentThreadIdState } from '@/comments/states/viewableCommentThreadIdState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
@ -10,11 +8,8 @@ import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDraw
import { CommentThread } from '../CommentThread';
export function RightDrawerEditCommentThread() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.RightDrawer,
customScopes: { goto: false, 'command-menu': true },
});
const commentThreadId = useRecoilValue(viewableCommentThreadIdState);
return (
<RightDrawerPage>
<RightDrawerTopBar title="" />

View File

@ -4,7 +4,7 @@ import { useFilteredSearchEntityQuery } from '@/relation-picker/hooks/useFiltere
import { relationPickerSearchFilterScopedState } from '@/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/relation-picker/types/EntityForSelect';
import { Entity } from '@/relation-picker/types/EntityTypeForSelect';
import { useEditableCell } from '@/ui/components/editable-cell/hooks/useCloseEditableCell';
import { useEditableCell } from '@/ui/components/editable-cell/hooks/useEditableCell';
import {
Company,
User,

View File

@ -1,4 +1,5 @@
import { HotkeysScopeStackItem } from '../types/internal/HotkeysScopeStackItems';
import { CustomHotkeysScopes } from '../types/internal/CustomHotkeysScope';
import { HotkeysScope } from '../types/internal/HotkeysScope';
import { InternalHotkeysScope } from '../types/internal/InternalHotkeysScope';
export const INITIAL_HOTKEYS_SCOPES: string[] = [InternalHotkeysScope.App];
@ -8,10 +9,15 @@ export const ALWAYS_ON_HOTKEYS_SCOPES: string[] = [
InternalHotkeysScope.App,
];
export const DEFAULT_HOTKEYS_SCOPE_STACK_ITEM: HotkeysScopeStackItem = {
export const DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES: CustomHotkeysScopes = {
commandMenu: true,
goto: false,
};
export const INITIAL_HOTKEYS_SCOPE: HotkeysScope = {
scope: InternalHotkeysScope.App,
customScopes: {
'command-menu': true,
commandMenu: true,
goto: true,
},
};

View File

@ -0,0 +1,29 @@
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { currentHotkeysScopeState } from '@/hotkeys/states/internal/currentHotkeysScopeState';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useHotkeysScopes } from './useHotkeysScopes';
export function useHotkeysScopeAutoSync() {
const { setHotkeysScopes } = useHotkeysScopes();
const currentHotkeysScope = useRecoilValue(currentHotkeysScopeState);
useEffect(() => {
const scopesToSet: string[] = [];
if (currentHotkeysScope.customScopes?.commandMenu) {
scopesToSet.push(InternalHotkeysScope.CommandMenu);
}
if (currentHotkeysScope?.customScopes?.goto) {
scopesToSet.push(InternalHotkeysScope.Goto);
}
scopesToSet.push(currentHotkeysScope.scope);
setHotkeysScopes(scopesToSet);
}, [setHotkeysScopes, currentHotkeysScope]);
}

View File

@ -1,36 +0,0 @@
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { customHotkeysScopesState } from '@/hotkeys/states/internal/customHotkeysScopesState';
import { hotkeysScopeStackState } from '@/hotkeys/states/internal/hotkeysScopeStackState';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useHotkeysScope } from './useHotkeysScope';
export function useHotkeysScopeStackAutoSync() {
const { setHotkeysScopes } = useHotkeysScope();
const hotkeysScopeStack = useRecoilValue(hotkeysScopeStackState);
const customHotkeysScopes = useRecoilValue(customHotkeysScopesState);
useEffect(() => {
if (hotkeysScopeStack.length === 0) {
return;
}
const scopesToSet: string[] = [];
const currentHotkeysScope = hotkeysScopeStack[hotkeysScopeStack.length - 1];
if (currentHotkeysScope.customScopes?.['command-menu']) {
scopesToSet.push(InternalHotkeysScope.CommandMenu);
}
if (currentHotkeysScope?.customScopes?.goto) {
scopesToSet.push(InternalHotkeysScope.Goto);
}
scopesToSet.push(currentHotkeysScope.scope);
setHotkeysScopes(scopesToSet);
}, [setHotkeysScopes, customHotkeysScopes, hotkeysScopeStack]);
}

View File

@ -3,7 +3,7 @@ import { useRecoilCallback } from 'recoil';
import { internalHotkeysEnabledScopesState } from '@/hotkeys/states/internal/internalHotkeysEnabledScopesState';
export function useHotkeysScope() {
export function useHotkeysScopes() {
const { disableScope, enableScope } = useHotkeysContext();
const disableAllHotkeysScopes = useRecoilCallback(

View File

@ -0,0 +1,39 @@
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { currentHotkeysScopeState } from '@/hotkeys/states/internal/currentHotkeysScopeState';
import { CustomHotkeysScopes } from '@/hotkeys/types/internal/CustomHotkeysScope';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { useSetHotkeysScope } from '../useSetHotkeysScope';
export function usePreviousHotkeysScope() {
const [previousHotkeysScope, setPreviousHotkeysScope] =
useState<HotkeysScope | null>();
const setHotkeysScope = useSetHotkeysScope();
const currentHotkeysScope = useRecoilValue(currentHotkeysScopeState);
function goBackToPreviousHotkeysScope() {
if (previousHotkeysScope) {
setHotkeysScope(
previousHotkeysScope.scope,
previousHotkeysScope.customScopes,
);
}
}
function setHotkeysScopeAndMemorizePreviousScope(
scope: string,
customScopes?: CustomHotkeysScopes,
) {
setPreviousHotkeysScope(currentHotkeysScope);
setHotkeysScope(scope, customScopes);
}
return {
setHotkeysScopeAndMemorizePreviousScope,
goBackToPreviousHotkeysScope,
};
}

View File

@ -1,48 +0,0 @@
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil';
import { hotkeysScopeStackState } from '../states/internal/hotkeysScopeStackState';
import { HotkeysScopeStackItem } from '../types/internal/HotkeysScopeStackItems';
export function useAddToHotkeysScopeStack() {
return useRecoilCallback(
({ snapshot, set }) =>
async ({
scope,
customScopes = {
'command-menu': true,
goto: false,
},
ancestorScope,
}: HotkeysScopeStackItem) => {
const hotkeysScopeStack = await snapshot.getPromise(
hotkeysScopeStackState,
);
const currentHotkeysScope =
hotkeysScopeStack.length > 0
? hotkeysScopeStack[hotkeysScopeStack.length - 1]
: null;
const previousHotkeysScope =
hotkeysScopeStack.length > 1
? hotkeysScopeStack[hotkeysScopeStack.length - 2]
: null;
if (
scope === currentHotkeysScope?.scope ||
scope === previousHotkeysScope?.scope
) {
return;
}
set(
hotkeysScopeStackState,
produce(hotkeysScopeStack, (draft) => {
draft.push({ scope, customScopes, ancestorScope });
}),
);
},
[],
);
}

View File

@ -1,16 +0,0 @@
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { hotkeysScopeStackState } from '../states/internal/hotkeysScopeStackState';
export function useCurrentHotkeysScope() {
const hotkeysScopeStack = useRecoilValue(hotkeysScopeStackState);
return useMemo(() => {
if (hotkeysScopeStack.length === 0) {
return null;
} else {
return hotkeysScopeStack[hotkeysScopeStack.length - 1];
}
}, [hotkeysScopeStack]);
}

View File

@ -1,27 +0,0 @@
import { useEffect } from 'react';
import { HotkeysScopeStackItem } from '../types/internal/HotkeysScopeStackItems';
import { useAddToHotkeysScopeStack } from './useAddToHotkeysScopeStack';
import { useRemoveFromHotkeysScopeStack } from './useRemoveFromHotkeysScopeStack';
export function useHotkeysScopeOnBooleanState(
hotkeysScopeStackItem: HotkeysScopeStackItem,
booleanState: boolean,
) {
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const removeFromHotkeysScopeStack = useRemoveFromHotkeysScopeStack();
useEffect(() => {
if (booleanState) {
addToHotkeysScopeStack(hotkeysScopeStackItem);
} else {
removeFromHotkeysScopeStack(hotkeysScopeStackItem.scope);
}
}, [
hotkeysScopeStackItem,
removeFromHotkeysScopeStack,
addToHotkeysScopeStack,
booleanState,
]);
}

View File

@ -1,32 +0,0 @@
import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { hotkeysScopeStackState } from '../states/internal/hotkeysScopeStackState';
import { HotkeysScopeStackItem } from '../types/internal/HotkeysScopeStackItems';
import { useAddToHotkeysScopeStack } from './useAddToHotkeysScopeStack';
export function useHotkeysScopeOnMountOnly(
hotkeysScopeStackItem: HotkeysScopeStackItem,
enabled = true,
) {
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const [hotkeysScopeStack] = useRecoilState(hotkeysScopeStackState);
const hotkeysScopeAlreadyInStack = hotkeysScopeStack.some(
(hotkeysScopeStackItemToFind) =>
hotkeysScopeStackItemToFind.scope === hotkeysScopeStackItem.scope,
);
useEffect(() => {
if (!hotkeysScopeAlreadyInStack && enabled) {
addToHotkeysScopeStack(hotkeysScopeStackItem);
}
}, [
enabled,
addToHotkeysScopeStack,
hotkeysScopeStackItem,
hotkeysScopeAlreadyInStack,
]);
}

View File

@ -1,54 +0,0 @@
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil';
import { DEFAULT_HOTKEYS_SCOPE_STACK_ITEM } from '../constants';
import { hotkeysScopeStackState } from '../states/internal/hotkeysScopeStackState';
import { InternalHotkeysScope } from '../types/internal/InternalHotkeysScope';
export function useRemoveFromHotkeysScopeStack() {
return useRecoilCallback(
({ snapshot, set }) =>
async (hotkeysScopeToRemove: string) => {
const hotkeysScopeStack = await snapshot.getPromise(
hotkeysScopeStackState,
);
if (hotkeysScopeStack.length < 1) {
set(hotkeysScopeStackState, [DEFAULT_HOTKEYS_SCOPE_STACK_ITEM]);
return;
}
const currentHotkeysScope =
hotkeysScopeStack[hotkeysScopeStack.length - 1];
if (hotkeysScopeStack.length === 1) {
if (currentHotkeysScope?.scope !== InternalHotkeysScope.App) {
set(hotkeysScopeStackState, [DEFAULT_HOTKEYS_SCOPE_STACK_ITEM]);
}
return;
}
const previousHotkeysScope =
hotkeysScopeStack[hotkeysScopeStack.length - 2];
if (
previousHotkeysScope.scope === hotkeysScopeToRemove ||
currentHotkeysScope.scope !== hotkeysScopeToRemove
) {
return;
}
set(
hotkeysScopeStackState,
produce(hotkeysScopeStack, (draft) => {
return draft.filter(
(hotkeysScope) => hotkeysScope.scope !== hotkeysScopeToRemove,
);
}),
);
},
[],
);
}

View File

@ -1,42 +0,0 @@
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil';
import { DEFAULT_HOTKEYS_SCOPE_STACK_ITEM } from '../constants';
import { hotkeysScopeStackState } from '../states/internal/hotkeysScopeStackState';
import { InternalHotkeysScope } from '../types/internal/InternalHotkeysScope';
export function useRemoveHighestHotkeysScopeStackItem() {
return useRecoilCallback(
({ snapshot, set }) =>
async () => {
const hotkeysScopeStack = await snapshot.getPromise(
hotkeysScopeStackState,
);
if (hotkeysScopeStack.length < 1) {
set(hotkeysScopeStackState, [DEFAULT_HOTKEYS_SCOPE_STACK_ITEM]);
return;
}
const currentHotkeysScope =
hotkeysScopeStack[hotkeysScopeStack.length - 1];
if (hotkeysScopeStack.length === 1) {
if (currentHotkeysScope?.scope !== InternalHotkeysScope.App) {
set(hotkeysScopeStackState, [DEFAULT_HOTKEYS_SCOPE_STACK_ITEM]);
}
return;
}
set(
hotkeysScopeStackState,
produce(hotkeysScopeStack, (draft) => {
draft.pop();
}),
);
},
[],
);
}

View File

@ -1,18 +0,0 @@
import { useResetRecoilState } from 'recoil';
import { hotkeysScopeStackState } from '../states/internal/hotkeysScopeStackState';
import { useAddToHotkeysScopeStack } from './useAddToHotkeysScopeStack';
export function useResetHotkeysScopeStack() {
const resetHotkeysScopeStack = useResetRecoilState(hotkeysScopeStackState);
const addHotkeysScopedStack = useAddToHotkeysScopeStack();
return function reset(toFirstScope?: string) {
resetHotkeysScopeStack();
if (toFirstScope) {
addHotkeysScopedStack({ scope: toFirstScope });
}
};
}

View File

@ -0,0 +1,59 @@
import { useRecoilCallback } from 'recoil';
import { isDefined } from '@/utils/type-guards/isDefined';
import { DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES } from '../constants';
import { currentHotkeysScopeState } from '../states/internal/currentHotkeysScopeState';
import { CustomHotkeysScopes } from '../types/internal/CustomHotkeysScope';
export function useSetHotkeysScope() {
return useRecoilCallback(
({ snapshot, set }) =>
async (hotkeysScopeToSet: string, customScopes?: CustomHotkeysScopes) => {
const currentHotkeysScope = await snapshot.getPromise(
currentHotkeysScopeState,
);
function isCustomScopesEqual(
customScopesA: CustomHotkeysScopes | undefined,
customScopesB: CustomHotkeysScopes | undefined,
) {
return (
customScopesA?.commandMenu === customScopesB?.commandMenu &&
customScopesA?.goto === customScopesB?.goto
);
}
if (currentHotkeysScope.scope === hotkeysScopeToSet) {
if (!isDefined(customScopes)) {
if (
isCustomScopesEqual(
currentHotkeysScope?.customScopes,
DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES,
)
) {
return;
}
} else {
if (
isCustomScopesEqual(
currentHotkeysScope?.customScopes,
customScopes,
)
) {
return;
}
}
}
set(currentHotkeysScopeState, {
scope: hotkeysScopeToSet,
customScopes: {
commandMenu: customScopes?.commandMenu ?? true,
goto: customScopes?.goto ?? false,
},
});
},
[],
);
}

View File

@ -0,0 +1,9 @@
import { atom } from 'recoil';
import { INITIAL_HOTKEYS_SCOPE } from '@/hotkeys/constants';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
export const currentHotkeysScopeState = atom<HotkeysScope>({
key: 'currentHotkeysScopeState',
default: INITIAL_HOTKEYS_SCOPE,
});

View File

@ -1,16 +0,0 @@
import { atom } from 'recoil';
import { InternalHotkeysScope } from '../../types/internal/InternalHotkeysScope';
export type CustomHotkeysScopes = {
[InternalHotkeysScope.Goto]: boolean;
[InternalHotkeysScope.CommandMenu]: boolean;
};
export const customHotkeysScopesState = atom<CustomHotkeysScopes>({
key: 'customHotkeysScopesState',
default: {
'command-menu': true,
goto: false,
},
});

View File

@ -1,9 +0,0 @@
import { atom } from 'recoil';
import { DEFAULT_HOTKEYS_SCOPE_STACK_ITEM } from '@/hotkeys/constants';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
export const hotkeysScopeStackState = atom<HotkeysScopeStackItem[]>({
key: 'hotkeysScopeStackState',
default: [DEFAULT_HOTKEYS_SCOPE_STACK_ITEM],
});

View File

@ -1,3 +0,0 @@
export enum HotkeysScope {
CompanyPage = 'company-page',
}

View File

@ -0,0 +1,4 @@
export type CustomHotkeysScopes = {
goto?: boolean;
commandMenu?: boolean;
};

View File

@ -0,0 +1,6 @@
import { CustomHotkeysScopes } from './CustomHotkeysScope';
export type HotkeysScope = {
scope: string;
customScopes?: CustomHotkeysScopes;
};

View File

@ -1,7 +0,0 @@
import { CustomHotkeysScopes } from '@/hotkeys/states/internal/customHotkeysScopesState';
export type HotkeysScopeStackItem = {
scope: string;
customScopes?: CustomHotkeysScopes;
ancestorScope?: string | null;
};

View File

@ -11,6 +11,7 @@ export enum InternalHotkeysScope {
TableHeaderDropdownButton = 'table-header-dropdown-button',
RelationPicker = 'relation-picker',
CellDoubleTextInput = 'cell-double-text-input',
TextInput = 'text-input',
Settings = 'settings',
CreateWokspace = 'create-workspace',
PasswordLogin = 'password-login',

View File

@ -0,0 +1,15 @@
export enum PageHotkeysScope {
Settings = 'settings',
CreateWokspace = 'create-workspace',
PasswordLogin = 'password-login',
AuthIndex = 'auth-index',
CreateProfile = 'create-profile',
ShowPage = 'show-page',
PersonShowPage = 'person-show-page',
CompanyShowPage = 'company-show-page',
CompaniesPage = 'companies-page',
PeoplePage = 'people-page',
OpportunitiesPage = 'opportunities-page',
ProfilePage = 'profile-page',
WorkspaceMemberPage = 'workspace-member-page',
}

View File

@ -1,13 +1,13 @@
import { Key } from 'ts-key-enum';
import { useAddToHotkeysScopeStack } from '@/hotkeys/hooks/useAddToHotkeysScopeStack';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { SingleEntitySelect } from '@/relation-picker/components/SingleEntitySelect';
import { useFilteredSearchEntityQuery } from '@/relation-picker/hooks/useFilteredSearchEntityQuery';
import { relationPickerSearchFilterScopedState } from '@/relation-picker/states/relationPickerSearchFilterScopedState';
import { useEditableCell } from '@/ui/components/editable-cell/hooks/useCloseEditableCell';
import { useEditableCell } from '@/ui/components/editable-cell/hooks/useEditableCell';
import { isCreateModeScopedState } from '@/ui/components/editable-cell/states/isCreateModeScopedState';
import { getLogoUrlFromDomainName } from '@/utils/utils';
import {
@ -32,7 +32,7 @@ export function PeopleCompanyPicker({ people }: OwnProps) {
const { closeEditableCell } = useEditableCell();
const addToScopeStack = useAddToHotkeysScopeStack();
const addToScopeStack = useSetHotkeysScope();
const companies = useFilteredSearchEntityQuery({
queryHook: useSearchCompanyQuery,
@ -62,7 +62,7 @@ export function PeopleCompanyPicker({ people }: OwnProps) {
function handleCreate() {
setIsCreating(true);
addToScopeStack({ scope: InternalHotkeysScope.CellDoubleTextInput });
addToScopeStack(InternalHotkeysScope.CellDoubleTextInput);
}
useScopedHotkeys(

View File

@ -1,9 +1,9 @@
import { useCallback, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilState } from 'recoil';
import { v4 as uuidv4 } from 'uuid';
import { useHotkeysScopeOnBooleanState } from '@/hotkeys/hooks/useHotkeysScopeOnBooleanState';
import { usePreviousHotkeysScope } from '@/hotkeys/hooks/internal/usePreviousHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { Column } from '@/ui/board/components/Board';
@ -26,16 +26,27 @@ type OwnProps = {
};
export function NewButton({ pipelineId, columnId }: OwnProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [isCreatingCard, setIsCreatingCard] = useState(false);
const [board, setBoard] = useRecoilState(boardColumnsState);
const [boardItems, setBoardItems] = useRecoilState(boardItemsState);
const {
goBackToPreviousHotkeysScope,
setHotkeysScopeAndMemorizePreviousScope,
} = usePreviousHotkeysScope();
const [createOnePipelineProgress] = useCreateOnePipelineProgressMutation({
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
});
const onEntitySelect = useCallback(
const handleEntitySelect = useCallback(
async (company: Pick<Company, 'id' | 'name' | 'domainName'>) => {
if (!company || !company.name || !company.domainName) return;
setIsCreatingCard(false);
goBackToPreviousHotkeysScope();
const newUuid = uuidv4();
const newBoard = JSON.parse(JSON.stringify(board));
const destinationColumnIndex = newBoard.findIndex(
@ -71,26 +82,35 @@ export function NewButton({ pipelineId, columnId }: OwnProps) {
setBoard,
boardItems,
setBoardItems,
goBackToPreviousHotkeysScope,
],
);
const onNewClick = useCallback(() => {
const handleNewClick = useCallback(() => {
setIsCreatingCard(true);
}, [setIsCreatingCard]);
setHotkeysScopeAndMemorizePreviousScope(
InternalHotkeysScope.RelationPicker,
);
}, [setIsCreatingCard, setHotkeysScopeAndMemorizePreviousScope]);
useHotkeysScopeOnBooleanState(
{ scope: InternalHotkeysScope.RelationPicker },
isCreatingCard,
);
function handleCancel() {
goBackToPreviousHotkeysScope();
setIsCreatingCard(false);
}
return (
<>
{isCreatingCard && (
<RecoilScope>
<NewCompanyBoardCard onEntitySelect={onEntitySelect} />
<div ref={containerRef}>
<NewCompanyBoardCard
onEntitySelect={handleEntitySelect}
onCancel={handleCancel}
/>
</div>
</RecoilScope>
)}
<UINewButton onClick={onNewClick} />
<UINewButton onClick={handleNewClick} />
</>
);
}

View File

@ -13,9 +13,10 @@ type OwnProps = {
onEntitySelect: (
company: Pick<Company, 'id' | 'name' | 'domainName'>,
) => void;
onCancel: () => void;
};
export function NewCompanyBoardCard({ onEntitySelect }: OwnProps) {
export function NewCompanyBoardCard({ onEntitySelect, onCancel }: OwnProps) {
const [searchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
@ -39,6 +40,7 @@ export function NewCompanyBoardCard({ onEntitySelect }: OwnProps) {
return (
<SingleEntitySelect
onEntitySelected={(value) => onEntitySelect(value)}
onCancel={onCancel}
entities={{
entitiesToSelect: companies.entitiesToSelect,
selectedEntity: companies.selectedEntities[0],

View File

@ -1,3 +1,4 @@
import { useRef } from 'react';
import { useTheme } from '@emotion/react';
import { IconPlus } from '@tabler/icons-react';
@ -7,6 +8,7 @@ import { DropdownMenuButton } from '@/ui/components/menu/DropdownMenuButton';
import { DropdownMenuItemContainer } from '@/ui/components/menu/DropdownMenuItemContainer';
import { DropdownMenuSearch } from '@/ui/components/menu/DropdownMenuSearch';
import { DropdownMenuSeparator } from '@/ui/components/menu/DropdownMenuSeparator';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { isDefined } from '@/utils/type-guards/isDefined';
import { useEntitySelectSearch } from '../hooks/useEntitySelectSearch';
@ -27,19 +29,27 @@ export function SingleEntitySelect<
entities,
onEntitySelected,
onCreate,
onCancel,
}: {
onCancel?: () => void;
onCreate?: () => void;
entities: EntitiesForSingleEntitySelect<CustomEntityForSelect>;
onEntitySelected: (entity: CustomEntityForSelect) => void;
}) {
const containerRef = useRef<HTMLDivElement>(null);
const theme = useTheme();
const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch();
const showCreateButton = isDefined(onCreate) && searchFilter !== '';
useListenClickOutsideArrayOfRef([containerRef], () => {
onCancel?.();
});
return (
<DropdownMenu>
<DropdownMenu ref={containerRef}>
<DropdownMenuSearch
value={searchFilter}
onChange={handleSearchFilterChange}
@ -60,6 +70,7 @@ export function SingleEntitySelect<
<SingleEntitySelectBase
entities={entities}
onEntitySelected={onEntitySelected}
onCancel={onCancel}
/>
</DropdownMenu>
);

View File

@ -1,4 +1,5 @@
import { useRef } from 'react';
import { Key } from 'ts-key-enum';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
@ -27,30 +28,41 @@ export function SingleEntitySelectBase<
>({
entities,
onEntitySelected,
onCancel,
}: {
entities: EntitiesForSingleEntitySelect<CustomEntityForSelect>;
onEntitySelected: (entity: CustomEntityForSelect) => void;
onCancel?: () => void;
}) {
const containerRef = useRef<HTMLDivElement>(null);
const entitiesInDropdown = isDefined(entities.selectedEntity)
? [entities.selectedEntity, ...(entities.entitiesToSelect ?? [])]
: entities.entitiesToSelect ?? [];
const { hoveredIndex } = useEntitySelectScroll({
const { hoveredIndex, resetScroll } = useEntitySelectScroll({
entities: entitiesInDropdown,
containerRef,
});
// TODO: move to better place for scopping
useScopedHotkeys(
'enter',
Key.Enter,
() => {
onEntitySelected(entitiesInDropdown[hoveredIndex]);
resetScroll();
},
InternalHotkeysScope.RelationPicker,
[entitiesInDropdown, hoveredIndex, onEntitySelected],
);
useScopedHotkeys(
Key.Escape,
() => {
onCancel?.();
},
InternalHotkeysScope.RelationPicker,
[onCancel],
);
return (
<DropdownMenuItemContainer ref={containerRef}>
{entities.loading ? (

View File

@ -21,6 +21,22 @@ export function useEntitySelectScroll<
relationPickerHoverIndexScopedState,
);
function resetScroll() {
setHoveredIndex(0);
const currentHoveredRef = containerRef.current?.children[0] as HTMLElement;
scrollIntoView(currentHoveredRef, {
align: {
top: 0,
},
isScrollable: (target) => {
return target === containerRef.current;
},
time: 0,
});
}
useScopedHotkeys(
Key.ArrowUp,
() => {
@ -60,7 +76,6 @@ export function useEntitySelectScroll<
] as HTMLElement;
if (currentHoveredRef) {
console.log({ currentHoveredRef, containerRef });
scrollIntoView(currentHoveredRef, {
align: {
top: 0.15,
@ -78,5 +93,6 @@ export function useEntitySelectScroll<
return {
hoveredIndex,
resetScroll,
};
}

View File

@ -1,4 +1,3 @@
import { useBoardCardField } from '@/ui/board-card-field/hooks/useBoardCardField';
import { InplaceInputDateEditMode } from '@/ui/inplace-inputs/components/InplaceInputDateEditMode';
type OwnProps = {
@ -10,11 +9,8 @@ export function BoardCardEditableFieldDateEditMode({
value,
onChange,
}: OwnProps) {
const { closeBoardCardField } = useBoardCardField();
function handleDateChange(newDate: Date) {
onChange(newDate);
closeBoardCardField();
}
return <InplaceInputDateEditMode value={value} onChange={handleDateChange} />;

View File

@ -19,9 +19,11 @@ export function BoardCardEditableFieldText({
editModeHorizontalAlign,
}: OwnProps) {
const [internalValue, setInternalValue] = useState(value);
const debouncedOnChange = useMemo(() => {
return debounce(onChange, 200);
}, [onChange]);
return (
<BoardCardEditableField
editModeHorizontalAlign={editModeHorizontalAlign}

View File

@ -1,6 +1,6 @@
import { ReactElement } from 'react';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { BoardCardFieldContext } from '../states/BoardCardFieldContext';
@ -12,7 +12,7 @@ type OwnProps = {
nonEditModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
editHotkeysScope?: HotkeysScopeStackItem;
editHotkeysScope?: HotkeysScope;
};
export function BoardCardEditableField(props: OwnProps) {

View File

@ -6,9 +6,9 @@ import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysSc
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { overlayBackground } from '@/ui/themes/effects';
import { useBoardCardField } from '../hooks/useBoardCardField';
export const BoardCardFieldEditModeContainer = styled.div<OwnProps>`
export const BoardCardFieldEditModeContainer = styled.div<
Omit<OwnProps, 'onExit'>
>`
align-items: center;
border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.sm};
@ -31,38 +31,37 @@ type OwnProps = {
children: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
onOutsideClick?: () => void;
onExit: () => void;
};
export function BoardCardEditableFieldEditMode({
editModeHorizontalAlign,
editModeVerticalPosition,
children,
onExit,
}: OwnProps) {
const wrapperRef = useRef(null);
const { closeBoardCardField } = useBoardCardField();
useListenClickOutsideArrayOfRef([wrapperRef], () => {
closeBoardCardField();
onExit();
});
useScopedHotkeys(
'enter',
() => {
closeBoardCardField();
onExit();
},
InternalHotkeysScope.BoardCardFieldEditMode,
[closeBoardCardField],
[onExit],
);
useScopedHotkeys(
'esc',
() => {
closeBoardCardField();
onExit();
},
InternalHotkeysScope.BoardCardFieldEditMode,
[closeBoardCardField],
[onExit],
);
return (

View File

@ -1,8 +1,8 @@
import { ReactElement } from 'react';
import styled from '@emotion/styled';
import { useAddToHotkeysScopeStack } from '@/hotkeys/hooks/useAddToHotkeysScopeStack';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { usePreviousHotkeysScope } from '@/hotkeys/hooks/internal/usePreviousHotkeysScope';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useBoardCardField } from '../hooks/useBoardCardField';
@ -26,7 +26,7 @@ type OwnProps = {
nonEditModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
editHotkeysScope?: HotkeysScopeStackItem;
editHotkeysScope?: HotkeysScope;
};
export function BoardCardEditableFieldInternal({
@ -39,25 +39,35 @@ export function BoardCardEditableFieldInternal({
const { openBoardCardField, isBoardCardFieldInEditMode } =
useBoardCardField();
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const { closeBoardCardField } = useBoardCardField();
const {
goBackToPreviousHotkeysScope,
setHotkeysScopeAndMemorizePreviousScope,
} = usePreviousHotkeysScope();
function handleOnClick() {
if (!isBoardCardFieldInEditMode) {
openBoardCardField();
addToHotkeysScopeStack(
editHotkeysScope ?? {
scope: InternalHotkeysScope.BoardCardFieldEditMode,
},
setHotkeysScopeAndMemorizePreviousScope(
editHotkeysScope?.scope ?? InternalHotkeysScope.BoardCardFieldEditMode,
editHotkeysScope?.customScopes ?? {},
);
}
}
function handleEditModeExit() {
goBackToPreviousHotkeysScope();
closeBoardCardField();
}
return (
<BoardCardFieldContainer onClick={handleOnClick}>
{isBoardCardFieldInEditMode ? (
<BoardCardEditableFieldEditMode
editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition}
onExit={handleEditModeExit}
>
{editModeContent}
</BoardCardEditableFieldEditMode>

View File

@ -1,11 +1,11 @@
import { ReactElement } from 'react';
import styled from '@emotion/styled';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useEditableCell } from './hooks/useCloseEditableCell';
import { useCurrentCellEditMode } from './hooks/useCurrentCellEditMode';
import { useEditableCell } from './hooks/useEditableCell';
import { useIsSoftFocusOnCurrentCell } from './hooks/useIsSoftFocusOnCurrentCell';
import { useSetSoftFocusOnCurrentCell } from './hooks/useSetSoftFocusOnCurrentCell';
import { EditableCellDisplayMode } from './EditableCellDisplayMode';
@ -28,7 +28,7 @@ type OwnProps = {
nonEditModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
editHotkeysScope?: HotkeysScopeStackItem;
editHotkeysScope?: HotkeysScope;
};
export function EditableCell({
@ -60,9 +60,9 @@ export function EditableCell({
scope: InternalHotkeysScope.CellEditMode,
},
);
} else {
setSoftFocusOnCurrentCell();
}
setSoftFocusOnCurrentCell();
}
return (

View File

@ -7,7 +7,7 @@ import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsid
import { useMoveSoftFocus } from '@/ui/tables/hooks/useMoveSoftFocus';
import { overlayBackground } from '@/ui/themes/effects';
import { useEditableCell } from './hooks/useCloseEditableCell';
import { useEditableCell } from './hooks/useEditableCell';
export const EditableCellEditModeContainer = styled.div<OwnProps>`
align-items: center;

View File

@ -1,17 +1,17 @@
import React from 'react';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { isNonTextWritingKey } from '@/utils/hotkeys/isNonTextWritingKey';
import { useEditableCell } from './hooks/useCloseEditableCell';
import { useEditableCell } from './hooks/useEditableCell';
import { EditableCellDisplayMode } from './EditableCellDisplayMode';
export function EditableCellSoftFocusMode({
children,
editHotkeysScope,
}: React.PropsWithChildren<{ editHotkeysScope?: HotkeysScopeStackItem }>) {
}: React.PropsWithChildren<{ editHotkeysScope?: HotkeysScope }>) {
const { closeEditableCell, openEditableCell } = useEditableCell();
useScopedHotkeys(

View File

@ -1,8 +1,8 @@
import { useRecoilCallback } from 'recoil';
import { useAddToHotkeysScopeStack } from '@/hotkeys/hooks/useAddToHotkeysScopeStack';
import { useRemoveHighestHotkeysScopeStackItem } from '@/hotkeys/hooks/useRemoveHighestHotkeysScopeStackItem';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useCloseCurrentCellInEditMode } from '@/ui/tables/hooks/useClearCellInEditMode';
import { isSoftFocusActiveState } from '@/ui/tables/states/isSoftFocusActiveState';
import { isSomeInputInEditModeState } from '@/ui/tables/states/isSomeInputInEditModeState';
@ -12,21 +12,18 @@ import { useCurrentCellEditMode } from './useCurrentCellEditMode';
export function useEditableCell() {
const { setCurrentCellInEditMode } = useCurrentCellEditMode();
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const setHotkeysScope = useSetHotkeysScope();
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
const removeHighestHotkeysScopedStackItem =
useRemoveHighestHotkeysScopeStackItem();
function closeEditableCell() {
closeCurrentCellInEditMode();
removeHighestHotkeysScopedStackItem();
setHotkeysScope(InternalHotkeysScope.TableSoftFocus);
}
const openEditableCell = useRecoilCallback(
({ snapshot, set }) =>
(hotkeysScopeStackItem: HotkeysScopeStackItem) => {
(hotkeysScope: HotkeysScope) => {
const isSomeInputInEditMode = snapshot
.getLoadable(isSomeInputInEditModeState)
.valueOrThrow();
@ -37,10 +34,10 @@ export function useEditableCell() {
setCurrentCellInEditMode();
addToHotkeysScopeStack(hotkeysScopeStackItem);
setHotkeysScope(hotkeysScope.scope);
}
},
[setCurrentCellInEditMode, addToHotkeysScopeStack],
[setCurrentCellInEditMode, setHotkeysScope],
);
return {

View File

@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react';
import { useRecoilState } from 'recoil';
import { useAddToHotkeysScopeStack } from '@/hotkeys/hooks/useAddToHotkeysScopeStack';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { useSetSoftFocusPosition } from '@/ui/tables/hooks/useSetSoftFocusPosition';
@ -34,16 +34,16 @@ export function useSetSoftFocusOnCurrentCell() {
const [, setIsSoftFocusActive] = useRecoilState(isSoftFocusActiveState);
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const setHotkeysScope = useSetHotkeysScope();
return useCallback(() => {
setSoftFocusPosition(currentTablePosition);
setIsSoftFocusActive(true);
addToHotkeysScopeStack({ scope: InternalHotkeysScope.TableSoftFocus });
setHotkeysScope(InternalHotkeysScope.TableSoftFocus);
}, [
setSoftFocusPosition,
currentTablePosition,
setIsSoftFocusActive,
addToHotkeysScopeStack,
setHotkeysScope,
]);
}

View File

@ -1,6 +1,6 @@
import { InplaceInputDateEditMode } from '@/ui/inplace-inputs/components/InplaceInputDateEditMode';
import { useEditableCell } from '../hooks/useCloseEditableCell';
import { useEditableCell } from '../hooks/useEditableCell';
export type EditableDateProps = {
value: Date;

View File

@ -7,7 +7,7 @@ import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysSc
import { InplaceInputTextEditMode } from '@/ui/inplace-inputs/components/InplaceInputTextEditMode';
import { useMoveSoftFocus } from '@/ui/tables/hooks/useMoveSoftFocus';
import { useEditableCell } from '../hooks/useCloseEditableCell';
import { useEditableCell } from '../hooks/useEditableCell';
type OwnProps = {
firstValue: string;

View File

@ -1,5 +1,10 @@
import { ChangeEvent } from 'react';
import { ChangeEvent, useRef } from 'react';
import styled from '@emotion/styled';
import { Key } from 'ts-key-enum';
import { usePreviousHotkeysScope } from '@/hotkeys/hooks/internal/usePreviousHotkeysScope';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
type OwnProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
@ -52,10 +57,37 @@ export function TextInput({
fullWidth,
...props
}: OwnProps): JSX.Element {
const inputRef = useRef<HTMLInputElement>(null);
const {
goBackToPreviousHotkeysScope,
setHotkeysScopeAndMemorizePreviousScope,
} = usePreviousHotkeysScope();
function handleFocus() {
setHotkeysScopeAndMemorizePreviousScope(InternalHotkeysScope.TextInput);
}
function handleBlur() {
goBackToPreviousHotkeysScope();
}
useScopedHotkeys(
[Key.Enter, Key.Escape],
() => {
inputRef.current?.blur();
},
InternalHotkeysScope.TextInput,
);
return (
<StyledContainer>
{label && <StyledLabel>{label}</StyledLabel>}
<StyledInput
ref={inputRef}
tabIndex={props.tabIndex ?? 0}
onFocus={handleFocus}
onBlur={handleBlur}
fullWidth={fullWidth ?? false}
value={value}
onChange={(event: ChangeEvent<HTMLInputElement>) => {

View File

@ -1,11 +1,13 @@
import { useCallback, useState } from 'react';
import { Key } from 'ts-key-enum';
import { activeTableFiltersScopedState } from '@/filters-and-sorts/states/activeTableFiltersScopedState';
import { filterDropdownSearchInputScopedState } from '@/filters-and-sorts/states/filterDropdownSearchInputScopedState';
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '@/filters-and-sorts/states/isFilterDropdownOperandSelectUnfoldedScopedState';
import { selectedOperandInDropdownScopedState } from '@/filters-and-sorts/states/selectedOperandInDropdownScopedState';
import { tableFilterDefinitionUsedInDropdownScopedState } from '@/filters-and-sorts/states/tableFilterDefinitionUsedInDropdownScopedState';
import { useHotkeysScopeOnBooleanState } from '@/hotkeys/hooks/useHotkeysScopeOnBooleanState';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { TableContext } from '@/ui/tables/states/TableContext';
@ -23,11 +25,6 @@ import { FilterDropdownTextSearchInput } from './FilterDropdownTextSearchInput';
export function FilterDropdownButton() {
const [isUnfolded, setIsUnfolded] = useState(false);
useHotkeysScopeOnBooleanState(
{ scope: InternalHotkeysScope.TableHeaderDropdownButton },
isUnfolded,
);
const [
isFilterDropdownOperandSelectUnfolded,
setIsFilterDropdownOperandSelectUnfolded,
@ -71,18 +68,27 @@ export function FilterDropdownButton() {
const isFilterSelected = (activeTableFilters?.length ?? 0) > 0;
const setHotkeysScope = useSetHotkeysScope();
function handleIsUnfoldedChange(newIsUnfolded: boolean) {
if (newIsUnfolded) {
setIsUnfolded(true);
} else {
if (tableFilterDefinitionUsedInDropdown?.type === 'entity') {
setHotkeysScope(InternalHotkeysScope.Table);
}
setIsUnfolded(false);
resetState();
}
}
useHotkeysScopeOnBooleanState(
{ scope: InternalHotkeysScope.RelationPicker },
tableFilterDefinitionUsedInDropdown?.type === 'entity',
useScopedHotkeys(
[Key.Escape],
() => {
handleIsUnfoldedChange(false);
},
InternalHotkeysScope.RelationPicker,
[handleIsUnfoldedChange],
);
return (

View File

@ -3,6 +3,8 @@ import { filterDropdownSearchInputScopedState } from '@/filters-and-sorts/states
import { selectedOperandInDropdownScopedState } from '@/filters-and-sorts/states/selectedOperandInDropdownScopedState';
import { tableFilterDefinitionUsedInDropdownScopedState } from '@/filters-and-sorts/states/tableFilterDefinitionUsedInDropdownScopedState';
import { getOperandsForFilterType } from '@/filters-and-sorts/utils/getOperandsForFilterType';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
import { TableContext } from '@/ui/tables/states/TableContext';
@ -33,6 +35,8 @@ export function FilterDropdownFilterSelect() {
TableContext,
);
const setHotkeysScope = useSetHotkeysScope();
return (
<DropdownMenuItemContainer style={{ maxHeight: '300px' }}>
{availableTableFilters.map((availableTableFilter, index) => (
@ -40,6 +44,11 @@ export function FilterDropdownFilterSelect() {
key={`select-filter-${index}`}
onClick={() => {
setTableFilterDefinitionUsedInDropdown(availableTableFilter);
if (availableTableFilter.type === 'entity') {
setHotkeysScope(InternalHotkeysScope.RelationPicker);
}
setSelectedOperandInDropdown(
getOperandsForFilterType(availableTableFilter.type)?.[0],
);

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil';
import { useCurrentHotkeysScope } from '@/hotkeys/hooks/useCurrentHotkeysScope';
import { useResetHotkeysScopeStack } from '@/hotkeys/hooks/useResetHotkeysScopeStack';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { currentHotkeysScopeState } from '@/hotkeys/states/internal/currentHotkeysScopeState';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { isSoftFocusActiveState } from '../states/isSoftFocusActiveState';
@ -11,12 +11,13 @@ import { useCloseCurrentCellInEditMode } from './useClearCellInEditMode';
import { useDisableSoftFocus } from './useDisableSoftFocus';
export function useLeaveTableFocus() {
const resetHotkeysScopeStack = useResetHotkeysScopeStack();
const currentHotkeysScope = useCurrentHotkeysScope();
const currentHotkeysScope = useRecoilValue(currentHotkeysScopeState);
const disableSoftFocus = useDisableSoftFocus();
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
const setHotkeysScope = useSetHotkeysScope();
const isSoftFocusActive = useRecoilValue(isSoftFocusActiveState);
const isSomeInputInEditMode = useRecoilValue(isSomeInputInEditModeState);
@ -32,6 +33,7 @@ export function useLeaveTableFocus() {
closeCurrentCellInEditMode();
disableSoftFocus();
resetHotkeysScopeStack(InternalHotkeysScope.Table);
setHotkeysScope(InternalHotkeysScope.Table, { goto: true });
};
}

View File

@ -1,8 +1,8 @@
import { useRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
import { useRemoveFromHotkeysScopeStack } from '@/hotkeys/hooks/useRemoveFromHotkeysScopeStack';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { isSomeInputInEditModeState } from '../states/isSomeInputInEditModeState';
@ -13,8 +13,8 @@ import { useMoveSoftFocus } from './useMoveSoftFocus';
export function useMapKeyboardToSoftFocus() {
const { moveDown, moveLeft, moveRight, moveUp } = useMoveSoftFocus();
const removeFromHotkeysScopedStack = useRemoveFromHotkeysScopeStack();
const disableSoftFocus = useDisableSoftFocus();
const setHotkeysScope = useSetHotkeysScope();
const [isSomeInputInEditMode] = useRecoilState(isSomeInputInEditModeState);
@ -65,10 +65,10 @@ export function useMapKeyboardToSoftFocus() {
useScopedHotkeys(
[Key.Escape],
() => {
removeFromHotkeysScopedStack(InternalHotkeysScope.TableSoftFocus);
setHotkeysScope(InternalHotkeysScope.Table, { goto: true });
disableSoftFocus();
},
InternalHotkeysScope.TableSoftFocus,
[removeFromHotkeysScopedStack, disableSoftFocus],
[disableSoftFocus],
);
}

View File

@ -11,7 +11,6 @@ import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { currentUserState } from '@/auth/states/currentUserState';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { NameFields } from '@/settings/profile/components/NameFields';
@ -39,10 +38,6 @@ const StyledButtonContainer = styled.div`
`;
export function CreateProfile() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.CreateProfile,
customScopes: { 'command-menu': false, goto: false },
});
const navigate = useNavigate();
const [, setMockMode] = useRecoilState(isMockModeState);
const onboardingStatus = useOnboardingStatus();

View File

@ -9,7 +9,6 @@ import { Title } from '@/auth/components/ui/Title';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
@ -37,10 +36,6 @@ const StyledButtonContainer = styled.div`
`;
export function CreateWorkspace() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.CreateWokspace,
customScopes: { 'command-menu': false, goto: false },
});
const [, setMockMode] = useRecoilState(isMockModeState);
const navigate = useNavigate();
const onboardingStatus = useOnboardingStatus();

View File

@ -13,7 +13,6 @@ import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { authProvidersState } from '@/client-config/states/authProvidersState';
import { isDemoModeState } from '@/client-config/states/isDemoModeState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { MainButton } from '@/ui/components/buttons/MainButton';
@ -33,10 +32,6 @@ const StyledFooterNote = styled(FooterNote)`
`;
export function Index() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.AuthIndex,
customScopes: { 'command-menu': false, goto: false },
});
const navigate = useNavigate();
const theme = useTheme();
const [, setMockMode] = useRecoilState(isMockModeState);

View File

@ -11,7 +11,6 @@ import { useAuth } from '@/auth/hooks/useAuth';
import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { isDemoModeState } from '@/client-config/states/isDemoModeState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { MainButton } from '@/ui/components/buttons/MainButton';
@ -51,10 +50,6 @@ const StyledErrorContainer = styled.div`
`;
export function PasswordLogin() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.PasswordLogin,
customScopes: { 'command-menu': false, goto: false },
});
const navigate = useNavigate();
const [isDemoMode] = useRecoilState(isDemoModeState);

View File

@ -4,8 +4,6 @@ import styled from '@emotion/styled';
import { v4 as uuidv4 } from 'uuid';
import { GET_COMPANIES } from '@/companies/services';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar';
import { IconBuildingSkyscraper } from '@/ui/icons/index';
@ -26,11 +24,6 @@ const StyledTableContainer = styled.div`
`;
export function Companies() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.Table,
customScopes: { 'command-menu': true, goto: true },
});
const [insertCompany] = useInsertCompanyMutation();
async function handleAddButtonClick() {

View File

@ -3,8 +3,6 @@ import { useTheme } from '@emotion/react';
import { Timeline } from '@/comments/components/timeline/Timeline';
import { useCompanyQuery } from '@/companies/services';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { RawLink } from '@/ui/components/links/RawLink';
import { PropertyBox } from '@/ui/components/property-box/PropertyBox';
import { PropertyBoxItem } from '@/ui/components/property-box/PropertyBoxItem';
@ -19,11 +17,6 @@ import { CommentableType } from '~/generated/graphql';
export function CompanyShow() {
const companyId = useParams().companyId ?? '';
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.ShowPage,
customScopes: { 'command-menu': true, goto: true },
});
const { data } = useCompanyQuery(companyId);
const company = data?.findUniqueCompany;

View File

@ -3,8 +3,6 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { v4 as uuidv4 } from 'uuid';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { GET_PEOPLE } from '@/people/services';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar';
@ -24,11 +22,6 @@ const StyledPeopleContainer = styled.div`
`;
export function People() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.Table,
customScopes: { 'command-menu': true, goto: true },
});
const [insertPersonMutation] = useInsertPersonMutation();
async function handleAddButtonClick() {

View File

@ -1,7 +1,5 @@
import styled from '@emotion/styled';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { EmailField } from '@/settings/profile/components/EmailField';
import { NameFields } from '@/settings/profile/components/NameFields';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
@ -27,11 +25,6 @@ const StyledSectionContainer = styled.div`
`;
export function SettingsProfile() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.Settings,
customScopes: { 'command-menu': true, goto: false },
});
return (
<NoTopBarContainer>
<div>

View File

@ -0,0 +1,7 @@
import { useHotkeysScopeAutoSync } from '@/hotkeys/hooks/internal/useHotkeysScopeAutoSync';
export function HotkeysScopeAutoSyncHook() {
useHotkeysScopeAutoSync();
return <></>;
}

View File

@ -0,0 +1,71 @@
import { useEffect } from 'react';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { PageHotkeysScope } from '@/hotkeys/types/internal/PageHotkeysScope';
import { useIsMatchingLocation } from './hooks/useIsMatchingLocation';
import { AppBasePath } from './types/AppBasePath';
import { AppPath } from './types/AppPath';
import { AuthPath } from './types/AuthPath';
import { SettingsPath } from './types/SettingsPath';
export function HotkeysScopeBrowserRouterSync() {
const isMatchingLocation = useIsMatchingLocation();
const setHotkeysScope = useSetHotkeysScope();
useEffect(() => {
switch (true) {
case isMatchingLocation(AppBasePath.Root, AppPath.CompaniesPage): {
setHotkeysScope(InternalHotkeysScope.Table, { goto: true });
break;
}
case isMatchingLocation(AppBasePath.Root, AppPath.PeoplePage): {
setHotkeysScope(InternalHotkeysScope.Table, { goto: true });
break;
}
case isMatchingLocation(AppBasePath.Root, AppPath.CompanyShowPage): {
setHotkeysScope(PageHotkeysScope.CompanyShowPage, { goto: true });
break;
}
case isMatchingLocation(AppBasePath.Root, AppPath.PersonShowPage): {
setHotkeysScope(PageHotkeysScope.PersonShowPage, { goto: true });
break;
}
case isMatchingLocation(AppBasePath.Root, AppPath.OpportunitiesPage): {
setHotkeysScope(PageHotkeysScope.OpportunitiesPage, { goto: true });
break;
}
case isMatchingLocation(AppBasePath.Auth, AuthPath.Index): {
setHotkeysScope(InternalHotkeysScope.AuthIndex);
break;
}
case isMatchingLocation(AppBasePath.Auth, AuthPath.CreateProfile): {
setHotkeysScope(InternalHotkeysScope.CreateProfile);
break;
}
case isMatchingLocation(AppBasePath.Auth, AuthPath.CreateWorkspace): {
setHotkeysScope(InternalHotkeysScope.CreateWokspace);
break;
}
case isMatchingLocation(AppBasePath.Auth, AuthPath.PasswordLogin): {
setHotkeysScope(InternalHotkeysScope.PasswordLogin);
break;
}
case isMatchingLocation(AppBasePath.Settings, SettingsPath.ProfilePage): {
setHotkeysScope(PageHotkeysScope.ProfilePage, { goto: true });
break;
}
case isMatchingLocation(
AppBasePath.Settings,
SettingsPath.WorkspaceMembersPage,
): {
setHotkeysScope(PageHotkeysScope.WorkspaceMemberPage, { goto: true });
break;
}
}
}, [isMatchingLocation, setHotkeysScope]);
return <></>;
}

View File

@ -1,7 +0,0 @@
import { useHotkeysScopeStackAutoSync } from '@/hotkeys/hooks/internal/useHotkeysScopeStackAutoSync';
export function HotkeysScopeStackAutoSyncHook() {
useHotkeysScopeStackAutoSync();
return <></>;
}

View File

@ -0,0 +1,16 @@
import { matchPath, useLocation } from 'react-router-dom';
import { parse } from 'url';
import { AppBasePath } from '../types/AppBasePath';
export function useIsMatchingLocation() {
const location = useLocation();
return function isMatchingLocation(basePath: AppBasePath, path: string) {
const constructedPath = basePath
? parse(`${basePath}/${path}`).pathname ?? ''
: path;
return !!matchPath(constructedPath, location.pathname);
};
}

View File

@ -0,0 +1,5 @@
export enum AppBasePath {
Auth = '/auth',
Settings = '/settings',
Root = '/',
}

View File

@ -0,0 +1,9 @@
export enum AppPath {
AuthCatchAll = `/auth/*`,
PeoplePage = '/people',
CompaniesPage = '/companies',
CompanyShowPage = '/companies/:companyId',
PersonShowPage = '/person/:personId',
OpportunitiesPage = '/opportunities',
SettingsCatchAll = `/settings/*`,
}

View File

@ -0,0 +1,7 @@
export enum AuthPath {
Index = '',
Callback = 'callback',
PasswordLogin = 'password-login',
CreateWorkspace = 'create/workspace',
CreateProfile = 'create/profile',
}

View File

@ -0,0 +1,5 @@
export enum SettingsPath {
ProfilePage = 'profile',
WorkspaceMembersPage = 'workspace-members',
Workspace = 'workspace',
}

View File

@ -15589,7 +15589,7 @@ pumpify@^1.3.3:
inherits "^2.0.3"
pump "^2.0.0"
punycode@^1.3.2:
punycode@^1.3.2, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
@ -15639,7 +15639,7 @@ qs@6.11.0:
dependencies:
side-channel "^1.0.4"
qs@^6.10.0, qs@^6.11.1, qs@^6.4.0:
qs@^6.10.0, qs@^6.11.0, qs@^6.11.1, qs@^6.4.0:
version "6.11.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9"
integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==
@ -18285,6 +18285,14 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
url@^0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.1.tgz#26f90f615427eca1b9f4d6a28288c147e2302a32"
integrity sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==
dependencies:
punycode "^1.4.1"
qs "^6.11.0"
urlpattern-polyfill@^8.0.0:
version "8.0.2"
resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5"