Skip to content

Commit

Permalink
button component
Browse files Browse the repository at this point in the history
  • Loading branch information
ari-party committed Jul 25, 2024
1 parent cc7ecb8 commit 535dcfb
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 26 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"no-shadow": "off",
"roblox-ts/lua-truthiness": "off",
"react/jsx-fragments": "off",
"react/react-in-jsx-scope": "off"
"react/react-in-jsx-scope": "off",
"react/prop-types": "off"
}
}
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Inspired by [Joy UI](https://mui.com/joy-ui), Edgar López's [UI components libr

## Installation

```bash
npm i @grand-hawk/ui-components
```
- `npm i @grand-hawk/ui-components`
- Add `@grand-hawk` to your typeRoots
- Add the `@grand-hawk` scope to your Rojo xxx.project.json

## Documentation

Expand All @@ -25,7 +25,7 @@ See the Typedoc [here](https://grand-hawk.github.io/ui-components).
- [x] Typography
- [x] Button
- [ ] Slider
- [ ] Input
- [x] Input
- [ ] Select
- [ ] Card

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@grand-hawk/ui-components",
"version": "0.1.2",
"version": "0.1.3",
"keywords": [
"roblox-ts",
"react",
Expand Down
29 changes: 28 additions & 1 deletion src/components/Box/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface BaseBoxProps

CornerProps?: InstanceProps<UICorner>;

StrokeProps?: InstanceProps<UIStroke>;

ListLayoutProps?: InstanceProps<UIListLayout>;

Padding?: number;
Expand All @@ -36,14 +38,20 @@ export interface BaseBoxProps
HorizontalAlignment?: Enum.HorizontalAlignment;

VerticalAlignment?: Enum.VerticalAlignment;

BorderColor3?: Color3;

BorderThickness?: number;
}

export type BoxProps<T extends keyof JSX.IntrinsicElements = 'frame'> =
JSX.IntrinsicElements[T] & BaseBoxProps;

export function Box<T extends keyof JSX.IntrinsicElements>(
props: PropsWithChildren<BoxProps<T>>,
componentProps: PropsWithChildren<BoxProps<T>>,
) {
const props = { ...componentProps };

const component = props.Component ?? 'frame';
props.Component = undefined;

Expand All @@ -56,6 +64,9 @@ export function Box<T extends keyof JSX.IntrinsicElements>(
const cornerProps = props.CornerProps;
props.CornerProps = undefined;

const strokeProps = props.StrokeProps;
props.StrokeProps = undefined;

const listLayoutProps = props.ListLayoutProps;
props.ListLayoutProps = undefined;

Expand Down Expand Up @@ -92,6 +103,12 @@ export function Box<T extends keyof JSX.IntrinsicElements>(
(shouldCenter ? Enum.VerticalAlignment.Center : undefined);
props.VerticalAlignment = undefined;

const borderColor3 = props.BorderColor3;
props.BorderColor3 = undefined;

const borderThickness = props.BorderThickness;
props.BorderThickness = undefined;

return createElement<BoxProps<T>>(
component,
{
Expand All @@ -116,6 +133,16 @@ export function Box<T extends keyof JSX.IntrinsicElements>(
<uicorner CornerRadius={borderRadius} {...cornerProps} />
) : undefined}

{borderColor3 || borderThickness || strokeProps ? (
<uistroke
ApplyStrokeMode={Enum.ApplyStrokeMode.Border}
Color={borderColor3}
Enabled
Thickness={borderThickness}
{...strokeProps}
/>
) : undefined}

<uilistlayout
FillDirection={direction}
HorizontalAlignment={horizontalAlignment}
Expand Down
4 changes: 3 additions & 1 deletion src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export interface ButtonProps extends BoxProps<'textbutton'> {
StrokeProps?: InstanceProps<UIStroke>;
}

export function Button(props: PropsWithChildren<ButtonProps>) {
export function Button(componentProps: PropsWithChildren<ButtonProps>) {
const props = { ...componentProps };

const strokeProps = props.StrokeProps;
props.StrokeProps = undefined;

Expand Down
59 changes: 59 additions & 0 deletions src/components/Input/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useState } from '@rbxts/react';

import { Box } from 'components/Box';
import { useTheme } from 'components/ThemeProvider';
import { mergeFunctions } from 'helpers';

import type { PropsWithChildren } from '@rbxts/react';
import type { BoxProps } from 'components/Box';

export interface InputProps extends BoxProps<'textbox'> {
Disabled?: boolean;
}

export function Input(componentProps: PropsWithChildren<InputProps>) {
const props = { ...componentProps };

const disabled = props.Disabled ?? false;
props.Disabled = undefined;

const theme = useTheme();
const [focused, setFocused] = useState<boolean>(false);

return (
<Box
AutomaticSize="Y"
BackgroundColor3={theme.overlay}
BorderColor3={focused && !disabled ? theme.accent : theme.borders}
BorderRadius={8}
BorderThickness={2}
ClearTextOnFocus={false}
ClipsDescendants
Component="textbox"
PaddingX={2}
PaddingY={1.5}
PlaceholderColor3={theme.primary60}
PlaceholderText="Placeholder"
Size={UDim2.fromScale(1, 0)}
Text=""
TextColor3={disabled ? theme.primary60 : theme.primary}
TextEditable={!disabled}
TextSize={12}
TextTransparency={disabled ? 0.25 : 0}
TextXAlignment={Enum.TextXAlignment.Left}
{...props}
Event={{
...props.Event,
Focused: mergeFunctions(() => setFocused(true), props.Event?.Focused),
FocusLost: mergeFunctions(
() => setFocused(false),
props.Event?.FocusLost,
),
}}
StrokeProps={{
Transparency: disabled ? 0.5 : 0,
...props.StrokeProps,
}}
/>
);
}
31 changes: 15 additions & 16 deletions src/components/SecondaryButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import React, { useEffect, useRef, useState } from '@rbxts/react';
import React, { useState } from '@rbxts/react';

import { Button, type ButtonProps } from 'components/Button';
import { useTheme } from 'components/ThemeProvider';
import { mergeProps } from 'helpers';
import { mergeFunctions, mergeProps } from 'helpers';

import type { InstanceProps, PropsWithChildren } from '@rbxts/react';
import type { PropsWithChildren } from '@rbxts/react';

export interface SecondaryButtonProps extends ButtonProps {
StrokeProps?: InstanceProps<UIStroke>;
}
export interface SecondaryButtonProps extends ButtonProps {}

export function SecondaryButton(
props: PropsWithChildren<SecondaryButtonProps>,
) {
const theme = useTheme();
const buttonRef = useRef<TextButton>(undefined);
const [hover, setHover] = useState<boolean>(false);

useEffect(() => {
const button = buttonRef.current;
if (!button) return;

button.MouseEnter.Connect(() => setHover(true));
button.MouseLeave.Connect(() => setHover(false));
});

return (
<Button
BackgroundColor3={theme.background}
Ref={buttonRef}
TextColor3={theme.primary}
{...props}
Event={{
...props.Event,
MouseEnter: mergeFunctions(
() => setHover(true),
props.Event?.MouseEnter,
),
MouseLeave: mergeFunctions(
() => setHover(false),
props.Event?.MouseLeave,
),
}}
StrokeProps={mergeProps(
{
Color: hover ? theme.primary : theme.primary60,
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from 'components/Box';
export * from 'components/Button';
export * from 'components/Input';
export * from 'components/SecondaryButton';
export * from 'components/Sheet';
export * from 'components/ThemeProvider';
Expand Down
52 changes: 52 additions & 0 deletions src/stories/Input.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from '@rbxts/react';
import ReactRoblox from '@rbxts/react-roblox';

import Center from './components/Center';
import { Box } from 'components/Box';
import { Input } from 'components/Input';
import { Sheet } from 'components/Sheet';
import { ThemeProvider } from 'components/ThemeProvider';
import { darkTheme } from 'themes';

import type { FunctionStory } from '@rbxts/ui-labs';

const story: FunctionStory = (target) => {
const element = (
<Center>
<Box Gap={2}>
<Sheet
AutomaticSize="Y"
Gap={2}
Padding={4}
Size={new UDim2(0, 300, 0, 0)}
>
<Input />

<Input Disabled PlaceholderText="Disabled" />
</Sheet>

<ThemeProvider theme={darkTheme}>
<Sheet
AutomaticSize="Y"
Gap={2}
Padding={4}
Size={new UDim2(0, 300, 0, 0)}
>
<Input />

<Input Disabled PlaceholderText="Disabled" />
</Sheet>
</ThemeProvider>
</Box>
</Center>
);

const root = ReactRoblox.createRoot(target);
root.render(element);

return () => {
root.unmount();
};
};

export = story;
2 changes: 1 addition & 1 deletion src/themes/dark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const darkTheme: Theme = {
background: Color3.fromRGB(24, 26, 27),
overlay: Color3.fromRGB(28, 30, 34),
borders: Color3.fromRGB(38, 41, 45),
accent: Color3.fromRGB(97, 0, 255),
accent: Color3.fromRGB(67, 147, 228),
font: Enum.Font.Arial,
fontBold: Enum.Font.ArialBold,
};
2 changes: 1 addition & 1 deletion src/themes/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const lightTheme: Theme = {
background: Color3.fromRGB(255, 255, 255),
borders: Color3.fromRGB(227, 229, 234),
overlay: Color3.fromRGB(250, 250, 250),
accent: Color3.fromRGB(0, 255, 224),
accent: Color3.fromRGB(67, 147, 228),
font: Enum.Font.Arial,
fontBold: Enum.Font.ArialBold,
};

0 comments on commit 535dcfb

Please sign in to comment.