Skip to content

Commit

Permalink
feat(ui): add box props and make button variant more interactive
Browse files Browse the repository at this point in the history
  • Loading branch information
spicyzboss committed Jan 4, 2024
1 parent 7b6fd27 commit c448bd9
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 16 deletions.
16 changes: 16 additions & 0 deletions libs/web/shared/ui/src/lib/box/box.props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,26 @@ export type BoxSize = (typeof BoxSizes)[number];
export type BoxWidth = BoxSize;
export type BoxHeight = BoxSize;

export const BoxAlignments = ['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'] as const;
export type BoxAlignment = (typeof BoxAlignments)[number];

export const BoxDirections = ['horizontal', 'vertical'] as const;
export type BoxDirection = (typeof BoxDirections)[number];

export const BoxGaps = ['none', '0.5', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '12', '16', '18', '20', '24'] as const;
export type BoxGap = (typeof BoxGaps)[number];

export const BoxBorders = ['none', 'full', '1', '2', '3', '4', '5', '6'] as const;
export type BoxBorder = (typeof BoxBorders)[number];

type NativeDiv = QwikIntrinsicElements['div'];

export interface BoxProps extends NativeDiv {
variant?: BoxVariant;
width?: BoxWidth;
height?: BoxHeight;
align?: BoxAlignment;
direction?: BoxDirection;
gap?: BoxGap;
border?: BoxBorder;
}
39 changes: 34 additions & 5 deletions libs/web/shared/ui/src/lib/box/box.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from 'storybook-framework-qwik';
import { Box } from './box';
import { BoxVariants, type BoxProps, BoxSizes } from './box.props';
import { BoxVariants, type BoxProps, BoxSizes, BoxAlignments, BoxDirections, BoxGaps, BoxBorders } from './box.props';

const meta = {
component: Box,
Expand All @@ -21,6 +21,26 @@ const meta = {
options: BoxSizes,
control: { type: 'inline-radio' },
},
align: {
description: 'Box alignment',
options: BoxAlignments,
control: { type: 'inline-radio' },
},
direction: {
description: 'Box direction',
options: BoxDirections,
control: { type: 'inline-radio' },
},
gap: {
description: 'Box gap',
options: BoxGaps,
control: { type: 'inline-radio' },
},
border: {
description: 'Box border',
options: BoxBorders,
control: { type: 'inline-radio' },
},
},
} satisfies Meta<BoxProps>;

Expand All @@ -30,13 +50,22 @@ export default meta;

export const Primary = {
args: {
variant: 'secondary',
width: '10',
height: '10',
variant: 'surface',
width: '24',
height: '24',
align: 'center',
direction: 'vertical',
gap: '0.5',
},
render: (props) => (
<div class="h-dvh">
<Box {...props} />
<Box {...props}>
<p>Mercury</p>
<p>Venus</p>
<p>Earth</p>
<p>Mars</p>
<p>Jupyter</p>
</Box>
</div>
),
} satisfies Story;
54 changes: 51 additions & 3 deletions libs/web/shared/ui/src/lib/box/box.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Slot, component$ } from '@builder.io/qwik';
import type { BoxProps, BoxVariant, BoxWidth } from './box.props';
import type { BoxAlignment, BoxBorder, BoxDirection, BoxGap, BoxProps, BoxVariant, BoxWidth } from './box.props';

