Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepinho committed Aug 8, 2024
1 parent 7175e44 commit ec240ff
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 86 deletions.
28 changes: 28 additions & 0 deletions packages/ui/src/Card/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReactNode } from 'react';
import styled, { WebTarget } from 'styled-components';
import { large } from '../utils/typography';
import { hexOpacity } from '../utils/hexOpacity';

const Root = styled.section``;

Expand All @@ -12,6 +13,11 @@ const Title = styled.h1`
`;

const Content = styled.div`
background: linear-gradient(
136deg,
${props => props.theme.color.neutral.contrast + hexOpacity(0.1)} 6.32%,
${props => props.theme.color.neutral.contrast + hexOpacity(0.01)} 75.55%
);
backdrop-filter: blur(${props => props.theme.blur.lg});
border-radius: ${props => props.theme.borderRadius.xl};
padding: ${props => props.theme.spacing(3)};
Expand Down Expand Up @@ -40,3 +46,25 @@ export const Card = ({ children, as = 'section', title }: CardProps) => {
</Root>
);
};

const StyledStack = styled.div`
display: flex;
flex-direction: column;
gap: ${props => props.theme.spacing(1)};
border-radius: ${props => props.theme.borderRadius.sm};
overflow: hidden; /** To enforce the border-radius */
`;
const Stack = ({ children }: { children?: ReactNode }) => {
return <StyledStack>{children}</StyledStack>;
};
Card.Stack = Stack;

const StyledSection = styled.div`
background-color: ${props => props.theme.color.other.tonalFill5};
padding: ${props => props.theme.spacing(3)};
`;
const Section = ({ children }: { children?: ReactNode }) => (
<StyledSection>{children}</StyledSection>
);
Card.Section = Section;
60 changes: 60 additions & 0 deletions packages/ui/src/FormField/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Meta, StoryObj } from '@storybook/react';
import { FormField } from '.';
import { TextInput } from '../TextInput';
import { useState } from 'react';
import { SegmentedControl } from '../SegmentedControl';

const meta: Meta<typeof FormField> = {
component: FormField,
tags: ['autodocs', '!dev'],
argTypes: {
children: { control: false },
},
};
export default meta;

type Story = StoryObj<typeof FormField>;

export const TextInputExample: Story = {
args: {
label: "Recipient's address",
asLabel: true,
helperText: 'The recipient can find their address via the Receive tab.',
disabled: false,
},

render: function Render(props) {
const [recipient, setRecipient] = useState('');

return (
<FormField {...props}>
<TextInput value={recipient} onChange={setRecipient} placeholder='penumbra1abc123...' />
</FormField>
);
},
};

export const SegmentedControlExample: Story = {
args: {
label: 'Fee Tier',
disabled: false,
},

render: function Render(props) {
const [feeTier, setFeeTier] = useState('low');

return (
<FormField {...props}>
<SegmentedControl
value={feeTier}
options={[
{ label: 'Low', value: 'low' },
{ label: 'Medium', value: 'medium' },
{ label: 'High', value: 'high' },
]}
onChange={setFeeTier}
/>
</FormField>
);
},
};
53 changes: 53 additions & 0 deletions packages/ui/src/FormField/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import styled from 'styled-components';
import { small, strong } from '../utils/typography';
import { ReactNode } from 'react';
import { DisabledContext } from '../utils/DisabledContext';

const Root = styled.label`
display: flex;
flex-direction: column;
gap: ${props => props.theme.spacing(2)};
position: relative;
`;

const HelperText = styled.div<{ $disabled: boolean }>`
color: ${props =>
props.$disabled ? props.theme.color.text.muted : props.theme.color.text.secondary};
${small}
`;

const LabelText = styled.div<{ $disabled: boolean }>`
${strong}
color: ${props =>
props.$disabled ? props.theme.color.text.muted : props.theme.color.text.primary};
`;

export interface FormFieldProps {
label: string;
asLabel?: boolean;
disabled?: boolean;
helperText?: string;
/**
* The form control to render for this field, whether a `<TextInput />`,
* `<SegmentedControl />`, etc.
*/
children: ReactNode;
}

export const FormField = ({
label,
asLabel = false,
disabled = false,
helperText,
children,
}: FormFieldProps) => (
<Root as={asLabel ? 'label' : 'div'}>
<LabelText $disabled={disabled}>{label}</LabelText>

<DisabledContext.Provider value={disabled}>{children}</DisabledContext.Provider>

{helperText && <HelperText $disabled={disabled}>{helperText}</HelperText>}
</Root>
);
31 changes: 20 additions & 11 deletions packages/ui/src/PenumbraUIProvider/theme.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { hexOpacity } from '../utils/hexOpacity';

/**
* Used for reference in the `theme` object below. Not intended to be used
* directly by consumers, but rather as a semantic reference for building the
Expand Down Expand Up @@ -95,11 +97,13 @@ const PALETTE = {
900: '#6B3F18',
950: '#201004',
},
base: {
black: '#000000',
white: '#ffffff',
transparent: 'transparent',
},
};

const FIFTEEN_PERCENT_OPACITY_IN_HEX = '26';
const EIGHTY_PERCENT_OPACITY_IN_HEX = 'cc';

export const theme = {
blur: {
none: '0px',
Expand Down Expand Up @@ -170,29 +174,33 @@ export const theme = {
contrast: PALETTE.green['50'],
},
base: {
black: '#000',
white: '#fff',
transparent: 'transparent',
black: PALETTE.base.black,
white: PALETTE.base.white,
transparent: PALETTE.base.transparent,
},
text: {
primary: PALETTE.neutral['50'],
secondary: PALETTE.neutral['400'],
disabled: PALETTE.neutral['500'],
muted: PALETTE.neutral['700'],
special: PALETTE.orange['400'],
},
action: {
hoverOverlay: PALETTE.teal['400'] + FIFTEEN_PERCENT_OPACITY_IN_HEX,
activeOverlay: PALETTE.neutral['950'] + FIFTEEN_PERCENT_OPACITY_IN_HEX,
disabledOverlay: PALETTE.neutral['950'] + EIGHTY_PERCENT_OPACITY_IN_HEX,
hoverOverlay: PALETTE.teal['400'] + hexOpacity(0.15),
activeOverlay: PALETTE.neutral['950'] + hexOpacity(0.15),
disabledOverlay: PALETTE.neutral['950'] + hexOpacity(0.8),
primaryFocusOutline: PALETTE.orange['400'],
secondaryFocusOutline: PALETTE.teal['400'],
unshieldFocusOutline: PALETTE.purple['400'],
neutralFocusOutline: PALETTE.neutral['400'],
destructiveFocusOutline: PALETTE.red['400'],
},
other: {
tonalStroke: PALETTE.neutral['50'] + FIFTEEN_PERCENT_OPACITY_IN_HEX,
tonalStroke: PALETTE.neutral['50'] + hexOpacity(0.15),
tonalFill5: PALETTE.neutral['50'] + hexOpacity(0.05),
tonalFill10: PALETTE.neutral['50'] + hexOpacity(0.1),
solidStroke: PALETTE.neutral['700'],
dialogBackground: PALETTE.teal['700'] + hexOpacity(0.1),
overlay: PALETTE.base.black + hexOpacity(0.5),
},
},
font: {
Expand Down Expand Up @@ -232,6 +240,7 @@ export const theme = {
},
spacing: (spacingUnits: number) => `${spacingUnits * 4}px`,
zIndex: {
disabledOverlay: 10,
dialogOverlay: 1000,
dialogContent: 1001,
},
Expand Down
7 changes: 5 additions & 2 deletions packages/ui/src/SegmentedControl/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { focusOutline, overlays, buttonBase } from '../utils/button';
import { Density } from '../types/Density';
import { useDensity } from '../hooks/useDensity';
import * as RadixRadioGroup from '@radix-ui/react-radio-group';
import { useDisabled } from '../hooks/useDisabled';

const Root = styled.div`
display: flex;
Expand Down Expand Up @@ -43,6 +44,7 @@ export interface SegmentedControlProps {
value: string;
onChange: (value: string) => void;
options: Option[];
disabled?: boolean;
}

