Uniformize folder structure (#693)
* Uniformize folder structure * Fix icons * Fix icons * Fix tests * Fix tests
This commit is contained in:
@ -0,0 +1,24 @@
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
||||
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { BoardCardFieldContext } from '../states/BoardCardFieldContext';
|
||||
|
||||
import { BoardCardEditableFieldInternal } from './BoardCardEditableFieldInternal';
|
||||
|
||||
type OwnProps = {
|
||||
editModeContent: ReactElement;
|
||||
nonEditModeContent: ReactElement;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
editModeVerticalPosition?: 'over' | 'below';
|
||||
editHotkeyScope?: HotkeyScope;
|
||||
};
|
||||
|
||||
export function BoardCardEditableField(props: OwnProps) {
|
||||
return (
|
||||
<RecoilScope SpecificContext={BoardCardFieldContext}>
|
||||
<BoardCardEditableFieldInternal {...props} />
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { InplaceInputDateDisplayMode } from '@/ui/display/component/InplaceInputDateDisplayMode';
|
||||
import { debounce } from '~/utils/debounce';
|
||||
|
||||
import { BoardCardEditableField } from './BoardCardEditableField';
|
||||
import { BoardCardEditableFieldDateEditMode } from './BoardCardEditableFieldDateEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
value: Date;
|
||||
onChange: (newValue: Date) => void;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export function BoardCardEditableFieldDate({
|
||||
value,
|
||||
onChange,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) {
|
||||
const [internalValue, setInternalValue] = useState(value);
|
||||
const debouncedOnChange = useMemo(() => {
|
||||
return debounce(onChange, 200);
|
||||
}, [onChange]);
|
||||
return (
|
||||
<BoardCardEditableField
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<BoardCardEditableFieldDateEditMode
|
||||
value={internalValue}
|
||||
onChange={(date: Date) => {
|
||||
setInternalValue(date);
|
||||
debouncedOnChange(date);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<InplaceInputDateDisplayMode value={value} />}
|
||||
></BoardCardEditableField>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { InplaceInputDate } from '@/ui/inplace-input/components/InplaceInputDate';
|
||||
|
||||
type OwnProps = {
|
||||
value: Date;
|
||||
onChange: (newValue: Date) => void;
|
||||
};
|
||||
|
||||
export function BoardCardEditableFieldDateEditMode({
|
||||
value,
|
||||
onChange,
|
||||
}: OwnProps) {
|
||||
function handleDateChange(newDate: Date) {
|
||||
onChange(newDate);
|
||||
}
|
||||
|
||||
return <InplaceInputDate value={value} onChange={handleDateChange} />;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const BoardCardFieldDisplayModeOuterContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const BoardCardFieldDisplayModeInnerContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function BoardCardEditableFieldDisplayMode({
|
||||
children,
|
||||
}: React.PropsWithChildren<unknown>) {
|
||||
return (
|
||||
<BoardCardFieldDisplayModeOuterContainer>
|
||||
<BoardCardFieldDisplayModeInnerContainer>
|
||||
{children}
|
||||
</BoardCardFieldDisplayModeInnerContainer>
|
||||
</BoardCardFieldDisplayModeOuterContainer>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
import { ReactElement, useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
|
||||
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
||||
import { overlayBackground } from '@/ui/themes/effects';
|
||||
|
||||
import { BoardCardFieldHotkeyScope } from '../types/BoardCardFieldHotkeyScope';
|
||||
|
||||
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};
|
||||
display: flex;
|
||||
left: ${(props) =>
|
||||
props.editModeHorizontalAlign === 'right' ? 'auto' : '0'};
|
||||
margin-left: -2px;
|
||||
min-height: 100%;
|
||||
min-width: calc(100% + 20px);
|
||||
position: absolute;
|
||||
|
||||
right: ${(props) =>
|
||||
props.editModeHorizontalAlign === 'right' ? '0' : 'auto'};
|
||||
top: ${(props) => (props.editModeVerticalPosition === 'over' ? '0' : '100%')};
|
||||
z-index: 1;
|
||||
${overlayBackground}
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
children: ReactElement;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
editModeVerticalPosition?: 'over' | 'below';
|
||||
onExit: () => void;
|
||||
};
|
||||
|
||||
export function BoardCardEditableFieldEditMode({
|
||||
editModeHorizontalAlign,
|
||||
editModeVerticalPosition,
|
||||
children,
|
||||
onExit,
|
||||
}: OwnProps) {
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
useListenClickOutsideArrayOfRef([wrapperRef], () => {
|
||||
onExit();
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'enter',
|
||||
() => {
|
||||
onExit();
|
||||
},
|
||||
BoardCardFieldHotkeyScope.BoardCardFieldEditMode,
|
||||
[onExit],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'esc',
|
||||
() => {
|
||||
onExit();
|
||||
},
|
||||
BoardCardFieldHotkeyScope.BoardCardFieldEditMode,
|
||||
[onExit],
|
||||
);
|
||||
|
||||
return (
|
||||
<BoardCardFieldEditModeContainer
|
||||
data-testid="editable-cell-edit-mode-container"
|
||||
ref={wrapperRef}
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeVerticalPosition={editModeVerticalPosition}
|
||||
>
|
||||
{children}
|
||||
</BoardCardFieldEditModeContainer>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
import { ReactElement } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { usePreviousHotkeyScope } from '@/ui/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
||||
|
||||
import { useBoardCardField } from '../hooks/useBoardCardField';
|
||||
import { BoardCardFieldHotkeyScope } from '../types/BoardCardFieldHotkeyScope';
|
||||
|
||||
import { BoardCardEditableFieldDisplayMode } from './BoardCardEditableFieldDisplayMode';
|
||||
import { BoardCardEditableFieldEditMode } from './BoardCardEditableFieldEditMode';
|
||||
|
||||
export const BoardCardFieldContainer = styled.div`
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
editModeContent: ReactElement;
|
||||
nonEditModeContent: ReactElement;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
editModeVerticalPosition?: 'over' | 'below';
|
||||
editHotkeyScope?: HotkeyScope;
|
||||
};
|
||||
|
||||
export function BoardCardEditableFieldInternal({
|
||||
editModeHorizontalAlign = 'left',
|
||||
editModeVerticalPosition = 'over',
|
||||
editModeContent,
|
||||
nonEditModeContent,
|
||||
editHotkeyScope,
|
||||
}: OwnProps) {
|
||||
const { openBoardCardField, isBoardCardFieldInEditMode } =
|
||||
useBoardCardField();
|
||||
|
||||
const { closeBoardCardField } = useBoardCardField();
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
function handleOnClick() {
|
||||
if (!isBoardCardFieldInEditMode) {
|
||||
openBoardCardField();
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
editHotkeyScope?.scope ??
|
||||
BoardCardFieldHotkeyScope.BoardCardFieldEditMode,
|
||||
editHotkeyScope?.customScopes ?? {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function handleEditModeExit() {
|
||||
goBackToPreviousHotkeyScope();
|
||||
closeBoardCardField();
|
||||
}
|
||||
|
||||
return (
|
||||
<BoardCardFieldContainer onClick={handleOnClick}>
|
||||
{isBoardCardFieldInEditMode ? (
|
||||
<BoardCardEditableFieldEditMode
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeVerticalPosition={editModeVerticalPosition}
|
||||
onExit={handleEditModeExit}
|
||||
>
|
||||
{editModeContent}
|
||||
</BoardCardEditableFieldEditMode>
|
||||
) : (
|
||||
<BoardCardEditableFieldDisplayMode>
|
||||
{nonEditModeContent}
|
||||
</BoardCardEditableFieldDisplayMode>
|
||||
)}
|
||||
</BoardCardFieldContainer>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
import { ChangeEvent, useMemo, useState } from 'react';
|
||||
|
||||
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
||||
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
||||
import { debounce } from '~/utils/debounce';
|
||||
|
||||
import { BoardCardEditableField } from './BoardCardEditableField';
|
||||
|
||||
type OwnProps = {
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
onChange: (newValue: string) => void;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export function BoardCardEditableFieldText({
|
||||
value,
|
||||
placeholder,
|
||||
onChange,
|
||||
editModeHorizontalAlign,
|
||||
}: OwnProps) {
|
||||
const [internalValue, setInternalValue] = useState(value);
|
||||
|
||||
const debouncedOnChange = useMemo(() => {
|
||||
return debounce(onChange, 200);
|
||||
}, [onChange]);
|
||||
|
||||
return (
|
||||
<BoardCardEditableField
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<InplaceInputTextEditMode
|
||||
placeholder={placeholder || ''}
|
||||
autoFocus
|
||||
value={internalValue}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setInternalValue(event.target.value);
|
||||
debouncedOnChange(event.target.value);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<InplaceInputTextDisplayMode>{value}</InplaceInputTextDisplayMode>
|
||||
}
|
||||
></BoardCardEditableField>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { BoardCardFieldContext } from '../states/BoardCardFieldContext';
|
||||
import { isBoardCardFieldInEditModeScopedState } from '../states/isBoardCardFieldInEditModeScopedState';
|
||||
|
||||
export function useBoardCardField() {
|
||||
const [isBoardCardFieldInEditMode, setIsBoardCardFieldInEditMode] =
|
||||
useRecoilScopedState(
|
||||
isBoardCardFieldInEditModeScopedState,
|
||||
BoardCardFieldContext,
|
||||
);
|
||||
|
||||
function openBoardCardField() {
|
||||
setIsBoardCardFieldInEditMode(true);
|
||||
}
|
||||
|
||||
function closeBoardCardField() {
|
||||
setIsBoardCardFieldInEditMode(false);
|
||||
}
|
||||
|
||||
return {
|
||||
isBoardCardFieldInEditMode,
|
||||
openBoardCardField,
|
||||
closeBoardCardField,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const BoardCardFieldContext = createContext<string | null>(null);
|
||||
@ -0,0 +1,9 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isBoardCardFieldInEditModeScopedState = atomFamily<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isBoardCardFieldInEditModeScopedState',
|
||||
default: false,
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
export enum BoardCardFieldHotkeyScope {
|
||||
BoardCardFieldEditMode = 'board-card-field-edit-mode',
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { debounce } from '@/utils/debounce';
|
||||
import { debounce } from '~/utils/debounce';
|
||||
|
||||
import { EditColumnTitleInput } from './EditColumnTitleInput';
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useScopedHotkeys } from '@/lib/hotkeys/hooks/useScopedHotkeys';
|
||||
import { useSetHotkeyScope } from '@/lib/hotkeys/hooks/useSetHotkeyScope';
|
||||
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
|
||||
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
||||
import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
import { ColumnHotkeyScope } from './ColumnHotkeyScope';
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconPlus } from '@/ui/icons/index';
|
||||
import { IconPlus } from '@/ui/icon/index';
|
||||
|
||||
const StyledButton = styled.button`
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user