export const Box = component$<BoxProps>((props) => {
const { variant = 'surface', width = 'full', height = 'full', ...rest } = props;
const { variant = 'surface', width = 'full', height = 'full', align = 'left', direction = 'vertical', gap = 'none', border = 'none', ...rest } = props;

const Variants = {
primary: 'bg-primary-container text-primary-container-on',
Expand Down Expand Up @@ -54,8 +54,56 @@ export const Box = component$<BoxProps>((props) => {
'24': 'h-96',
} satisfies { [K in BoxWidth]: string };

const Alignments = {
'top-left': 'items-start justify-start',
'top': direction === 'horizontal' ? 'items-start justify-center' : 'items-center justify-start',
'top-right': direction === 'horizontal' ? 'items-start justify-end' : 'justify-start items-end',
'left': direction === 'horizontal' ? 'items-center justify-start' : 'justify-center items-start',
'center': 'items-center justify-center',
'right': direction === 'horizontal' ? 'items-center justify-end' : 'justify-center items-end',
'bottom-left': direction === 'horizontal' ? 'items-end justify-start' : 'items-start justify-end',
'bottom': direction === 'horizontal' ? 'items-end justify-center' : 'justify-end items-center',
'bottom-right': 'items-end justify-end',
} satisfies { [K in BoxAlignment]: string };

const Directions = {
horizontal: 'flex-row',
vertical: 'flex-col',
} satisfies { [K in BoxDirection]: string };

const Gaps = {
none: 'gap-0',
'0.5': 'gap-2',
'1': 'gap-4',
'2': 'gap-8',
'3': 'gap-12',
'4': 'gap-16',
'5': 'gap-20',
'6': 'gap-24',
'7': 'gap-28',
'8': 'gap-32',
'9': 'gap-36',
'10': 'gap-40',
'12': 'gap-48',
'16': 'gap-64',
'18': 'gap-72',
'20': 'gap-80',
'24': 'gap-96',
} satisfies { [K in BoxGap]: string };

const Borders = {
none: 'rounded-none',
full: 'rounded-full',
'1': 'rounded',
'2': 'rounded-md',
'3': 'rounded-lg',
'4': 'rounded-xl',
'5': 'rounded-2xl',
'6': 'rounded-3xl',
} satisfies { [K in BoxBorder]: string };

return (
<div {...rest} class={[Variants[variant], Widths[width], Heights[height]]}>
<div {...rest} class={['flex', Directions[direction], Variants[variant], Widths[width], Heights[height], Alignments[align], Gaps[gap], Borders[border]]}>
<Slot />
</div>
);
Expand Down
40 changes: 32 additions & 8 deletions libs/web/shared/ui/src/lib/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,38 @@ import type { ButtonProps, ButtonShape, ButtonSize, ButtonVariant } from './butt
export const Button = component$<ButtonProps>((props) => {
const { size = 'base', variant = 'primary', rounded = false, disabled = false, ...rest } = props;

const currentVariant = disabled ? 'disabled' : variant;
const cursor = disabled ? 'cursor-default' : 'cursor-pointer';
const shape = rounded ? 'rounded' : 'base';

const Sizes = {
small: 'px-2 py-1 text-sm',
base: 'px-4 py-2 text-lg',
large: 'px-6 py-4 text-xl',
} satisfies { [K in ButtonSize]: string };

const Variants = {
primary: 'bg-primary text-primary-on',
secondary: 'bg-secondary text-secondary-on',
tertiary: 'bg-tertiary text-tertiary-on',
error: 'bg-error text-error-on',
disabled: 'bg-surface-on/[.12] text-surface-on/[.38] cursor-default',
const ContainerVariants = {
primary: 'bg-primary',
secondary: 'ring-1 ring-outline',
tertiary: 'bg-tertiary',
error: 'bg-error',
disabled: 'bg-surface-on/[.12]',
} satisfies { [K in ButtonVariant | 'disabled']: string };

const LabelVariants = {
primary: 'text-primary-on',
secondary: 'text-primary',
tertiary: 'text-tertiary-on',
error: 'text-error-on',
disabled: 'text-surface-on/[.38]',
} satisfies { [K in ButtonVariant | 'disabled']: string };

const StateLayerVariants = {
primary: 'group-hover/button:bg-surface-variant-on/[.08] group-active/button:bg-surface-variant-on/[.10] group-focus/button:bg-surface-variant-on/[.10]',
secondary: 'group-hover/button:bg-primary/[.08] group-active/button:bg-primary/[.10] group-focus/button:bg-primary/[.10]',
tertiary: 'bg-tertiary-on/[.08]',
error: 'bg-error-on/[.08]',
disabled: 'bg-surface-on/[.12]',
} satisfies { [K in ButtonVariant | 'disabled']: string };

const Shapes = {
Expand All @@ -24,8 +44,12 @@ export const Button = component$<ButtonProps>((props) => {
} satisfies { [K in ButtonShape]: string };

return (
<button {...rest} class={[Sizes[size], Variants[disabled ? 'disabled' : variant], Shapes[rounded ? 'rounded' : 'base']]}>
<Slot />
<button {...rest} class={['relative flex group/button justify-center items-center', Sizes[size], Shapes[shape], cursor]}>
<div class={['absolute inset-0 rounded-[inherit]', ContainerVariants[currentVariant]]} />
<div class={['absolute inset-0 rounded-[inherit]', StateLayerVariants[currentVariant]]} />
<span class={['z-10', LabelVariants[currentVariant]]}>
<Slot />
</span>
</button>
);
});

0 comments on commit c448bd9

Please sign in to comment.