/**
Expand All @@ -66,8 +68,9 @@ export interface SegmentedControlProps {
* />
* ```
*/
export const SegmentedControl = ({ value, onChange, options }: SegmentedControlProps) => {
export const SegmentedControl = ({ value, onChange, options, disabled }: SegmentedControlProps) => {
const density = useDensity();
disabled = useDisabled(disabled);

return (
<RadixRadioGroup.Root asChild value={value} onValueChange={onChange}>
Expand All @@ -80,7 +83,7 @@ export const SegmentedControl = ({ value, onChange, options }: SegmentedControlP
$getFocusOutlineColor={theme => theme.color.neutral.light}
$selected={value === option.value}
$density={density}
disabled={option.disabled}
disabled={disabled || option.disabled}
>
{option.label}
</Segment>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useArgs } from '@storybook/preview-api';

import { Input } from '.';
import { TextInput } from '.';

const meta: Meta<typeof Input> = {
component: Input,
const meta: Meta<typeof TextInput> = {
component: TextInput,
tags: ['autodocs', '!dev'],
};
export default meta;

type Story = StoryObj<typeof Input>;
type Story = StoryObj<typeof TextInput>;

export const Basic: Story = {
args: {
actionType: 'default',
label: "Recipient's address",
placeholder: 'penumbra1abc123...',
value: '',
disabled: false,
helperText: 'The recipient can find their address via the Receive tab above.',
type: 'text',
},

Expand All @@ -27,6 +25,6 @@ export const Basic: Story = {

const onChange = (value: string) => updateArgs({ value });

return <Input {...props} onChange={onChange} />;
return <TextInput {...props} onChange={onChange} />;
},
};
Loading

0 comments on commit ec240ff

Please sign in to comment.