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

4599-feat(front): Add Copy Button to Floating Inputs #4789

Merged
merged 33 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b875b19
feat: create new component for copy button
Anchit1909 Mar 28, 2024
d364fee
feat: add copy button to input fields
Anchit1909 Mar 28, 2024
ae44a88
feat: center button
Anchit1909 Mar 28, 2024
40d49ea
feat: allow click of button
Anchit1909 Mar 28, 2024
b3f102d
style: Phone Input Field
Anchit1909 Mar 31, 2024
fc8ae39
fix: eslint issues
Anchit1909 Mar 31, 2024
1bcfb24
Merge branch 'main' of github.com:Anchit1909/twenty into anchit-4599-…
Anchit1909 Apr 3, 2024
4859763
chore: change icon import
Anchit1909 Apr 3, 2024
1228507
style: phone input field
Anchit1909 Apr 3, 2024
188266a
Merge main
FelixMalfait Apr 4, 2024
a3f796b
Move to right side
FelixMalfait Apr 4, 2024
82cd820
Remove unclean code
FelixMalfait Apr 4, 2024
c70654e
code review changes
Anchit1909 Apr 6, 2024
5184d04
eslint fix
Anchit1909 Apr 6, 2024
d70c411
phoneinput style fix
Anchit1909 Apr 6, 2024
be1ec3b
Merge branch 'main' of github.com:Anchit1909/twenty into anchit-4599-…
Anchit1909 Apr 8, 2024
04f8b4a
Merge branch 'main' into anchit-4599-copy-button
lucasbordeau Apr 10, 2024
eaa3ecf
Fixes
lucasbordeau Apr 10, 2024
bee1d04
feat: decrease size of copy icon
Anchit1909 Apr 14, 2024
23e0064
style: phone input
Anchit1909 Apr 14, 2024
ccd2e90
style: textareainput
Anchit1909 Apr 14, 2024
337dc48
fix: eslint errors
Anchit1909 Apr 14, 2024
0a2cf15
style: phone input minor style change
Anchit1909 Apr 14, 2024
5ad08e5
style: minor change
Anchit1909 Apr 14, 2024
11a62b8
Merge branch 'main' into anchit-4599-copy-button
Anchit1909 Apr 14, 2024
d994000
style: minor change
Anchit1909 Apr 14, 2024
795eae7
Fix phone input padding
FelixMalfait Apr 17, 2024
e9f2a7e
Merge branch 'main' into anchit-4599-copy-button
FelixMalfait Apr 17, 2024
75e3900
Merge branch 'main' into anchit-4599-copy-button
FelixMalfait Apr 18, 2024
3615d43
Merge branch 'main' into anchit-4599-copy-button
Weiko Apr 30, 2024
cb8333d
Merge branch 'main' into anchit-4599-copy-button
charlesBochet May 7, 2024
ca62a79
Merge branch 'main' into anchit-4599-copy-button
Weiko May 13, 2024
eb7d659
fix stories missing snackbar scope
Weiko May 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconCopy } from 'twenty-ui';

import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';

const StyledButtonContainer = styled.div`
padding: 0 ${({ theme }) => theme.spacing(1)};
`;

export type LightCopyIconButtonProps = {
copyText: string;
};

export const LightCopyIconButton = ({ copyText }: LightCopyIconButtonProps) => {
const { enqueueSnackBar } = useSnackBar();
const theme = useTheme();

return (
<StyledButtonContainer>
<LightIconButton
className="copy-button"
Icon={IconCopy}
onClick={() => {
enqueueSnackBar('Text copied to clipboard', {
variant: 'success',
icon: <IconCopy size={theme.icon.size.md} />,
duration: 2000,
});
navigator.clipboard.writeText(copyText);
}}
aria-label="Copy to Clipboard"
/>
</StyledButtonContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { expect, fn, userEvent, waitFor, within } from '@storybook/test';

import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldMetadataType } from '~/generated/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';

import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
import { useEmailField } from '../../../hooks/useEmailField';
Expand Down Expand Up @@ -104,7 +105,7 @@ const meta: Meta = {
onTab: { control: false },
onShiftTab: { control: false },
},
decorators: [clearMocksDecorator],
decorators: [clearMocksDecorator, SnackBarDecorator],
parameters: {
clearMocks: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { expect, fn, userEvent, waitFor, within } from '@storybook/test';

import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldMetadataType } from '~/generated/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';

import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
import { useNumberField } from '../../../hooks/useNumberField';
Expand Down Expand Up @@ -105,7 +106,7 @@ const meta: Meta = {
onTab: { control: false },
onShiftTab: { control: false },
},
decorators: [clearMocksDecorator],
decorators: [clearMocksDecorator, SnackBarDecorator],
parameters: {
clearMocks: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { expect, fn, userEvent, waitFor, within } from '@storybook/test';

import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldMetadataType } from '~/generated/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';

import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
import { usePhoneField } from '../../../hooks/usePhoneField';
Expand Down Expand Up @@ -105,7 +106,7 @@ const meta: Meta = {
onTab: { control: false },
onShiftTab: { control: false },
},
decorators: [clearMocksDecorator],
decorators: [clearMocksDecorator, SnackBarDecorator],
parameters: {
clearMocks: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { expect, fn, userEvent, waitFor, within } from '@storybook/test';

import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldMetadataType } from '~/generated/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';

import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
import { useTextField } from '../../../hooks/useTextField';
Expand Down Expand Up @@ -104,7 +105,7 @@ const meta: Meta = {
onTab: { control: false },
onShiftTab: { control: false },
},
decorators: [clearMocksDecorator],
decorators: [clearMocksDecorator, SnackBarDecorator],
parameters: {
clearMocks: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isDefined } from '~/utils/isDefined';

export const useRegisterInputEvents = <T>({
inputRef,
copyRef,
inputValue,
onEscape,
onEnter,
Expand All @@ -15,6 +16,7 @@ export const useRegisterInputEvents = <T>({
hotkeyScope,
}: {
inputRef: React.RefObject<any>;
copyRef?: React.RefObject<any>;
inputValue: T;
onEscape: (inputValue: T) => void;
onEnter: (inputValue: T) => void;
Expand All @@ -24,10 +26,9 @@ export const useRegisterInputEvents = <T>({
hotkeyScope: string;
}) => {
useListenClickOutside({
refs: [inputRef],
refs: [inputRef, copyRef].filter(isDefined),
callback: (event) => {
event.stopImmediatePropagation();

onClickOutside?.(event, inputValue);
},
enabled: isDefined(onClickOutside),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import styled from '@emotion/styled';
import { OVERLAY_BACKGROUND } from '@/ui/theme/constants/OverlayBackground';

const StyledFieldInputOverlay = styled.div`
align-items: center;
border: ${({ theme }) => `1px solid ${theme.border.color.light}`};
border-radius: ${({ theme }) => theme.border.radius.sm};
${OVERLAY_BACKGROUND}
border-radius: ${({ theme }) => theme.border.radius.sm};
display: flex;
height: 32px;
justify-content: space-between;
margin: -1px;
width: 100%;
`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import styled from '@emotion/styled';

const StyledFieldTextAreaOverlay = styled.div`
border-radius: ${({ theme }) => theme.border.radius.sm};
background: ${({ theme }) => theme.background.transparent.secondary};
align-items: center;
backdrop-filter: blur(8px);
background: ${({ theme }) => theme.background.transparent.secondary};
border-radius: ${({ theme }) => theme.border.radius.sm};
display: flex;
height: 32px;
margin: -1px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useEffect, useRef, useState } from 'react';
import ReactPhoneNumberInput from 'react-phone-number-input';
import styled from '@emotion/styled';
import { TEXT_INPUT_STYLE } from 'twenty-ui';

import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
import { PhoneCountryPickerDropdownButton } from '@/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownButton';

Expand All @@ -13,14 +15,17 @@ const StyledContainer = styled.div`
border: none;
border-radius: ${({ theme }) => theme.border.radius.sm};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
width: 100%;

display: flex;
justify-content: center;
justify-content: start;
`;

const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
font-family: ${({ theme }) => theme.font.family};
height: 32px;
${TEXT_INPUT_STYLE}
padding: 0;

.PhoneInputInput {
background: ${({ theme }) => theme.background.transparent.secondary};
Expand All @@ -43,6 +48,14 @@ const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
border-radius: ${({ theme }) => theme.border.radius.xs};
height: 12px;
}
width: calc(100% - ${({ theme }) => theme.spacing(8)});
`;

const StyledLightIconButtonContainer = styled.div`
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
`;

export type PhoneInputProps = {
Expand All @@ -56,6 +69,7 @@ export type PhoneInputProps = {
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
onChange?: (newText: string) => void;
hotkeyScope: string;
copyButton?: boolean;
};

export const PhoneInput = ({
Expand All @@ -68,10 +82,12 @@ export const PhoneInput = ({
onClickOutside,
hotkeyScope,
onChange,
copyButton = true,
}: PhoneInputProps) => {
const [internalValue, setInternalValue] = useState<string | undefined>(value);

const wrapperRef = useRef<HTMLDivElement>(null);
const copyRef = useRef<HTMLDivElement>(null);

const handleChange = (newValue: string) => {
setInternalValue(newValue);
Expand All @@ -84,6 +100,7 @@ export const PhoneInput = ({

useRegisterInputEvents({
inputRef: wrapperRef,
copyRef: copyRef,
inputValue: internalValue ?? '',
onEnter,
onEscape,
Expand All @@ -104,6 +121,11 @@ export const PhoneInput = ({
withCountryCallingCode={true}
countrySelectComponent={PhoneCountryPickerDropdownButton}
/>
{copyButton && (
<StyledLightIconButtonContainer ref={copyRef}>
<LightCopyIconButton copyText={value} />
</StyledLightIconButtonContainer>
)}
</StyledContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ChangeEvent, useEffect, useRef, useState } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import styled from '@emotion/styled';

import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
import { TEXT_INPUT_STYLE } from '@/ui/theme/constants/TextInputStyle';
import { isDefined } from '~/utils/isDefined';
Expand All @@ -20,19 +21,35 @@ export type TextAreaInputProps = {
hotkeyScope: string;
onChange?: (newText: string) => void;
maxRows?: number;
copyButton?: boolean;
};

const StyledTextArea = styled(TextareaAutosize)`
${TEXT_INPUT_STYLE}
width: 100%;
align-items: center;
display: flex;
justify-content: center;
resize: none;
width: calc(100% - ${({ theme }) => theme.spacing(7)});
`;

const StyledTextAreaContainer = styled.div`
box-shadow: ${({ theme }) => theme.boxShadow.strong};
border: ${({ theme }) => `1px solid ${theme.border.color.light}`};
padding: ${({ theme }) => theme.spacing(2)};
position: relative;
width: 100%;
padding: ${({ theme }) => theme.spacing(2)} ${({ theme }) => theme.spacing(1)};
background-color: ${({ theme }) => theme.background.primary};
border-radius: ${({ theme }) => theme.border.radius.sm};
`;

const StyledLightIconButtonContainer = styled.div`
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
`;

export const TextAreaInput = ({
disabled,
className,
Expand All @@ -47,6 +64,7 @@ export const TextAreaInput = ({
onClickOutside,
onChange,
maxRows,
copyButton = true,
}: TextAreaInputProps) => {
const [internalText, setInternalText] = useState(value);

Expand All @@ -56,6 +74,7 @@ export const TextAreaInput = ({
};

const wrapperRef = useRef<HTMLTextAreaElement>(null);
const copyRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (isDefined(wrapperRef.current)) {
Expand All @@ -68,6 +87,7 @@ export const TextAreaInput = ({

useRegisterInputEvents({
inputRef: wrapperRef,
copyRef: copyRef,
inputValue: internalText,
onEnter,
onEscape,
Expand All @@ -78,15 +98,22 @@ export const TextAreaInput = ({
});

return (
<StyledTextArea
placeholder={placeholder}
disabled={disabled}
className={className}
ref={wrapperRef}
onChange={handleChange}
autoFocus={autoFocus}
value={internalText}
maxRows={maxRows}
/>
<StyledTextAreaContainer>
<StyledTextArea
placeholder={placeholder}
disabled={disabled}
className={className}
ref={wrapperRef}
onChange={handleChange}
autoFocus={autoFocus}
value={internalText}
maxRows={maxRows}
/>
{copyButton && (
<StyledLightIconButtonContainer ref={copyRef}>
<LightCopyIconButton copyText={internalText} />
</StyledLightIconButtonContainer>
)}
</StyledTextAreaContainer>
);
};